diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 98f50c6826..918d3021c4 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -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); diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index a7c9af0a2b..0e01c5e403 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -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) {