From a164d9d5aab40cdcf5685bbfa286d8e08ae76150 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 6 Mar 2023 18:25:25 +0100 Subject: [PATCH 1/3] dissect: implement external helper plugin interface for /bin/mount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change we'll install a symlink /sbin/mount.ddi → systemd-dissect. If invoked that way we'll do the equivalent of systemd-dissect --mount. This makes DDIs mountable directly via the "mount" command, by specifying the "-t ddi" pseudo file system type. Moreover you can now mount DDIs directly via /etc/fstab, by specifying "ddi" in the file system column (3rd column). --- meson.build | 4 +++ src/dissect/dissect.c | 79 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a274de5601..ff8706d94c 100644 --- a/meson.build +++ b/meson.build @@ -2478,6 +2478,10 @@ if conf.get('HAVE_BLKID') == 1 dependencies : [versiondep], install_rpath : rootpkglibdir, install : true) + + meson.add_install_script(meson_make_symlink, + bindir / 'systemd-dissect', + rootsbindir / 'mount.ddi') endif if conf.get('ENABLE_RESOLVE') == 1 diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index f08e745a55..db26645fd9 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -522,6 +522,80 @@ static int parse_argv(int argc, char *argv[]) { return 1; } +static int parse_argv_as_mount_helper(int argc, char *argv[]) { + const char *options = NULL; + bool fake = false; + int c, r; + + /* 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) { + + case 'f': + fake = true; + break; + + case 'o': + options = optarg; + break; + + case 't': + if (!streq(optarg, "ddi")) + log_debug("Unexpected file system type '%s', ignoring.", optarg); + break; + + case 's': /* sloppy mount options */ + case 'n': /* aka --no-mtab */ + case 'v': /* aka --verbose */ + log_debug("Ignoring option -%c, not implemented.", c); + break; + + case 'N': /* aka --namespace= */ + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Option -%c is not implemented, refusing.", c); + + case '?': + return -EINVAL; + } + } + + if (optind + 2 != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected an image file path and target directory as only argument."); + + for (const char *p = options;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, ",", EXTRACT_KEEP_QUOTE); + if (r < 0) + return log_error_errno(r, "Failed to extract mount option: %m"); + if (r == 0) + break; + + if (streq(word, "ro")) + SET_FLAG(arg_flags, DISSECT_IMAGE_READ_ONLY, true); + else if (streq(word, "rw")) + SET_FLAG(arg_flags, DISSECT_IMAGE_READ_ONLY, false); + else if (streq(word, "discard")) + SET_FLAG(arg_flags, DISSECT_IMAGE_DISCARD_ANY, true); + else if (streq(word, "nodiscard")) + SET_FLAG(arg_flags, DISSECT_IMAGE_DISCARD_ANY, false); + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown mount option '%s'.", word); + } + + if (fake) + return 0; + + arg_image = argv[optind]; + arg_path = argv[optind+1]; + + arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT; + arg_action = ACTION_MOUNT; + return 1; +} + static int strv_pair_to_json(char **l, JsonVariant **ret) { _cleanup_strv_free_ char **jl = NULL; @@ -1420,7 +1494,10 @@ static int run(int argc, char *argv[]) { log_setup(); - r = parse_argv(argc, argv); + if (invoked_as(argv, "mount.ddi")) + r = parse_argv_as_mount_helper(argc, argv); + else + r = parse_argv(argc, argv); if (r <= 0) return r; From 46012433750869c5ff6792adcdc6bc973b1b9e0e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 6 Mar 2023 18:27:38 +0100 Subject: [PATCH 2/3] test: add test for new /sbin/mount.ddi helper --- test/units/testsuite-50.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index a072305739..cb99b811c9 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -424,6 +424,12 @@ grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/ grep -q -F '{"name":"c","type":"raw","class":"extension","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw +# Check that the /sbin/mount.ddi helper works +T="/tmp/mounthelper.$RANDOM" +mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard +umount -R "$T" +rmdir "$T" + echo OK >/testok exit 0 From 92828ba6032bee0530111e229fa4b878ad274c48 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 6 Mar 2023 18:48:18 +0100 Subject: [PATCH 3/3] man: document /sbin/mount.ddi --- man/rules/meson.build | 2 +- man/systemd-dissect.xml | 49 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/man/rules/meson.build b/man/rules/meson.build index 2bf5936d64..ff4794b528 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -902,7 +902,7 @@ manpages = [ ['systemd-debug-generator', '8', [], ''], ['systemd-delta', '1', [], ''], ['systemd-detect-virt', '1', [], ''], - ['systemd-dissect', '1', [], 'HAVE_BLKID'], + ['systemd-dissect', '1', ['mount.ddi'], 'HAVE_BLKID'], ['systemd-environment-d-generator', '8', ['30-systemd-environment-d-generator'], diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index 6549aa92c8..01eda2702d 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -18,6 +18,7 @@ systemd-dissect + mount.ddi Dissect Discoverable Disk Images (DDIs) @@ -99,6 +100,14 @@ what it operates with when mounting the image. To display the complete list of partitions use a tool such as fdisk8. + + The systemd-dissect command may be invoked as mount.ddi in + which case it implements the mount8 "external + helper" interface. This ensures disk images compatible with systemd-dissect can be + mounted directly by mount and fstab5. For + details see below. @@ -132,7 +141,13 @@ All mounted file systems are checked with the appropriate fsck8 implementation in automatic fixing mode, unless explicitly turned off () or - read-only operation is requested (). + read-only operation is requested (). + + Note that this functionality is also available in mount8 via a + command such as mount -t ddi myimage.raw targetdir/, as well as in fstab5. For + details, see below. @@ -359,6 +374,35 @@ command is used the exit status of the invoked command is propagated. + + Invocation as <command>/sbin/mount.ddi</command> + + The systemd-dissect executable may be symlinked to + /sbin/mount.ddi. If invoked through that it implements mount8's + "external helper" interface for the (pseudo) file system type ddi. This means + conformant disk images may be mounted directly via + + # mount -t ddi myimage.raw targetdir/ + + in a fashion mostly equivalent to: + + # systemd-dissect --mount myimage.raw targetdir/ + + Note that since a single DDI may contain multiple file systems it should later be unmounted with + umount -R targetdir/, for recursive operation. + + This functionality is particularly useful to mount DDIs automatically at boot via simple + /etc/fstab entries. For example: + + /path/to/myimage.raw /images/myimage/ ddi defaults 0 0 + + When invoked this way the mount options ro, rw, + discard, nodiscard map to the corresponding options listed above + (i.e. , , + ). Mount options are not generically passed on to + the file systems inside the images. + Examples @@ -366,7 +410,7 @@ Generate a tarball from an OS disk image - $ systemd-dissect --with foo.raw tar cz . >foo.tar.gz + # systemd-dissect --with foo.raw tar cz . >foo.tar.gz @@ -377,6 +421,7 @@ systemd-nspawn1, systemd.exec5, Discoverable Partitions Specification, + mount8, umount8, fdisk8