summaryrefslogtreecommitdiff
path: root/src/dissect
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2023-03-06 11:59:16 +0100
committerLennart Poettering <lennart@poettering.net>2023-03-09 16:40:55 +0100
commit07d6072e0ea25e6047007bca53b329b03c76db43 (patch)
treedf83be86e3f7a28a4c552d2c3b0b2b573b0e6bb6 /src/dissect
parent999ac3e2b0b729cba0db27cdcace28ff509e0fe4 (diff)
downloadsystemd-07d6072e0ea25e6047007bca53b329b03c76db43.tar.gz
dissect: add commands for attaching/detaching loopback devices
Sometimes it is useful attaching DDIs without mounting them. We could use "losetup" for that, but doing this in systemd-dissect has various benefits: 1. we superficially validate the DDI first 2. we set the sector size depending on what we determine 3. we synchronously create the per-partition block devices
Diffstat (limited to 'src/dissect')
-rw-r--r--src/dissect/dissect.c146
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,