mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
importd: port export-tar code to use the one systemd-dissect already uses (#39405)
Split out of #38728. (Testcase is part of that PR)
This commit is contained in:
@@ -53,6 +53,7 @@
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tar-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "uid-classification.h"
|
||||
@@ -1432,109 +1433,6 @@ static int mtree_print_item(
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
}
|
||||
|
||||
#if HAVE_LIBARCHIVE
|
||||
static int archive_item(
|
||||
RecurseDirEvent event,
|
||||
const char *path,
|
||||
int dir_fd,
|
||||
int inode_fd,
|
||||
const struct dirent *de,
|
||||
const struct statx *sx,
|
||||
void *userdata) {
|
||||
|
||||
struct archive *a = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
|
||||
assert(inode_fd >= 0);
|
||||
assert(sx);
|
||||
|
||||
log_debug("Archiving %s\n", path);
|
||||
|
||||
_cleanup_(archive_entry_freep) struct archive_entry *entry = NULL;
|
||||
entry = sym_archive_entry_new();
|
||||
if (!entry)
|
||||
return log_oom();
|
||||
|
||||
assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
|
||||
sym_archive_entry_set_pathname(entry, path);
|
||||
sym_archive_entry_set_filetype(entry, sx->stx_mode);
|
||||
|
||||
if (!S_ISLNK(sx->stx_mode))
|
||||
sym_archive_entry_set_perm(entry, sx->stx_mode);
|
||||
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_UID))
|
||||
sym_archive_entry_set_uid(entry, sx->stx_uid);
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_GID))
|
||||
sym_archive_entry_set_gid(entry, sx->stx_gid);
|
||||
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Unable to determine file size of '%s'.", path);
|
||||
|
||||
sym_archive_entry_set_size(entry, sx->stx_size);
|
||||
}
|
||||
|
||||
if (S_ISCHR(sx->stx_mode) || S_ISBLK(sx->stx_mode)) {
|
||||
sym_archive_entry_set_rdevmajor(entry, sx->stx_rdev_major);
|
||||
sym_archive_entry_set_rdevminor(entry, sx->stx_rdev_minor);
|
||||
}
|
||||
|
||||
/* We care about a modicum of reproducibility here, hence we don't save atime/btime here */
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_MTIME))
|
||||
sym_archive_entry_set_mtime(entry, sx->stx_mtime.tv_sec, sx->stx_mtime.tv_nsec);
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_CTIME))
|
||||
sym_archive_entry_set_ctime(entry, sx->stx_ctime.tv_sec, sx->stx_ctime.tv_nsec);
|
||||
|
||||
if (S_ISLNK(sx->stx_mode)) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
assert(dir_fd >= 0);
|
||||
assert(de);
|
||||
|
||||
r = readlinkat_malloc(dir_fd, de->d_name, &s);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read symlink target of '%s': %m", path);
|
||||
|
||||
sym_archive_entry_set_symlink(entry, s);
|
||||
}
|
||||
|
||||
if (sym_archive_write_header(a, entry) != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(a));
|
||||
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
_cleanup_close_ int data_fd = -EBADF;
|
||||
|
||||
/* Convert the O_PATH fd in a proper fd */
|
||||
data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
|
||||
if (data_fd < 0)
|
||||
return log_error_errno(data_fd, "Failed to open '%s': %m", path);
|
||||
|
||||
for (;;) {
|
||||
char buffer[64*1024];
|
||||
ssize_t l;
|
||||
|
||||
l = read(data_fd, buffer, sizeof(buffer));
|
||||
if (l < 0)
|
||||
return log_error_errno(errno, "Failed to read '%s': %m", path);
|
||||
if (l == 0)
|
||||
break;
|
||||
|
||||
la_ssize_t k;
|
||||
k = sym_archive_write_data(a, buffer, l);
|
||||
if (k < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
|
||||
}
|
||||
}
|
||||
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d, int userns_fd) {
|
||||
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
@@ -1736,56 +1634,34 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
|
||||
case ACTION_MAKE_ARCHIVE: {
|
||||
#if HAVE_LIBARCHIVE
|
||||
_cleanup_(unlink_and_freep) char *tar = NULL;
|
||||
_cleanup_close_ int dfd = -EBADF;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
dfd = open(root, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
|
||||
if (dfd < 0)
|
||||
return log_error_errno(errno, "Failed to open mount directory: %m");
|
||||
|
||||
_cleanup_(archive_write_freep) struct archive *a = sym_archive_write_new();
|
||||
if (!a)
|
||||
return log_oom();
|
||||
|
||||
if (arg_target)
|
||||
r = sym_archive_write_set_format_filter_by_ext(a, arg_target);
|
||||
else
|
||||
r = sym_archive_write_set_format_gnutar(a);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));
|
||||
|
||||
_cleanup_(unlink_and_freep) char *tar = NULL;
|
||||
_cleanup_close_ int tmp_fd = -EBADF;
|
||||
int output_fd;
|
||||
if (arg_target) {
|
||||
r = fopen_tmpfile_linkable(arg_target, O_WRONLY|O_CLOEXEC, &tar, &f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create target file '%s': %m", arg_target);
|
||||
tmp_fd = open_tmpfile_linkable(arg_target, O_WRONLY|O_CLOEXEC, &tar);
|
||||
if (tmp_fd < 0)
|
||||
return log_error_errno(tmp_fd, "Failed to create target file '%s': %m", arg_target);
|
||||
|
||||
r = sym_archive_write_open_FILE(a, f);
|
||||
output_fd = tmp_fd;
|
||||
} else {
|
||||
if (isatty_safe(STDOUT_FILENO))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Refusing to write archive to TTY.");
|
||||
|
||||
r = sym_archive_write_open_fd(a, STDOUT_FILENO);
|
||||
output_fd = STDOUT_FILENO;
|
||||
}
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a));
|
||||
|
||||
r = recurse_dir(dfd,
|
||||
".",
|
||||
STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE|STATX_ATIME|STATX_CTIME,
|
||||
UINT_MAX,
|
||||
RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
|
||||
archive_item,
|
||||
a);
|
||||
r = tar_c(dfd, output_fd, arg_target, /* flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make archive: %m");
|
||||
|
||||
r = sym_archive_write_close(a);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to finish writing archive: %s", sym_archive_error_string(a));
|
||||
return r;
|
||||
|
||||
if (arg_target) {
|
||||
r = flink_tmpfile(f, tar, arg_target, LINK_TMPFILE_REPLACE);
|
||||
r = link_tmpfile(tmp_fd, tar, arg_target, LINK_TMPFILE_REPLACE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to move archive file into place: %m");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "iovec-util.h"
|
||||
#include "libarchive-util.h"
|
||||
#include "path-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
@@ -674,6 +675,145 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int archive_item(
|
||||
RecurseDirEvent event,
|
||||
const char *path,
|
||||
int dir_fd,
|
||||
int inode_fd,
|
||||
const struct dirent *de,
|
||||
const struct statx *sx,
|
||||
void *userdata) {
|
||||
|
||||
struct archive *a = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
|
||||
assert(inode_fd >= 0);
|
||||
assert(sx);
|
||||
|
||||
log_debug("Archiving %s\n", path);
|
||||
|
||||
_cleanup_(archive_entry_freep) struct archive_entry *entry = NULL;
|
||||
entry = sym_archive_entry_new();
|
||||
if (!entry)
|
||||
return log_oom();
|
||||
|
||||
assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
|
||||
sym_archive_entry_set_pathname(entry, path);
|
||||
sym_archive_entry_set_filetype(entry, sx->stx_mode);
|
||||
|
||||
if (!S_ISLNK(sx->stx_mode))
|
||||
sym_archive_entry_set_perm(entry, sx->stx_mode);
|
||||
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_UID))
|
||||
sym_archive_entry_set_uid(entry, sx->stx_uid);
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_GID))
|
||||
sym_archive_entry_set_gid(entry, sx->stx_gid);
|
||||
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Unable to determine file size of '%s'.", path);
|
||||
|
||||
sym_archive_entry_set_size(entry, sx->stx_size);
|
||||
}
|
||||
|
||||
if (S_ISCHR(sx->stx_mode) || S_ISBLK(sx->stx_mode)) {
|
||||
sym_archive_entry_set_rdevmajor(entry, sx->stx_rdev_major);
|
||||
sym_archive_entry_set_rdevminor(entry, sx->stx_rdev_minor);
|
||||
}
|
||||
|
||||
/* We care about a modicum of reproducibility here, hence we don't save atime/btime here */
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_MTIME))
|
||||
sym_archive_entry_set_mtime(entry, sx->stx_mtime.tv_sec, sx->stx_mtime.tv_nsec);
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_CTIME))
|
||||
sym_archive_entry_set_ctime(entry, sx->stx_ctime.tv_sec, sx->stx_ctime.tv_nsec);
|
||||
|
||||
if (S_ISLNK(sx->stx_mode)) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
assert(dir_fd >= 0);
|
||||
assert(de);
|
||||
|
||||
r = readlinkat_malloc(dir_fd, de->d_name, &s);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read symlink target of '%s': %m", path);
|
||||
|
||||
sym_archive_entry_set_symlink(entry, s);
|
||||
}
|
||||
|
||||
if (sym_archive_write_header(a, entry) != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(a));
|
||||
|
||||
if (S_ISREG(sx->stx_mode)) {
|
||||
_cleanup_close_ int data_fd = -EBADF;
|
||||
|
||||
/* Convert the O_PATH fd in a proper fd */
|
||||
data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
|
||||
if (data_fd < 0)
|
||||
return log_error_errno(data_fd, "Failed to open '%s': %m", path);
|
||||
|
||||
for (;;) {
|
||||
char buffer[64*1024];
|
||||
ssize_t l;
|
||||
|
||||
l = read(data_fd, buffer, sizeof(buffer));
|
||||
if (l < 0)
|
||||
return log_error_errno(errno, "Failed to read '%s': %m", path);
|
||||
if (l == 0)
|
||||
break;
|
||||
|
||||
la_ssize_t k;
|
||||
k = sym_archive_write_data(a, buffer, l);
|
||||
if (k < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
|
||||
}
|
||||
}
|
||||
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
}
|
||||
|
||||
int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
|
||||
int r;
|
||||
|
||||
assert(tree_fd >= 0);
|
||||
assert(output_fd >= 0);
|
||||
|
||||
_cleanup_(archive_write_freep) struct archive *a = sym_archive_write_new();
|
||||
if (!a)
|
||||
return log_oom();
|
||||
|
||||
if (filename)
|
||||
r = sym_archive_write_set_format_filter_by_ext(a, filename);
|
||||
else
|
||||
r = sym_archive_write_set_format_gnutar(a);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));
|
||||
|
||||
r = sym_archive_write_open_fd(a, output_fd);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a));
|
||||
|
||||
r = recurse_dir(tree_fd,
|
||||
".",
|
||||
STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE|STATX_ATIME|STATX_CTIME,
|
||||
UINT_MAX,
|
||||
RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
|
||||
archive_item,
|
||||
a);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make archive: %m");
|
||||
|
||||
r = sym_archive_write_close(a);
|
||||
if (r != ARCHIVE_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to finish writing archive: %s", sym_archive_error_string(a));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int tar_x(int input_fd, int tree_fd, TarFlags flags) {
|
||||
@@ -683,4 +823,11 @@ 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);
|
||||
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libarchive support not available.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,3 +6,4 @@ typedef enum TarFlags {
|
||||
} TarFlags;
|
||||
|
||||
int tar_x(int input_fd, int tree_fd, TarFlags flags);
|
||||
int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags);
|
||||
|
||||
Reference in New Issue
Block a user