]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
export-tar: port to common libarchive tar generation code 39405/head
authorLennart Poettering <lennart@poettering.net>
Thu, 21 Aug 2025 09:26:17 +0000 (11:26 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 29 Oct 2025 09:09:44 +0000 (10:09 +0100)
src/import/export-tar.c
src/import/export-tar.h
src/import/export.c
src/import/import-common.c
src/import/import-common.h
src/shared/tar-util.c

index afad246042729ff5cbcf7963065298d9e82db363..e945abe8c37ad1565b43b06581292b418dc96cf6 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "alloc-util.h"
 #include "btrfs-util.h"
+#include "dissect-image.h"
 #include "export-tar.h"
 #include "fd-util.h"
 #include "format-util.h"
@@ -27,11 +28,15 @@ typedef struct TarExport {
         TarExportFinished on_finished;
         void *userdata;
 
+        ImportFlags flags;
+
         char *path;
         char *temp_path;
 
-        int output_fd;
-        int tar_fd;
+        int output_fd; /* compressed tar file in the fs */
+        int tar_fd;    /* uncompressed tar stream coming from child doing the libarchive loop */
+        int tree_fd;   /* directory fd of the tree to set up */
+        int userns_fd;
 
         ImportCompress compress;
 
@@ -98,6 +103,8 @@ int tar_export_new(
         *e = (TarExport) {
                 .output_fd = -EBADF,
                 .tar_fd = -EBADF,
+                .tree_fd = -EBADF,
+                .userns_fd = -EBADF,
                 .on_finished = on_finished,
                 .userdata = userdata,
                 .quota_referenced = UINT64_MAX,
@@ -271,7 +278,13 @@ static int tar_export_on_defer(sd_event_source *s, void *userdata) {
         return tar_export_process(i);
 }
 
-int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) {
+int tar_export_start(
+                TarExport *e,
+                const char *path,
+                int fd,
+                ImportCompressType compress,
+                ImportFlags flags) {
+
         _cleanup_close_ int sfd = -EBADF;
         int r;
 
@@ -299,6 +312,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
         if (r < 0)
                 return r;
 
+        e->flags = flags;
         e->quota_referenced = UINT64_MAX;
 
         if (btrfs_might_be_subvol(&e->st)) {
@@ -337,7 +351,33 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
         if (r < 0)
                 return r;
 
-        e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid);
+        const char *p = e->temp_path ?: e->path;
+
+        if (FLAGS_SET(e->flags, IMPORT_FOREIGN_UID)) {
+                r = import_make_foreign_userns(&e->userns_fd);
+                if (r < 0)
+                        return r;
+
+                _cleanup_close_ int directory_fd = open(p, O_DIRECTORY|O_CLOEXEC|O_PATH);
+                if (directory_fd < 0)
+                        return log_error_errno(r, "Failed to open '%s': %m", p);
+
+                _cleanup_close_ int mapped_fd = -EBADF;
+                r = mountfsd_mount_directory_fd(directory_fd, e->userns_fd, DISSECT_IMAGE_FOREIGN_UID, &mapped_fd);
+                if (r < 0)
+                        return r;
+
+                /* Drop O_PATH */
+                e->tree_fd = fd_reopen(mapped_fd, O_DIRECTORY|O_CLOEXEC);
+                if (e->tree_fd < 0)
+                        return log_error_errno(errno, "Failed to re-open mapped '%s': %m", p);
+        } else {
+                e->tree_fd = open(p, O_DIRECTORY|O_CLOEXEC);
+                if (e->tree_fd < 0)
+                        return log_error_errno(errno, "Failed to open '%s': %m", p);
+        }
+
+        e->tar_fd = import_fork_tar_c(e->tree_fd, e->userns_fd, &e->tar_pid);
         if (e->tar_fd < 0) {
                 e->output_event_source = sd_event_source_unref(e->output_event_source);
                 return e->tar_fd;
index 9eeec80dda3d1843638e447ba52731ef235a4910..382b5f58c0fac5d9572f4b9ad5184e4012c7c8b5 100644 (file)
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "shared-forward.h"
+#include "import-common.h"
 #include "import-compress.h"
+#include "shared-forward.h"
 
 typedef struct TarExport TarExport;
 
@@ -13,4 +14,4 @@ TarExport* tar_export_unref(TarExport *export);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(TarExport*, tar_export_unref);
 
-int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress);
+int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress, ImportFlags flags);
index bbea5b0343b83f711102b498190a2681d769e9b5..af9e8c15ec9599c950c6910c45c6b04d45e1d963 100644 (file)
@@ -21,6 +21,7 @@
 #include "terminal-util.h"
 #include "verbs.h"
 
+static ImportFlags arg_import_flags = 0;
 static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
 static ImageClass arg_class = IMAGE_MACHINE;
 static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
@@ -111,7 +112,12 @@ static int export_tar(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate exporter: %m");
 
-        r = tar_export_start(export, local, fd, arg_compress);
+        r = tar_export_start(
+                        export,
+                        local,
+                        fd,
+                        arg_compress,
+                        arg_import_flags & IMPORT_FLAGS_MASK_TAR);
         if (r < 0)
                 return log_error_errno(r, "Failed to export image: %m");
 
@@ -283,6 +289,9 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached();
                 }
 
+        if (arg_runtime_scope == RUNTIME_SCOPE_USER)
+                arg_import_flags |= IMPORT_FOREIGN_UID;
+
         return 1;
 }
 
index f3d70c6f0fefa23e3b841e5c9e486df40bfebe09..eaa68fae5a29bf4f2fd157e2e68cf27cb2912f50 100644 (file)
@@ -90,62 +90,61 @@ int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
         return TAKE_FD(pipefd[1]);
 }
 
-int import_fork_tar_c(const char *path, PidRef *ret) {
-        _cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
-        _cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
-        bool use_selinux;
+int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
         int r;
 
-        assert(path);
-        assert(ret);
+        assert(tree_fd >= 0);
+        assert(ret_pid);
+
+        r = dlopen_libarchive();
+        if (r < 0)
+                return r;
+
+        TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
 
+        _cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
         if (pipe2(pipefd, O_CLOEXEC) < 0)
                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
 
         (void) fcntl(pipefd[0], F_SETPIPE_SZ, IMPORT_BUFFER_SIZE);
 
-        use_selinux = mac_selinux_use();
-
         r = pidref_safe_fork_full(
-                        "(tar)",
-                        (int[]) { -EBADF, pipefd[1], STDERR_FILENO },
-                        NULL, 0,
-                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG,
-                        &pid);
+                        "tar-c",
+                        /* stdio_fds= */ NULL,
+                        (int[]) { tree_fd, pipefd[1], userns_fd }, userns_fd >= 0 ? 3 : 2,
+                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG,
+                        ret_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
-                const char *cmdline[] = {
-                        "tar",
-                        "-C", path,
-                        "-c",
-                        "--xattrs",
-                        "--xattrs-include=*",
-                       use_selinux ? "--selinux" : "--no-selinux",
-                        ".",
-                        NULL
-                };
-
-                uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
+                static const uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
 
                 /* Child */
 
+                if (userns_fd >= 0) {
+                        r = detach_mount_namespace_userns(userns_fd);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to join user namespace: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+                }
+
                 if (unshare(CLONE_NEWNET) < 0)
-                        log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
+                        log_debug_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
 
                 r = capability_bounding_set_drop(retain, true);
                 if (r < 0)
-                        log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
+                        log_debug_errno(r, "Failed to drop capabilities, ignoring: %m");
+
+                if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
+                        log_warning_errno(errno, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m");
 
-                execvp("gtar", (char* const*) cmdline);
-                execvp("tar", (char* const*) cmdline);
+                if (tar_c(tree_fd, pipefd[1], /* filename= */ NULL, flags) < 0)
+                        _exit(EXIT_FAILURE);
 
-                log_error_errno(errno, "Failed to execute tar: %m");
-                _exit(EXIT_FAILURE);
+                _exit(EXIT_SUCCESS);
         }
 
-        *ret = TAKE_PIDREF(pid);
-
         return TAKE_FD(pipefd[0]);
 }
 
index 6c6ac655b0fad9ef2245fee7f47560bff8ba7b04..a922280d0ab0c9bdfdea4c3edfddee4ee0f3ed09 100644 (file)
@@ -34,7 +34,7 @@ typedef enum ImportFlags {
         _IMPORT_FLAGS_INVALID = -EINVAL,
 } ImportFlags;
 
-int import_fork_tar_c(const char *path, PidRef *ret);
+int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid);
 int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
 
 int import_mangle_os_tree(const char *path);
index 3967041b6d7959b782c8120d07fc34ac598e6a3b..c75d7eb6668fd023a56c3393c12c468d35ac61d6 100644 (file)
@@ -823,7 +823,6 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libarchive support not available.");
 }
 
-
 int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
         assert(tree_fd >= 0);
         assert(output_fd >= 0);