]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
machine: introduce machine_copy_from_to() helper
authorIvan Kruglov <mail@ikruglov.com>
Tue, 17 Dec 2024 11:25:34 +0000 (12:25 +0100)
committerIvan Kruglov <mail@ikruglov.com>
Mon, 6 Jan 2025 13:51:57 +0000 (14:51 +0100)
src/machine/machine.c
src/machine/machine.h

index d94805c418351659ff6f56141a4576ba01f8a2b4..1e6a5e0606b3d02ec095be7e25288e6e590fce8b 100644 (file)
@@ -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);
 
index 30e4a6a4663974326c10cbb9c22dbb23627963fc..c538c671fffbd2760ffc07360a7bd0484386fb8d 100644 (file)
@@ -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);