]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: change mount options settings so that last defined wins
authorLuca Boccassi <luca.boccassi@gmail.com>
Sat, 25 Oct 2025 17:40:44 +0000 (18:40 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 6 Jan 2026 16:59:10 +0000 (17:59 +0100)
Currently mount options are handled in such a way that the first
definition for a given partition wins, and documented as such.
Change them so that they behave like other options, and the
last specified wins.
Applies to RootImageOptions=, MountImages= and ExtensionImages=.
Switch from a linked list to an array indexed by the partition
specifier to store them.

14 files changed:
man/systemd.exec.xml
src/core/dbus-execute.c
src/core/dbus-util.c
src/core/dbus-util.h
src/core/execute-serialize.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment.c
src/core/namespace.c
src/core/namespace.h
src/core/varlink-execute.c
src/shared/dissect-image.c
src/shared/dissect-image.h
test/units/TEST-50-DISSECT.dissect.sh

index 927fa3e0c42329340e0ae6eff56ea62a002e4f9f..813ed4602eac9d67463c8822f59d8c89cb4ac3a5 100644 (file)
         <varname>RootImage=</varname>. Optionally a partition name can be prefixed, followed by colon, in
         case the image has multiple partitions, otherwise partition name <literal>root</literal> is implied.
         Options for multiple partitions can be specified in a single line with space separators. Assigning an empty
-        string removes previous assignments. Duplicated options are ignored. For a list of valid mount options, please
-        refer to
+        string removes previous assignments. For a list of valid mount options, please refer to
         <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
         </para>
 
index 658fc1ee060d3ea7d1631f9bdb2f213a593835e7..1558e136228fb23ade16c15d91fc6131e8f6455c 100644 (file)
@@ -793,6 +793,33 @@ static int property_get_root_hash_sig(
         return sd_bus_message_append_array(reply, 'y', c->root_hash_sig.iov_base, c->root_hash_sig.iov_len);
 }
 
+static int bus_append_mount_options(
+                sd_bus_message *reply,
+                MountOptions *options) {
+
+        int r;
+
+        assert(reply);
+
+        r = sd_bus_message_open_container(reply, 'a', "(ss)");
+        if (r < 0)
+                return r;
+
+        if (options)
+                for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+                        if (isempty(options->options[i]))
+                                continue;
+
+                        r = sd_bus_message_append(reply, "(ss)",
+                                                  partition_designator_to_string(i),
+                                                  options->options[i]);
+                        if (r < 0)
+                                return r;
+                }
+
+        return sd_bus_message_close_container(reply);
+}
+
 static int property_get_root_image_options(
                 sd_bus *bus,
                 const char *path,
@@ -803,25 +830,12 @@ static int property_get_root_image_options(
                 sd_bus_error *reterr_error) {
 
         ExecContext *c = ASSERT_PTR(userdata);
-        int r;
 
         assert(bus);
         assert(property);
         assert(reply);
 
-        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, "(ss)",
-                                          partition_designator_to_string(m->partition_designator),
-                                          m->options);
-                if (r < 0)
-                        return r;
-        }
-
-        return sd_bus_message_close_container(reply);
+        return bus_append_mount_options(reply, c->root_image_options);
 }
 
 static int property_get_mount_images(
@@ -857,19 +871,7 @@ static int property_get_mount_images(
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_open_container(reply, 'a', "(ss)");
-                if (r < 0)
-                        return r;
-
-                LIST_FOREACH(mount_options, m, 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);
+                r = bus_append_mount_options(reply, i->mount_options);
                 if (r < 0)
                         return r;
 
@@ -913,19 +915,7 @@ static int property_get_extension_images(
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_open_container(reply, 'a', "(ss)");
-                if (r < 0)
-                        return r;
-
-                LIST_FOREACH(mount_options, m, 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);
+                r = bus_append_mount_options(reply, i->mount_options);
                 if (r < 0)
                         return r;
 
@@ -1899,7 +1889,17 @@ int bus_exec_context_set_transient_property(
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         if (options) {
-                                LIST_JOIN(mount_options, c->root_image_options, options);
+                                if (!c->root_image_options)
+                                        c->root_image_options = TAKE_PTR(options);
+                                else
+                                        for (PartitionDesignator j = 0; j < _PARTITION_DESIGNATOR_MAX; j++) {
+                                                if (isempty(options->options[j])) {
+                                                        if (options->options[j]) /* Free current value if "" is passed */
+                                                                c->root_image_options->options[j] = mfree(c->root_image_options->options[j]);
+                                                        continue;
+                                                }
+                                                free_and_replace(c->root_image_options->options[j], options->options[j]);
+                                        }
                                 unit_write_settingf(
                                                 u, flags|UNIT_ESCAPE_SPECIFIERS, name,
                                                 "%s=%s",
index 74d559c6a329dba58c019a7926d21f0186db2162..f8d14d4209dcac3738b068eb47322db585da8f6b 100644 (file)
@@ -256,12 +256,13 @@ int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_
                         reterr_error);
 }
 
-/* ret_format_str is an accumulator, so if it has any pre-existing content, new options will be appended to it */
+/* in_out_format_str is an accumulator, so if it has any pre-existing content it will be read, and new
+ * options will be appended to it */
 int bus_read_mount_options(
                 sd_bus_message *message,
                 sd_bus_error *reterr_error,
                 MountOptions **ret_options,
-                char **ret_format_str,
+                char **in_out_format_str,
                 const char *separator) {
 
         _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
@@ -279,7 +280,6 @@ int bus_read_mount_options(
 
         while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) {
                 _cleanup_free_ char *escaped = NULL;
-                _cleanup_free_ MountOptions *o = NULL;
                 PartitionDesignator partition_designator;
 
                 if (chars_intersect(mount_options, WHITESPACE))
@@ -295,19 +295,21 @@ int bus_read_mount_options(
                 if (!escaped)
                         return -ENOMEM;
 
-                if (!strextend_with_separator(&format_str, separator, partition, ":", escaped))
-                        return -ENOMEM;
+                if (!isempty(escaped)) {
+                        if (!strextend_with_separator(&format_str, separator, partition, ":", escaped))
+                                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 (!options) {
+                                options = new0(MountOptions, 1);
+                                if (!options)
+                                        return -ENOMEM;
+                        }
+
+                        r = free_and_strdup(&options->options[partition_designator], mount_options);
+                        if (r < 0)
+                                return r;
+                } else if (options)
+                        options->options[partition_designator] = mfree(options->options[partition_designator]);
         }
         if (r < 0)
                 return r;
@@ -317,13 +319,9 @@ int bus_read_mount_options(
                 return r;
 
         if (options) {
-                if (ret_format_str) {
-                        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);
+                if (in_out_format_str && !strextend_with_separator(in_out_format_str, separator, format_str))
+                        return -ENOMEM;
+                *ret_options = TAKE_PTR(options);
         }
 
         return 0;
index 5de3f988c7a276dcf9605ab998009b5fb43ae500..a58d24a4b629c889aa2eab6ac7a135c22727a97f 100644 (file)
@@ -261,6 +261,6 @@ int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_erro
 int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *reterr_error);
 int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_bus_error *reterr_error);
 
-int bus_read_mount_options(sd_bus_message *message, sd_bus_error *reterr_error, MountOptions **ret_options, char **ret_format_str, const char *separator);
+int bus_read_mount_options(sd_bus_message *message, sd_bus_error *reterr_error, MountOptions **ret_options, char **in_out_format_str, const char *separator);
 
 int bus_property_get_activation_details(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *reterr_error);
index ba28e913aed8a43358cdd6474bfc2dda8f73ac22..6c71d1a0a10367d6e3cade740d4f01300c452a4b 100644 (file)
@@ -1547,6 +1547,77 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
         return 0;
 }
 
+static int serialize_mount_options(const MountOptions *mount_options, char **s) {
+        assert(s);
+
+        if (!mount_options)
+                return 0;
+
+        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+                _cleanup_free_ char *escaped = NULL;
+
+                if (isempty(mount_options->options[i]))
+                        continue;
+
+                escaped = shell_escape(mount_options->options[i], ":");
+                if (!escaped)
+                        return log_oom_debug();
+
+                if (!strextend(s,
+                               " ",
+                               partition_designator_to_string(i),
+                               ":",
+                               escaped))
+                        return log_oom_debug();
+        }
+
+        return 0;
+}
+
+static int deserialize_mount_options(const char *s, MountOptions **ret_mount_options) {
+        _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
+        int r;
+
+        assert(ret_mount_options);
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL;
+                PartitionDesignator partition_designator;
+                const char *p;
+
+                r = extract_first_word(&s, &word, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                p = word;
+                r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+                if (r != 2) {
+                        log_warning("Failed to parse mount options entry '%s', ignoring.", word);
+                        continue;
+                }
+
+                partition_designator = partition_designator_from_string(partition);
+                if (partition_designator < 0) {
+                        log_warning_errno(partition_designator, "Unknown partition designator '%s' in exec-context-root-image-options= entry, ignoring.", partition);
+                        continue;
+                }
+
+                r = mount_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options));
+                if (r < 0)
+                        return r;
+        }
+
+        *ret_mount_options = TAKE_PTR(options);
+
+        return 0;
+}
+
 static int exec_context_serialize(const ExecContext *c, FILE *f) {
         int r;
 
@@ -1594,22 +1665,9 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
         if (c->root_image_options) {
                 _cleanup_free_ char *options = NULL;
 
-                LIST_FOREACH(mount_options, o, c->root_image_options) {
-                        if (isempty(o->options))
-                                continue;
-
-                        _cleanup_free_ char *escaped = NULL;
-                        escaped = shell_escape(o->options, ":");
-                        if (!escaped)
-                                return log_oom_debug();
-
-                        if (!strextend(&options,
-                                        " ",
-                                        partition_designator_to_string(o->partition_designator),
-                                               ":",
-                                               escaped))
-                                        return log_oom_debug();
-                }
+                r = serialize_mount_options(c->root_image_options, &options);
+                if (r < 0)
+                        return r;
 
                 r = serialize_item(f, "exec-context-root-image-options", options);
                 if (r < 0)
@@ -2398,23 +2456,9 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
                 if (!s)
                         return log_oom_debug();
 
-                LIST_FOREACH(mount_options, o, mount->mount_options) {
-                        _cleanup_free_ char *escaped = NULL;
-
-                        if (isempty(o->options))
-                                continue;
-
-                        escaped = shell_escape(o->options, ":");
-                        if (!escaped)
-                                return log_oom_debug();
-
-                        if (!strextend(&s,
-                                       " ",
-                                       partition_designator_to_string(o->partition_designator),
-                                       ":",
-                                       escaped))
-                                return log_oom_debug();
-                }
+                r = serialize_mount_options(mount->mount_options, &s);
+                if (r < 0)
+                        return r;
 
                 r = serialize_item(f, "exec-context-mount-image", s);
                 if (r < 0)
@@ -2433,23 +2477,9 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
                 if (!s)
                         return log_oom_debug();
 
-                LIST_FOREACH(mount_options, o, mount->mount_options) {
-                        _cleanup_free_ char *escaped = NULL;
-
-                        if (isempty(o->options))
-                                continue;
-
-                        escaped = shell_escape(o->options, ":");
-                        if (!escaped)
-                                return log_oom_debug();
-
-                        if (!strextend(&s,
-                                       " ",
-                                       partition_designator_to_string(o->partition_designator),
-                                       ":",
-                                       escaped))
-                                return log_oom_debug();
-                }
+                r = serialize_mount_options(mount->mount_options, &s);
+                if (r < 0)
+                        return r;
 
                 r = serialize_item(f, "exec-context-extension-image", s);
                 if (r < 0)
@@ -2565,38 +2595,13 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                                 return k;
                         free_and_replace(c->root_image, p);
                 } else if ((val = startswith(l, "exec-context-root-image-options="))) {
-                        for (;;) {
-                                _cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL;
-                                PartitionDesignator partition_designator;
-                                MountOptions *o = NULL;
-                                const char *p;
-
-                                r = extract_first_word(&val, &word, NULL, 0);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        break;
-
-                                p = word;
-                                r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        continue;
+                        _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
 
-                                partition_designator = partition_designator_from_string(partition);
-                                if (partition_designator < 0)
-                                        return -EINVAL;
+                        r = deserialize_mount_options(val, &options);
+                        if (r < 0)
+                                return r;
 
-                                o = new(MountOptions, 1);
-                                if (!o)
-                                        return log_oom_debug();
-                                *o = (MountOptions) {
-                                        .partition_designator = partition_designator,
-                                        .options = TAKE_PTR(mount_options),
-                                };
-                                LIST_APPEND(mount_options, c->root_image_options, o);
-                        }
+                        free_and_replace_full(c->root_image_options, options, mount_options_free_all);
                 } else if ((val = startswith(l, "exec-context-root-verity="))) {
                         r = free_and_strdup(&c->root_verity, val);
                         if (r < 0)
@@ -3538,54 +3543,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                         if (isempty(destination))
                                 continue;
 
-                        for (;;) {
-                                _cleanup_free_ char *tuple = NULL, *partition = NULL, *opts = NULL;
-                                PartitionDesignator partition_designator;
-                                MountOptions *o = NULL;
-                                const char *p;
-
-                                r = extract_first_word(&val, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        break;
-
-                                p = tuple;
-                                r = extract_many_words(&p,
-                                                       ":",
-                                                       EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
-                                                       &partition,
-                                                       &opts);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        continue;
-                                if (r == 1) {
-                                        o = new(MountOptions, 1);
-                                        if (!o)
-                                                return log_oom_debug();
-                                        *o = (MountOptions) {
-                                                .partition_designator = PARTITION_ROOT,
-                                                .options = TAKE_PTR(partition),
-                                        };
-                                        LIST_APPEND(mount_options, options, o);
-
-                                        continue;
-                                }
-
-                                partition_designator = partition_designator_from_string(partition);
-                                if (partition_designator < 0)
-                                        continue;
-
-                                o = new(MountOptions, 1);
-                                if (!o)
-                                        return log_oom_debug();
-                                *o = (MountOptions) {
-                                        .partition_designator = partition_designator,
-                                        .options = TAKE_PTR(opts),
-                                };
-                                LIST_APPEND(mount_options, options, o);
-                        }
+                        r = deserialize_mount_options(val, &options);
+                        if (r < 0)
+                                return r;
 
                         r = mount_image_add(&c->mount_images, &c->n_mount_images,
                                         &(MountImage) {
@@ -3618,54 +3578,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
                                 s++;
                         }
 
-                        for (;;) {
-                                _cleanup_free_ char *tuple = NULL, *partition = NULL, *opts = NULL;
-                                PartitionDesignator partition_designator;
-                                MountOptions *o = NULL;
-                                const char *p;
-
-                                r = extract_first_word(&val, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        break;
-
-                                p = tuple;
-                                r = extract_many_words(&p,
-                                                       ":",
-                                                       EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
-                                                       &partition,
-                                                       &opts);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        continue;
-                                if (r == 1) {
-                                        o = new(MountOptions, 1);
-                                        if (!o)
-                                                return log_oom_debug();
-                                        *o = (MountOptions) {
-                                                .partition_designator = PARTITION_ROOT,
-                                                .options = TAKE_PTR(partition),
-                                        };
-                                        LIST_APPEND(mount_options, options, o);
-
-                                        continue;
-                                }
-
-                                partition_designator = partition_designator_from_string(partition);
-                                if (partition_designator < 0)
-                                        continue;
-
-                                o = new(MountOptions, 1);
-                                if (!o)
-                                        return log_oom_debug();
-                                *o = (MountOptions) {
-                                        .partition_designator = partition_designator,
-                                        .options = TAKE_PTR(opts),
-                                };
-                                LIST_APPEND(mount_options, options, o);
-                        }
+                        r = deserialize_mount_options(val, &options);
+                        if (r < 0)
+                                return r;
 
                         r = mount_image_add(&c->extension_images, &c->n_extension_images,
                                         &(MountImage) {
index a5cb664d6120d98e071669613eaeeabf2ffcd6e8..00f2c7bd2b66a6a3e8c5d740edff8e58669a12e4 100644 (file)
@@ -1167,13 +1167,10 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
 
         if (c->root_image_options) {
-                fprintf(f, "%sRootImageOptions:", prefix);
-                LIST_FOREACH(mount_options, o, c->root_image_options)
-                        if (!isempty(o->options))
-                                fprintf(f, " %s:%s",
-                                        partition_designator_to_string(o->partition_designator),
-                                        o->options);
-                fprintf(f, "\n");
+                _cleanup_free_ char *opts_str = NULL;
+
+                if (mount_options_to_string(c->root_image_options, &opts_str) >= 0 && !isempty(opts_str))
+                        fprintf(f, "%sRootImageOptions: %s\n", prefix, opts_str);
         }
 
         if (iovec_is_set(&c->root_hash)) {
@@ -1580,10 +1577,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                         mount->ignore_enoent ? "-": "",
                         mount->source,
                         mount->destination);
-                LIST_FOREACH(mount_options, o, mount->mount_options)
-                        fprintf(f, ":%s:%s",
-                                partition_designator_to_string(o->partition_designator),
-                                strempty(o->options));
+                if (mount->mount_options) {
+                        _cleanup_free_ char *opts = NULL;
+
+                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
+                                fprintf(f, " %s", opts);
+                }
                 fprintf(f, "\n");
         }
 
@@ -1591,10 +1590,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f, "%sExtensionImages: %s%s", prefix,
                         mount->ignore_enoent ? "-": "",
                         mount->source);
-                LIST_FOREACH(mount_options, o, mount->mount_options)
-                        fprintf(f, ":%s:%s",
-                                partition_designator_to_string(o->partition_designator),
-                                strempty(o->options));
+                if (mount->mount_options) {
+                        _cleanup_free_ char *opts = NULL;
+
+                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
+                                fprintf(f, " %s", opts);
+                }
                 fprintf(f, "\n");
         }
 
index ae26be6781c2bee98a38a751c869d43b9e00288c..9dca4c2001bdcf01c265fca0c600d6c9888893a0 100644 (file)
@@ -183,7 +183,7 @@ typedef struct ExecContext {
         struct rlimit *rlimit[_RLIMIT_MAX];
         char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
         struct iovec root_hash, root_hash_sig;
-        LIST_HEAD(MountOptions, root_image_options);
+        MountOptions *root_image_options;
         bool root_ephemeral;
         bool working_directory_missing_ok:1;
         bool working_directory_home:1;
index 6a59d33af25799668596f6c44e85373705be68c3..a81f5e4607c5e22fe00e733b8c27efbde88c8aad 100644 (file)
@@ -1662,7 +1662,6 @@ int config_parse_root_image_options(
         }
 
         STRV_FOREACH_PAIR(first, second, l) {
-                MountOptions *o = NULL;
                 _cleanup_free_ char *mount_options_resolved = NULL;
                 const char *mount_options = NULL, *partition = "root";
                 PartitionDesignator partition_designator;
@@ -1686,23 +1685,12 @@ int config_parse_root_image_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, TAKE_PTR(o));
+                r = mount_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options_resolved));
+                if (r < 0)
+                        return r;
         }
 
-        if (options)
-                LIST_JOIN(mount_options, c->root_image_options, options);
-        else
-                /* empty spaces/separators only */
-                c->root_image_options = mount_options_free_all(c->root_image_options);
-
-        return 0;
+        return free_and_replace_full(c->root_image_options, options, mount_options_free_all);
 }
 
 int config_parse_exec_root_hash(
@@ -5272,7 +5260,6 @@ int config_parse_mount_images(
 
                 for (;;) {
                         _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
-                        MountOptions *o = NULL;
                         PartitionDesignator partition_designator;
 
                         r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
@@ -5292,14 +5279,9 @@ int config_parse_mount_images(
                                         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);
+                                r = mount_options_set_and_consume(&options, PARTITION_ROOT, TAKE_PTR(mount_options_resolved));
+                                if (r < 0)
+                                        return r;
 
                                 break;
                         }
@@ -5316,14 +5298,9 @@ int config_parse_mount_images(
                                 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_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options_resolved));
+                        if (r < 0)
+                                return r;
                 }
 
                 r = mount_image_add(&c->mount_images, &c->n_mount_images,
@@ -5414,7 +5391,6 @@ int config_parse_extension_images(
 
                 for (;;) {
                         _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
-                        MountOptions *o = NULL;
                         PartitionDesignator partition_designator;
 
                         r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
@@ -5434,14 +5410,12 @@ int config_parse_extension_images(
                                         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);
+                                if (!options) {
+                                        options = new0(MountOptions, 1);
+                                        if (!options)
+                                                return log_oom();
+                                }
+                                free_and_replace(options->options[PARTITION_ROOT], mount_options_resolved);
 
                                 break;
                         }
@@ -5457,14 +5431,12 @@ int config_parse_extension_images(
                                 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);
+                        if (!options) {
+                                options = new0(MountOptions, 1);
+                                if (!options)
+                                        return log_oom();
+                        }
+                        free_and_replace(options->options[partition_designator], mount_options_resolved);
                 }
 
                 r = mount_image_add(&c->extension_images, &c->n_extension_images,
index d75d70f1bc2f0a0d1103fc6190bc504ab4f72113..623ef24ef7be05ff8b7fb935ceba74991515daae 100644 (file)
@@ -27,7 +27,6 @@
 #include "glyph-util.h"
 #include "iovec-util.h"
 #include "label-util.h"
-#include "list.h"
 #include "lock-util.h"
 #include "log.h"
 #include "loop-util.h"
@@ -117,7 +116,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_const);
+        MountOptions *image_options_const;
         char **overlay_layers;
         VeritySettings verity;
         ImageClass filter_class; /* Used for live updates to skip inapplicable images */
@@ -3128,7 +3127,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;
+        _cleanup_(mount_options_free_allp) MountOptions *o = NULL;
+        int r;
 
         assert(m);
         assert(n);
@@ -3144,21 +3144,10 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
                         return -ENOMEM;
         }
 
-        LIST_FOREACH(mount_options, i, item->mount_options) {
-                _cleanup_(mount_options_free_allp) MountOptions *o = NULL;
-
-                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));
+        if (item->mount_options) {
+                r = mount_options_dup(item->mount_options, &o);
+                if (r < 0)
+                        return r;
         }
 
         if (!GREEDY_REALLOC(*m, *n + 1))
@@ -3167,7 +3156,7 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
         (*m)[(*n)++] = (MountImage) {
                 .source = TAKE_PTR(s),
                 .destination = TAKE_PTR(d),
-                .mount_options = TAKE_PTR(options),
+                .mount_options = TAKE_PTR(o),
                 .ignore_enoent = item->ignore_enoent,
                 .type = item->type,
         };
index aae43db3896c79fb5ae41091ffa2f415a3f5c72d..ba4f9b80a2d99787160c328f81a1c6a66d341245 100644 (file)
@@ -6,7 +6,6 @@
 ***/
 
 #include "core-forward.h"
-#include "list.h"
 #include "runtime-scope.h"
 
 typedef enum ProtectHome {
@@ -120,7 +119,7 @@ typedef enum MountImageType {
 typedef struct MountImage {
         char *source;
         char *destination; /* Unused if MountImageType == MOUNT_IMAGE_EXTENSION */
-        LIST_HEAD(MountOptions, mount_options);
+        MountOptions *mount_options;
         bool ignore_enoent;
         MountImageType type;
 } MountImage;
index 70e43386c4ed4f78628fa7632552966cc209e219..5dcb043a555437bbdd374d4de28dc5101b4feac4 100644 (file)
@@ -45,27 +45,43 @@ static int working_directory_build_json(sd_json_variant **ret, const char *name,
                         SD_JSON_BUILD_PAIR_BOOLEAN("missingOK", c->working_directory_missing_ok));
 }
 
-static int root_image_options_build_json(sd_json_variant **ret, const char *name, void *userdata) {
-        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
-        MountOptions *root_image_options = userdata;
+static int json_append_mount_options(sd_json_variant **v, MountOptions *options) {
         int r;
 
-        assert(ret);
-        assert(name);
+        assert(v);
+
+        if (!options)
+                return 0;
+
+        for (PartitionDesignator j = 0; j < _PARTITION_DESIGNATOR_MAX; j++) {
+                if (!options->options[j])
+                        continue;
 
-        LIST_FOREACH(mount_options, m, root_image_options) {
                 r = sd_json_variant_append_arraybo(
-                                &v,
-                                SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(m->partition_designator)),
-                                SD_JSON_BUILD_PAIR_STRING("options", m->options));
+                                v,
+                                SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(j)),
+                                SD_JSON_BUILD_PAIR_STRING("options", options->options[j]));
                 if (r < 0)
                         return r;
         }
 
-        *ret = TAKE_PTR(v);
         return 0;
 }
 
+static int root_image_options_build_json(sd_json_variant **ret, const char *name, void *userdata) {
+        MountOptions *root_image_options = userdata;
+
+        assert(ret);
+        assert(name);
+
+        if (!root_image_options) {
+                *ret = NULL;
+                return 0;
+        }
+
+        return json_append_mount_options(ret, root_image_options);
+}
+
 static int image_policy_build_json(sd_json_variant **ret, const char *name, void *userdata) {
         _cleanup_free_ char *s = NULL;
         ImagePolicy *policy = userdata;
@@ -119,14 +135,9 @@ static int mount_images_build_json(sd_json_variant **ret, const char *name, void
         FOREACH_ARRAY(i, c->mount_images, c->n_mount_images) {
                 _cleanup_(sd_json_variant_unrefp) sd_json_variant *mo = NULL;
 
-                LIST_FOREACH(mount_options, m, i->mount_options) {
-                        r = sd_json_variant_append_arraybo(
-                                        &mo,
-                                        SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(m->partition_designator)),
-                                        SD_JSON_BUILD_PAIR_STRING("options", m->options));
-                        if (r < 0)
-                                return r;
-                }
+                r = json_append_mount_options(&mo, i->mount_options);
+                if (r < 0)
+                        return r;
 
                 r = sd_json_variant_append_arraybo(
                                 &v,
@@ -153,14 +164,9 @@ static int extension_images_build_json(sd_json_variant **ret, const char *name,
         FOREACH_ARRAY(i, c->extension_images, c->n_extension_images) {
                 _cleanup_(sd_json_variant_unrefp) sd_json_variant *mo = NULL;
 
-                LIST_FOREACH(mount_options, m, i->mount_options) {
-                        r = sd_json_variant_append_arraybo(
-                                        &mo,
-                                        SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(m->partition_designator)),
-                                        SD_JSON_BUILD_PAIR_STRING("options", m->options));
-                        if (r < 0)
-                                return r;
-                }
+                r = json_append_mount_options(&mo, i->mount_options);
+                if (r < 0)
+                        return r;
 
                 r = sd_json_variant_append_arraybo(
                                 &v,
index cc37c429f67a4efe33cdc6813cf6678a7b2fe226..03edb6a72d9cdaf8f72450cca9c8a6a72f7a27e9 100644 (file)
@@ -4619,23 +4619,78 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
         return k >= 0 && image->partitions[k].found;
 }
 
+int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s) {
+        assert(options);
+        assert(d >= 0);
+
+        if (!*options) {
+                *options = new0(MountOptions, 1);
+                if (!*options) {
+                        free(s);
+                        return log_oom();
+                }
+        }
+
+        free_and_replace((*options)->options[d], s);
+
+        return 0;
+}
+
+int mount_options_dup(const MountOptions *source, MountOptions **ret) {
+        _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
+
+        assert(source);
+        assert(ret);
+
+        options = new0(MountOptions, 1);
+        if (!options)
+                return log_oom();
+
+        for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++)
+                if (source->options[d]) {
+                        options->options[d] = strdup(source->options[d]);
+                        if (!options->options[d])
+                                return log_oom_debug();
+                }
+
+        *ret = TAKE_PTR(options);
+        return 0;
+}
+
+int mount_options_to_string(const MountOptions *mount_options, char **ret) {
+        _cleanup_free_ char *s = NULL;
+
+        assert(mount_options);
+        assert(ret);
+
+        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
+                if (!isempty(mount_options->options[i]))
+                        if (!strextend_with_separator(&s, ":", "%s:%s",
+                                       partition_designator_to_string(i),
+                                       mount_options->options[i]))
+                                return log_oom_debug();
+
+        *ret = TAKE_PTR(s);
+
+        return 0;
+}
+
 MountOptions* mount_options_free_all(MountOptions *options) {
-        MountOptions *m;
+        if (!options)
+                return NULL;
 
-        while ((m = LIST_POP(mount_options, options))) {
-                free(m->options);
-                free(m);
-        }
+        free_many_charp(options->options, _PARTITION_DESIGNATOR_MAX);
 
-        return NULL;
+        return mfree(options);
 }
 
 const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
-        LIST_FOREACH(mount_options, m, options)
-                if (designator == m->partition_designator && !isempty(m->options))
-                        return m->options;
+        assert(designator >= 0 && designator < _PARTITION_DESIGNATOR_MAX);
 
-        return NULL;
+        if (!options)
+                return NULL;
+
+        return options->options[designator];
 }
 
 int mount_image_privately_interactively(
@@ -4754,8 +4809,7 @@ static bool mount_options_relax_extension_release_checks(const MountOptions *opt
                 return false;
 
         return string_contains_word(mount_options_from_designator(options, PARTITION_ROOT), ",", "x-systemd.relax-extension-release-check") ||
-                        string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check") ||
-                        string_contains_word(options->options, ",", "x-systemd.relax-extension-release-check");
+                        string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check");
 }
 
 int verity_dissect_and_mount(
index c442d624d42480c697d7c8102acc7ac86a6851a4..16b42c2ed32e705a6980361782c829de7d64a92e 100644 (file)
@@ -6,7 +6,6 @@
 #include "architecture.h"
 #include "gpt.h"
 #include "iovec-util.h"
-#include "list.h"
 #include "shared-forward.h"
 
 typedef struct DecryptedImage DecryptedImage;
@@ -114,9 +113,7 @@ typedef struct DissectedImage {
 } DissectedImage;
 
 typedef struct MountOptions {
-        PartitionDesignator partition_designator;
-        char *options;
-        LIST_FIELDS(MountOptions, mount_options);
+        char *options[_PARTITION_DESIGNATOR_MAX];
 } MountOptions;
 
 typedef struct VeritySettings {
@@ -154,6 +151,9 @@ typedef struct ExtensionReleaseData {
 MountOptions* mount_options_free_all(MountOptions *options);
 DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
 const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
+int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s);
+int mount_options_dup(const MountOptions *source, MountOptions **ret);
+int mount_options_to_string(const MountOptions *mount_options, char **ret);
 
 int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t size, bool restrict_fstypes, char **ret_fstype);
 static inline int probe_filesystem(const char *path, char **ret_fstype) {
index f16c6856cece70e878f4946717c0fec01bca6270..ad8b14139c85ac08131d775f5401ab68ad6a7b41 100755 (executable)
@@ -270,11 +270,11 @@ systemd-run -P \
             cat /usr/lib/os-release | grep -F "MARKER=1" >/dev/null
 systemd-run -P \
             -p RootImage="$MINIMAL_IMAGE.raw" \
-            -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
+            -p RootImageOptions="root:noatime,dev home:ro,dev ro,nosuid" \
             mount | grep -F "squashfs" | grep -F "nosuid" >/dev/null
 systemd-run -P \
             -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootImageOptions="root:ro,noatime root:ro,dev" \
+            -p RootImageOptions="root:ro,dev root:ro,noatime" \
             mount | grep -F "squashfs" | grep -F "noatime" >/dev/null
 
 mkdir -p "$IMAGE_DIR/result"
@@ -285,8 +285,8 @@ ExecStart=bash -c "mount >/run/result/a"
 BindPaths=$IMAGE_DIR/result:/run/result
 TemporaryFileSystem=/run
 RootImage=$MINIMAL_IMAGE.raw
-RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
-RootImageOptions=nosuid,dev
+RootImageOptions=root:relatime,dev home:ro,dev nosuid,dev
+RootImageOptions=ro,noatime
 EOF
 systemctl start testservice-50a.service
 grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -F "noatime" >/dev/null
@@ -299,8 +299,8 @@ ExecStart=bash -c "mount >/run/result/b"
 BindPaths=$IMAGE_DIR/result:/run/result
 TemporaryFileSystem=/run
 RootImage=$MINIMAL_IMAGE.gpt
-RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
-RootImageOptions=home:ro,dev nosuid,dev,%%foo
+RootImageOptions=nosuid,dev home:ro,dev nosuid,dev
+RootImageOptions=home:ro,dev,%%foo root:ro,noatime,nosuid
 # this is the default, but let's specify once to test the parser
 MountAPIVFS=yes
 EOF
@@ -310,7 +310,7 @@ grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -F "noatime" >/dev/null
 # 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"
+                    org.freedesktop.systemd1.Service RootImageOptions | grep -F "ro,dev,%foo"
 
 # 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 -P \