summaryrefslogtreecommitdiffstats
path: root/src/dissect/dissect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dissect/dissect.c')
-rw-r--r--src/dissect/dissect.c496
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);