]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect-image: Generalize foreign tree logic from import
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 15 Dec 2025 14:08:15 +0000 (15:08 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 21 Jan 2026 11:03:07 +0000 (12:03 +0100)
src/import/import-common.c
src/import/import-common.h
src/import/pull-tar.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/dissect-image.h

index 7be3ffeecce951537e8a91c05cc1bf8e3c02ee8e..0c448355fdcb41d172c989c2fda7693cb69b25fd 100644 (file)
@@ -7,7 +7,6 @@
 #include "sd-event.h"
 
 #include "capability-util.h"
-#include "copy.h"
 #include "dirent-util.h"
 #include "dissect-image.h"
 #include "fd-util.h"
@@ -383,64 +382,7 @@ int import_make_foreign_userns(int *userns_fd) {
         return 1;
 }
 
-int import_copy_foreign(
-                int source_fd,
-                int target_fd,
-                int *userns_fd) {
-
-        int r;
-
-        assert(source_fd >= 0);
-        assert(target_fd >= 0);
-        assert(userns_fd);
-
-        /* Copies dir referenced by source_fd into dir referenced by source_fd, moves to the specified userns
-         * for that (allocated if needed), which should be foreign UID range */
-
-        r = import_make_foreign_userns(userns_fd);
-        if (r < 0)
-                return r;
-
-        r = pidref_safe_fork_full(
-                        "copy-tree",
-                        /* stdio_fds= */ NULL,
-                        (int[]) { *userns_fd, source_fd, target_fd }, 3,
-                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
-                        /* ret= */ NULL);
-        if (r < 0)
-                return r;
-        if (r == 0) {
-                r = namespace_enter(
-                                /* pidns_fd= */ -EBADF,
-                                /* mntns_fd= */ -EBADF,
-                                /* netns_fd= */ -EBADF,
-                                *userns_fd,
-                                /* root_fd= */ -EBADF);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to join user namespace: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                r = copy_tree_at(
-                                source_fd, /* from= */ NULL,
-                                target_fd, /* to= */ NULL,
-                                /* override_uid= */ UID_INVALID,
-                                /* override_gid= */ GID_INVALID,
-                                COPY_REFLINK|COPY_HARDLINKS|COPY_MERGE_EMPTY|COPY_MERGE_APPLY_STAT|COPY_SAME_MOUNT|COPY_ALL_XATTRS,
-                                /* denylist= */ NULL,
-                                /* subvolumes= */ NULL);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to copy tree: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                _exit(EXIT_SUCCESS);
-        }
-
-        return 0;
-}
-
-int import_remove_tree_foreign(const char *path, int *userns_fd) {
+int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags) {
         int r;
 
         assert(path);
@@ -450,62 +392,9 @@ int import_remove_tree_foreign(const char *path, int *userns_fd) {
         if (r < 0)
                 return r;
 
-        _cleanup_close_ int tree_fd = -EBADF;
-        r = mountfsd_mount_directory(
-                        path,
-                        *userns_fd,
-                        DISSECT_IMAGE_FOREIGN_UID,
-                        &tree_fd);
-        if (r < 0)
-                return r;
-
-        r = pidref_safe_fork_full(
-                        "rm-tree",
-                        /* stdio_fds= */ NULL,
-                        (int[]) { *userns_fd, tree_fd }, 2,
-                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
-                        /* ret= */ NULL);
-        if (r < 0)
-                return r;
-        if (r == 0) {
-                /* child */
-
-                r = namespace_enter(
-                                /* pidns_fd= */ -EBADF,
-                                /* mntns_fd= */ -EBADF,
-                                /* netns_fd= */ -EBADF,
-                                *userns_fd,
-                                /* root_fd= */ -EBADF);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to join user namespace: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                _cleanup_close_ int dfd = fd_reopen(tree_fd, O_DIRECTORY|O_CLOEXEC);
-                if (dfd < 0) {
-                        log_error_errno(r, "Failed to reopen tree fd: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                r = rm_rf_children(dfd, REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, /* root_dev= */ NULL);
-                if (r < 0)
-                        log_warning_errno(r, "Failed to empty '%s' directory in foreign UID mode, ignoring: %m", path);
-
-                _exit(EXIT_SUCCESS);
-        }
-
-        return 0;
-}
-
-int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags) {
-        int r;
-
-        assert(path);
-        assert(userns_fd);
-
         /* Try the userns dance first, to remove foreign UID range owned trees */
         if (FLAGS_SET(flags, IMPORT_FOREIGN_UID))
-                (void) import_remove_tree_foreign(path, userns_fd);
+                (void) remove_tree_foreign(path, *userns_fd);
 
         r = rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_MISSING_OK|REMOVE_CHMOD);
         if (r < 0)
index 8fab3f380bba5e1abe0308fbbc18fa7504b30129..b568906a8a6c6094eb6d6a5ce116c77d2f3c6b71 100644 (file)
@@ -47,9 +47,6 @@ int import_allocate_event_with_signals(sd_event **ret);
 
 int import_make_foreign_userns(int *userns_fd);
 
-int import_copy_foreign(int source_fd, int target_fd, int *userns_fd);
-
-int import_remove_tree_foreign(const char *path, int *userns_fd);
 int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags);
 
 #define IMPORT_BUFFER_SIZE (128U*1024U)
index 5171f8e2dfbebc714bd29b2a6fa5eea640de2d6a..b0cafd95f4903367e6702c659a8478a0df48738b 100644 (file)
@@ -309,7 +309,7 @@ static int tar_pull_make_local_copy(TarPull *p) {
                         if (r < 0)
                                 return r;
 
-                        r = import_copy_foreign(p->tree_fd, copy_fd, &p->userns_fd);
+                        r = copy_tree_at_foreign(p->tree_fd, copy_fd, p->userns_fd);
                         if (r < 0)
                                 return r;
                 } else {
index 69163f8878fa90e5c7c5f7f0f8176c26232bf6ed..2935cbd97e736dc7f5012b9b5a5a01bb3525775a 100644 (file)
@@ -1469,7 +1469,7 @@ static int get_pool_directory(
         return 0;
 }
 
-static int unpriviled_clone(Image *i, const char *new_path) {
+static int unprivileged_clone(Image *i, const char *new_path) {
         int r;
 
         assert(i);
@@ -1510,45 +1510,7 @@ static int unpriviled_clone(Image *i, const char *new_path) {
                 return r;
 
         /* Fork off child that moves into userns and does the copying */
-        r = pidref_safe_fork_full(
-                        "clone-tree",
-                        /* stdio_fds= */ NULL,
-                        (int[]) { userns_fd, tree_fd, target_fd }, 3,
-                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_REOPEN_LOG,
-                        /* ret= */ NULL);
-        if (r < 0)
-                return log_debug_errno(r, "Process that was supposed to clone tree failed: %m");
-        if (r == 0) {
-                /* child */
-
-                r = namespace_enter(
-                                /* pidns_fd= */ -EBADF,
-                                /* mntns_fd= */ -EBADF,
-                                /* netns_fd= */ -EBADF,
-                                userns_fd,
-                                /* root_fd= */ -EBADF);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to join user namespace: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                r = copy_tree_at(
-                                tree_fd, /* from= */ NULL,
-                                target_fd, /* to= */ NULL,
-                                /* override_uid= */ UID_INVALID,
-                                /* override_gid= */ GID_INVALID,
-                                COPY_REFLINK|COPY_HARDLINKS|COPY_MERGE_EMPTY|COPY_MERGE_APPLY_STAT|COPY_SAME_MOUNT|COPY_ALL_XATTRS,
-                                /* denylist= */ NULL,
-                                /* subvolumes= */ NULL);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to copy clone tree: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                _exit(EXIT_SUCCESS);
-        }
-
-        return 0;
+        return copy_tree_at_foreign(tree_fd, target_fd, userns_fd);
 }
 
 int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope) {
@@ -1596,7 +1558,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
                         return r;
 
                 if (i->foreign_uid_owned)
-                        r = unpriviled_clone(i, new_path);
+                        r = unprivileged_clone(i, new_path);
                 else {
                         r = btrfs_subvol_snapshot_at(
                                         AT_FDCWD, i->path,
index c8c4bc561d4f2e1391bc801bf8310ca6099d8557..27c93198ab8285a44fc24b05cdc8e9fea6e9f0ca 100644 (file)
@@ -63,6 +63,7 @@
 #include "proc-cmdline.h"
 #include "process-util.h"
 #include "resize-fs.h"
+#include "rm-rf.h"
 #include "runtime-scope.h"
 #include "siphash24.h"
 #include "stat-util.h"
@@ -5491,3 +5492,105 @@ int mountfsd_make_directory(
 
         return mountfsd_make_directory_fd(fd, dirname, mode, flags, ret_directory_fd);
 }
+
+int copy_tree_at_foreign(int source_fd, int target_fd, int userns_fd) {
+        int r;
+
+        assert(source_fd >= 0);
+        assert(target_fd >= 0);
+        assert(userns_fd >= 0);
+
+        /* Copies dir referenced by source_fd into dir referenced by source_fd, moves to the specified userns
+         * for that, which should be foreign UID range */
+
+        r = pidref_safe_fork_full(
+                        "copy-tree",
+                        /* stdio_fds= */ NULL,
+                        (int[]) { userns_fd, source_fd, target_fd }, 3,
+                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_REOPEN_LOG,
+                        /* ret= */ NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                r = namespace_enter(
+                                /* pidns_fd= */ -EBADF,
+                                /* mntns_fd= */ -EBADF,
+                                /* netns_fd= */ -EBADF,
+                                userns_fd,
+                                /* root_fd= */ -EBADF);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to join user namespace: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = copy_tree_at(
+                                source_fd, /* from= */ NULL,
+                                target_fd, /* to= */ NULL,
+                                /* override_uid= */ UID_INVALID,
+                                /* override_gid= */ GID_INVALID,
+                                COPY_REFLINK|COPY_HARDLINKS|COPY_MERGE_EMPTY|COPY_MERGE_APPLY_STAT|COPY_SAME_MOUNT|COPY_ALL_XATTRS,
+                                /* denylist= */ NULL,
+                                /* subvolumes= */ NULL);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to copy tree: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        return 0;
+}
+
+int remove_tree_foreign(const char *path, int userns_fd) {
+        int r;
+
+        assert(path);
+        assert(userns_fd >= 0);
+
+        _cleanup_close_ int tree_fd = -EBADF;
+        r = mountfsd_mount_directory(
+                        path,
+                        userns_fd,
+                        DISSECT_IMAGE_FOREIGN_UID,
+                        &tree_fd);
+        if (r < 0)
+                return r;
+
+        r = pidref_safe_fork_full(
+                        "rm-tree",
+                        /* stdio_fds= */ NULL,
+                        (int[]) { userns_fd, tree_fd }, 2,
+                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
+                        /* ret= */ NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* child */
+
+                r = namespace_enter(
+                                /* pidns_fd= */ -EBADF,
+                                /* mntns_fd= */ -EBADF,
+                                /* netns_fd= */ -EBADF,
+                                userns_fd,
+                                /* root_fd= */ -EBADF);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to join user namespace: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                _cleanup_close_ int dfd = fd_reopen(tree_fd, O_DIRECTORY|O_CLOEXEC);
+                if (dfd < 0) {
+                        log_error_errno(r, "Failed to reopen tree fd: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = rm_rf_children(dfd, REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, /* root_dev= */ NULL);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to empty '%s' directory in foreign UID mode, ignoring: %m", path);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        return 0;
+}
index 55c291c4abae9a3e7f35262655bdffcd7a064a05..dfafa623f9c542d88d92193a641ad1aceb184187 100644 (file)
@@ -275,3 +275,6 @@ int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags
 
 int mountfsd_make_directory_fd(int parent_fd, const char *name, mode_t mode, DissectImageFlags flags, int *ret_directory_fd);
 int mountfsd_make_directory(const char *path, mode_t mode, DissectImageFlags flags, int *ret_directory_fd);
+
+int copy_tree_at_foreign(int source_fd, int target_fd, int userns_fd);
+int remove_tree_foreign(const char *path, int userns_fd);