repart: fix CopyBlocks=auto for verity-sig partitions, even harder (#37704)

@DaanDeMeyer, this is for you.

Seems to work great here to duplicate ParticleOS onto another disk.
This commit is contained in:
Lennart Poettering
2025-06-06 16:06:26 +02:00
committed by GitHub
3 changed files with 130 additions and 11 deletions

View File

@@ -2612,9 +2612,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
}
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((IN_SET(p->type.designator,
PARTITION_ROOT_VERITY,
PARTITION_USR_VERITY) || p->verity == VERITY_DATA) && p->read_only < 0)
if ((partition_designator_is_verity(p->type.designator) || p->verity == VERITY_DATA) && p->read_only < 0)
p->read_only = true;
/* Default to "growfs" on, unless read-only */
@@ -5325,7 +5323,7 @@ static int context_copy_blocks(Context *context) {
continue;
/* For offline signing case */
if (!set_isempty(arg_verity_settings) && IN_SET(p->type.designator, PARTITION_ROOT_VERITY_SIG, PARTITION_USR_VERITY_SIG))
if (!set_isempty(arg_verity_settings) && partition_designator_is_verity_sig(p->type.designator))
return partition_format_verity_sig(context, p);
if (p->copy_blocks_fd < 0)
@@ -6298,7 +6296,7 @@ static int context_mkfs(Context *context) {
continue;
/* For offline signing case */
if (!set_isempty(arg_verity_settings) && IN_SET(p->type.designator, PARTITION_ROOT_VERITY_SIG, PARTITION_USR_VERITY_SIG))
if (!set_isempty(arg_verity_settings) && partition_designator_is_verity_sig(p->type.designator))
return partition_format_verity_sig(context, p);
/* Minimized partitions will use the copy blocks logic so skip those here. */
@@ -7250,6 +7248,82 @@ static int resolve_copy_blocks_auto_candidate(
return true;
}
static int resolve_copy_blocks_auto_candidate_harder(
dev_t start_devno,
GptPartitionType partition_type,
dev_t restrict_devno,
dev_t *ret_found_devno,
sd_id128_t *ret_uuid) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL, *nd = NULL;
int r;
/* A wrapper around resolve_copy_blocks_auto_candidate(), but looks for verity/verity-sig associated
* partitions, too. i.e. if the input is a data or verity partition, will try to find the
* verity/verity-sig partition for it, based on udev metadata. */
const char *property;
if (partition_designator_is_verity(partition_type.designator))
property = "ID_DISSECT_PART_VERITY_DEVICE";
else if (partition_designator_is_verity_sig(partition_type.designator))
property = "ID_DISSECT_PART_VERITY_SIG_DEVICE";
else
goto not_found;
r = sd_device_new_from_devnum(&d, 'b', start_devno);
if (r < 0)
return log_error_errno(r, "Failed to allocate device object for " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(start_devno));
const char *node;
r = sd_device_get_property_value(d, property, &node);
if (r == -ENOENT) {
log_debug_errno(r, "Property %s not set on " DEVNUM_FORMAT_STR ", skipping.", property, DEVNUM_FORMAT_VAL(start_devno));
goto not_found;
}
if (r < 0)
return log_error_errno(r, "Failed to read property %s from device " DEVNUM_FORMAT_STR ": %m", property, DEVNUM_FORMAT_VAL(start_devno));
r = sd_device_new_from_devname(&nd, node);
if (ERRNO_IS_NEG_DEVICE_ABSENT(r)) {
log_debug_errno(r, "Device %s referenced in %s property not found, skipping: %m", node, property);
goto not_found;
}
if (r < 0)
return log_error_errno(r, "Failed to allocate device object for '%s': %m", node);
r = device_in_subsystem(nd, "block");
if (r < 0)
return log_error_errno(r, "Failed to determine if '%s' is a block device: %m", node);
if (r == 0) {
log_debug("Device referenced by %s property of %s does not refer to block device, refusing.", property, node);
goto not_found;
}
dev_t found_devno = 0;
r = sd_device_get_devnum(nd, &found_devno);
if (r < 0)
return log_error_errno(r, "Failed to get device number for '%s': %m", node);
r = resolve_copy_blocks_auto_candidate(found_devno, partition_type, restrict_devno, ret_uuid);
if (r < 0)
return r;
if (r == 0)
goto not_found;
if (ret_found_devno)
*ret_found_devno = found_devno;
return 1;
not_found:
if (ret_found_devno)
*ret_found_devno = 0;
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
return 0;
}
static int find_backing_devno(
const char *path,
const char *root,
@@ -7391,7 +7465,7 @@ static int resolve_copy_blocks_auto(
return r;
if (r > 0) {
/* We found a matching one! */
if (found != 0)
if (found != 0 && found != sl)
return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
"Multiple matching partitions found for partition type %s, refusing.",
partition_designator_to_string(type.designator));
@@ -7399,6 +7473,20 @@ static int resolve_copy_blocks_auto(
found = sl;
found_uuid = u;
}
dev_t harder_devno = 0;
r = resolve_copy_blocks_auto_candidate_harder(sl, type, restrict_devno, &harder_devno, &u);
if (r < 0)
return r;
if (r > 0) {
/* We found a matching one! */
if (found != 0 && found != harder_devno)
return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
"Multiple matching partitions found, refusing.");
found = harder_devno;
found_uuid = u;
}
}
} else if (errno != ENOENT)
return log_error_errno(errno, "Failed open %s: %m", p);

View File

@@ -31,15 +31,19 @@ typedef enum PartitionDesignator {
bool partition_designator_is_versioned(PartitionDesignator d) _const_;
static inline bool partition_designator_is_verity_sig(PartitionDesignator d) {
return IN_SET(d, PARTITION_ROOT_VERITY_SIG, PARTITION_USR_VERITY_SIG);
}
PartitionDesignator partition_verity_of(PartitionDesignator p) _const_;
PartitionDesignator partition_verity_sig_of(PartitionDesignator p) _const_;
PartitionDesignator partition_verity_to_data(PartitionDesignator d) _const_;
PartitionDesignator partition_verity_sig_to_data(PartitionDesignator d) _const_;
static inline bool partition_designator_is_verity(PartitionDesignator d) {
return partition_verity_to_data(d) >= 0;
}
static inline bool partition_designator_is_verity_sig(PartitionDesignator d) {
return partition_verity_sig_to_data(d) >= 0;
}
const char* partition_designator_to_string(PartitionDesignator d) _const_;
PartitionDesignator partition_designator_from_string(const char *name) _pure_;

View File

@@ -124,6 +124,11 @@ static int verb_probe(UdevEvent *event, sd_device *dev) {
return 0;
}
uint64_t diskseq;
r = sd_device_get_diskseq(dev, &diskseq);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get diskseq of '%s': %m", devnode);
r = blockdev_partscan_enabled(dev);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to determine if block device '%s' supports partitions: %m", devnode);
@@ -262,11 +267,22 @@ static int verb_probe(UdevEvent *event, sd_device *dev) {
/* Indicate whether this partition has verity protection */
PartitionDesignator dv = partition_verity_of(d);
if (dv >= 0 && image->partitions[dv].found) {
/* Add one property that indicates as a boolean whether Verity is available at all for this */
_cleanup_free_ char *f = NULL;
if (asprintf(&f, "ID_DISSECT_PART%i_HAS_VERITY", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_property(event, f, "1");
/* Add a second property that indicates where the block device is found with the
* Verity data. We maintain this in an independent property, since Verity data might
* be available from other sources too, not just block devices, and we'd like to keep
* the props somewhat open for that. */
f = mfree(f);
if (asprintf(&f, "ID_DISSECT_PART%i_VERITY_DEVICE", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_propertyf(event, f, "/dev/disk/by-diskseq/%" PRIu64 "-part%i", diskseq, image->partitions[dv].partno);
}
dv = partition_verity_sig_of(d);
@@ -276,6 +292,12 @@ static int verb_probe(UdevEvent *event, sd_device *dev) {
return log_oom_debug();
(void) udev_builtin_add_property(event, f, "1");
f = mfree(f);
if (asprintf(&f, "ID_DISSECT_PART%i_VERITY_SIG_DEVICE", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_propertyf(event, f, "/dev/disk/by-diskseq/%" PRIu64 "-part%i", diskseq, image->partitions[dv].partno);
}
if (d == verity.designator) {
@@ -344,7 +366,12 @@ static int verb_copy(UdevEvent *event, sd_device *dev) {
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get partition number of partition block device '%s': %m", devnode);
FOREACH_STRING(f, "_DESIGNATOR", "_ARCHITECTURE", "_HAS_VERITY", "_HAS_VERITY_SIG", "_ROOTHASH", "_ROOTHASH_SIG") {
FOREACH_STRING(f,
"_DESIGNATOR", "_ARCHITECTURE",
"_HAS_VERITY", "_HAS_VERITY_SIG",
"_ROOTHASH", "_ROOTHASH_SIG",
"_VERITY_DEVICE", "_VERITY_SIG_DEVICE") {
/* The property on the parent device contains the partition number */
_cleanup_free_ char *p = strjoin("ID_DISSECT_PART", partn, f);
if (!p)