From: Ivan Kruglov Date: Tue, 17 Dec 2024 11:25:34 +0000 (+0100) Subject: machine: introduce machine_copy_from_to() helper X-Git-Tag: v258-rc1~1692^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9af9d66184caf565805d0cabc1dd99de5469931e;p=thirdparty%2Fsystemd.git machine: introduce machine_copy_from_to() helper --- diff --git a/src/machine/machine.c b/src/machine/machine.c index d94805c4183..1e6a5e0606b 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -24,6 +24,7 @@ #include "machine-dbus.h" #include "machine.h" #include "mkdir-label.h" +#include "namespace-util.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -978,6 +979,125 @@ char** machine_default_shell_args(const char *user) { return TAKE_PTR(args); } +int machine_copy_from_to( + Manager *manager, + Machine *machine, + const char *host_path, + const char *container_path, + bool copy_from_container, + CopyFlags copy_flags, + Operation **ret) { + + _cleanup_close_ int host_fd = -EBADF, mntns_fd = -EBADF; + _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR; + _cleanup_free_ char *host_basename = NULL, *container_basename = NULL; + uid_t uid_shift; + pid_t child; + int r; + + assert(manager); + assert(machine); + assert(ret); + + if (isempty(host_path) || isempty(container_path)) + return -EINVAL; + + r = path_extract_filename(host_path, &host_basename); + if (r < 0) + return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", host_path); + + r = path_extract_filename(container_path, &container_basename); + if (r < 0) + return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path); + + host_fd = open_parent(host_path, O_CLOEXEC, 0); + if (host_fd < 0) + return log_debug_errno(host_fd, "Failed to open host directory '%s': %m", host_path); + + r = machine_get_uid_shift(machine, &uid_shift); + if (r < 0) + return log_debug_errno(r, "Failed to get UID shift of machine '%s': %m", machine->name); + + r = pidref_namespace_open(&machine->leader, + /* ret_pidns_fd = */ NULL, + &mntns_fd, + /* ret_netns_fd = */ NULL, + /* ret_userns_fd = */ NULL, + /* ret_root_fd = */ NULL); + if (r < 0) + return log_debug_errno(r, "Failed to open mount namespace of machine '%s': %m", machine->name); + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return log_debug_errno(errno, "Failed to create pipe: %m"); + + r = namespace_fork("(sd-copyns)", + "(sd-copy)", + /* except_fds = */ NULL, + /* n_except_fds = */ 0, + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, + /* pidns_fd = */ -EBADF, + mntns_fd, + /* netns_fd = */ -EBADF, + /* userns_fd = */ -EBADF, + /* root_fd = */ -EBADF, + &child); + if (r < 0) + return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name); + if (r == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + _cleanup_close_ int container_fd = -EBADF; + container_fd = open_parent(container_path, O_CLOEXEC, 0); + if (container_fd < 0) { + log_debug_errno(container_fd, "Failed to open destination directory: %m"); + report_errno_and_exit(errno_pipe_fd[1], container_fd); + } + + /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to + * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy + * the UID/GIDs as they are. */ + if (copy_from_container) + r = copy_tree_at( + container_fd, + container_basename, + host_fd, + host_basename, + uid_shift == 0 ? UID_INVALID : 0, + uid_shift == 0 ? GID_INVALID : 0, + copy_flags, + /* denylist = */ NULL, + /* subvolumes = */ NULL); + else + r = copy_tree_at( + host_fd, + host_basename, + container_fd, + container_basename, + uid_shift == 0 ? UID_INVALID : uid_shift, + uid_shift == 0 ? GID_INVALID : uid_shift, + copy_flags, + /* denylist = */ NULL, + /* subvolumes = */ NULL); + if (r < 0) + log_debug_errno(r, "Failed to copy tree: %m"); + + report_errno_and_exit(errno_pipe_fd[1], r); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + Operation *operation; + r = operation_new(manager, machine, child, errno_pipe_fd[0], &operation); + if (r < 0) { + sigkill_wait(child); + return r; + } + + TAKE_FD(errno_pipe_fd[0]); + *ret = operation; + return 0; +} + void machine_release_unit(Machine *m) { assert(m); diff --git a/src/machine/machine.h b/src/machine/machine.h index 30e4a6a4663..c538c671fff 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -4,6 +4,7 @@ typedef struct Machine Machine; typedef enum KillWhom KillWhom; +#include "copy.h" #include "list.h" #include "machined.h" #include "operation.h" @@ -107,6 +108,15 @@ int machine_start_shell(Machine *m, int ptmx_fd, const char *ptmx_name, const ch #define machine_default_shell_path() ("/bin/sh") char** machine_default_shell_args(const char *user); +int machine_copy_from_to( + Manager *manager, + Machine *machine, + const char *host_path, + const char *container_path, + bool copy_from_container, + CopyFlags copy_flags, + Operation **ret); + int machine_get_uid_shift(Machine *m, uid_t *ret); int machine_owns_uid(Machine *m, uid_t host_uid, uid_t *ret_internal_uid);