From: Daan De Meyer Date: Mon, 15 Dec 2025 14:08:15 +0000 (+0100) Subject: dissect-image: Generalize foreign tree logic from import X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec1ab076050065e9518cf48273b76243aac2f1c7;p=thirdparty%2Fsystemd.git dissect-image: Generalize foreign tree logic from import --- diff --git a/src/import/import-common.c b/src/import/import-common.c index 7be3ffeecce..0c448355fdc 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -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) diff --git a/src/import/import-common.h b/src/import/import-common.h index 8fab3f380bb..b568906a8a6 100644 --- a/src/import/import-common.h +++ b/src/import/import-common.h @@ -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) diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 5171f8e2dfb..b0cafd95f49 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -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 { diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 69163f8878f..2935cbd97e7 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -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, diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index c8c4bc561d4..27c93198ab8 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -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; +} diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 55c291c4aba..dfafa623f9c 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -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);