From: Daan De Meyer Date: Tue, 17 Feb 2026 19:57:01 +0000 (+0100) Subject: sysupdate: Use partition types for pending/partial partitions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=10eaca4159a93485054c4e7c7c35ce16114591d7;p=thirdparty%2Fsystemd.git sysupdate: Use partition types for pending/partial partitions Fixes #40658 --- diff --git a/src/sysupdate/sysupdate-partition.c b/src/sysupdate/sysupdate-partition.c index 237c157831f..94a69850af5 100644 --- a/src/sysupdate/sysupdate-partition.c +++ b/src/sysupdate/sysupdate-partition.c @@ -9,6 +9,20 @@ #include "string-util.h" #include "sysupdate-partition.h" +/* App-specific IDs used as HMAC keys to derive "partial" and "pending" partition type UUIDs from the + * original partition type UUID. This way we can indicate sysupdate transfer state via a separate GPT + * partition type UUID instead of using a label prefix, saving precious label space. */ +#define GPT_SYSUPDATE_PARTIAL_APP_ID SD_ID128_MAKE(ac,cf,a0,c2,da,24,46,0a,9f,c9,0b,b8,fc,78,52,19) +#define GPT_SYSUPDATE_PENDING_APP_ID SD_ID128_MAKE(80,f3,d6,1e,23,83,43,b9,81,f5,ce,37,93,f4,7d,4c) + +int gpt_partition_type_uuid_for_sysupdate_partial(sd_id128_t type, sd_id128_t *ret) { + return sd_id128_get_app_specific(type, GPT_SYSUPDATE_PARTIAL_APP_ID, ret); +} + +int gpt_partition_type_uuid_for_sysupdate_pending(sd_id128_t type, sd_id128_t *ret) { + return sd_id128_get_app_specific(type, GPT_SYSUPDATE_PENDING_APP_ID, ret); +} + void partition_info_destroy(PartitionInfo *p) { assert(p); @@ -242,6 +256,22 @@ int patch_partition( return log_error_errno(r, "Failed to update partition UUID: %m"); } + if (change & PARTITION_TYPE) { + _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *pt = NULL; + + pt = fdisk_new_parttype(); + if (!pt) + return log_oom(); + + r = fdisk_parttype_set_typestr(pt, SD_ID128_TO_UUID_STRING(info->type)); + if (r < 0) + return log_error_errno(r, "Failed to initialize partition type: %m"); + + r = fdisk_partition_set_type(pa, pt); + if (r < 0) + return log_error_errno(r, "Failed to update partition type: %m"); + } + type = gpt_partition_type_from_uuid(info->type); /* Tweak the read-only flag, but only if supported by the partition type */ diff --git a/src/sysupdate/sysupdate-partition.h b/src/sysupdate/sysupdate-partition.h index fbda14de6f5..5ae8c571ddf 100644 --- a/src/sysupdate/sysupdate-partition.h +++ b/src/sysupdate/sysupdate-partition.h @@ -12,7 +12,8 @@ typedef enum PartitionChange { PARTITION_GROWFS = 1 << 3, PARTITION_UUID = 1 << 4, PARTITION_LABEL = 1 << 5, - _PARTITION_CHANGE_MAX = (1 << 6) - 1, /* all of the above */ + PARTITION_TYPE = 1 << 6, + _PARTITION_CHANGE_MAX = (1 << 7) - 1, /* all of the above */ _PARTITION_CHANGE_INVALID = -EINVAL, } PartitionChange; @@ -40,5 +41,8 @@ int partition_info_copy(PartitionInfo *dest, const PartitionInfo *src); int read_partition_info(struct fdisk_context *c, struct fdisk_table *t, size_t i, PartitionInfo *ret); +int gpt_partition_type_uuid_for_sysupdate_partial(sd_id128_t type, sd_id128_t *ret); +int gpt_partition_type_uuid_for_sysupdate_pending(sd_id128_t type, sd_id128_t *ret); + int find_suitable_partition(const char *device, uint64_t space, sd_id128_t *partition_type, PartitionInfo *ret); int patch_partition(const char *device, const PartitionInfo *info, PartitionChange change); diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index 821854cda8e..3be0943e4c0 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -31,6 +31,7 @@ #include "strv.h" #include "sysupdate-cache.h" #include "sysupdate-instance.h" +#include "sysupdate-partition.h" #include "sysupdate-pattern.h" #include "sysupdate-resource.h" #include "time-util.h" @@ -244,9 +245,7 @@ static int resource_load_from_blockdev(Resource *rr) { _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL; _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL; Instance *instance; - const char *pinfo_label_stripped; bool is_partial = false, is_pending = false; - const char *stripped; r = read_partition_info(c, t, i, &pinfo); if (r < 0) @@ -254,9 +253,28 @@ static int resource_load_from_blockdev(Resource *rr) { if (r == 0) /* not assigned */ continue; - /* Check if partition type matches */ - if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type.uuid)) - continue; + /* Check if partition type matches, either directly or via derived partial/pending type + * UUIDs. The derived UUIDs are computed from the configured partition type by hashing it + * with a fixed app-specific ID, so we can detect the state without relying on label + * prefixes. */ + if (rr->partition_type_set) { + sd_id128_t partial_type, pending_type; + + r = gpt_partition_type_uuid_for_sysupdate_partial(rr->partition_type.uuid, &partial_type); + if (r < 0) + return log_error_errno(r, "Failed to derive partial partition type UUID: %m"); + + r = gpt_partition_type_uuid_for_sysupdate_pending(rr->partition_type.uuid, &pending_type); + if (r < 0) + return log_error_errno(r, "Failed to derive pending partition type UUID: %m"); + + if (sd_id128_equal(pinfo.type, partial_type)) + is_partial = true; + else if (sd_id128_equal(pinfo.type, pending_type)) + is_pending = true; + else if (!sd_id128_equal(pinfo.type, rr->partition_type.uuid)) + continue; + } /* A label of "_empty" means "not used so far" for us */ if (streq_ptr(pinfo.label, "_empty")) { @@ -264,18 +282,7 @@ static int resource_load_from_blockdev(Resource *rr) { continue; } - /* Match the label with any partial/pending prefix removed so the user’s existing patterns - * match regardless of the instance’s state. */ - if ((stripped = startswith(pinfo.label, "PRT#"))) { - pinfo_label_stripped = stripped; - is_partial = true; - } else if ((stripped = startswith(pinfo.label, "PND#"))) { - pinfo_label_stripped = stripped; - is_pending = true; - } else - pinfo_label_stripped = pinfo.label; - - r = pattern_match_many(rr->patterns, pinfo_label_stripped, &extracted_fields); + r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields); if (r < 0) return log_error_errno(r, "Failed to match pattern: %m"); if (IN_SET(r, PATTERN_MATCH_NO, PATTERN_MATCH_RETRY)) diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index 868917b0099..46f3129f5d9 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -67,8 +67,6 @@ Transfer* transfer_free(Transfer *t) { strv_free(t->appstream); partition_info_destroy(&t->partition_info); - free(t->temporary_partial_partition_label); - free(t->temporary_pending_partition_label); free(t->final_partition_label); resource_destroy(&t->source); @@ -766,12 +764,21 @@ static int transfer_instance_vacuum( case RESOURCE_PARTITION: { PartitionInfo pinfo = instance->partition_info; + PartitionChange change = PARTITION_LABEL; /* label "_empty" means "no contents" for our purposes */ pinfo.label = (char*) "_empty"; - log_debug("Relabelling partition '%s' to '%s'.", pinfo.device, pinfo.label); - r = patch_partition(t->target.path, &pinfo, PARTITION_LABEL); + /* If the partition had a derived partial/pending type UUID, restore the original + * partition type so that the slot is properly recognized as empty in subsequent + * scans. */ + if ((instance->is_partial || instance->is_pending) && t->target.partition_type_set) { + pinfo.type = t->target.partition_type.uuid; + change |= PARTITION_TYPE; + } + + log_debug("Resetting partition '%s' to empty.", pinfo.device); + r = patch_partition(t->target.path, &pinfo, change); if (r < 0) return r; @@ -1172,7 +1179,7 @@ static int run_callout( * and pending instances which are about to be installed (in which case, transfer_acquire_instance() is * skipped). */ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata *f) { - _cleanup_free_ char *formatted_pattern = NULL, *formatted_partial_pattern = NULL, *formatted_pending_pattern = NULL; + _cleanup_free_ char *formatted_pattern = NULL; int r; assert(t); @@ -1182,8 +1189,6 @@ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata assert(!t->temporary_partial_path); assert(!t->temporary_pending_path); assert(!t->final_partition_label); - assert(!t->temporary_partial_partition_label); - assert(!t->temporary_pending_partition_label); assert(!strv_isempty(t->target.patterns)); /* Format the target name using the first pattern specified */ @@ -1234,25 +1239,18 @@ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata if (!r) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern); - if (!strprepend(&formatted_partial_pattern, "PRT#", formatted_pattern)) - return log_oom(); - r = gpt_partition_label_valid(formatted_partial_pattern); - if (r < 0) - return log_error_errno(r, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_partial_pattern); - if (!r) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_partial_pattern); - - free_and_replace(t->temporary_partial_partition_label, formatted_partial_pattern); + if (!t->target.partition_type_set) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition type must be set for partition targets."); - if (!strprepend(&formatted_pending_pattern, "PND#", formatted_pattern)) - return log_oom(); - r = gpt_partition_label_valid(formatted_pending_pattern); + /* Derive temporary partition type UUIDs for partial/pending states from the configured + * partition type. This avoids the need for label prefixes. */ + r = gpt_partition_type_uuid_for_sysupdate_partial(t->target.partition_type.uuid, &t->partition_type_partial); if (r < 0) - return log_error_errno(r, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_pending_pattern); - if (!r) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pending_pattern); + return log_error_errno(r, "Failed to derive partial partition type UUID: %m"); - free_and_replace(t->temporary_pending_partition_label, formatted_pending_pattern); + r = gpt_partition_type_uuid_for_sysupdate_pending(t->target.partition_type.uuid, &t->partition_type_pending); + if (r < 0) + return log_error_errno(r, "Failed to derive pending partition type UUID: %m"); t->final_partition_label = TAKE_PTR(formatted_pattern); } @@ -1312,13 +1310,18 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra where = t->partition_info.device; - /* Rename the partition to `PRT#` to indicate that a transfer to it is in progress. */ - r = free_and_strdup_warn(&t->partition_info.label, t->temporary_partial_partition_label); + /* Set the partition label and change the partition type to the derived "partial" type UUID + * to indicate that a transfer to it is in progress. */ + r = free_and_strdup_warn(&t->partition_info.label, t->final_partition_label); if (r < 0) return r; - t->partition_change = PARTITION_LABEL; + t->partition_info.type = t->partition_type_partial; + t->partition_change = PARTITION_LABEL | PARTITION_TYPE; - log_debug("Relabelling partition '%s' to '%s'.", t->partition_info.device, t->partition_info.label); + log_debug("Marking partition '%s' as partial (label='%s', type=%s).", + t->partition_info.device, + t->partition_info.label, + SD_ID128_TO_UUID_STRING(t->partition_info.type)); r = patch_partition( t->target.path, &t->partition_info, @@ -1545,12 +1548,10 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra } if (t->target.type == RESOURCE_PARTITION) { - /* Now rename the partition again to `PND#` to indicate that the acquire is complete - * and the partition is ready for install. */ - r = free_and_strdup_warn(&t->partition_info.label, t->temporary_pending_partition_label); - if (r < 0) - return r; - t->partition_change = PARTITION_LABEL; + /* Now change the partition type to the derived "pending" type UUID to indicate that the + * acquire is complete and the partition is ready for install. */ + t->partition_info.type = t->partition_type_pending; + t->partition_change = PARTITION_TYPE; if (f->partition_uuid_set) { t->partition_info.uuid = f->partition_uuid; @@ -1577,7 +1578,9 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra t->partition_change |= PARTITION_GROWFS; } - log_debug("Relabelling partition '%s' to '%s'.", t->partition_info.device, t->partition_info.label); + log_debug("Marking partition '%s' as pending (type=%s).", + t->partition_info.device, + SD_ID128_TO_UUID_STRING(t->partition_info.type)); r = patch_partition( t->target.path, &t->partition_info, @@ -1617,7 +1620,7 @@ int transfer_process_partial_and_pending_instance(Transfer *t, Instance *i) { /* This is the analogue of find_suitable_partition(), but since finding the suitable partition has * already happened in the acquire phase, the target should already have that information and it - * should already have been claimed as `PND#`. */ + * should already have been claimed with the pending partition type UUID. */ if (t->target.type == RESOURCE_PARTITION) { assert(i->resource == &t->target); assert(i->is_pending); @@ -1665,14 +1668,17 @@ int transfer_install_instance( t->temporary_pending_path = mfree(t->temporary_pending_path); } - if (t->temporary_pending_partition_label) { + if (t->final_partition_label) { assert(t->target.type == RESOURCE_PARTITION); - assert(t->final_partition_label); + assert(t->target.partition_type_set); r = free_and_strdup_warn(&t->partition_info.label, t->final_partition_label); if (r < 0) return r; - t->partition_change = PARTITION_LABEL; + + /* Restore the original partition type UUID now that the partition is fully installed. */ + t->partition_info.type = t->target.partition_type.uuid; + t->partition_change = PARTITION_LABEL | PARTITION_TYPE; r = patch_partition( t->target.path, diff --git a/src/sysupdate/sysupdate-transfer.h b/src/sysupdate/sysupdate-transfer.h index d6dee0234aa..64af8c2fc31 100644 --- a/src/sysupdate/sysupdate-transfer.h +++ b/src/sysupdate/sysupdate-transfer.h @@ -48,8 +48,11 @@ typedef struct Transfer { PartitionInfo partition_info; PartitionChange partition_change; char *final_partition_label; - char *temporary_partial_partition_label; - char *temporary_pending_partition_label; + + /* Derived partition type UUIDs used to indicate partial/pending state on the partition type level, + * instead of polluting the partition label with prefixes */ + sd_id128_t partition_type_partial; + sd_id128_t partition_type_pending; Context *context; } Transfer;