dissect: add --make-archive option to convert DDI to tarball

This commit is contained in:
Lennart Poettering
2024-01-25 18:47:39 +01:00
parent 6811774510
commit b68f4cade4
18 changed files with 374 additions and 5 deletions
+6
View File
@@ -238,6 +238,12 @@ const char* const systemd_features =
" -SYSVINIT"
#endif
#if HAVE_LIBARCHIVE
" +LIBARCHIVE"
#else
" -LIBARCHIVE"
#endif
" default-hierarchy=" DEFAULT_HIERARCHY_NAME
;
+197 -3
View File
@@ -27,6 +27,7 @@
#include "format-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "libarchive-util.h"
#include "log.h"
#include "loop-util.h"
#include "main-func.h"
@@ -63,6 +64,7 @@ static enum {
ACTION_COPY_TO,
ACTION_DISCOVER,
ACTION_VALIDATE,
ACTION_MAKE_ARCHIVE,
} arg_action = ACTION_DISSECT;
static char *arg_image = NULL;
static char *arg_root = NULL;
@@ -116,6 +118,7 @@ static int help(void) {
"%1$s [OPTIONS...] --with IMAGE [COMMAND…]\n"
"%1$s [OPTIONS...] --copy-from IMAGE PATH [TARGET]\n"
"%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n"
"%1$s [OPTIONS...] --make-archive IMAGE [TARGET]\n"
"%1$s [OPTIONS...] --discover\n"
"%1$s [OPTIONS...] --validate IMAGE\n"
"\n%5$sDissect a Discoverable Disk Image (DDI).%6$s\n\n"
@@ -157,6 +160,7 @@ static int help(void) {
" --with Mount, run command, unmount\n"
" -x --copy-from Copy files from image to host\n"
" -a --copy-to Copy files from host to image\n"
" --make-archive Convert the DDI to an archive file\n"
" --discover Discover DDIs in well known directories\n"
" --validate Validate image and image policy\n"
"\nSee the %2$s for details.\n",
@@ -263,6 +267,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_IMAGE_POLICY,
ARG_VALIDATE,
ARG_MTREE_HASH,
ARG_MAKE_ARCHIVE,
};
static const struct option options[] = {
@@ -295,6 +300,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "validate", no_argument, NULL, ARG_VALIDATE },
{ "mtree-hash", required_argument, NULL, ARG_MTREE_HASH },
{ "make-archive", no_argument, NULL, ARG_MAKE_ARCHIVE },
{}
};
@@ -518,6 +524,15 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
case ARG_MAKE_ARCHIVE:
r = dlopen_libarchive();
if (r < 0)
return log_error_errno(r, "Archive support not available (compiled without libarchive, or libarchive not installed?).");
arg_action = ACTION_MAKE_ARCHIVE;
break;
case '?':
return -EINVAL;
@@ -600,6 +615,19 @@ static int parse_argv(int argc, char *argv[]) {
arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT;
break;
case ACTION_MAKE_ARCHIVE:
if (argc < optind + 1 || argc > optind + 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Expected an image file, and an optional target path as only arguments.");
r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
if (r < 0)
return r;
arg_target = argc > optind + 1 ? empty_or_dash_to_null(argv[optind + 1]) : NULL;
arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT;
break;
case ACTION_COPY_FROM:
if (argc < optind + 2 || argc > optind + 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -1274,13 +1302,116 @@ static int mtree_print_item(
return RECURSE_DIR_CONTINUE;
}
static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
#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_(sym_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) {
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
_cleanup_free_ char *t = NULL;
const char *root;
int r;
assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO));
assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE));
if (arg_image) {
assert(m);
@@ -1466,6 +1597,68 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
return 0;
}
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_(sym_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));
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);
r = sym_archive_write_open_FILE(a, f);
} else {
if (isatty(STDOUT_FILENO))
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Refusing to write archive to TTY.");
r = sym_archive_write_open_fd(a, 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);
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));
if (arg_target) {
r = flink_tmpfile(f, tar, arg_target, LINK_TMPFILE_REPLACE);
if (r < 0)
return log_error_errno(r, "Failed to move archive file into place: %m");
}
return 0;
#else
assert_not_reached();
#endif
}
default:
assert_not_reached();
}
@@ -1914,7 +2107,8 @@ static int run(int argc, char *argv[]) {
case ACTION_MTREE:
case ACTION_COPY_FROM:
case ACTION_COPY_TO:
return action_list_or_mtree_or_copy(m, d);
case ACTION_MAKE_ARCHIVE:
return action_list_or_mtree_or_copy_or_make_archive(m, d);
case ACTION_WITH:
return action_with(m, d);
+62
View File
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "libarchive-util.h"
#if HAVE_LIBARCHIVE
static void *libarchive_dl = NULL;
DLSYM_FUNCTION(archive_entry_free);
DLSYM_FUNCTION(archive_entry_new);
DLSYM_FUNCTION(archive_entry_set_ctime);
DLSYM_FUNCTION(archive_entry_set_filetype);
DLSYM_FUNCTION(archive_entry_set_gid);
DLSYM_FUNCTION(archive_entry_set_mtime);
DLSYM_FUNCTION(archive_entry_set_pathname);
DLSYM_FUNCTION(archive_entry_set_perm);
DLSYM_FUNCTION(archive_entry_set_rdevmajor);
DLSYM_FUNCTION(archive_entry_set_rdevminor);
DLSYM_FUNCTION(archive_entry_set_symlink);
DLSYM_FUNCTION(archive_entry_set_size);
DLSYM_FUNCTION(archive_entry_set_uid);
DLSYM_FUNCTION(archive_error_string);
DLSYM_FUNCTION(archive_write_close);
DLSYM_FUNCTION(archive_write_data);
DLSYM_FUNCTION(archive_write_free);
DLSYM_FUNCTION(archive_write_header);
DLSYM_FUNCTION(archive_write_new);
DLSYM_FUNCTION(archive_write_open_FILE);
DLSYM_FUNCTION(archive_write_open_fd);
DLSYM_FUNCTION(archive_write_set_format_filter_by_ext);
DLSYM_FUNCTION(archive_write_set_format_gnutar);
int dlopen_libarchive(void) {
return dlopen_many_sym_or_warn(
&libarchive_dl,
"libarchive.so.13",
LOG_DEBUG,
DLSYM_ARG(archive_entry_free),
DLSYM_ARG(archive_entry_new),
DLSYM_ARG(archive_entry_set_ctime),
DLSYM_ARG(archive_entry_set_filetype),
DLSYM_ARG(archive_entry_set_gid),
DLSYM_ARG(archive_entry_set_mtime),
DLSYM_ARG(archive_entry_set_pathname),
DLSYM_ARG(archive_entry_set_perm),
DLSYM_ARG(archive_entry_set_rdevmajor),
DLSYM_ARG(archive_entry_set_rdevminor),
DLSYM_ARG(archive_entry_set_size),
DLSYM_ARG(archive_entry_set_symlink),
DLSYM_ARG(archive_entry_set_uid),
DLSYM_ARG(archive_error_string),
DLSYM_ARG(archive_write_close),
DLSYM_ARG(archive_write_data),
DLSYM_ARG(archive_write_free),
DLSYM_ARG(archive_write_header),
DLSYM_ARG(archive_write_new),
DLSYM_ARG(archive_write_open_FILE),
DLSYM_ARG(archive_write_open_fd),
DLSYM_ARG(archive_write_set_format_filter_by_ext),
DLSYM_ARG(archive_write_set_format_gnutar));
}
#endif
+45
View File
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "dlfcn-util.h"
#if HAVE_LIBARCHIVE
#include <archive.h>
#include <archive_entry.h>
DLSYM_PROTOTYPE(archive_entry_free);
DLSYM_PROTOTYPE(archive_entry_new);
DLSYM_PROTOTYPE(archive_entry_set_ctime);
DLSYM_PROTOTYPE(archive_entry_set_filetype);
DLSYM_PROTOTYPE(archive_entry_set_gid);
DLSYM_PROTOTYPE(archive_entry_set_mtime);
DLSYM_PROTOTYPE(archive_entry_set_pathname);
DLSYM_PROTOTYPE(archive_entry_set_perm);
DLSYM_PROTOTYPE(archive_entry_set_rdevmajor);
DLSYM_PROTOTYPE(archive_entry_set_rdevminor);
DLSYM_PROTOTYPE(archive_entry_set_symlink);
DLSYM_PROTOTYPE(archive_entry_set_size);
DLSYM_PROTOTYPE(archive_entry_set_uid);
DLSYM_PROTOTYPE(archive_error_string);
DLSYM_PROTOTYPE(archive_write_close);
DLSYM_PROTOTYPE(archive_write_data);
DLSYM_PROTOTYPE(archive_write_free);
DLSYM_PROTOTYPE(archive_write_header);
DLSYM_PROTOTYPE(archive_write_new);
DLSYM_PROTOTYPE(archive_write_open_FILE);
DLSYM_PROTOTYPE(archive_write_open_fd);
DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext);
DLSYM_PROTOTYPE(archive_write_set_format_gnutar);
int dlopen_libarchive(void);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive_entry*, sym_archive_entry_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive*, sym_archive_write_free, NULL);
#else
static inline int dlopen_libarchive(void) {
return -EOPNOTSUPP;
}
#endif
+1
View File
@@ -103,6 +103,7 @@ shared_sources = files(
'keyring-util.c',
'killall.c',
'label-util.c',
'libarchive-util.c',
'libcrypt-util.c',
'libfido2-util.c',
'libmount-util.c',
+5
View File
@@ -7,6 +7,7 @@
#include "cryptsetup-util.h"
#include "elf-util.h"
#include "idn-util.h"
#include "libarchive-util.h"
#include "libfido2-util.h"
#include "macro.h"
#include "main-func.h"
@@ -70,6 +71,10 @@ static int run(int argc, char **argv) {
assert_se(dlopen_p11kit() >= 0);
#endif
#if HAVE_LIBARCHIVE
assert_se(dlopen_libarchive() >= 0);
#endif
return 0;
}