#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);
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 */
#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"
_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)
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")) {
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))
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);
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;
* 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);
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 */
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);
}
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,
}
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;
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,
/* 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);
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,