From: Luca Boccassi Date: Sat, 25 Oct 2025 17:40:44 +0000 (+0100) Subject: core: change mount options settings so that last defined wins X-Git-Tag: v260-rc1~421^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9de41f677c5928ed9779bc03c0b09b36c11570bc;p=thirdparty%2Fsystemd.git core: change mount options settings so that last defined wins 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. --- diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 927fa3e0c42..813ed4602ea 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -213,8 +213,7 @@ 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. 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 mount8. diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 658fc1ee060..1558e136228 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -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", diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c index 74d559c6a32..f8d14d4209d 100644 --- a/src/core/dbus-util.c +++ b/src/core/dbus-util.c @@ -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; diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h index 5de3f988c7a..a58d24a4b62 100644 --- a/src/core/dbus-util.h +++ b/src/core/dbus-util.h @@ -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); diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c index ba28e913aed..6c71d1a0a10 100644 --- a/src/core/execute-serialize.c +++ b/src/core/execute-serialize.c @@ -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) { diff --git a/src/core/execute.c b/src/core/execute.c index a5cb664d612..00f2c7bd2b6 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -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"); } diff --git a/src/core/execute.h b/src/core/execute.h index ae26be6781c..9dca4c2001b 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -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; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 6a59d33af25..a81f5e4607c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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, diff --git a/src/core/namespace.c b/src/core/namespace.c index d75d70f1bc2..623ef24ef7b 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -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, }; diff --git a/src/core/namespace.h b/src/core/namespace.h index aae43db3896..ba4f9b80a2d 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -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; diff --git a/src/core/varlink-execute.c b/src/core/varlink-execute.c index 70e43386c4e..5dcb043a555 100644 --- a/src/core/varlink-execute.c +++ b/src/core/varlink-execute.c @@ -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, diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index cc37c429f67..03edb6a72d9 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -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( diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index c442d624d42..16b42c2ed32 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -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) { diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh index f16c6856cec..ad8b14139c8 100755 --- a/test/units/TEST-50-DISSECT.dissect.sh +++ b/test/units/TEST-50-DISSECT.dissect.sh @@ -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 \