Create a directory (and all parents) and optionally chown it. Three cases: 1. do_chown=true (root, normal mode): create with fs::create_directories, then chown. 2. do_chown=false, running as root (CLICKHOUSE_DO_NOT_CHOWN=1): delegate to `clickhouse su UID:GID` so the directory is created as the target user. This handles NFS mounts where root is mapped to nobody. 3. do_chown=false, running as non-
| 291 | /// This handles NFS mounts where root is mapped to nobody. |
| 292 | /// 3. do_chown=false, running as non-root: create directly — we are already the target user. |
| 293 | bool createDirectoryAndChown(const std::string & dir, uid_t uid, gid_t gid, bool do_chown) |
| 294 | { |
| 295 | if (dir.empty()) |
| 296 | return true; |
| 297 | |
| 298 | if (do_chown) |
| 299 | { |
| 300 | std::error_code ec; |
| 301 | fs::create_directories(dir, ec); |
| 302 | if (ec) |
| 303 | { |
| 304 | std::cerr << "docker-init: couldn't create directory " << dir << ": " << ec.message() << "\n"; |
| 305 | return false; |
| 306 | } |
| 307 | |
| 308 | /// Chown only if the owner needs to change (avoids slow recursive chown on already-correct dirs). |
| 309 | struct stat st{}; |
| 310 | if (stat(dir.c_str(), &st) == 0 && (st.st_uid != uid || st.st_gid != gid)) |
| 311 | recursiveChown(dir, uid, gid); |
| 312 | |
| 313 | return true; |
| 314 | } |
| 315 | |
| 316 | if (getuid() == 0) |
| 317 | { |
| 318 | /// Running as root with CLICKHOUSE_DO_NOT_CHOWN or CLICKHOUSE_RUN_AS_ROOT. |
| 319 | /// On NFS mounts root may be remapped to nobody, so create the directory as |
| 320 | /// the target user. Fork a child that drops privileges before calling |
| 321 | /// fs::create_directories — distroless has no mkdir binary. |
| 322 | pid_t pid = fork(); |
| 323 | if (pid < 0) |
| 324 | { |
| 325 | std::cerr << "docker-init: fork failed for directory creation: " << strerror(errno) << "\n"; // NOLINT(concurrency-mt-unsafe) |
| 326 | return false; |
| 327 | } |
| 328 | if (pid == 0) |
| 329 | { |
| 330 | if (setgroups(0, nullptr) < 0 || setgid(gid) < 0 || setuid(uid) < 0) |
| 331 | _exit(1); |
| 332 | std::error_code ec; |
| 333 | fs::create_directories(dir, ec); |
| 334 | _exit(ec ? 1 : 0); |
| 335 | } |
| 336 | int status = 0; |
| 337 | while (waitpid(pid, &status, 0) < 0 && errno == EINTR) {} |
| 338 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) |
| 339 | { |
| 340 | /// Fallback: try direct creation (works when root is not remapped). |
| 341 | std::error_code ec; |
| 342 | fs::create_directories(dir, ec); |
| 343 | if (ec) |
| 344 | { |
| 345 | std::cerr << "docker-init: couldn't create directory " << dir << ": " << ec.message() << "\n"; |
| 346 | return false; |
| 347 | } |
| 348 | } |
| 349 | return true; |
| 350 | } |
no test coverage detected