#include "alloc-util.h"
#include "btrfs-util.h"
+#include "dissect-image.h"
#include "export-tar.h"
#include "fd-util.h"
#include "format-util.h"
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;
*e = (TarExport) {
.output_fd = -EBADF,
.tar_fd = -EBADF,
+ .tree_fd = -EBADF,
+ .userns_fd = -EBADF,
.on_finished = on_finished,
.userdata = userdata,
.quota_referenced = UINT64_MAX,
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;
if (r < 0)
return r;
+ e->flags = flags;
e->quota_referenced = UINT64_MAX;
if (btrfs_might_be_subvol(&e->st)) {
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;
/* 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;
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);
#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;
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");
assert_not_reached();
}
+ if (arg_runtime_scope == RUNTIME_SCOPE_USER)
+ arg_import_flags |= IMPORT_FOREIGN_UID;
+
return 1;
}
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]);
}
_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);
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);