From c20acbb2bd0810f6832b71e62624ec065dd0457f Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 6 Aug 2020 19:06:22 +0100 Subject: [PATCH 1/4] core: cleanup unused variables Leftovers from previous implementation of MountImages feature, unused now --- src/core/namespace.c | 1 - src/core/namespace.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/core/namespace.c b/src/core/namespace.c index 858ccc4118..16e12c59fc 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -73,7 +73,6 @@ typedef struct MountEntry { char *options_malloc; unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */ unsigned n_followed; - LIST_FIELDS(MountEntry, mount_entry); } MountEntry; /* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted diff --git a/src/core/namespace.h b/src/core/namespace.h index d1e0a28562..e41d2aa68d 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -9,7 +9,6 @@ typedef struct NamespaceInfo NamespaceInfo; typedef struct BindMount BindMount; typedef struct TemporaryFileSystem TemporaryFileSystem; typedef struct MountImage MountImage; -typedef struct MountEntry MountEntry; #include From bc8d56d30502db5462be6dbc0c0801267be1deca Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 6 Aug 2020 19:43:22 +0100 Subject: [PATCH 2/4] core: use strv_split_colon_pairs when parsing RootImageOptions --- src/core/load-fragment.c | 48 ++++++++++++++------------------------ src/shared/bus-unit-util.c | 34 +++++++++++---------------- 2 files changed, 31 insertions(+), 51 deletions(-) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 266382c84c..aed5674f2f 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1429,9 +1429,10 @@ int config_parse_root_image_options( void *userdata) { _cleanup_(mount_options_free_allp) MountOptions *options = NULL; + _cleanup_strv_free_ char **l = NULL; + char **first = NULL, **second = NULL; ExecContext *c = data; const Unit *u = userdata; - const char *p = rvalue; int r; assert(filename); @@ -1444,43 +1445,30 @@ int config_parse_root_image_options( return 0; } - for (;;) { - _cleanup_free_ char *mount_options_resolved = NULL, *first = NULL, *tuple = NULL; - const char *mount_options = NULL, *second = NULL; + r = strv_split_colon_pairs(&l, rvalue); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue); + return 0; + } + + STRV_FOREACH_PAIR(first, second, l) { + _cleanup_free_ char *mount_options_resolved = NULL; + const char *mount_options = NULL; MountOptions *o = NULL; unsigned int partition_number = 0; - r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue); - return 0; - } - - second = tuple; - r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == 0) - continue; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue); - continue; - } - /* Format is either '0:foo' or 'foo' (0 is implied) */ - if (!isempty(second) && second[-1] == ':') { - mount_options = second; - r = safe_atou(first, &partition_number); + if (!isempty(*second)) { + mount_options = *second; + r = safe_atou(*first, &partition_number); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", first); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", *first); continue; } } else - mount_options = first; + mount_options = *first; r = unit_full_printf(u, mount_options, &mount_options_resolved); if (r < 0) { diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 3f4ab65af8..d307e746f1 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1456,6 +1456,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con } if (streq(field, "RootImageOptions")) { + _cleanup_strv_free_ char **l = NULL; + char **first = NULL, **second = NULL; const char *p = eq; r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); @@ -1474,34 +1476,24 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (r < 0) return bus_log_create_error(r); - for (;;) { - _cleanup_free_ char *first = NULL, *tuple = NULL; - const char *mount_options = NULL, *second = NULL; + r = strv_split_colon_pairs(&l, p); + if (r < 0) + return log_error_errno(r, "Failed to parse argument: %m"); + + STRV_FOREACH_PAIR(first, second, l) { + const char *mount_options; unsigned partition_number = 0; - r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE); - if (r == 0) - break; - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - - second = tuple; - r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == 0) - continue; - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - /* Format is either '0:foo' or 'foo' (0 is implied) */ - if (!isempty(second) && second[-1] == ':') { - mount_options = second; - r = safe_atou(first, &partition_number); + if (!isempty(*second)) { + mount_options = *second; + r = safe_atou(*first, &partition_number); if (r < 0) { - log_error_errno(r, "Failed to parse partition number from %s: %m", first); + log_error_errno(r, "Failed to parse partition number from %s: %m", *first); continue; } } else - mount_options = first; + mount_options = *first; r = sd_bus_message_append(m, "(us)", partition_number, mount_options); if (r < 0) From 9ece644435c13ab01ce322b5c243fc2def3f4ec6 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 14 Aug 2020 18:50:46 +0100 Subject: [PATCH 3/4] core: change RootImageOptions to use names instead of partition numbers Follow the designations from the Discoverable Partitions Specification --- man/systemd.exec.xml | 50 +++++++++++++++++++++++++++++++++++--- src/core/dbus-execute.c | 26 +++++++++++++------- src/core/execute.c | 4 ++- src/core/load-fragment.c | 23 +++++++++--------- src/shared/bus-unit-util.c | 25 +++++++------------ src/shared/dissect-image.c | 12 ++++----- src/shared/dissect-image.h | 4 +-- test/units/testsuite-50.sh | 25 +++++++++++-------- 8 files changed, 110 insertions(+), 59 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index c339f3b885..c2e9cf2187 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -149,11 +149,53 @@ RootImageOptions= Takes a comma-separated list of mount options that will be used on disk images specified by - RootImage=. Optionally a partition number can be prefixed, followed by colon, in - case the image has multiple partitions, otherwise partition number 0 is implied. + RootImage=. Optionally a partition name can be prefixed, followed by colon, in + case the image has multiple partitions, otherwise partition name root is implied. Options for multiple partitions can be specified in a single line with space separators. Assigning an empty - string removes previous assignments. For a list of valid mount options, please refer to - mount8. + string removes previous assignments. Duplicated options are ignored. For a list of valid mount options, please + refer to mount8. + + Valid partition names follow the Discoverable + Partitions Specification. + + + Accepted partition names + + + + + + Partition Name + + + + + root + + + root-secondary + + + home + + + srv + + + esp + + + xbootldr + + + tmp + + + var + + + +
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index d5a24b9ab7..f611d677dd 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -802,12 +802,14 @@ static int property_get_root_image_options( assert(property); assert(reply); - r = sd_bus_message_open_container(reply, 'a', "(us)"); + r = sd_bus_message_open_container(reply, 'a', "(ss)"); if (r < 0) return r; LIST_FOREACH(mount_options, m, c->root_image_options) { - r = sd_bus_message_append(reply, "(us)", m->partition_number, m->options); + r = sd_bus_message_append(reply, "(ss)", + partition_designator_to_string(m->partition_designator), + m->options); if (r < 0) return r; } @@ -891,7 +893,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RootImageOptions", "a(us)", property_get_root_image_options, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootImageOptions", "a(ss)", property_get_root_image_options, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -1371,29 +1373,35 @@ int bus_exec_context_set_transient_property( if (streq(name, "RootImageOptions")) { _cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_free_ char *format_str = NULL; - const char *mount_options; - unsigned partition_number; + const char *mount_options, *partition; - r = sd_bus_message_enter_container(message, 'a', "(us)"); + r = sd_bus_message_enter_container(message, 'a', "(ss)"); if (r < 0) return r; - while ((r = sd_bus_message_read(message, "(us)", &partition_number, &mount_options)) > 0) { + while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) { _cleanup_free_ char *previous = TAKE_PTR(format_str); _cleanup_free_ MountOptions *o = NULL; + int partition_designator; if (chars_intersect(mount_options, WHITESPACE)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mount options string, contains whitespace character(s): %s", mount_options); - if (asprintf(&format_str, "%s%s%u:%s", strempty(previous), previous ? " " : "", partition_number, mount_options) < 0) + partition_designator = partition_designator_from_string(partition); + if (partition_designator < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid partition name: %s", partition); + + format_str = strjoin(previous, previous ? " " : "", partition, ":", mount_options); + if (!format_str) return -ENOMEM; o = new(MountOptions, 1); if (!o) return -ENOMEM; *o = (MountOptions) { - .partition_number = partition_number, + .partition_designator = partition_designator, .options = strdup(mount_options), }; if (!o->options) diff --git a/src/core/execute.c b/src/core/execute.c index 123396f6f0..ef8212fcb7 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -4634,7 +4634,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sRootImageOptions:", prefix); LIST_FOREACH(mount_options, o, c->root_image_options) if (!isempty(o->options)) - fprintf(f, " %u:%s", o->partition_number, o->options); + fprintf(f, " %s:%s", + partition_designator_to_string(o->partition_designator), + o->options); fprintf(f, "\n"); } diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index aed5674f2f..5da5f929cf 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1454,22 +1454,23 @@ int config_parse_root_image_options( } STRV_FOREACH_PAIR(first, second, l) { - _cleanup_free_ char *mount_options_resolved = NULL; - const char *mount_options = NULL; MountOptions *o = NULL; - unsigned int partition_number = 0; + _cleanup_free_ char *mount_options_resolved = NULL; + const char *mount_options = NULL, *partition = "root"; + int partition_designator; - /* Format is either '0:foo' or 'foo' (0 is implied) */ + /* Format is either 'root:foo' or 'foo' (root is implied) */ if (!isempty(*second)) { + partition = *first; mount_options = *second; - r = safe_atou(*first, &partition_number); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", *first); - continue; - } } else mount_options = *first; + partition_designator = partition_designator_from_string(partition); + if (partition_designator < 0) { + log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid partition name %s, ignoring", partition); + continue; + } r = unit_full_printf(u, mount_options, &mount_options_resolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options); @@ -1480,10 +1481,10 @@ int config_parse_root_image_options( if (!o) return log_oom(); *o = (MountOptions) { - .partition_number = partition_number, + .partition_designator = partition_designator, .options = TAKE_PTR(mount_options_resolved), }; - LIST_APPEND(mount_options, options, o); + LIST_APPEND(mount_options, options, TAKE_PTR(o)); } /* empty spaces/separators only */ diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index d307e746f1..b5f98afa35 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -10,6 +10,7 @@ #include "condition.h" #include "coredump-util.h" #include "cpu-set-util.h" +#include "dissect-image.h" #include "escape.h" #include "exec-util.h" #include "exit-status.h" @@ -1468,11 +1469,11 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'v', "a(us)"); + r = sd_bus_message_open_container(m, 'v', "a(ss)"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'a', "(us)"); + r = sd_bus_message_open_container(m, 'a', "(ss)"); if (r < 0) return bus_log_create_error(r); @@ -1481,21 +1482,13 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con return log_error_errno(r, "Failed to parse argument: %m"); STRV_FOREACH_PAIR(first, second, l) { - const char *mount_options; - unsigned partition_number = 0; + /* Format is either 'root:foo' or 'foo' (root is implied) */ + if (!isempty(*second) && partition_designator_from_string(*first) < 0) + return bus_log_create_error(-EINVAL); - /* Format is either '0:foo' or 'foo' (0 is implied) */ - if (!isempty(*second)) { - mount_options = *second; - r = safe_atou(*first, &partition_number); - if (r < 0) { - log_error_errno(r, "Failed to parse partition number from %s: %m", *first); - continue; - } - } else - mount_options = *first; - - r = sd_bus_message_append(m, "(us)", partition_number, mount_options); + r = sd_bus_message_append(m, "(ss)", + !isempty(*second) ? *first : "root", + !isempty(*second) ? *second : *first); if (r < 0) return bus_log_create_error(r); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index f41d1a0e48..98cf15cd33 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -420,7 +420,7 @@ int dissect_image( m->verity = root_hash && verity_data; m->can_verity = !!verity_data; - options = mount_options_from_part(mount_options, 0); + options = mount_options_from_part(mount_options, PARTITION_ROOT); if (options) { o = strdup(options); if (!o) @@ -716,7 +716,7 @@ int dissect_image( if (!n) return -ENOMEM; - options = mount_options_from_part(mount_options, nr); + options = mount_options_from_part(mount_options, designator); if (options) { o = strdup(options); if (!o) @@ -773,7 +773,7 @@ int dissect_image( if (!n) return -ENOMEM; - options = mount_options_from_part(mount_options, nr); + options = mount_options_from_part(mount_options, PARTITION_XBOOTLDR); if (options) { o = strdup(options); if (!o) @@ -827,7 +827,7 @@ int dissect_image( if (multiple_generic) return -ENOTUNIQ; - options = mount_options_from_part(mount_options, generic_nr); + options = mount_options_from_part(mount_options, PARTITION_ROOT); if (options) { o = strdup(options); if (!o) @@ -2023,11 +2023,11 @@ MountOptions* mount_options_free_all(MountOptions *options) { return NULL; } -const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number) { +const char* mount_options_from_part(const MountOptions *options, int designator) { MountOptions *m; LIST_FOREACH(mount_options, m, (MountOptions *)options) - if (partition_number == m->partition_number && !isempty(m->options)) + if (designator == m->partition_designator && !isempty(m->options)) return m->options; return NULL; diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 4d21789e18..96f9840400 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -87,14 +87,14 @@ struct DissectedImage { }; struct MountOptions { - unsigned partition_number; + int partition_designator; char *options; LIST_FIELDS(MountOptions, mount_options); }; MountOptions* mount_options_free_all(MountOptions *options); DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all); -const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number); +const char* mount_options_from_part(const MountOptions *options, int designator); int probe_filesystem(const char *node, char **ret_fstype); int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret); diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index bb3b20da3e..8a9cedf375 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -128,33 +128,38 @@ umount ${image_dir}/mount systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" -systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="1:ro,noatime 2:ro,dev nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid" -systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="1:ro,noatime 1:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime" +systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="root:ro,noatime root:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime" +mkdir -p mkdir -p ${image_dir}/result cat > /run/systemd/system/testservice-50a.service < /run/result/a" +BindPaths=${image_dir}/result:/run/result +TemporaryFileSystem=/run MountAPIVFS=yes RootImage=${image}.raw -RootImageOptions=1:ro,noatime,nosuid 2:ro,dev noatime,dev +RootImageOptions=root:ro,noatime home:ro,dev relatime,dev RootImageOptions=nosuid,dev EOF systemctl start testservice-50a.service -journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F "noatime" -journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F -v "nosuid" +grep -F "squashfs" ${image_dir}/result/a | grep -q -F "noatime" +grep -F "squashfs" ${image_dir}/result/a | grep -q -F -v "nosuid" cat > /run/systemd/system/testservice-50b.service < /run/result/b" +BindPaths=${image_dir}/result:/run/result +TemporaryFileSystem=/run MountAPIVFS=yes RootImage=${image}.gpt -RootImageOptions=1:ro,noatime,nosuid 2:ro,dev nosuid,dev -RootImageOptions=2:ro,dev nosuid,dev,%%foo +RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev +RootImageOptions=home:ro,dev nosuid,dev,%%foo EOF systemctl start testservice-50b.service -journalctl -b -u testservice-50b.service | grep -F "squashfs" | grep -q -F "noatime" +grep -F "squashfs" ${image_dir}/result/b | grep -q -F "noatime" # Check that specifier escape is applied %%foo -> %foo busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo" From 427353f6689e236d6b35ce3ab2dacab7d61a0c77 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 31 Jul 2020 15:06:15 +0100 Subject: [PATCH 4/4] core: add mount options support for MountImages Follow the same model established for RootImage and RootImageOptions, and allow to either append a single list of options or tuples of partition_number:options. --- man/systemd.exec.xml | 16 ++-- src/core/dbus-execute.c | 172 +++++++++++++++++++++++++++---------- src/core/execute.c | 15 +++- src/core/load-fragment.c | 91 ++++++++++++++++---- src/core/namespace.c | 28 +++++- src/core/namespace.h | 1 + src/shared/bus-unit-util.c | 78 ++++++++++++++--- src/systemctl/systemctl.c | 46 ++++++++-- test/units/testsuite-50.sh | 24 ++++-- 9 files changed, 372 insertions(+), 99 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index c2e9cf2187..6ac877f9bc 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -310,13 +310,19 @@ system hierarchy from a block device node or loopback file, but the destination directory can be specified as well as mount options. This option expects a whitespace separated list of mount definitions. Each definition consists of a colon-separated tuple of source path and destination - directory. Each mount definition may be prefixed with -, in which case it will be + definitions, optionally followed by another colon and a list of mount options. + + Mount options may be defined as a single comma-separated list of options, in which case they + will be implicitly applied to the root partition on the image, or a series of colon-separated tuples + of partition name and mount options. Valid partition names and mount options are the same as for + RootImageOptions= setting described above. + + Each mount definition may be prefixed with -, in which case it will be ignored when its source path does not exist. The source argument is a path to a block device node or regular file. If source or destination contain a :, it needs to be escaped as - \:. - The device node or file system image file needs to follow the same rules as specified - for RootImage=. Any mounts created with this option are specific to the unit, and - are not visible in the host's mount table. + \:. The device node or file system image file needs to follow the same rules as + specified for RootImage=. Any mounts created with this option are specific to the + unit, and are not visible in the host's mount table. These settings may be used more than once, each usage appends to the unit's list of mount paths. If the empty string is assigned, the entire list of mount paths defined prior to this is diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index f611d677dd..17d128c1b1 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -834,18 +834,39 @@ static int property_get_mount_images( assert(property); assert(reply); - r = sd_bus_message_open_container(reply, 'a', "(ssb)"); + r = sd_bus_message_open_container(reply, 'a', "(ssba(ss))"); if (r < 0) return r; for (size_t i = 0; i < c->n_mount_images; i++) { + MountOptions *m; + + r = sd_bus_message_open_container(reply, SD_BUS_TYPE_STRUCT, "ssba(ss)"); + if (r < 0) + return r; r = sd_bus_message_append( - reply, "(ssb)", + reply, "ssb", c->mount_images[i].source, c->mount_images[i].destination, c->mount_images[i].ignore_enoent); if (r < 0) return r; + r = sd_bus_message_open_container(reply, 'a', "(ss)"); + if (r < 0) + return r; + LIST_FOREACH(mount_options, m, c->mount_images[i].mount_options) { + r = sd_bus_message_append(reply, "(ss)", + partition_designator_to_string(m->partition_designator), + m->options); + if (r < 0) + return r; + } + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; } return sd_bus_message_close_container(reply); @@ -899,7 +920,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MountImages", "a(ssb)", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MountImages", "a(ssba(ss))", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -1340,6 +1361,74 @@ static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint6 static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flags_to_string); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check); +/* ret_format_str is an accumulator, so if it has any pre-existing content, new options will be appended to it */ +static int read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator) { + _cleanup_(mount_options_free_allp) MountOptions *options = NULL; + _cleanup_free_ char *format_str = NULL; + const char *mount_options, *partition; + int r; + + assert(message); + assert(ret_options); + assert(ret_format_str); + assert(separator); + + r = sd_bus_message_enter_container(message, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) { + _cleanup_free_ char *previous = NULL, *escaped = NULL; + _cleanup_free_ MountOptions *o = NULL; + int partition_designator; + + if (chars_intersect(mount_options, WHITESPACE)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid mount options string, contains whitespace character(s): %s", mount_options); + + partition_designator = partition_designator_from_string(partition); + if (partition_designator < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid partition name %s", partition); + + /* Need to store them in the unit with the escapes, so that they can be parsed again */ + escaped = shell_escape(mount_options, ":"); + if (!escaped) + return -ENOMEM; + + previous = TAKE_PTR(format_str); + format_str = strjoin(previous, previous ? separator : "", partition, ":", escaped); + if (!format_str) + return -ENOMEM; + + o = new(MountOptions, 1); + if (!o) + return -ENOMEM; + *o = (MountOptions) { + .partition_designator = partition_designator, + .options = strdup(mount_options), + }; + if (!o->options) + return -ENOMEM; + LIST_APPEND(mount_options, options, TAKE_PTR(o)); + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!LIST_IS_EMPTY(options)) { + char *final = strjoin(*ret_format_str, !isempty(*ret_format_str) ? separator : "", format_str); + if (!final) + return -ENOMEM; + free_and_replace(*ret_format_str, final); + LIST_JOIN(mount_options, *ret_options, options); + } + + return 0; +} + int bus_exec_context_set_transient_property( Unit *u, ExecContext *c, @@ -1373,45 +1462,8 @@ int bus_exec_context_set_transient_property( if (streq(name, "RootImageOptions")) { _cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_free_ char *format_str = NULL; - const char *mount_options, *partition; - r = sd_bus_message_enter_container(message, 'a', "(ss)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) { - _cleanup_free_ char *previous = TAKE_PTR(format_str); - _cleanup_free_ MountOptions *o = NULL; - int partition_designator; - - if (chars_intersect(mount_options, WHITESPACE)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, - "Invalid mount options string, contains whitespace character(s): %s", mount_options); - - partition_designator = partition_designator_from_string(partition); - if (partition_designator < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, - "Invalid partition name: %s", partition); - - format_str = strjoin(previous, previous ? " " : "", partition, ":", mount_options); - if (!format_str) - return -ENOMEM; - - o = new(MountOptions, 1); - if (!o) - return -ENOMEM; - *o = (MountOptions) { - .partition_designator = partition_designator, - .options = strdup(mount_options), - }; - if (!o->options) - return -ENOMEM; - LIST_APPEND(mount_options, options, TAKE_PTR(o)); - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); + r = read_mount_options(message, error, &options, &format_str, " "); if (r < 0) return r; @@ -2946,13 +2998,23 @@ int bus_exec_context_set_transient_property( char *source, *destination; int permissive; - r = sd_bus_message_enter_container(message, 'a', "(ssb)"); + r = sd_bus_message_enter_container(message, 'a', "(ssba(ss))"); if (r < 0) return r; - while ((r = sd_bus_message_read(message, "(ssb)", &source, &destination, &permissive)) > 0) { + for (;;) { + _cleanup_(mount_options_free_allp) MountOptions *options = NULL; + _cleanup_free_ char *source_escaped = NULL, *destination_escaped = NULL; char *tuple; + r = sd_bus_message_enter_container(message, 'r', "ssba(ss)"); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "ssb", &source, &destination, &permissive); + if (r <= 0) + break; + if (!path_is_absolute(source)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not absolute.", source); if (!path_is_normalized(source)) @@ -2962,15 +3024,37 @@ int bus_exec_context_set_transient_property( if (!path_is_normalized(destination)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination); - tuple = strjoin(format_str, format_str ? " " : "", permissive ? "-" : "", source, ":", destination); + /* Need to store them in the unit with the escapes, so that they can be parsed again */ + source_escaped = shell_escape(source, ":"); + if (!source_escaped) + return -ENOMEM; + destination_escaped = shell_escape(destination, ":"); + if (!destination_escaped) + return -ENOMEM; + + tuple = strjoin(format_str, + format_str ? " " : "", + permissive ? "-" : "", + source_escaped, + ":", + destination_escaped); if (!tuple) return -ENOMEM; free_and_replace(format_str, tuple); + r = read_mount_options(message, error, &options, &format_str, ":"); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + r = mount_image_add(&mount_images, &n_mount_images, &(MountImage) { .source = source, .destination = destination, + .mount_options = options, .ignore_enoent = permissive, }); if (r < 0) diff --git a/src/core/execute.c b/src/core/execute.c index ef8212fcb7..fd041e6ed3 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -5037,11 +5037,20 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%d\n", c->syscall_errno); } - for (i = 0; i < c->n_mount_images; i++) - fprintf(f, "%sMountImages: %s%s:%s\n", prefix, + for (i = 0; i < c->n_mount_images; i++) { + MountOptions *o; + + fprintf(f, "%sMountImages: %s%s:%s%s", prefix, c->mount_images[i].ignore_enoent ? "-": "", c->mount_images[i].source, - c->mount_images[i].destination); + c->mount_images[i].destination, + LIST_IS_EMPTY(c->mount_images[i].mount_options) ? "": ":"); + LIST_FOREACH(mount_options, o, c->mount_images[i].mount_options) + fprintf(f, "%s:%s", + partition_designator_to_string(o->partition_designator), + o->options); + fprintf(f, "\n"); + } } bool exec_context_maintains_privileges(const ExecContext *c) { diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 5da5f929cf..958c79915f 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -4676,10 +4676,9 @@ int config_parse_mount_images( void *data, void *userdata) { - _cleanup_strv_free_ char **l = NULL; ExecContext *c = data; const Unit *u = userdata; - char **source = NULL, **destination = NULL; + const char *p = rvalue; int r; assert(filename); @@ -4693,23 +4692,31 @@ int config_parse_mount_images( return 0; } - r = strv_split_colon_pairs(&l, rvalue); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue); - return 0; - } - - STRV_FOREACH_PAIR(source, destination, l) { + for (;;) { + _cleanup_(mount_options_free_allp) MountOptions *options = NULL; + _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL; _cleanup_free_ char *sresolved = NULL, *dresolved = NULL; + const char *q = NULL; char *s = NULL; bool permissive = false; - r = unit_full_printf(u, *source, &sresolved); + r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + q = tuple; + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + + r = unit_full_printf(u, first, &sresolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve unit specifiers in \"%s\", ignoring: %m", *source); + "Failed to resolve unit specifiers in \"%s\", ignoring: %m", first); continue; } @@ -4723,15 +4730,15 @@ int config_parse_mount_images( if (r < 0) continue; - if (isempty(*destination)) { + if (isempty(second)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue); continue; } - r = unit_full_printf(u, *destination, &dresolved); + r = unit_full_printf(u, second, &dresolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers in \"%s\", ignoring: %m", *destination); + "Failed to resolve specifiers in \"%s\", ignoring: %m", second); continue; } @@ -4739,10 +4746,62 @@ int config_parse_mount_images( if (r < 0) continue; + for (;;) { + _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL; + MountOptions *o = NULL; + int partition_designator; + + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL); + if (r < 0) + return r; + if (r == 0) + break; + /* Single set of options, applying to the root partition/single filesystem */ + if (r == 1) { + r = unit_full_printf(u, partition, &mount_options_resolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first); + continue; + } + + o = new(MountOptions, 1); + if (!o) + return log_oom(); + *o = (MountOptions) { + .partition_designator = PARTITION_ROOT, + .options = TAKE_PTR(mount_options_resolved), + }; + LIST_APPEND(mount_options, options, o); + + break; + } + + partition_designator = partition_designator_from_string(partition); + if (partition_designator < 0) { + log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid partition name %s, ignoring", partition); + continue; + } + r = unit_full_printf(u, mount_options, &mount_options_resolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options); + continue; + } + + o = new(MountOptions, 1); + if (!o) + return log_oom(); + *o = (MountOptions) { + .partition_designator = partition_designator, + .options = TAKE_PTR(mount_options_resolved), + }; + LIST_APPEND(mount_options, options, o); + } + r = mount_image_add(&c->mount_images, &c->n_mount_images, &(MountImage) { .source = s, .destination = dresolved, + .mount_options = options, .ignore_enoent = permissive, }); if (r < 0) diff --git a/src/core/namespace.c b/src/core/namespace.c index 16e12c59fc..79b3ac4f9d 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -73,6 +73,7 @@ typedef struct MountEntry { char *options_malloc; unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */ unsigned n_followed; + LIST_HEAD(MountOptions, image_options); } MountEntry; /* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted @@ -246,6 +247,7 @@ static void mount_entry_done(MountEntry *p) { p->path_malloc = mfree(p->path_malloc); p->source_malloc = mfree(p->source_malloc); p->options_malloc = mfree(p->options_malloc); + p->image_options = mount_options_free_all(p->image_options); } static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) { @@ -338,6 +340,7 @@ static int append_mount_images(MountEntry **p, const MountImage *mount_images, s .path_const = m->destination, .mode = MOUNT_IMAGES, .source_const = m->source, + .image_options = m->mount_options, .ignore = m->ignore_enoent, }; } @@ -924,10 +927,10 @@ static int mount_images(const MountEntry *m) { if (r < 0) return log_debug_errno(r, "Failed to create loop device for image: %m"); - r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, NULL, dissect_image_flags, &dissected_image); + r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, m->image_options, dissect_image_flags, &dissected_image); /* No partition table? Might be a single-filesystem image, try again */ if (!verity_data && r < 0 && r == -ENOPKG) - r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, NULL, dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE, &dissected_image); + r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, m->image_options, dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE, &dissected_image); if (r < 0) return log_debug_errno(r, "Failed to dissect image: %m"); @@ -1837,6 +1840,7 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) { for (i = 0; i < *n; i++) { free(m[i].source); free(m[i].destination); + mount_options_free_all(m[i].mount_options); } free(m); @@ -1846,6 +1850,8 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) { int mount_image_add(MountImage **m, size_t *n, const MountImage *item) { _cleanup_free_ char *s = NULL, *d = NULL; + _cleanup_(mount_options_free_allp) MountOptions *options = NULL; + MountOptions *i; MountImage *c; assert(m); @@ -1860,6 +1866,23 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) { if (!d) return -ENOMEM; + LIST_FOREACH(mount_options, i, item->mount_options) { + _cleanup_(mount_options_free_allp) MountOptions *o; + + o = new(MountOptions, 1); + if (!o) + return -ENOMEM; + + *o = (MountOptions) { + .partition_designator = i->partition_designator, + .options = strdup(i->options), + }; + if (!o->options) + return -ENOMEM; + + LIST_APPEND(mount_options, options, TAKE_PTR(o)); + } + c = reallocarray(*m, *n + 1, sizeof(MountImage)); if (!c) return -ENOMEM; @@ -1869,6 +1892,7 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) { c[(*n) ++] = (MountImage) { .source = TAKE_PTR(s), .destination = TAKE_PTR(d), + .mount_options = TAKE_PTR(options), .ignore_enoent = item->ignore_enoent, }; diff --git a/src/core/namespace.h b/src/core/namespace.h index e41d2aa68d..dac53c76ef 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -76,6 +76,7 @@ struct TemporaryFileSystem { struct MountImage { char *source; char *destination; + LIST_HEAD(MountOptions, mount_options); bool ignore_enoent; }; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index b5f98afa35..7fd2595c0b 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1509,8 +1509,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con } if (streq(field, "MountImages")) { - _cleanup_strv_free_ char **l = NULL; - char **source = NULL, **destination = NULL; const char *p = eq; r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); @@ -1521,33 +1519,85 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'v', "a(ssb)"); + r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'a', "(ssb)"); + r = sd_bus_message_open_container(m, 'a', "(ssba(ss))"); if (r < 0) return bus_log_create_error(r); - r = strv_split_colon_pairs(&l, p); - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - - STRV_FOREACH_PAIR(source, destination, l) { - char *s = *source; + for (;;) { + _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL; + const char *q = NULL, *source = NULL; bool permissive = false; - if (s[0] == '-') { + r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + q = tuple; + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + + source = first; + if (source[0] == '-') { permissive = true; - s++; + source++; } - if (isempty(*destination)) + if (isempty(second)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument after ':': %s", eq); - r = sd_bus_message_append(m, "(ssb)", s, *destination, permissive); + r = sd_bus_message_open_container(m, 'r', "ssba(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssb", source, second, permissive); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(ss)"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *partition = NULL, *mount_options = NULL; + + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL); + if (r < 0) + return r; + if (r == 0) + break; + /* Single set of options, applying to the root partition/single filesystem */ + if (r == 1) { + r = sd_bus_message_append(m, "(ss)", "root", partition); + if (r < 0) + return bus_log_create_error(r); + + break; + } + + if (partition_designator_from_string(partition) < 0) + return bus_log_create_error(-EINVAL); + + r = sd_bus_message_append(m, "(ss)", partition, mount_options); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 032932fd77..24856572a8 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5411,24 +5411,56 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return 1; } else if (streq(name, "MountImages")) { _cleanup_free_ char *paths = NULL; - const char *source, *dest; - int ignore_enoent; - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssb)"); + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssba(ss))"); if (r < 0) return bus_log_parse_error(r); - while ((r = sd_bus_message_read(m, "(ssb)", &source, &dest, &ignore_enoent)) > 0) { + for (;;) { _cleanup_free_ char *str = NULL; + const char *source, *destination, *partition, *mount_options; + int ignore_enoent; - if (isempty(source)) - continue; + r = sd_bus_message_enter_container(m, 'r', "ssba(ss)"); + if (r < 0) + return r; - if (asprintf(&str, "%s%s:%s", ignore_enoent ? "-" : "", source, dest) < 0) + r = sd_bus_message_read(m, "ssb", &source, &destination, &ignore_enoent); + if (r <= 0) + break; + + str = strjoin(ignore_enoent ? "-" : "", + source, + ":", + destination); + if (!str) return log_oom(); + r = sd_bus_message_enter_container(m, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(m, "(ss)", &partition, &mount_options)) > 0) { + _cleanup_free_ char *previous = NULL; + + previous = TAKE_PTR(str); + str = strjoin(strempty(previous), previous ? ":" : "", partition, ":", mount_options); + if (!str) + return log_oom(); + } + if (r < 0) + return r; + if (!strextend_with_separator(&paths, " ", str, NULL)) return log_oom(); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; } if (r < 0) return bus_log_parse_error(r); diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index 8a9cedf375..376d4c9e23 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -164,25 +164,33 @@ grep -F "squashfs" ${image_dir}/result/b | grep -q -F "noatime" # Check that specifier escape is applied %%foo -> %foo busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo" -# Now do some checks with MountImages, both by itself and in combination with RootImage, and as single FS or GPT image +# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid" +systemd-run -t --property MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid" systemd-run -t --property MountImages="${image}.raw:/run/img2\:3" /usr/bin/cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -t --property MountImages="${image}.raw:/run/img2\:3:nosuid" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid" systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.gpt --property RootHash=${roothash} --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" -cat >/run/systemd/system/testservice-50.service </run/systemd/system/testservice-50c.service < /run/result/c" +ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >> /run/result/c" +ExecStart=bash -c "mount >> /run/result/c" +BindPaths=${image_dir}/result:/run/result Type=oneshot EOF -systemctl start testservice-50.service -journalctl -b -u testservice-50.service | grep -q -F "MARKER=1" +systemctl start testservice-50c.service +grep -q -F "MARKER=1" ${image_dir}/result/c +grep -F "squashfs" ${image_dir}/result/c | grep -q -F "noatime" +grep -F "squashfs" ${image_dir}/result/c | grep -q -F -v "nosuid" echo OK > /testok