]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect-image: do an strverscmp() on the partition label of root/usr if multiple...
authorLennart Poettering <lennart@poettering.net>
Wed, 10 Mar 2021 15:37:28 +0000 (16:37 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 16 Mar 2021 13:57:31 +0000 (14:57 +0100)
Let's add a very simple mechanism for doing A/B updating of disk images:
for root + /usr and their verity partitions let's ue strverscmp() on the
label to determine which one to use when dissecting a disk image. That
way, if the root partition label contains a string such as "foo-0.15"
and another one "foo-0.16", the latter wins.

For other partition types let's stick to the logic of "first partition
found" win, as before. Versioning makes sense for partitions that
typically and primarily may carry software packages, but the other
partition types usuall don't.

src/shared/dissect-image.c
src/shared/dissect-image.h

index 98f50c6826f883c42b2cc1cf60b3b701d971dc17..918d3021c4226fa0c9d04fecc7cd50da4d7bcfb3 100644 (file)
@@ -456,6 +456,22 @@ static int device_wait_for_initialization_harder(
 
 #define DEVICE_TIMEOUT_USEC (45 * USEC_PER_SEC)
 
+static void dissected_partition_done(DissectedPartition *p) {
+        assert(p);
+
+        free(p->fstype);
+        free(p->node);
+        free(p->label);
+        free(p->decrypted_fstype);
+        free(p->decrypted_node);
+        free(p->mount_options);
+
+        *p = (DissectedPartition) {
+                .partno = -1,
+                .architecture = -1
+        };
+}
+
 int dissect_image(
                 int fd,
                 const VeritySettings *verity,
@@ -727,7 +743,7 @@ int dissect_image(
                 if (is_gpt) {
                         PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID;
                         int architecture = _ARCHITECTURE_INVALID;
-                        const char *stype, *sid, *fstype = NULL;
+                        const char *stype, *sid, *fstype = NULL, *label;
                         sd_id128_t type_id, id;
                         bool rw = true;
 
@@ -743,6 +759,8 @@ int dissect_image(
                         if (sd_id128_from_string(stype, &type_id) < 0)
                                 continue;
 
+                        label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
+
                         if (sd_id128_equal(type_id, GPT_HOME)) {
 
                                 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
@@ -997,12 +1015,21 @@ int dissect_image(
                         }
 
                         if (designator != _PARTITION_DESIGNATOR_INVALID) {
-                                _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
+                                _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL, *l = NULL;
                                 const char *options = NULL;
 
-                                /* First one wins */
-                                if (m->partitions[designator].found)
-                                        continue;
+                                if (m->partitions[designator].found) {
+                                        /* For most partition types the first one we see wins. Except for the
+                                         * rootfs and /usr, where we do a version compare of the label, and
+                                         * let the newest version win. This permits a simple A/B versioning
+                                         * scheme in OS images. */
+
+                                        if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
+                                            strverscmp_improved(m->partitions[designator].label, label) >= 0)
+                                                continue;
+
+                                        dissected_partition_done(m->partitions + designator);
+                                }
 
                                 if (fstype) {
                                         t = strdup(fstype);
@@ -1014,6 +1041,12 @@ int dissect_image(
                                 if (!n)
                                         return -ENOMEM;
 
+                                if (label) {
+                                        l = strdup(label);
+                                        if (!l)
+                                                return -ENOMEM;
+                                }
+
                                 options = mount_options_from_designator(mount_options, designator);
                                 if (options) {
                                         o = strdup(options);
@@ -1028,6 +1061,7 @@ int dissect_image(
                                         .architecture = architecture,
                                         .node = TAKE_PTR(n),
                                         .fstype = TAKE_PTR(t),
+                                        .label = TAKE_PTR(l),
                                         .uuid = id,
                                         .mount_options = TAKE_PTR(o),
                                 };
@@ -1242,13 +1276,8 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
         if (!m)
                 return NULL;
 
-        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
-                free(m->partitions[i].fstype);
-                free(m->partitions[i].node);
-                free(m->partitions[i].decrypted_fstype);
-                free(m->partitions[i].decrypted_node);
-                free(m->partitions[i].mount_options);
-        }
+        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
+                dissected_partition_done(m->partitions + i);
 
         free(m->image_name);
         free(m->hostname);
index a7c9af0a2bcc72397c8a19501970ea8411571844..0e01c5e403b5e9c408a916622a0336f0555e7fad 100644 (file)
@@ -23,6 +23,7 @@ struct DissectedPartition {
         sd_id128_t uuid;   /* Partition entry UUID as reported by the GPT */
         char *fstype;
         char *node;
+        char *label;
         char *decrypted_node;
         char *decrypted_fstype;
         char *mount_options;
@@ -48,6 +49,23 @@ typedef enum PartitionDesignator {
         _PARTITION_DESIGNATOR_INVALID = -EINVAL,
 } PartitionDesignator;
 
+static inline bool PARTITION_DESIGNATOR_VERSIONED(PartitionDesignator d) {
+        /* Returns true for all designators where we want to support a concept of "versioning", i.e. which
+         * likely contain software binaries (or hashes thereof) that make sense to be versioned as a
+         * whole. We use this check to automatically pick the newest version of these partitions, by version
+         * comparing the partition labels. */
+
+        return IN_SET(d,
+                      PARTITION_ROOT,
+                      PARTITION_ROOT_SECONDARY,
+                      PARTITION_USR,
+                      PARTITION_USR_SECONDARY,
+                      PARTITION_ROOT_VERITY,
+                      PARTITION_ROOT_SECONDARY_VERITY,
+                      PARTITION_USR_VERITY,
+                      PARTITION_USR_SECONDARY_VERITY);
+}
+
 static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
         switch (p) {