export-tar: port to common libarchive tar generation code

This commit is contained in:
Lennart Poettering
2025-08-21 11:26:17 +02:00
parent faaed501e0
commit 3780a0b446
6 changed files with 90 additions and 42 deletions

View 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;

View 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);

View 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;
}

View 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");
execvp("gtar", (char* const*) cmdline);
execvp("tar", (char* const*) cmdline);
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");
log_error_errno(errno, "Failed to execute tar: %m");
_exit(EXIT_FAILURE);
if (tar_c(tree_fd, pipefd[1], /* filename= */ NULL, flags) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
*ret = TAKE_PIDREF(pid);
return TAKE_FD(pipefd[0]);
}

View 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);

View 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);