diff options
Diffstat (limited to 'src/dissect')
-rw-r--r-- | src/dissect/dissect.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index db26645fd9..6f7b44f0aa 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -52,6 +52,8 @@ static enum { ACTION_DISSECT, ACTION_MOUNT, ACTION_UMOUNT, + ACTION_ATTACH, + ACTION_DETACH, ACTION_LIST, ACTION_MTREE, ACTION_WITH, @@ -94,6 +96,8 @@ static int help(void) { printf("%1$s [OPTIONS...] IMAGE\n" "%1$s [OPTIONS...] --mount IMAGE PATH\n" "%1$s [OPTIONS...] --umount PATH\n" + "%1$s [OPTIONS...] --attach IMAGE\n" + "%1$s [OPTIONS...] --detach PATH\n" "%1$s [OPTIONS...] --list IMAGE\n" "%1$s [OPTIONS...] --mtree IMAGE\n" "%1$s [OPTIONS...] --with IMAGE [COMMAND…]\n" @@ -126,6 +130,8 @@ static int help(void) { " -M Shortcut for --mount --mkdir\n" " -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" " -l --list List all the files and directories of the specified\n" " OS image\n" " --mtree Show BSD mtree manifest of OS image\n" @@ -206,6 +212,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_JSON, ARG_MTREE, ARG_DISCOVER, + ARG_ATTACH, + ARG_DETACH, }; static const struct option options[] = { @@ -215,6 +223,8 @@ static int parse_argv(int argc, char *argv[]) { { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "mount", no_argument, NULL, 'm' }, { "umount", no_argument, NULL, 'u' }, + { "attach", no_argument, NULL, ARG_ATTACH }, + { "detach", no_argument, NULL, ARG_DETACH }, { "with", no_argument, NULL, ARG_WITH }, { "read-only", no_argument, NULL, 'r' }, { "discard", required_argument, NULL, ARG_DISCARD }, @@ -291,6 +301,14 @@ static int parse_argv(int argc, char *argv[]) { arg_rmdir = true; break; + case ARG_ATTACH: + arg_action = ACTION_ATTACH; + break; + + case ARG_DETACH: + arg_action = ACTION_DETACH; + break; + case 'l': arg_action = ACTION_LIST; arg_flags |= DISSECT_IMAGE_READ_ONLY; @@ -454,6 +472,22 @@ static int parse_argv(int argc, char *argv[]) { arg_path = argv[optind]; break; + case ACTION_ATTACH: + if (optind + 1 != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected an image file path as only argument."); + + arg_image = argv[optind]; + break; + + case ACTION_DETACH: + if (optind + 1 != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected an image file path or loopback device as only argument."); + + arg_image = argv[optind]; + break; + case ACTION_LIST: case ACTION_MTREE: if (optind + 1 != argc) @@ -1486,6 +1520,113 @@ static int action_discover(void) { return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); } +static int action_attach(DissectedImage *m, LoopDevice *d) { + int r; + + assert(m); + assert(d); + + r = loop_device_set_autoclear(d, false); + if (r < 0) + return log_error_errno(r, "Failed to disable auto-clear logic on loopback device: %m"); + + r = dissected_image_relinquish(m); + if (r < 0) + return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); + + puts(d->node); + return 0; +} + +static int action_detach(const char *path) { + _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL; + _cleanup_close_ int fd = -EBADF; + struct stat st; + int r; + + fd = open(path, O_PATH|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open '%s': %m", path); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", path); + + if (S_ISBLK(st.st_mode)) { + r = loop_device_open_from_fd(fd, O_RDONLY, LOCK_EX, &loop); + if (r < 0) + return log_error_errno(r, "Failed to open '%s' as loopback block device: %m", path); + + } else if (S_ISREG(st.st_mode)) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d; + + /* If a regular file is specified, search for a loopback block device that is backed by it */ + + r = sd_device_enumerator_new(&e); + if (r < 0) + return log_error_errno(r, "Failed to allocate enumerator: %m"); + + r = sd_device_enumerator_add_match_subsystem(e, "block", true); + if (r < 0) + return log_error_errno(r, "Failed to match block devices: %m"); + + r = sd_device_enumerator_add_match_sysname(e, "loop*"); + if (r < 0) + return log_error_errno(r, "Failed to match loopback block devices: %m"); + + (void) sd_device_enumerator_allow_uninitialized(e); + + 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 */ + 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); + continue; + } + + if (entry_loop->backing_devno == st.st_dev && entry_loop->backing_inode == st.st_ino) { + /* Found it! The kernel allows attaching a single file to multiple loopback + * devices. Let's destruct them in reverse order, i.e. find the last matching + * loopback device here, rather than the first. */ + + loop_device_unref(loop); + loop = TAKE_PTR(entry_loop); + } + } + + if (!loop) + return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No loopback block device backed by '%s' found.", path); + + r = loop_device_flock(loop, LOCK_EX); + if (r < 0) + return log_error_errno(r, "Failed to upgrade device lock: %m"); + } + + r = loop_device_set_autoclear(loop, true); + if (r < 0) + log_warning_errno(r, "Failed to enable autoclear logic on '%s', ignoring: %m", loop->node); + + loop_device_unrelinquish(loop); + return 0; +} + static int run(int argc, char *argv[]) { _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; @@ -1503,6 +1644,8 @@ static int run(int argc, char *argv[]) { if (arg_action == ACTION_UMOUNT) return action_umount(arg_path); + if (arg_action == ACTION_DETACH) + return action_detach(arg_image); if (arg_action == ACTION_DISCOVER) return action_discover(); @@ -1537,6 +1680,9 @@ static int run(int argc, char *argv[]) { 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, |