mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
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
This commit is contained in:
@@ -32,6 +32,12 @@
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--umount</option> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--attach</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--detach</option> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--list</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
@@ -173,6 +179,25 @@
|
||||
<listitem><para>This is a shortcut for <option>--umount --rmdir</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--attach</option></term>
|
||||
|
||||
<listitem><para>Attach the specified disk image to an automatically allocated loopback block device,
|
||||
and print the path to the loopback block device to standard output. This is similar to an invocation
|
||||
of <command>losetup --find --show</command>, but will validate the image as DDI before attaching, and
|
||||
derive the correct sector size to use automatically. Moreover, it ensures the per-partition block
|
||||
devices are created before returning. Takes a path to a disk image file.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--detach</option></term>
|
||||
|
||||
<listitem><para>Detach the specified disk image from a loopback block device. This undoes the effect
|
||||
of <option>--attach</option> above. This expects either a path to a loopback block device as an
|
||||
argument, or the path to the backing image file. In the latter case it will automatically determine
|
||||
the right device to detach.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--list</option></term>
|
||||
<term><option>-l</option></term>
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user