diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 3618b52808..c5d755e897 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -145,6 +145,19 @@
+
+ RootImageOptions=
+
+ Takes a comma-separated list of mount options that will be used on disk images specified by
+ RootImage=. Optionally a partition number can be prefixed, followed by colon, in
+ case the image has multiple partitions, otherwise partition number 0 is implied.
+ Options for multiple partitions can be specified in a single line with space separators. Assigning an empty
+ string removes previous assignments. For a list of valid mount options, please refer to
+ mount8.
+
+
+
+
RootHash=
diff --git a/src/basic/list.h b/src/basic/list.h
index f7f97000e0..b62c374985 100644
--- a/src/basic/list.h
+++ b/src/basic/list.h
@@ -169,3 +169,18 @@
#define LIST_IS_EMPTY(head) \
(!(head))
+
+/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
+#define LIST_JOIN(name,a,b) \
+ do { \
+ assert(b); \
+ if (!(a)) \
+ (a) = (b); \
+ else { \
+ typeof(*(a)) *_head = (b), *_tail; \
+ LIST_FIND_TAIL(name, (a), _tail); \
+ _tail->name##_next = _head; \
+ _head->name##_prev = _tail; \
+ } \
+ (b) = NULL; \
+ } while (false)
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 50f7ada8ce..49729799ab 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -784,6 +784,37 @@ static int property_get_root_hash_sig(
return sd_bus_message_append_array(reply, 'y', c->root_hash_sig, c->root_hash_sig_size);
}
+static int property_get_root_image_options(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ MountOptions *m;
+ int r;
+
+ assert(bus);
+ assert(c);
+ assert(property);
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "(us)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(mount_options, m, c->root_image_options) {
+ r = sd_bus_message_append(reply, "(us)", m->partition_number, m->options);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -826,6 +857,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RootImageOptions", "a(us)", property_get_root_image_options, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1301,6 +1333,62 @@ int bus_exec_context_set_transient_property(
if (streq(name, "RootImage"))
return bus_set_transient_path(u, name, &c->root_image, message, flags, error);
+ if (streq(name, "RootImageOptions")) {
+ _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
+ _cleanup_free_ char *format_str = NULL;
+ const char *mount_options;
+ unsigned partition_number;
+
+ r = sd_bus_message_enter_container(message, 'a', "(us)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(us)", &partition_number, &mount_options)) > 0) {
+ _cleanup_free_ char *previous = TAKE_PTR(format_str);
+ _cleanup_free_ MountOptions *o = NULL;
+
+ if (chars_intersect(mount_options, WHITESPACE))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Invalid mount options string, contains whitespace character(s): %s", mount_options);
+
+ if (asprintf(&format_str, "%s%s%u:%s", strempty(previous), previous ? " " : "", partition_number, mount_options) < 0)
+ return -ENOMEM;
+
+ o = new(MountOptions, 1);
+ if (!o)
+ return -ENOMEM;
+ *o = (MountOptions) {
+ .partition_number = partition_number,
+ .options = strdup(mount_options),
+ };
+ if (!o->options)
+ return -ENOMEM;
+ LIST_APPEND(mount_options, options, TAKE_PTR(o));
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ if (LIST_IS_EMPTY(options)) {
+ c->root_image_options = mount_options_free_all(c->root_image_options);
+ unit_write_settingf(u, flags, name, "%s=", name);
+ } else {
+ LIST_JOIN(mount_options, c->root_image_options, options);
+ unit_write_settingf(
+ u, flags|UNIT_ESCAPE_SPECIFIERS, name,
+ "%s=%s",
+ name,
+ format_str);
+ }
+ }
+
+ return 1;
+ }
+
if (streq(name, "RootHash")) {
const void *roothash_decoded;
size_t roothash_decoded_size;
diff --git a/src/core/execute.c b/src/core/execute.c
index 2a4840a3a9..39ffcba580 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -2660,7 +2660,7 @@ static int apply_mount_namespace(
if (context->mount_flags == MS_SHARED)
log_unit_debug(u, "shared mount propagation hidden by other fs namespacing unit settings: ignoring");
- r = setup_namespace(root_dir, root_image,
+ r = setup_namespace(root_dir, root_image, context->root_image_options,
&ns_info, context->read_write_paths,
needs_sandboxing ? context->read_only_paths : NULL,
needs_sandboxing ? context->inaccessible_paths : NULL,
@@ -4207,6 +4207,7 @@ void exec_context_done(ExecContext *c) {
c->working_directory = mfree(c->working_directory);
c->root_directory = mfree(c->root_directory);
c->root_image = mfree(c->root_image);
+ c->root_image_options = mount_options_free_all(c->root_image_options);
c->root_hash = mfree(c->root_hash);
c->root_hash_size = 0;
c->root_hash_path = mfree(c->root_hash_path);
@@ -4618,6 +4619,16 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
if (c->root_image)
fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
+ if (c->root_image_options) {
+ MountOptions *o;
+
+ fprintf(f, "%sRootImageOptions:", prefix);
+ LIST_FOREACH(mount_options, o, c->root_image_options)
+ if (!isempty(o->options))
+ fprintf(f, " %u:%s", o->partition_number, o->options);
+ fprintf(f, "\n");
+ }
+
if (c->root_hash) {
_cleanup_free_ char *encoded = NULL;
encoded = hexmem(c->root_hash, c->root_hash_size);
diff --git a/src/core/execute.h b/src/core/execute.h
index fc7bc5c24b..349f583c1a 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -158,6 +158,7 @@ struct ExecContext {
char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
void *root_hash, *root_hash_sig;
size_t root_hash_size, root_hash_sig_size;
+ LIST_HEAD(MountOptions, root_image_options);
bool working_directory_missing_ok:1;
bool working_directory_home:1;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 12ae78eb7d..a7c9bd9f71 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -23,6 +23,7 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
$1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory)
$1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image)
+$1.RootImageOptions, config_parse_root_image_options, 0, offsetof($1, exec_context)
$1.RootHash, config_parse_exec_root_hash, 0, offsetof($1, exec_context)
$1.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof($1, exec_context)
$1.RootVerity, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_verity)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 3036aa8ba4..2a2a5af58f 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1416,6 +1416,97 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
return 0;
}
+int config_parse_root_image_options(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
+ ExecContext *c = data;
+ const Unit *u = userdata;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ c->root_image_options = mount_options_free_all(c->root_image_options);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *mount_options_resolved = NULL, *first = NULL, *tuple = NULL;
+ const char *mount_options = NULL, *second = NULL;
+ MountOptions *o = NULL;
+ unsigned int partition_number = 0;
+
+ r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ second = tuple;
+ r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r == 0)
+ continue;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
+ continue;
+ }
+
+ /* Format is either '0:foo' or 'foo' (0 is implied) */
+ if (!isempty(second) && second[-1] == ':') {
+ mount_options = second;
+ r = safe_atou(first, &partition_number);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", first);
+ continue;
+ }
+ } else
+ mount_options = first;
+
+ r = unit_full_printf(u, mount_options, &mount_options_resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
+ continue;
+ }
+
+ o = new(MountOptions, 1);
+ if (!o)
+ return log_oom();
+ *o = (MountOptions) {
+ .partition_number = partition_number,
+ .options = TAKE_PTR(mount_options_resolved),
+ };
+ LIST_APPEND(mount_options, options, o);
+ }
+
+ /* empty spaces/separators only */
+ if (LIST_IS_EMPTY(options))
+ c->root_image_options = mount_options_free_all(c->root_image_options);
+ else
+ LIST_JOIN(mount_options, c->root_image_options, options);
+
+ return 0;
+}
+
int config_parse_exec_root_hash(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ac3940a1b7..253de9467f 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -44,6 +44,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
+CONFIG_PARSER_PROTOTYPE(config_parse_root_image_options);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 36d5ff67ae..16d40fedc0 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1257,6 +1257,7 @@ static bool home_read_only(
int setup_namespace(
const char* root_directory,
const char* root_image,
+ const MountOptions *root_image_options,
const NamespaceInfo *ns_info,
char** read_write_paths,
char** read_only_paths,
@@ -1331,6 +1332,7 @@ int setup_namespace(
root_hash ?: root_hash_decoded,
root_hash_size,
root_verity ?: verity_data,
+ root_image_options,
dissect_image_flags,
&dissected_image);
if (r < 0)
diff --git a/src/core/namespace.h b/src/core/namespace.h
index b182223bd4..258bd7c131 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -75,6 +75,7 @@ struct TemporaryFileSystem {
int setup_namespace(
const char *root_directory,
const char *root_image,
+ const MountOptions *root_image_options,
const NamespaceInfo *ns_info,
char **read_write_paths,
char **read_only_paths,
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 66ac638401..318cd37c6f 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -244,7 +244,7 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
- r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &m);
+ r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, NULL, arg_flags, &m);
if (r < 0)
return r;
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index a3f442518e..78abcbeff6 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -933,7 +933,7 @@ static int setup_image(char **ret_mount_dir, LoopDevice **ret_loop_device, Decry
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device: %m");
- r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, f, &dissected_image);
+ r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, NULL, f, &dissected_image);
if (r < 0)
return r;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index a9478b9dbd..02d8837ca9 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -665,7 +665,7 @@ static int enumerate_partitions(dev_t devnum) {
if (r <= 0)
return r;
- r = dissect_image(fd, NULL, 0, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
+ r = dissect_image(fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
if (r == -ENOPKG) {
log_debug_errno(r, "No suitable partition table found, ignoring.");
return 0;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index eb7c3321ac..6d6fe87ed1 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -5223,6 +5223,7 @@ static int run(int argc, char *argv[]) {
arg_image,
arg_root_hash, arg_root_hash_size,
arg_verity_data,
+ NULL,
dissect_image_flags,
&dissected_image);
if (r == -ENOPKG) {
diff --git a/src/portable/portable.c b/src/portable/portable.c
index 3a1367ec2b..bd7edce807 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -380,7 +380,7 @@ static int portable_extract_by_path(
if (r < 0)
return log_debug_errno(r, "Failed to create temporary directory: %m");
- r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
+ r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r == -ENOPKG)
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
else if (r == -EADDRNOTAVAIL)
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index f2652ed9a5..30a872342f 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1454,6 +1454,74 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
}
+ if (streq(field, "RootImageOptions")) {
+ const char *p = eq;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'v', "a(us)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "(us)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ for (;;) {
+ _cleanup_free_ char *first = NULL, *tuple = NULL;
+ const char *mount_options = NULL, *second = NULL;
+ unsigned partition_number = 0;
+
+ r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
+ if (r == 0)
+ break;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse argument: %m");
+
+ second = tuple;
+ r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r == 0)
+ continue;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse argument: %m");
+
+ /* Format is either '0:foo' or 'foo' (0 is implied) */
+ if (!isempty(second) && second[-1] == ':') {
+ mount_options = second;
+ r = safe_atou(first, &partition_number);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse partition number from %s: %m", first);
+ continue;
+ }
+ } else
+ mount_options = first;
+
+ r = sd_bus_message_append(m, "(us)", partition_number, mount_options);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
return 0;
}
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 24be6de6c5..e96658ca66 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -308,6 +308,7 @@ int dissect_image(
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
+ const MountOptions *mount_options,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -400,8 +401,8 @@ int dissect_image(
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
- _cleanup_free_ char *t = NULL, *n = NULL;
- const char *fstype = NULL;
+ _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
+ const char *fstype = NULL, *options = NULL;
/* OK, we have found a file system, that's our root partition then. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
@@ -420,6 +421,13 @@ int dissect_image(
m->verity = root_hash && verity_data;
m->can_verity = !!verity_data;
+ options = mount_options_from_part(mount_options, 0);
+ if (options) {
+ o = strdup(options);
+ if (!o)
+ return -ENOMEM;
+ }
+
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = !m->verity,
@@ -427,6 +435,7 @@ int dissect_image(
.architecture = _ARCHITECTURE_INVALID,
.fstype = TAKE_PTR(t),
.node = TAKE_PTR(n),
+ .mount_options = TAKE_PTR(o),
};
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
@@ -691,7 +700,8 @@ int dissect_image(
}
if (designator != _PARTITION_DESIGNATOR_INVALID) {
- _cleanup_free_ char *t = NULL, *n = NULL;
+ _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
+ const char *options = NULL;
/* First one wins */
if (m->partitions[designator].found)
@@ -707,6 +717,13 @@ int dissect_image(
if (!n)
return -ENOMEM;
+ options = mount_options_from_part(mount_options, nr);
+ if (options) {
+ o = strdup(options);
+ if (!o)
+ return -ENOMEM;
+ }
+
m->partitions[designator] = (DissectedPartition) {
.found = true,
.partno = nr,
@@ -715,6 +732,7 @@ int dissect_image(
.node = TAKE_PTR(n),
.fstype = TAKE_PTR(t),
.uuid = id,
+ .mount_options = TAKE_PTR(o),
};
}
@@ -740,9 +758,9 @@ int dissect_image(
break;
case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
- _cleanup_free_ char *n = NULL;
+ _cleanup_free_ char *n = NULL, *o = NULL;
sd_id128_t id = SD_ID128_NULL;
- const char *sid;
+ const char *sid, *options = NULL;
/* First one wins */
if (m->partitions[PARTITION_XBOOTLDR].found)
@@ -756,6 +774,13 @@ int dissect_image(
if (!n)
return -ENOMEM;
+ options = mount_options_from_part(mount_options, nr);
+ if (options) {
+ o = strdup(options);
+ if (!o)
+ return -ENOMEM;
+ }
+
m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
.found = true,
.partno = nr,
@@ -763,6 +788,7 @@ int dissect_image(
.architecture = _ARCHITECTURE_INVALID,
.node = TAKE_PTR(n),
.uuid = id,
+ .mount_options = TAKE_PTR(o),
};
break;
@@ -785,6 +811,8 @@ int dissect_image(
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
} else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
+ _cleanup_free_ char *o = NULL;
+ const char *options = NULL;
/* If the root has was set, then we won't fallback to a generic node, because the root hash
* decides */
@@ -800,6 +828,13 @@ int dissect_image(
if (multiple_generic)
return -ENOTUNIQ;
+ options = mount_options_from_part(mount_options, generic_nr);
+ if (options) {
+ o = strdup(options);
+ if (!o)
+ return -ENOMEM;
+ }
+
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = generic_rw,
@@ -807,6 +842,7 @@ int dissect_image(
.architecture = _ARCHITECTURE_INVALID,
.node = TAKE_PTR(generic_node),
.uuid = generic_uuid,
+ .mount_options = TAKE_PTR(o),
};
}
}
@@ -869,6 +905,7 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
free(m->partitions[i].node);
free(m->partitions[i].decrypted_fstype);
free(m->partitions[i].decrypted_node);
+ free(m->partitions[i].mount_options);
}
free(m->hostname);
@@ -1008,6 +1045,10 @@ static int mount_partition(
return -ENOMEM;
}
+ if (!isempty(m->mount_options))
+ if (!strextend_with_separator(&options, ",", m->mount_options, NULL))
+ return -ENOMEM;
+
r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
if (r < 0)
return r;
@@ -1819,6 +1860,7 @@ int dissect_image_and_warn(
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
+ const MountOptions *mount_options,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -1833,7 +1875,7 @@ int dissect_image_and_warn(
name = buffer;
}
- r = dissect_image(fd, root_hash, root_hash_size, verity_data, flags, ret);
+ r = dissect_image(fd, root_hash, root_hash_size, verity_data, mount_options, flags, ret);
switch (r) {
@@ -1880,6 +1922,27 @@ bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_
return k >= 0 && image->partitions[k].found;
}
+MountOptions* mount_options_free_all(MountOptions *options) {
+ MountOptions *m;
+
+ while ((m = options)) {
+ LIST_REMOVE(mount_options, options, m);
+ free(m->options);
+ free(m);
+ }
+
+ return NULL;
+}
+
+const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number) {
+ MountOptions *m;
+
+ LIST_FOREACH(mount_options, m, (MountOptions *)options)
+ if (partition_number == m->partition_number && !isempty(m->options))
+ return m->options;
+ return NULL;
+}
+
static const char *const partition_designator_table[] = {
[PARTITION_ROOT] = "root",
[PARTITION_ROOT_SECONDARY] = "root-secondary",
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 84ec1ce331..52aa377a67 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -5,11 +5,13 @@
#include "sd-id128.h"
+#include "list.h"
#include "macro.h"
typedef struct DissectedImage DissectedImage;
typedef struct DissectedPartition DissectedPartition;
typedef struct DecryptedImage DecryptedImage;
+typedef struct MountOptions MountOptions;
struct DissectedPartition {
bool found:1;
@@ -21,6 +23,7 @@ struct DissectedPartition {
char *node;
char *decrypted_node;
char *decrypted_fstype;
+ char *mount_options;
};
enum {
@@ -81,9 +84,19 @@ struct DissectedImage {
char **os_release;
};
+struct MountOptions {
+ unsigned partition_number;
+ char *options;
+ LIST_FIELDS(MountOptions, mount_options);
+};
+
+MountOptions* mount_options_free_all(MountOptions *options);
+DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
+const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number);
+
int probe_filesystem(const char *node, char **ret_fstype);
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret);
-int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret);
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
+int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 1b7cfb5028..c6ff41898b 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -1171,7 +1171,7 @@ int image_read_metadata(Image *i) {
if (r < 0)
return r;
- r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
+ r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r < 0)
return r;
diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c
index 13ff8add5d..fe78216b78 100644
--- a/src/test/test-dissect-image.c
+++ b/src/test/test-dissect-image.c
@@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
+ r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r < 0) {
log_error_errno(r, "Failed to dissect image: %m");
return EXIT_FAILURE;
diff --git a/src/test/test-list.c b/src/test/test-list.c
index 24e0496d46..ca5361adb9 100644
--- a/src/test/test-list.c
+++ b/src/test/test-list.c
@@ -12,11 +12,14 @@ int main(int argc, const char *argv[]) {
LIST_FIELDS(struct list_item, item);
} list_item;
LIST_HEAD(list_item, head);
+ LIST_HEAD(list_item, head2);
list_item items[4];
list_item *cursor;
LIST_HEAD_INIT(head);
+ LIST_HEAD_INIT(head2);
assert_se(head == NULL);
+ assert_se(head2 == NULL);
for (i = 0; i < ELEMENTSOF(items); i++) {
LIST_INIT(item, &items[i]);
@@ -203,5 +206,49 @@ int main(int argc, const char *argv[]) {
assert_se(head == NULL);
+ for (i = 0; i < ELEMENTSOF(items) / 2; i++) {
+ LIST_INIT(item, &items[i]);
+ assert_se(LIST_JUST_US(item, &items[i]));
+ LIST_PREPEND(item, head, &items[i]);
+ }
+
+ for (i = ELEMENTSOF(items) / 2; i < ELEMENTSOF(items); i++) {
+ LIST_INIT(item, &items[i]);
+ assert_se(LIST_JUST_US(item, &items[i]));
+ LIST_PREPEND(item, head2, &items[i]);
+ }
+
+ assert_se(items[0].item_next == NULL);
+ assert_se(items[1].item_next == &items[0]);
+ assert_se(items[2].item_next == NULL);
+ assert_se(items[3].item_next == &items[2]);
+
+ assert_se(items[0].item_prev == &items[1]);
+ assert_se(items[1].item_prev == NULL);
+ assert_se(items[2].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
+ LIST_JOIN(item, head2, head);
+ assert_se(head == NULL);
+
+ assert_se(items[0].item_next == NULL);
+ assert_se(items[1].item_next == &items[0]);
+ assert_se(items[2].item_next == &items[1]);
+ assert_se(items[3].item_next == &items[2]);
+
+ assert_se(items[0].item_prev == &items[1]);
+ assert_se(items[1].item_prev == &items[2]);
+ assert_se(items[2].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
+ LIST_JOIN(item, head, head2);
+ assert_se(head2 == NULL);
+ assert_se(!LIST_IS_EMPTY(head));
+
+ for (i = 0; i < ELEMENTSOF(items); i++)
+ LIST_REMOVE(item, head, &items[i]);
+
+ assert_se(head == NULL);
+
return 0;
}
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index a7ad482837..95021ee7bf 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -150,6 +150,7 @@ static void test_protect_kernel_logs(void) {
assert_se(fd > 0);
r = setup_namespace(NULL,
+ NULL,
NULL,
&ns_info,
NULL,
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index cbc41b7a38..ced287dd6e 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -62,6 +62,7 @@ int main(int argc, char *argv[]) {
log_info("Not chrooted");
r = setup_namespace(root_directory,
+ NULL,
NULL,
&ns_info,
(char **) writable,
diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh
index 3882658053..00f51479f4 100755
--- a/test/TEST-50-DISSECT/test.sh
+++ b/test/TEST-50-DISSECT/test.sh
@@ -34,6 +34,7 @@ test_create_image() {
BASICTOOLS=(
bash
cat
+ mount
)
oldinitdir=$initdir
export initdir=$TESTDIR/minimal
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
index 81e48e0ad1..28144b378f 100755
--- a/test/units/testsuite-50.sh
+++ b/test/units/testsuite-50.sh
@@ -124,6 +124,37 @@ umount ${image_dir}/mount
systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="1:ro,noatime 2:ro,dev nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="1:ro,noatime 1:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime"
+
+cat > /run/systemd/system/testservice-50a.service < /run/systemd/system/testservice-50b.service < %foo
+busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
+
echo OK > /testok
exit 0