sysupdate: Use partition types for pending/partial partitions
Fixes #40658
This commit is contained in:
committed by
Yu Watanabe
parent
d8da141348
commit
10eaca4159
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user