sysupdate: Use partition types for pending/partial partitions

Fixes #40658
This commit is contained in:
Daan De Meyer
2026-02-18 11:15:04 +09:00
committed by Yu Watanabe
parent d8da141348
commit 10eaca4159
5 changed files with 108 additions and 58 deletions
+30
View File
@@ -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 */
+5 -1
View File
@@ -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);
+24 -17
View File
@@ -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))
+44 -38
View File
@@ -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 (!t->target.partition_type_set)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition type must be set for partition targets.");
/* 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_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);
return log_error_errno(r, "Failed to derive partial partition type UUID: %m");
free_and_replace(t->temporary_partial_partition_label, formatted_partial_pattern);
if (!strprepend(&formatted_pending_pattern, "PND#", formatted_pattern))
return log_oom();
r = gpt_partition_label_valid(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 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);
free_and_replace(t->temporary_pending_partition_label, formatted_pending_pattern);
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#<VERSION>` 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#<VERSION>` 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,
+5 -2
View File
@@ -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;