diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
commit | fc53809803cd2bc2434e312b19a18fa36776da12 (patch) | |
tree | b4b43bd6538f51965ce32856e9c053d0f90919c8 /src/dissect | |
parent | Adding upstream version 255.5. (diff) | |
download | systemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip |
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/dissect/dissect.c | 496 |
1 files changed, 373 insertions, 123 deletions
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index c858e6a..d68c29c 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -27,13 +27,16 @@ #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" +#include "missing_syscall.h" #include "mkdir.h" #include "mount-util.h" #include "mountpoint-util.h" #include "namespace-util.h" +#include "nsresource.h" #include "parse-argument.h" #include "parse-util.h" #include "path-util.h" @@ -46,8 +49,9 @@ #include "strv.h" #include "terminal-util.h" #include "tmpfile-util.h" -#include "uid-alloc-range.h" +#include "uid-classification.h" #include "user-util.h" +#include "vpick.h" static enum { ACTION_DISSECT, @@ -62,6 +66,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; @@ -76,7 +81,8 @@ static DissectImageFlags arg_flags = DISSECT_IMAGE_USR_NO_ROOT | DISSECT_IMAGE_GROWFS | DISSECT_IMAGE_PIN_PARTITION_DEVICES | - DISSECT_IMAGE_ADD_PARTITION_DEVICES; + DISSECT_IMAGE_ADD_PARTITION_DEVICES | + DISSECT_IMAGE_ALLOW_USERSPACE_VERITY; static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; @@ -87,6 +93,7 @@ static char **arg_argv = NULL; static char *arg_loop_ref = NULL; static ImagePolicy *arg_image_policy = NULL; static bool arg_mtree_hash = true; +static bool arg_via_service = false; STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); @@ -116,6 +123,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" @@ -150,13 +158,14 @@ static int help(void) { " -u --umount Unmount the image from the specified directory\n" " -U Shortcut for --umount --rmdir\n" " --attach Attach the disk image to a loopback block device\n" - " --detach Detach a loopback block device gain\n" + " --detach Detach a loopback block device again\n" " -l --list List all the files and directories of the specified\n" " OS image\n" " --mtree Show BSD mtree manifest of OS image\n" " --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 +272,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 +305,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 }, {} }; @@ -423,7 +434,7 @@ static int parse_argv(int argc, char *argv[]) { _cleanup_free_ void *p = NULL; size_t l; - r = unhexmem(optarg, strlen(optarg), &p, &l); + r = unhexmem(optarg, &p, &l); if (r < 0) return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg); if (l < sizeof(sd_id128_t)) @@ -441,7 +452,7 @@ static int parse_argv(int argc, char *argv[]) { void *p; if ((value = startswith(optarg, "base64:"))) { - r = unbase64mem(value, strlen(value), &p, &l); + r = unbase64mem(value, &p, &l); if (r < 0) return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg); } else { @@ -518,6 +529,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 +620,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), @@ -674,6 +707,18 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached(); } + r = getenv_bool("SYSTEMD_USE_MOUNTFSD"); + if (r < 0) { + if (r != -ENXIO) + return log_error_errno(r, "Failed to parse $SYSTEMD_USE_MOUNTFSD: %m"); + } else + arg_via_service = r; + + if (!IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_DISCOVER, ACTION_VALIDATE) && geteuid() != 0) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); + + SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty(STDIN_FILENO)); + return 1; } @@ -685,7 +730,7 @@ static int parse_argv_as_mount_helper(int argc, char *argv[]) { /* Implements util-linux "external helper" command line interface, as per mount(8) man page. */ while ((c = getopt(argc, argv, "sfnvN:o:t:")) >= 0) { - switch(c) { + switch (c) { case 'f': fake = true; @@ -811,7 +856,11 @@ static int get_extension_scopes(DissectedImage *m, ImageClass class, char ***ret return 1; } -static int action_dissect(DissectedImage *m, LoopDevice *d) { +static int action_dissect( + DissectedImage *m, + LoopDevice *d, + int userns_fd) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(table_unrefp) Table *t = NULL; _cleanup_free_ char *bn = NULL; @@ -819,7 +868,6 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { int r; assert(m); - assert(d); r = path_extract_filename(arg_image, &bn); if (r < 0) @@ -828,16 +876,15 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) pager_open(arg_pager_flags); - if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal()); + if (arg_json_format_flags & JSON_FORMAT_OFF) { + printf(" File Name: %s%s%s\n", + ansi_highlight(), bn, ansi_normal()); - if (ioctl(d->fd, BLKGETSIZE64, &size) < 0) - log_debug_errno(errno, "Failed to query size of loopback device: %m"); - else if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Size: %s\n", FORMAT_BYTES(size)); + printf(" Size: %s\n", + FORMAT_BYTES(m->image_size)); - if (arg_json_format_flags & JSON_FORMAT_OFF) { - printf(" Sec. Size: %" PRIu32 "\n", m->sector_size); + printf(" Sec. Size: %" PRIu32 "\n", + m->sector_size); printf(" Arch.: %s\n", strna(architecture_to_string(dissected_image_architecture(m)))); @@ -846,7 +893,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { fflush(stdout); } - r = dissected_image_acquire_metadata(m, 0); + r = dissected_image_acquire_metadata(m, userns_fd, /* extra_flags= */ 0); if (r == -ENXIO) return log_error_errno(r, "No root partition discovered."); if (r == -EUCLEAN) @@ -861,6 +908,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { return log_error_errno(r, "Failed to acquire image metadata: %m"); else if (arg_json_format_flags & JSON_FORMAT_OFF) { + if (m->image_name && !streq(m->image_name, bn)) + printf("Image Name: %s\n", m->image_name); + if (!sd_id128_is_null(m->image_uuid)) printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid)); @@ -963,6 +1013,11 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { table_set_ersatz_string(t, TABLE_ERSATZ_DASH); (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100); + /* Hide the device path if this is a loopback device that is not relinquished, since that means the + * device node is not going to be useful the instant our command exits */ + if ((!d || d->created) && (arg_json_format_flags & JSON_FORMAT_OFF)) + table_hide_column_from_display(t, 8); + for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) { DissectedPartition *p = m->partitions + i; @@ -1050,7 +1105,6 @@ static int action_mount(DissectedImage *m, LoopDevice *d) { int r; assert(m); - assert(d); assert(arg_action == ACTION_MOUNT); r = dissected_image_mount_and_warn( @@ -1063,9 +1117,11 @@ static int action_mount(DissectedImage *m, LoopDevice *d) { if (r < 0) return r; - r = loop_device_flock(d, LOCK_UN); - if (r < 0) - return log_error_errno(r, "Failed to unlock loopback block device: %m"); + if (d) { + r = loop_device_flock(d, LOCK_UN); + if (r < 0) + return log_error_errno(r, "Failed to unlock loopback block device: %m"); + } r = dissected_image_relinquish(m); if (r < 0) @@ -1095,7 +1151,6 @@ static int list_print_item( static int get_file_sha256(int inode_fd, uint8_t ret[static SHA256_DIGEST_SIZE]) { _cleanup_close_ int fd = -EBADF; - struct sha256_ctx ctx; /* convert O_PATH fd into a regular one */ fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC); @@ -1105,23 +1160,7 @@ static int get_file_sha256(int inode_fd, uint8_t ret[static SHA256_DIGEST_SIZE]) /* Calculating the SHA sum might be slow, hence let's flush STDOUT first, to give user an idea where we are slow. */ fflush(stdout); - sha256_init_ctx(&ctx); - - for (;;) { - uint8_t buffer[64 * 1024]; - ssize_t n; - - n = read(fd, buffer, sizeof(buffer)); - if (n < 0) - return -errno; - if (n == 0) - break; - - sha256_process_bytes(buffer, n, &ctx); - } - - sha256_finish_ctx(&ctx, ret); - return 0; + return sha256_fd(fd, UINT64_MAX, ret); } static const char *pick_color_for_uid_gid(uid_t uid) { @@ -1267,36 +1306,136 @@ static int mtree_print_item( return RECURSE_DIR_CONTINUE; } -static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { - _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; - _cleanup_(rmdir_and_freep) char *created_dir = NULL; - _cleanup_free_ char *temp = NULL; +#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, int userns_fd) { + _cleanup_(umount_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); - assert(d); - r = detach_mount_namespace(); + if (userns_fd < 0) + r = detach_mount_namespace_harder(0, 0); + else + r = detach_mount_namespace_userns(userns_fd); if (r < 0) return log_error_errno(r, "Failed to detach mount namespace: %m"); - r = tempfn_random_child(NULL, program_invocation_short_name, &temp); + /* Create a place we can mount things onto soon. We use a fixed path shared by all invocations. Given + * the mounts are done in a mount namespace there's not going to be a collision here */ + r = get_common_dissect_directory(&t); if (r < 0) - return log_error_errno(r, "Failed to generate temporary mount directory: %m"); - - r = mkdir_p(temp, 0700); - if (r < 0) - return log_error_errno(r, "Failed to create mount point: %m"); - - created_dir = TAKE_PTR(temp); + return log_error_errno(r, "Failed generate private mount directory: %m"); r = dissected_image_mount_and_warn( m, - created_dir, + t, /* uid_shift= */ UID_INVALID, /* uid_range= */ UID_INVALID, /* userns_fd= */ -EBADF, @@ -1304,11 +1443,13 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { if (r < 0) return r; - mounted_dir = TAKE_PTR(created_dir); + mounted_dir = TAKE_PTR(t); - r = loop_device_flock(d, LOCK_UN); - if (r < 0) - return log_error_errno(r, "Failed to unlock loopback block device: %m"); + if (d) { + r = loop_device_flock(d, LOCK_UN); + if (r < 0) + return log_error_errno(r, "Failed to unlock loopback block device: %m"); + } r = dissected_image_relinquish(m); if (r < 0) @@ -1317,6 +1458,8 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { root = mounted_dir ?: arg_root; + dissected_image_close(m); + switch (arg_action) { case ACTION_COPY_FROM: { @@ -1466,6 +1609,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(); } @@ -1538,7 +1743,6 @@ static int action_with(DissectedImage *m, LoopDevice *d) { int r, rcode; assert(m); - assert(d); assert(arg_action == ACTION_WITH); r = tempfn_random_child(NULL, program_invocation_short_name, &temp); @@ -1567,9 +1771,11 @@ static int action_with(DissectedImage *m, LoopDevice *d) { if (r < 0) return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); - r = loop_device_flock(d, LOCK_UN); - if (r < 0) - return log_error_errno(r, "Failed to unlock loopback block device: %m"); + if (d) { + r = loop_device_flock(d, LOCK_UN); + if (r < 0) + return log_error_errno(r, "Failed to unlock loopback block device: %m"); + } rcode = safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL); if (rcode == 0) { @@ -1610,14 +1816,16 @@ static int action_with(DissectedImage *m, LoopDevice *d) { } /* Let's manually detach everything, to make things synchronous */ - r = loop_device_flock(d, LOCK_SH); - if (r < 0) - log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m"); + if (d) { + r = loop_device_flock(d, LOCK_SH); + if (r < 0) + log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m"); + } r = umount_recursive(mounted_dir, 0); if (r < 0) log_warning_errno(r, "Failed to unmount '%s', ignoring: %m", mounted_dir); - else + else if (d) loop_device_unrelinquish(d); /* Let's try to destroy the loopback device */ created_dir = TAKE_PTR(mounted_dir); @@ -1705,6 +1913,8 @@ static int action_detach(const char *path) { struct stat st; int r; + assert(path); + fd = open(path, O_PATH|O_CLOEXEC); if (fd < 0) return log_error_errno(errno, "Failed to open '%s': %m", path); @@ -1738,26 +1948,13 @@ static int action_detach(const char *path) { FOREACH_DEVICE(e, d) { _cleanup_(loop_device_unrefp) LoopDevice *entry_loop = NULL; - const char *name, *devtype; - r = sd_device_get_sysname(d, &name); - if (r < 0) { - log_warning_errno(r, "Failed to get enumerated device's sysname, skipping: %m"); - continue; - } - - r = sd_device_get_devtype(d, &devtype); - if (r < 0) { - log_warning_errno(r, "Failed to get devtype of '%s', skipping: %m", name); - continue; - } - - if (!streq(devtype, "disk")) /* Filter out partition block devices */ + if (!device_is_devtype(d, "disk")) /* Filter out partition block devices */ continue; r = loop_device_open(d, O_RDONLY, LOCK_SH, &entry_loop); if (r < 0) { - log_warning_errno(r, "Failed to open loopback block device '%s', skipping: %m", name); + log_device_warning_errno(d, r, "Failed to open loopback block device, skipping: %m"); continue; } @@ -1815,8 +2012,8 @@ static int action_validate(void) { static int run(int argc, char *argv[]) { _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; - uint32_t loop_flags; - int open_flags, r; + _cleanup_close_ int userns_fd = -EBADF; + int r; log_setup(); @@ -1827,6 +2024,16 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + if (arg_image) { + r = path_pick_update_warn( + &arg_image, + &pick_filter_image_raw, + PICK_ARCHITECTURE|PICK_TRIES, + /* ret_result= */ NULL); + if (r < 0) + return r; + } + switch (arg_action) { case ACTION_UMOUNT: return action_umount(arg_path); @@ -1838,7 +2045,7 @@ static int run(int argc, char *argv[]) { return action_discover(); default: - /* All other actions need the image dissected */ + /* All other actions need the image dissected (except for ACTION_VALIDATE, see below) */ break; } @@ -1854,50 +2061,92 @@ static int run(int argc, char *argv[]) { * hence if there's external Verity data * available we turn off partition table * support */ + } - if (arg_action == ACTION_VALIDATE) - return action_validate(); + if (arg_action == ACTION_VALIDATE) + return action_validate(); - open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR; - loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN; - if (arg_in_memory) - r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); - else - r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); - if (r < 0) - return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image); + if (arg_image) { + /* First try locally, if we are allowed to */ + if (!arg_via_service) { + uint32_t loop_flags; + int open_flags; + + open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR; + loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN; + + if (arg_in_memory) + r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); + else + r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); + if (r < 0) { + if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO)) + return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image); - if (arg_loop_ref) { - r = loop_device_set_filename(d, arg_loop_ref); - if (r < 0) - log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref); + log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image); + arg_via_service = true; + } else { + if (arg_loop_ref) { + r = loop_device_set_filename(d, arg_loop_ref); + if (r < 0) + log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref); + } + + r = dissect_loop_device_and_warn( + d, + &arg_verity_settings, + /* mount_options= */ NULL, + arg_image_policy, + arg_flags, + &m); + if (r < 0) + return r; + + if (arg_action == ACTION_ATTACH) + return action_attach(m, d); + + r = dissected_image_load_verity_sig_partition( + m, + d->fd, + &arg_verity_settings); + if (r < 0) + return log_error_errno(r, "Failed to load verity signature partition: %m"); + + if (arg_action != ACTION_DISSECT) { + r = dissected_image_decrypt_interactively( + m, NULL, + &arg_verity_settings, + arg_flags); + if (r < 0) + return r; + } + } } - r = dissect_loop_device_and_warn( - d, - &arg_verity_settings, - /* mount_options= */ NULL, - arg_image_policy, - arg_flags, - &m); - if (r < 0) - return r; + /* Try via service */ + if (arg_via_service) { + if (arg_in_memory) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--in-memory= not supported when operating via systemd-mountfsd."); - if (arg_action == ACTION_ATTACH) - return action_attach(m, d); + if (arg_loop_ref) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref= not supported when operating via systemd-mountfsd."); - r = dissected_image_load_verity_sig_partition( - m, - d->fd, - &arg_verity_settings); - if (r < 0) - return log_error_errno(r, "Failed to load verity signature partition: %m"); + if (verity_settings_set(&arg_verity_settings)) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Externally configured verity settings not supported when operating via systemd-mountfsd."); + + /* Don't run things in private userns, if the mount shall be attached to the host */ + if (!IN_SET(arg_action, ACTION_MOUNT, ACTION_WITH)) { + userns_fd = nsresource_allocate_userns(/* name= */ NULL, UINT64_C(0x10000)); /* allocate 64K users by default */ + if (userns_fd < 0) + return log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m"); + } - if (arg_action != ACTION_DISSECT) { - r = dissected_image_decrypt_interactively( - m, NULL, - &arg_verity_settings, - arg_flags); + r = mountfsd_mount_image( + arg_image, + userns_fd, + arg_image_policy, + arg_flags, + &m); if (r < 0) return r; } @@ -1906,7 +2155,7 @@ static int run(int argc, char *argv[]) { switch (arg_action) { case ACTION_DISSECT: - return action_dissect(m, d); + return action_dissect(m, d, userns_fd); case ACTION_MOUNT: return action_mount(m, d); @@ -1915,7 +2164,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, userns_fd); case ACTION_WITH: return action_with(m, d); |