]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add mount options support for MountImages 16686/head
authorLuca Boccassi <luca.boccassi@microsoft.com>
Fri, 31 Jul 2020 14:06:15 +0000 (15:06 +0100)
committerLuca Boccassi <bluca@debian.org>
Thu, 20 Aug 2020 13:45:40 +0000 (14:45 +0100)
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
src/core/dbus-execute.c
src/core/execute.c
src/core/load-fragment.c
src/core/namespace.c
src/core/namespace.h
src/shared/bus-unit-util.c
src/systemctl/systemctl.c
test/units/testsuite-50.sh

index c2e9cf21879d075e2c6d5e82af323e3443850d1a..6ac877f9bc53a88c62df3eceaca5611fee341872 100644 (file)
         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 <literal>-</literal>, in which case it will be
+        definitions, optionally followed by another colon and a list of mount options.</para>
+
+        <para>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
+        <varname>RootImageOptions=</varname> setting described above.</para>
+
+        <para>Each mount definition may be prefixed with <literal>-</literal>, 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 <literal>:</literal>, it needs to be escaped as
-        <literal>\:</literal>.
-        The device node or file system image file needs to follow the same rules as specified
-        for <varname>RootImage=</varname>. Any mounts created with this option are specific to the unit, and
-        are not visible in the host's mount table.</para>
+        <literal>\:</literal>. The device node or file system image file needs to follow the same rules as
+        specified for <varname>RootImage=</varname>. Any mounts created with this option are specific to the
+        unit, and are not visible in the host's mount table.</para>
 
         <para>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
index f611d677dd21509d37a4109768ff55120c6641db..17d128c1b16420648f004f78b80337c3e11b6960 100644 (file)
@@ -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)
index ef8212fcb78df4bbb1ae387c212f2044bdc649bf..fd041e6ed34dba4d44b26c7c96db3f77529a418f 100644 (file)
@@ -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) {
index 5da5f929cfa966229a1d207d1c63efde9125c8af..958c79915f453c1d14c9412db3a2b585728ac9fe 100644 (file)
@@ -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)
index 16e12c59fc4f57a0125a548d5b25ad4661237fef..79b3ac4f9d7e02c41d7760c0f000e0fd75e7e661 100644 (file)
@@ -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,
         };
 
index e41d2aa68d9967f1f4a88be056a521f1d3526296..dac53c76ef7c57d9905b11fd4b8f892b0527f42b 100644 (file)
@@ -76,6 +76,7 @@ struct TemporaryFileSystem {
 struct MountImage {
         char *source;
         char *destination;
+        LIST_HEAD(MountOptions, mount_options);
         bool ignore_enoent;
 };
 
index b5f98afa35f4968e56d4672004f338582416c5de..7fd2595c0b05e1891e1631d642e919ccf870fe12 100644 (file)
@@ -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);
                 }
index 032932fd776080b5569f9ed5a541138397feb2c9..24856572a87708dc198d38779db03e92c8ff22b4 100644 (file)
@@ -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);
index 8a9cedf37559c5f226d31e5b8bc3e270a2da2c6a..376d4c9e23c4fb40060b2e31bc6e17adae43e8d7 100755 (executable)
@@ -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 <<EOF
+cat >/run/systemd/system/testservice-50c.service <<EOF
 [Service]
+MountAPIVFS=yes
 TemporaryFileSystem=/run
 RootImage=${image}.raw
-MountImages=${image}.gpt:/run/img1
-MountImages=${image}.raw:/run/img2\:3
-ExecStart=/usr/bin/cat /run/img1/usr/lib/os-release
-ExecStart=/usr/bin/cat /run/img2:3/usr/lib/os-release
+MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
+MountImages=${image}.raw:/run/img2\:3:nosuid
+ExecStart=bash -c "cat /run/img1/usr/lib/os-release > /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