dissect-image: Generalize foreign tree logic from import
This commit is contained in:
+5
-116
@@ -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,129 +382,19 @@ 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 r;
|
||||
|
||||
assert(path);
|
||||
assert(userns_fd);
|
||||
|
||||
r = import_make_foreign_userns(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);
|
||||
|
||||
r = import_make_foreign_userns(userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user