]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: add new ReadOnly= and Flags= settings for repart dropins
authorLennart Poettering <lennart@poettering.net>
Tue, 23 Mar 2021 15:16:42 +0000 (16:16 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 19 Apr 2021 21:16:02 +0000 (23:16 +0200)
Let's make the GPT partition flags configurable when creating new
partitions. This is primarily useful for the read-only flag (which we
want to set for verity enabled partitions).

This adds two settings for this: Flags= and ReadOnly=, which strictly
speaking are redundant. The main reason to have both is that usually the
ReadOnly= setting is the one wants to control, and it' more generic.
Moreover we might later on introduce inherting of flags from CopyBlocks=
partitions, where one might want to control most flags as is except for
the RO flag and similar, hence let's keep them separate.

man/repart.d.xml
src/partition/repart.c
src/shared/gpt.c
src/shared/gpt.h

index d404645588f806a0e19ade3a440676d5c8f861ad..5223f5036497e36f72f395d690ef60ee40d3e659 100644 (file)
         factory reset operation. This functionality is useful to implement schemes where images can be reset
         into their original state by removing partitions and creating them anew. Defaults to off.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>Flags=</varname></term>
+
+        <listitem><para>Configures the 64bit GPT partition flags to set for the partition when creating
+        it. This option has no effect if the partition already exists. If not specified the flags values is
+        set to all zeroes, except if the partition type (as configured with <varname>Type=</varname> above)
+        refers to a Verity partition, in wich case bit 60 is set (i.e. the read-only bit). This bit may also
+        be configured separately via <varname>ReadOnly=</varname>, see below. Specify the flags value in
+        hexadecimal (by prefixing it with <literal>0x</literal>), binary (prefix <literal>0b</literal>) or
+        decimal (no prefix).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>ReadOnly=</varname></term>
+
+        <listitem><para>Configures the Read-Only partition flags (bit 60) of the partition table entry. This
+        option is a friendly way to set bit 60 of the partition flags value without setting any of the other
+        bits, and may be set via <varname>Flags=</varname> too, see above.</para>
+
+        <para>If both <varname>Flags=</varname> and <varname>ReadOnly=</varname> are set the latter controls
+        the value of the flag.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 12ad0dd49e56bea9f1928f999d6db54519a93ef1..af319d2e33c0983eb0dbed1c21676ef97b626f65 100644 (file)
@@ -168,6 +168,9 @@ struct Partition {
         char **make_directories;
         EncryptMode encrypt;
 
+        uint64_t gpt_flags;
+        int read_only;
+
         LIST_FIELDS(Partition, partitions);
 };
 
@@ -239,6 +242,7 @@ static Partition *partition_new(void) {
                 .offset = UINT64_MAX,
                 .copy_blocks_fd = -1,
                 .copy_blocks_size = UINT64_MAX,
+                .read_only = -1,
         };
 
         return p;
@@ -1263,6 +1267,34 @@ static int config_parse_make_dirs(
 
 static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
 
+static int config_parse_gpt_flags(
+                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) {
+
+        uint64_t *gpt_flags = data;
+        int r;
+
+        assert(rvalue);
+        assert(gpt_flags);
+
+        r = safe_atou64(rvalue, gpt_flags);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse Flags= value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
 static int partition_read_definition(Partition *p, const char *path) {
 
         ConfigTableItem table[] = {
@@ -1282,6 +1314,8 @@ static int partition_read_definition(Partition *p, const char *path) {
                 { "Partition", "CopyFiles",       config_parse_copy_files,  0, p                    },
                 { "Partition", "MakeDirectories", config_parse_make_dirs,   0, p                    },
                 { "Partition", "Encrypt",         config_parse_encrypt,     0, &p->encrypt          },
+                { "Partition", "Flags",           config_parse_gpt_flags,   0, &p->gpt_flags        },
+                { "Partition", "ReadOnly",        config_parse_tristate,    0, &p->read_only        },
                 {}
         };
         int r;
@@ -1323,6 +1357,12 @@ static int partition_read_definition(Partition *p, const char *path) {
                         return log_oom();
         }
 
+        /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
+        if ((gpt_partition_type_is_root_verity(p->type_uuid) ||
+             gpt_partition_type_is_usr_verity(p->type_uuid)) &&
+            p->read_only < 0)
+                p->read_only = true;
+
         return 0;
 }
 
@@ -3184,6 +3224,24 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
         return 0;
 }
 
+static int set_gpt_flags(struct fdisk_partition *q, uint64_t flags) {
+        _cleanup_free_ char *a = NULL;
+
+        for (unsigned i = 0; i < sizeof(flags) * 8; i++) {
+                uint64_t bit = UINT64_C(1) << i;
+                char buf[DECIMAL_STR_MAX(unsigned)+1];
+
+                if (!FLAGS_SET(flags, bit))
+                        continue;
+
+                xsprintf(buf, "%u", i);
+                if (!strextend_with_separator(&a, ",", buf))
+                        return -ENOMEM;
+        }
+
+        return fdisk_partition_set_attrs(q, a);
+}
+
 static int context_mangle_partitions(Context *context) {
         Partition *p;
         int r;
@@ -3252,6 +3310,7 @@ static int context_mangle_partitions(Context *context) {
                         _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
                         _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
                         char ids[ID128_UUID_STRING_MAX];
+                        uint64_t f;
 
                         assert(!p->new_partition);
                         assert(p->offset % 512 == 0);
@@ -3299,6 +3358,22 @@ static int context_mangle_partitions(Context *context) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set partition label: %m");
 
+                        /* Merge the read only setting with the literal flags */
+                        f = p->gpt_flags;
+                        if (p->read_only >= 0) {
+                                if (gpt_partition_type_knows_read_only(p->type_uuid))
+                                        SET_FLAG(f, GPT_FLAG_READ_ONLY, p->read_only);
+                                else {
+                                        char buffer[ID128_UUID_STRING_MAX];
+                                        log_warning("Configured ReadOnly=yes for partition type '%s' that doesn't support it, ignoring.",
+                                                    gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
+                                }
+                        }
+
+                        r = set_gpt_flags(q, f);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set GPT partition flags: %m");
+
                         log_info("Adding new partition %" PRIu64 " to partition table.", p->partno);
 
                         r = fdisk_add_partition(context->fdisk_context, q, NULL);
index 5f2de0d947e5dc6291793f089cca8bb31c00587c..846b2fe48ff4a38247fa23f95b84665abfc0699b 100644 (file)
@@ -146,3 +146,14 @@ bool gpt_partition_type_is_usr_verity(sd_id128_t id) {
                 sd_id128_equal(id, GPT_USR_RISCV32_VERITY) ||
                 sd_id128_equal(id, GPT_USR_RISCV64_VERITY);
 }
+
+bool gpt_partition_type_knows_read_only(sd_id128_t id) {
+        return gpt_partition_type_is_root(id) ||
+                gpt_partition_type_is_usr(id) ||
+                sd_id128_equal(id, GPT_HOME) ||
+                sd_id128_equal(id, GPT_SRV) ||
+                sd_id128_equal(id, GPT_VAR) ||
+                sd_id128_equal(id, GPT_TMP) ||
+                gpt_partition_type_is_root_verity(id) || /* pretty much implied, but let's set the bit to make things really clear */
+                gpt_partition_type_is_usr_verity(id);    /* ditto */
+}
index 22b1d68d5f0954545313a27d008a8040ff26f101..f3a74813f072c601db663206b62620143be42872 100644 (file)
@@ -133,3 +133,5 @@ bool gpt_partition_type_is_root(sd_id128_t id);
 bool gpt_partition_type_is_root_verity(sd_id128_t id);
 bool gpt_partition_type_is_usr(sd_id128_t id);
 bool gpt_partition_type_is_usr_verity(sd_id128_t id);
+
+bool gpt_partition_type_knows_read_only(sd_id128_t id);