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.
This commit is contained in:
Luca Boccassi
2026-01-06 17:59:10 +01:00
parent 9573ad4e76
commit 9de41f677c
14 changed files with 311 additions and 378 deletions
+1 -2
View File
@@ -213,8 +213,7 @@
<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>
+41 -41
View 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",
+20 -22
View 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;
+1 -1
View 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);
+91 -176
View 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;
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
r = extract_first_word(&val, &word, NULL, 0);
if (r < 0)
return r;
if (r == 0)
break;
r = deserialize_mount_options(val, &options);
if (r < 0)
return r;
p = word;
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
if (r < 0)
return r;
if (r == 0)
continue;
partition_designator = partition_designator_from_string(partition);
if (partition_designator < 0)
return -EINVAL;
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) {
+16 -15
View 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");
}
+1 -1
View 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;
+22 -50
View 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,
+8 -19
View 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,
};
+1 -2
View 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;
+32 -26
View 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,
+66 -12
View File
@@ -4619,23 +4619,78 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
return k >= 0 && image->partitions[k].found;
}
MountOptions* mount_options_free_all(MountOptions *options) {
MountOptions *m;
int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s) {
assert(options);
assert(d >= 0);
while ((m = LIST_POP(mount_options, options))) {
free(m->options);
free(m);
if (!*options) {
*options = new0(MountOptions, 1);
if (!*options) {
free(s);
return log_oom();
}
}
return NULL;
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) {
if (!options)
return NULL;
free_many_charp(options->options, _PARTITION_DESIGNATOR_MAX);
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(
+4 -4
View 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) {
+7 -7
View File
@@ -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 \