From 821d0cf0dc5cc9abef25f509ffa88b53d1317151 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 1 Aug 2023 21:20:05 +0200 Subject: [PATCH 1/3] repart: Rename partition_exclude/defer() to partition_type_exclude/defer() --- src/partition/repart.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 8c9bc08d20..40ce435620 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -423,24 +423,20 @@ static void partition_foreignize(Partition *p) { p->verity = VERITY_OFF; } -static bool partition_exclude(const Partition *p) { - assert(p); - +static bool partition_type_exclude(GptPartitionType type) { if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE) return false; for (size_t i = 0; i < arg_n_filter_partitions; i++) - if (sd_id128_equal(p->type.uuid, arg_filter_partitions[i].uuid)) + if (sd_id128_equal(type.uuid, arg_filter_partitions[i].uuid)) return arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE; return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE; } -static bool partition_defer(const Partition *p) { - assert(p); - +static bool partition_type_defer(GptPartitionType type) { for (size_t i = 0; i < arg_n_defer_partitions; i++) - if (sd_id128_equal(p->type.uuid, arg_defer_partitions[i].uuid)) + if (sd_id128_equal(type.uuid, arg_defer_partitions[i].uuid)) return true; return false; @@ -1657,7 +1653,7 @@ static int partition_read_definition(Partition *p, const char *path, const char if (r < 0) return r; - if (partition_exclude(p)) + if (partition_type_exclude(p->type)) return 0; if (p->size_min != UINT64_MAX && p->size_max != UINT64_MAX && p->size_min > p->size_max) @@ -3130,7 +3126,7 @@ static int context_wipe_and_discard(Context *context) { if (!p->allocated_to_area) continue; - if (partition_defer(p)) + if (partition_type_defer(p->type)) continue; r = context_wipe_partition(context, p); @@ -3922,7 +3918,7 @@ static int context_copy_blocks(Context *context) { if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */ continue; - if (partition_defer(p)) + if (partition_type_defer(p->type)) continue; assert(p->new_size != UINT64_MAX); @@ -3960,14 +3956,14 @@ static int context_copy_blocks(Context *context) { if (r < 0) return r; - if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) { + if (p->siblings[VERITY_HASH] && !partition_type_defer(p->siblings[VERITY_HASH]->type)) { r = partition_format_verity_hash(context, p->siblings[VERITY_HASH], /* node = */ NULL, partition_target_path(t)); if (r < 0) return r; } - if (p->siblings[VERITY_SIG] && !partition_defer(p->siblings[VERITY_SIG])) { + if (p->siblings[VERITY_SIG] && !partition_type_defer(p->siblings[VERITY_SIG]->type)) { r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]); if (r < 0) return r; @@ -4362,7 +4358,7 @@ static int context_mkfs(Context *context) { if (p->copy_blocks_fd >= 0) continue; - if (partition_defer(p)) + if (partition_type_defer(p->type)) continue; assert(p->offset != UINT64_MAX); @@ -4446,14 +4442,14 @@ static int context_mkfs(Context *context) { if (r < 0) return r; - if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) { + if (p->siblings[VERITY_HASH] && !partition_type_defer(p->siblings[VERITY_HASH]->type)) { r = partition_format_verity_hash(context, p->siblings[VERITY_HASH], /* node = */ NULL, partition_target_path(t)); if (r < 0) return r; } - if (p->siblings[VERITY_SIG] && !partition_defer(p->siblings[VERITY_SIG])) { + if (p->siblings[VERITY_SIG] && !partition_type_defer(p->siblings[VERITY_SIG]->type)) { r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]); if (r < 0) return r; @@ -4782,7 +4778,7 @@ static int context_mangle_partitions(Context *context) { if (p->dropped) continue; - if (partition_defer(p)) + if (partition_type_defer(p->type)) continue; assert(p->new_size != UINT64_MAX); @@ -5031,7 +5027,7 @@ static int context_split(Context *context) { if (!p->split_path) continue; - if (partition_defer(p)) + if (partition_type_defer(p->type)) continue; fdt = open(p->split_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666); From 2d9b3468b2ad83810d21ccf2c00a5d2ac18dccc0 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 1 Aug 2023 21:38:11 +0200 Subject: [PATCH 2/3] sysupdate: Move fdisk partition flags helpers to fdisk-util.c --- src/shared/fdisk-util.c | 77 +++++++++++++++++++++++++++ src/shared/fdisk-util.h | 3 ++ src/sysupdate/sysupdate-partition.c | 81 ----------------------------- 3 files changed, 80 insertions(+), 81 deletions(-) diff --git a/src/shared/fdisk-util.c b/src/shared/fdisk-util.c index e88adb2d43..9a301f38ac 100644 --- a/src/shared/fdisk-util.c +++ b/src/shared/fdisk-util.c @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "dissect-image.h" +#include "extract-word.h" #include "fd-util.h" #include "fdisk-util.h" +#include "parse-util.h" #if HAVE_LIBFDISK @@ -75,4 +77,79 @@ int fdisk_partition_get_type_as_id128(struct fdisk_partition *p, sd_id128_t *ret return sd_id128_from_string(pts, ret); } +int fdisk_partition_get_attrs_as_uint64(struct fdisk_partition *pa, uint64_t *ret) { + uint64_t flags = 0; + const char *a; + int r; + + assert(pa); + assert(ret); + + /* Retrieve current flags as uint64_t mask */ + + a = fdisk_partition_get_attrs(pa); + if (!a) { + *ret = 0; + return 0; + } + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&a, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; + + if (streq(word, "RequiredPartition")) + flags |= SD_GPT_FLAG_REQUIRED_PARTITION; + else if (streq(word, "NoBlockIOProtocol")) + flags |= SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL; + else if (streq(word, "LegacyBIOSBootable")) + flags |= SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE; + else { + const char *e; + unsigned u; + + /* Drop "GUID" prefix if specified */ + e = startswith(word, "GUID:") ?: word; + + if (safe_atou(e, &u) < 0) { + log_debug("Unknown partition flag '%s', ignoring.", word); + continue; + } + + if (u >= sizeof(flags)*8) { /* partition flags on GPT are 64-bit. Let's ignore any further + bits should libfdisk report them */ + log_debug("Partition flag above bit 63 (%s), ignoring.", word); + continue; + } + + flags |= UINT64_C(1) << u; + } + } + + *ret = flags; + return 0; +} + +int fdisk_partition_set_attrs_as_uint64(struct fdisk_partition *pa, uint64_t flags) { + _cleanup_free_ char *attrs = NULL; + int r; + + assert(pa); + + for (unsigned i = 0; i < sizeof(flags) * 8; i++) { + if (!FLAGS_SET(flags, UINT64_C(1) << i)) + continue; + + r = strextendf_with_separator(&attrs, ",", "%u", i); + if (r < 0) + return r; + } + + return fdisk_partition_set_attrs(pa, strempty(attrs)); +} + #endif diff --git a/src/shared/fdisk-util.h b/src/shared/fdisk-util.h index 4845132927..b82ff705d7 100644 --- a/src/shared/fdisk-util.h +++ b/src/shared/fdisk-util.h @@ -19,4 +19,7 @@ int fdisk_new_context_fd(int fd, bool read_only, uint32_t sector_size, struct fd int fdisk_partition_get_uuid_as_id128(struct fdisk_partition *p, sd_id128_t *ret); int fdisk_partition_get_type_as_id128(struct fdisk_partition *p, sd_id128_t *ret); +int fdisk_partition_get_attrs_as_uint64(struct fdisk_partition *pa, uint64_t *ret); +int fdisk_partition_set_attrs_as_uint64(struct fdisk_partition *pa, uint64_t flags); + #endif diff --git a/src/sysupdate/sysupdate-partition.c b/src/sysupdate/sysupdate-partition.c index 8f33469663..587265482b 100644 --- a/src/sysupdate/sysupdate-partition.c +++ b/src/sysupdate/sysupdate-partition.c @@ -18,87 +18,6 @@ void partition_info_destroy(PartitionInfo *p) { p->device = mfree(p->device); } -static int fdisk_partition_get_attrs_as_uint64( - struct fdisk_partition *pa, - uint64_t *ret) { - - uint64_t flags = 0; - const char *a; - int r; - - assert(pa); - assert(ret); - - /* Retrieve current flags as uint64_t mask */ - - a = fdisk_partition_get_attrs(pa); - if (!a) { - *ret = 0; - return 0; - } - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&a, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - break; - - if (streq(word, "RequiredPartition")) - flags |= SD_GPT_FLAG_REQUIRED_PARTITION; - else if (streq(word, "NoBlockIOProtocol")) - flags |= SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL; - else if (streq(word, "LegacyBIOSBootable")) - flags |= SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE; - else { - const char *e; - unsigned u; - - /* Drop "GUID" prefix if specified */ - e = startswith(word, "GUID:") ?: word; - - if (safe_atou(e, &u) < 0) { - log_debug("Unknown partition flag '%s', ignoring.", word); - continue; - } - - if (u >= sizeof(flags)*8) { /* partition flags on GPT are 64-bit. Let's ignore any further - bits should libfdisk report them */ - log_debug("Partition flag above bit 63 (%s), ignoring.", word); - continue; - } - - flags |= UINT64_C(1) << u; - } - } - - *ret = flags; - return 0; -} - -static int fdisk_partition_set_attrs_as_uint64( - struct fdisk_partition *pa, - uint64_t flags) { - - _cleanup_free_ char *attrs = NULL; - int r; - - assert(pa); - - for (unsigned i = 0; i < sizeof(flags) * 8; i++) { - if (!FLAGS_SET(flags, UINT64_C(1) << i)) - continue; - - r = strextendf_with_separator(&attrs, ",", "%u", i); - if (r < 0) - return r; - } - - return fdisk_partition_set_attrs(pa, strempty(attrs)); -} - int read_partition_info( struct fdisk_context *c, struct fdisk_table *t, From 1e46985a604314a23775a7b0e54e310c8a4e808f Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 1 Aug 2023 21:38:39 +0200 Subject: [PATCH 3/3] repart: Add --copy-from option --copy-from synthesizes partition definitions from the given image which are then applied to the repart algorithm. In its most basic form, this allows copying an image to another device but it can also be combined with --definitions to copy + add partitions in the same call to repart. --- man/systemd-repart.xml | 8 + src/partition/repart.c | 338 +++++++++++++++++++++++++++---------- test/units/testsuite-58.sh | 21 +++ 3 files changed, 274 insertions(+), 93 deletions(-) diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index 1799961527..06bccad8c3 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -440,6 +440,14 @@ due to missing permissions. + + IMAGE + + Instructs systemd-repart to copy the partitions from the given + image. The partitions from the given image are synthesized into partition definitions that are + parsed before the partition definition files. + + diff --git a/src/partition/repart.c b/src/partition/repart.c index 40ce435620..4fe2c63a46 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -153,6 +153,7 @@ static uint64_t arg_sector_size = 0; static ImagePolicy *arg_image_policy = NULL; static Architecture arg_architecture = _ARCHITECTURE_INVALID; static int arg_offline = -1; +static char *arg_copy_from = NULL; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); @@ -164,6 +165,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); +STATIC_DESTRUCTOR_REGISTER(arg_copy_from, freep); typedef struct FreeArea FreeArea; @@ -228,6 +230,7 @@ typedef struct Partition { bool copy_blocks_auto; const char *copy_blocks_root; int copy_blocks_fd; + uint64_t copy_blocks_offset; uint64_t copy_blocks_size; char *format; @@ -346,6 +349,7 @@ static Partition *partition_new(void) { .partno = UINT64_MAX, .offset = UINT64_MAX, .copy_blocks_fd = -EBADF, + .copy_blocks_offset = UINT64_MAX, .copy_blocks_size = UINT64_MAX, .no_auto = -1, .read_only = -1, @@ -1801,9 +1805,231 @@ static int find_verity_sibling(Context *context, Partition *p, VerityMode mode, return 0; } +static int context_open_and_lock_backing_fd(const char *node, int *backing_fd) { + _cleanup_close_ int fd = -EBADF; + + assert(node); + assert(backing_fd); + + if (*backing_fd >= 0) + return 0; + + fd = open(node, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open device '%s': %m", node); + + /* Tell udev not to interfere while we are processing the device */ + if (flock(fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0) + return log_error_errno(errno, "Failed to lock device '%s': %m", node); + + log_debug("Device %s opened and locked.", node); + *backing_fd = TAKE_FD(fd); + return 1; +} + +static int determine_current_padding( + struct fdisk_context *c, + struct fdisk_table *t, + struct fdisk_partition *p, + uint64_t secsz, + uint64_t grainsz, + uint64_t *ret) { + + size_t n_partitions; + uint64_t offset, next = UINT64_MAX; + + assert(c); + assert(t); + assert(p); + assert(ret); + + if (!fdisk_partition_has_end(p)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!"); + + offset = fdisk_partition_get_end(p); + assert(offset < UINT64_MAX); + offset++; /* The end is one sector before the next partition or padding. */ + assert(offset < UINT64_MAX / secsz); + offset *= secsz; + + n_partitions = fdisk_table_get_nents(t); + for (size_t i = 0; i < n_partitions; i++) { + struct fdisk_partition *q; + uint64_t start; + + q = fdisk_table_get_partition(t, i); + if (!q) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m"); + + if (fdisk_partition_is_used(q) <= 0) + continue; + + if (!fdisk_partition_has_start(q)) + continue; + + start = fdisk_partition_get_start(q); + assert(start < UINT64_MAX / secsz); + start *= secsz; + + if (start >= offset && (next == UINT64_MAX || next > start)) + next = start; + } + + if (next == UINT64_MAX) { + /* No later partition? In that case check the end of the usable area */ + next = fdisk_get_last_lba(c); + assert(next < UINT64_MAX); + next++; /* The last LBA is one sector before the end */ + + assert(next < UINT64_MAX / secsz); + next *= secsz; + + if (offset > next) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end."); + } + + assert(next >= offset); + offset = round_up_size(offset, grainsz); + next = round_down_size(next, grainsz); + + *ret = LESS_BY(next, offset); /* Saturated subtraction, rounding might have fucked things up */ + return 0; +} + +static int context_copy_from(Context *context) { + _cleanup_close_ int fd = -EBADF; + _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; + _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL; + Partition *last = NULL; + unsigned long secsz, grainsz; + size_t n_partitions; + int r; + + if (!arg_copy_from) + return 0; + + r = context_open_and_lock_backing_fd(arg_copy_from, &fd); + if (r < 0) + return r; + + r = fd_verify_regular(fd); + if (r < 0) + return log_error_errno(r, "%s is not a file: %m", arg_copy_from); + + r = fdisk_new_context_fd(fd, /* read_only = */ true, /* sector_size = */ UINT32_MAX, &c); + if (r < 0) + return log_error_errno(r, "Failed to create fdisk context: %m"); + + secsz = fdisk_get_sector_size(c); + grainsz = fdisk_get_grain_size(c); + + /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */ + if (secsz < 512 || !ISPOWEROF2(secsz)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz); + + if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT)) + return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Cannot copy from disk %s with no GPT disk label.", arg_copy_from); + + r = fdisk_get_partitions(c, &t); + if (r < 0) + return log_error_errno(r, "Failed to acquire partition table: %m"); + + n_partitions = fdisk_table_get_nents(t); + for (size_t i = 0; i < n_partitions; i++) { + _cleanup_(partition_freep) Partition *np = NULL; + _cleanup_free_ char *label_copy = NULL; + struct fdisk_partition *p; + const char *label; + uint64_t sz, start, padding; + sd_id128_t ptid, id; + GptPartitionType type; + + p = fdisk_table_get_partition(t, i); + if (!p) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m"); + + if (fdisk_partition_is_used(p) <= 0) + continue; + + if (fdisk_partition_has_start(p) <= 0 || + fdisk_partition_has_size(p) <= 0 || + fdisk_partition_has_partno(p) <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number."); + + r = fdisk_partition_get_type_as_id128(p, &ptid); + if (r < 0) + return log_error_errno(r, "Failed to query partition type UUID: %m"); + + type = gpt_partition_type_from_uuid(ptid); + + r = fdisk_partition_get_uuid_as_id128(p, &id); + if (r < 0) + return log_error_errno(r, "Failed to query partition UUID: %m"); + + label = fdisk_partition_get_name(p); + if (!isempty(label)) { + label_copy = strdup(label); + if (!label_copy) + return log_oom(); + } + + sz = fdisk_partition_get_size(p); + assert(sz <= UINT64_MAX/secsz); + sz *= secsz; + + start = fdisk_partition_get_start(p); + assert(start <= UINT64_MAX/secsz); + start *= secsz; + + if (partition_type_exclude(type)) + continue; + + np = partition_new(); + if (!np) + return log_oom(); + + np->type = type; + np->new_uuid = id; + np->new_uuid_is_set = true; + np->size_min = np->size_max = sz; + np->new_label = TAKE_PTR(label_copy); + + np->definition_path = strdup(arg_copy_from); + if (!np->definition_path) + return log_oom(); + + r = determine_current_padding(c, t, p, secsz, grainsz, &padding); + if (r < 0) + return r; + + np->padding_min = np->padding_max = padding; + + np->copy_blocks_path = strdup(arg_copy_from); + if (!np->copy_blocks_path) + return log_oom(); + + np->copy_blocks_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (np->copy_blocks_fd < 0) + return log_error_errno(r, "Failed to duplicate file descriptor of %s: %m", arg_copy_from); + + np->copy_blocks_offset = start; + np->copy_blocks_size = sz; + + r = fdisk_partition_get_attrs_as_uint64(p, &np->gpt_flags); + if (r < 0) + return log_error_errno(r, "Failed to get partition flags: %m"); + + LIST_INSERT_AFTER(partitions, context->partitions, last, np); + last = TAKE_PTR(np); + context->n_partitions++; + } + + return 0; +} + static int context_read_definitions(Context *context) { _cleanup_strv_free_ char **files = NULL; - Partition *last = NULL; + Partition *last = LIST_FIND_TAIL(partitions, context->partitions); const char *const *dirs; int r; @@ -1899,74 +2125,6 @@ static int context_read_definitions(Context *context) { return 0; } -static int determine_current_padding( - struct fdisk_context *c, - struct fdisk_table *t, - struct fdisk_partition *p, - uint64_t secsz, - uint64_t grainsz, - uint64_t *ret) { - - size_t n_partitions; - uint64_t offset, next = UINT64_MAX; - - assert(c); - assert(t); - assert(p); - - if (!fdisk_partition_has_end(p)) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!"); - - offset = fdisk_partition_get_end(p); - assert(offset < UINT64_MAX); - offset++; /* The end is one sector before the next partition or padding. */ - assert(offset < UINT64_MAX / secsz); - offset *= secsz; - - n_partitions = fdisk_table_get_nents(t); - for (size_t i = 0; i < n_partitions; i++) { - struct fdisk_partition *q; - uint64_t start; - - q = fdisk_table_get_partition(t, i); - if (!q) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m"); - - if (fdisk_partition_is_used(q) <= 0) - continue; - - if (!fdisk_partition_has_start(q)) - continue; - - start = fdisk_partition_get_start(q); - assert(start < UINT64_MAX / secsz); - start *= secsz; - - if (start >= offset && (next == UINT64_MAX || next > start)) - next = start; - } - - if (next == UINT64_MAX) { - /* No later partition? In that case check the end of the usable area */ - next = fdisk_get_last_lba(c); - assert(next < UINT64_MAX); - next++; /* The last LBA is one sector before the end */ - - assert(next < UINT64_MAX / secsz); - next *= secsz; - - if (offset > next) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end."); - } - - assert(next >= offset); - offset = round_up_size(offset, grainsz); - next = round_down_size(next, grainsz); - - *ret = LESS_BY(next, offset); /* Saturated subtraction, rounding might have fucked things up */ - return 0; -} - static int fdisk_ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *data) { _cleanup_free_ char *ids = NULL; int r; @@ -2022,28 +2180,6 @@ static int derive_uuid(sd_id128_t base, const char *token, sd_id128_t *ret) { return 0; } -static int context_open_and_lock_backing_fd(Context *context, const char *node) { - _cleanup_close_ int fd = -EBADF; - - assert(context); - assert(node); - - if (context->backing_fd >= 0) - return 0; - - fd = open(node, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open device '%s': %m", node); - - /* Tell udev not to interfere while we are processing the device */ - if (flock(fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0) - return log_error_errno(errno, "Failed to lock device '%s': %m", node); - - log_debug("Device %s opened and locked.", node); - context->backing_fd = TAKE_FD(fd); - return 1; -} - static int context_load_partition_table(Context *context) { _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL; @@ -2072,7 +2208,7 @@ static int context_load_partition_table(Context *context) { else { uint32_t ssz; - r = context_open_and_lock_backing_fd(context, context->node); + r = context_open_and_lock_backing_fd(context->node, &context->backing_fd); if (r < 0) return r; @@ -2119,7 +2255,7 @@ static int context_load_partition_table(Context *context) { if (context->backing_fd < 0) { /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */ - r = context_open_and_lock_backing_fd(context, FORMAT_PROC_FD_PATH(fdisk_get_devfd(c))); + r = context_open_and_lock_backing_fd(FORMAT_PROC_FD_PATH(fdisk_get_devfd(c)), &context->backing_fd); if (r < 0) return r; } @@ -3940,6 +4076,9 @@ static int context_copy_blocks(Context *context) { log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".", p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno); + if (p->copy_blocks_offset != UINT64_MAX && lseek(p->copy_blocks_fd, p->copy_blocks_offset, SEEK_SET) < 0) + return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path); + r = copy_bytes(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK); if (r < 0) return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path); @@ -5996,6 +6135,7 @@ static int help(void) { " --sector-size=SIZE Set the logical sector size for the image\n" " --architecture=ARCH Set the generic architecture for the image\n" " --offline=BOOL Whether to build the image offline\n" + " --copy-from=IMAGE Copy partitions from the given image\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -6039,6 +6179,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SKIP_PARTITIONS, ARG_ARCHITECTURE, ARG_OFFLINE, + ARG_COPY_FROM, }; static const struct option options[] = { @@ -6073,6 +6214,7 @@ static int parse_argv(int argc, char *argv[]) { { "sector-size", required_argument, NULL, ARG_SECTOR_SIZE }, { "architecture", required_argument, NULL, ARG_ARCHITECTURE }, { "offline", required_argument, NULL, ARG_OFFLINE }, + { "copy-from", required_argument, NULL, ARG_COPY_FROM }, {} }; @@ -6394,6 +6536,12 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_COPY_FROM: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_copy_from); + if (r < 0) + return r; + break; + case '?': return -EINVAL; @@ -6945,6 +7093,10 @@ static int run(int argc, char *argv[]) { if (!context) return log_oom(); + r = context_copy_from(context); + if (r < 0) + return r; + strv_uniq(arg_definitions); r = context_read_definitions(context); diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index 13e40bd82a..118d797f2b 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -160,6 +160,27 @@ last-lba: 2097118 $imgs/zzz1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" $imgs/zzz2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"" + systemd-repart --offline="$OFFLINE" \ + --empty=create \ + --size=1G \ + --dry-run=no \ + --seed="$seed" \ + --copy-from="$imgs/zzz" \ + "$imgs/copy" + + output=$(sfdisk -d "$imgs/copy" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/copy +unit: sectors +first-lba: 2048 +last-lba: 2097118 +$imgs/copy1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/copy2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"" + + rm "$imgs/copy" # Save disk space + systemd-repart --offline="$OFFLINE" \ --definitions="$defs" \ --dry-run=no \