]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
service: add new RootImageOptions feature 16308/head
authorLuca Boccassi <luca.boccassi@microsoft.com>
Mon, 29 Jun 2020 12:19:31 +0000 (13:19 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 29 Jul 2020 16:17:32 +0000 (17:17 +0100)
Allows to specify mount options for RootImage.
In case of multi-partition images, the partition number can be prefixed
followed by colon. Eg:

RootImageOptions=1:ro,dev 2:nosuid nodev

In absence of a partition number, 0 is assumed.

23 files changed:
man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/namespace.c
src/core/namespace.h
src/dissect/dissect.c
src/firstboot/firstboot.c
src/gpt-auto-generator/gpt-auto-generator.c
src/nspawn/nspawn.c
src/portable/portable.c
src/shared/bus-unit-util.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/machine-image.c
src/test/test-dissect-image.c
src/test/test-namespace.c
src/test/test-ns.c
test/TEST-50-DISSECT/test.sh
test/units/testsuite-50.sh

index 3618b52808e86c4ae2dc974a5db92054aa09647b..c5d755e8976471c959537fc956fb624de142e062 100644 (file)
         <xi:include href="system-only.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RootImageOptions=</varname></term>
+
+        <listitem><para>Takes a comma-separated list of mount options that will be used on disk images specified by
+        <varname>RootImage=</varname>. 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
+        <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>RootHash=</varname></term>
 
index 50f7ada8cefb2f32efe790c5698bdcbbc44ff578..49729799abfd9c6418efa2bf3a342d3ab99b66f2 100644 (file)
@@ -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;
index 2a4840a3a9b99c418793a6c6046c68de9097b4ab..39ffcba5802dff4b4e78c4b216fcef9239da1514 100644 (file)
@@ -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);
index fc7bc5c24b2a89d0b98b0f182644264fa841117b..349f583c1a6697330e83144f8652b57da4d81c8b 100644 (file)
@@ -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;
 
index 12ae78eb7dc5d4552665ac3d1916faa3f8bb1244..a7c9bd9f711ec6bfb255bad69c0431a6e4aadb08 100644 (file)
@@ -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)
index 3036aa8ba4432c934fe36f0d9f68a7c574dbc8da..2a2a5af58fd8de5dcd1e17b26b4fbe74bf05caae 100644 (file)
@@ -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,
index ac3940a1b7fc092335fc6e0400020b2e4d14c9ae..253de9467f09d10bade6b0e28c24529745ede8b2 100644 (file)
@@ -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);
index 5865dfe93ea6b0592460e09f721ce1396744b178..8b5efa825219fd3c315dabcbad0c74dff27d0803 100644 (file)
@@ -1259,6 +1259,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,
@@ -1333,6 +1334,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)
index b182223bd41dddfc50fa51d4c21f29be1e511daa..258bd7c131443865ae236ac3e24e3e8c230b932b 100644 (file)
@@ -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,
index 66ac638401cf50d8c19fceda7073a37df7833d71..318cd37c6f264cf2d1ce89881ff4778d4468c57f 100644 (file)
@@ -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;
 
index a3f442518ec43d207c8290523e2716a33f5cb1b5..78abcbeff6ca4d4dd7ff29593cf2b5df6f8391c4 100644 (file)
@@ -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;
 
index a9478b9dbd886d2ce3dd96ccf76775e0747d9df3..02d8837ca9b09b8119a5d2054a8881122257d8e0 100644 (file)
@@ -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;
index 3b9493f232e26f4c15a0b7e236f5288e14afe8e2..a1613f076d9f64e766375275d60c1ae54d7b5cc4 100644 (file)
@@ -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) {
index 3a1367ec2b0af217fd5892a939dc92ae03720073..bd7edce8075f5500754f66d93c88cf9de6c30d25 100644 (file)
@@ -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)
index f2652ed9a59dafeef5a03940b338a96d066056f0..30a872342ff732847d1310690eedbda887c3c633 100644 (file)
@@ -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;
 }
 
index 24be6de6c546fa84e39afa693ff26051ebb8d6b7..e96658ca66933ab0ed23255ffa664cb6276a1436 100644 (file)
@@ -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",
index 84ec1ce331163c6bffd61474ec233d09119ad549..52aa377a671a32616ce11a7162ae5c68c1b7912c 100644 (file)
@@ -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);
index 1b7cfb5028adf763b2724dc9733c69e4774f24ee..c6ff41898b25b3befbed373bd7e295bba21aa9a7 100644 (file)
@@ -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;
 
index 13ff8add5da3e970de3d0d0458b7e249e2722be1..fe78216b781b37fc18157701a61800a39bb76419 100644 (file)
@@ -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;
index a7ad4828370f4444d784732dcf761d58182b7c42..95021ee7bf7260c597f401f6d24f9e61fd05c9a1 100644 (file)
@@ -150,6 +150,7 @@ static void test_protect_kernel_logs(void) {
                 assert_se(fd > 0);
 
                 r = setup_namespace(NULL,
+                                    NULL,
                                     NULL,
                                     &ns_info,
                                     NULL,
index cbc41b7a385ef57a115f46207effc0dbb17d60f7..ced287dd6e06d83a1b63d869ac8690ac35f9571c 100644 (file)
@@ -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,
index 3882658053854cf426a4b01b0f60de571cc0c701..00f51479f40c4048b05a1c025f262d22ee1efd4e 100755 (executable)
@@ -34,6 +34,7 @@ test_create_image() {
         BASICTOOLS=(
             bash
             cat
+            mount
         )
         oldinitdir=$initdir
         export initdir=$TESTDIR/minimal
index 81e48e0ad191cfa1a1c92aadbad84622d2cf05bd..28144b378fdd73b40d0b6b3ef04755e6d355b181 100755 (executable)
@@ -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 <<EOF
+[Service]
+Type=oneshot
+ExecStart=mount
+MountAPIVFS=yes
+RootImage=${image}.raw
+RootImageOptions=1:ro,noatime,nosuid 2:ro,dev noatime,dev
+RootImageOptions=nosuid,dev
+EOF
+systemctl start testservice-50a.service
+journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F "noatime"
+journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F -v "nosuid"
+
+cat > /run/systemd/system/testservice-50b.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=mount
+MountAPIVFS=yes
+RootImage=${image}.gpt
+RootImageOptions=1:ro,noatime,nosuid 2:ro,dev nosuid,dev
+RootImageOptions=2:ro,dev nosuid,dev,%%foo
+EOF
+systemctl start testservice-50b.service
+journalctl -b -u testservice-50b.service | grep -F "squashfs" | grep -q -F "noatime"
+
+# Check that specifier escape is applied %%foo -> %foo
+busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
+
 echo OK > /testok
 
 exit 0