]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
import-common: rework import_mangle_os_tree() to operate based on fd to tree
authorLennart Poettering <lennart@poettering.net>
Mon, 25 Aug 2025 09:19:02 +0000 (11:19 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 30 Oct 2025 21:56:19 +0000 (22:56 +0100)
src/import/import-common.c
src/import/import-common.h
src/import/import-tar.c
src/import/pull-tar.c

index eaa68fae5a29bf4f2fd157e2e68cf27cb2912f50..70b3c3be023c6f8c86dd8f449e7c2c8ca83d5d5e 100644 (file)
@@ -148,19 +148,24 @@ int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
         return TAKE_FD(pipefd[0]);
 }
 
-int import_mangle_os_tree(const char *path) {
+int import_mangle_os_tree_fd(int tree_fd) {
         _cleanup_free_ char *child = NULL, *t = NULL, *joined = NULL;
         _cleanup_closedir_ DIR *d = NULL, *cd = NULL;
         struct dirent *dent;
         struct stat st;
         int r;
 
-        assert(path);
+        assert(tree_fd >= 0);
 
         /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
          * recognize this, and move the tree one level up. */
 
-        r = path_is_os_tree(path);
+        _cleanup_free_ char *path = NULL;
+        r = fd_get_path(tree_fd, &path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine path of fd: %m");
+
+        r = fd_is_os_tree(tree_fd);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", path);
         if (r > 0) {
@@ -170,9 +175,9 @@ int import_mangle_os_tree(const char *path) {
 
         log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path);
 
-        d = opendir(path);
+        d = xopendirat(tree_fd, /* path= */ NULL, /* flags= */ 0);
         if (!d)
-                return log_error_errno(r, "Failed to open directory '%s': %m", path);
+                return log_error_errno(errno, "Failed to open directory '%s': %m", path);
 
         errno = 0;
         dent = readdir_no_dot(d);
@@ -191,29 +196,29 @@ int import_mangle_os_tree(const char *path) {
         errno = 0;
         dent = readdir_no_dot(d);
         if (dent) {
-                if (errno != 0)
-                        return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
-
                 log_debug("Directory '%s' does not look like an OS tree, and has multiple children, leaving as it is.", path);
                 return 0;
+        } else if (errno != 0)
+                return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
+
+        _cleanup_close_ int child_fd = openat(dirfd(d), child, O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW|O_NONBLOCK);
+        if (child_fd < 0) {
+                if (IN_SET(errno, ENOTDIR, ELOOP)) {
+                        log_debug_errno(errno, "Child '%s' of directory '%s' is not a directory, leaving things as they are.", child, path);
+                        return 0;
+                }
+
+                return log_debug_errno(errno, "Failed to open file '%s/%s': %m", path, child);
         }
 
-        if (fstatat(dirfd(d), child, &st, AT_SYMLINK_NOFOLLOW) < 0)
+        if (fstat(child_fd, &st) < 0)
                 return log_debug_errno(errno, "Failed to stat file '%s/%s': %m", path, child);
-        r = stat_verify_directory(&st);
-        if (r < 0) {
-                log_debug_errno(r, "Child '%s' of directory '%s' is not a directory, leaving things as they are.", child, path);
-                return 0;
-        }
 
         joined = path_join(path, child);
         if (!joined)
                 return log_oom();
-        r = path_is_os_tree(joined);
-        if (r == -ENOTDIR) {
-                log_debug("Directory '%s' does not look like an OS tree, and contains a single regular file only, leaving as it is.", path);
-                return 0;
-        }
+
+        r = fd_is_os_tree(child_fd);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", joined);
         if (r == 0) {
@@ -230,7 +235,7 @@ int import_mangle_os_tree(const char *path) {
          *
          * Let's now rearrange things, moving everything in the inner directory one level up */
 
-        cd = xopendirat(dirfd(d), child, O_NOFOLLOW);
+        cd = take_fdopendir(&child_fd);
         if (!cd)
                 return log_error_errno(errno, "Can't open directory '%s': %m", joined);
 
@@ -238,7 +243,7 @@ int import_mangle_os_tree(const char *path) {
 
         /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
          * safely moved up and won't collide with the name. */
-        r = tempfn_random(child, NULL, &t);
+        r = tempfn_random(child, /* extra= */ NULL, &t);
         if (r < 0)
                 return log_oom();
         r = rename_noreplace(dirfd(d), child, dirfd(d), t);
@@ -259,17 +264,26 @@ int import_mangle_os_tree(const char *path) {
 
         r = futimens(dirfd(d), (struct timespec[2]) { st.st_atim, st.st_mtim });
         if (r < 0)
-                log_debug_errno(r, "Failed to adjust top-level timestamps '%s', ignoring: %m", path);
+                log_debug_errno(errno, "Failed to adjust top-level timestamps '%s', ignoring: %m", path);
 
         r = fchmod_and_chown(dirfd(d), st.st_mode, st.st_uid, st.st_gid);
         if (r < 0)
                 return log_error_errno(r, "Failed to adjust top-level directory mode/ownership '%s': %m", path);
 
         log_info("Successfully rearranged OS tree.");
-
         return 0;
 }
 
+int import_mangle_os_tree(const char *path) {
+        assert(path);
+
+        _cleanup_close_ int fd = open(path, O_DIRECTORY|O_CLOEXEC|O_PATH);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to open '%s': %m", path);
+
+        return import_mangle_os_tree_fd(fd);
+}
+
 bool import_validate_local(const char *name, ImportFlags flags) {
 
         /* By default we insist on a valid hostname for naming images. But optionally we relax that, in which
index a922280d0ab0c9bdfdea4c3edfddee4ee0f3ed09..289b83b4e6bed4341ddfa4eec09470372929bb89 100644 (file)
@@ -37,6 +37,7 @@ typedef enum ImportFlags {
 int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid);
 int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
 
+int import_mangle_os_tree_fd(int tree_fd);
 int import_mangle_os_tree(const char *path);
 
 bool import_validate_local(const char *name, ImportFlags flags);
index 85fbf5abd149b1624ff30db0738cfa29aafce402..e30be005fc7ea9242633a45d6eb6bb80227eaff9 100644 (file)
@@ -199,7 +199,7 @@ static int tar_import_finish(TarImport *i) {
 
         assert_se(d = i->temp_path ?: i->local);
 
-        r = import_mangle_os_tree(d);
+        r = import_mangle_os_tree_fd(i->tree_fd);
         if (r < 0)
                 return r;
 
index c9b9facaa56fda1a70d364e7c9d12be199e16077..65f88e7a8870265cfbcc4de4ffa02029b09eb7e4 100644 (file)
@@ -497,7 +497,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
 
                 tar_pull_report_progress(i, TAR_FINALIZING);
 
-                r = import_mangle_os_tree(i->local);
+                r = import_mangle_os_tree_fd(i->tree_fd);
                 if (r < 0)
                         goto finish;
 
@@ -523,7 +523,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
 
                         tar_pull_report_progress(i, TAR_FINALIZING);
 
-                        r = import_mangle_os_tree(i->temp_path);
+                        r = import_mangle_os_tree_fd(i->tree_fd);
                         if (r < 0)
                                 goto finish;