repart: add new CopyBlocks= setting

This allows copying in arbitrary file systems on the block level into
newly created partitions.

Usecase: simple replicating OS installers or OS image builders.
This commit is contained in:
Lennart Poettering
2020-06-25 18:52:52 +02:00
parent 3f11426f52
commit 757bc2e4c1
3 changed files with 213 additions and 13 deletions
+158 -12
View File
@@ -114,6 +114,10 @@ struct Partition {
FreeArea *padding_area;
FreeArea *allocated_to_area;
char *copy_blocks_path;
int copy_blocks_fd;
uint64_t copy_blocks_size;
LIST_FIELDS(Partition, partitions);
};
@@ -174,6 +178,8 @@ static Partition *partition_new(void) {
.padding_max = UINT64_MAX,
.partno = UINT64_MAX,
.offset = UINT64_MAX,
.copy_blocks_fd = -1,
.copy_blocks_size = UINT64_MAX,
};
return p;
@@ -192,6 +198,9 @@ static Partition* partition_free(Partition *p) {
if (p->new_partition)
fdisk_unref_partition(p->new_partition);
free(p->copy_blocks_path);
safe_close(p->copy_blocks_fd);
return mfree(p);
}
@@ -339,7 +348,11 @@ static uint64_t partition_min_size(const Partition *p) {
}
sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
return MAX(p->size_min == UINT64_MAX ? DEFAULT_MIN_SIZE : p->size_min, sz);
if (p->copy_blocks_size != UINT64_MAX)
sz = MAX(p->copy_blocks_size, sz);
return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz);
}
static uint64_t partition_max_size(const Partition *p) {
@@ -986,17 +999,18 @@ static int config_parse_size4096(
static int partition_read_definition(Partition *p, const char *path) {
ConfigTableItem table[] = {
{ "Partition", "Type", config_parse_type, 0, &p->type_uuid },
{ "Partition", "Label", config_parse_label, 0, &p->new_label },
{ "Partition", "UUID", config_parse_id128, 0, &p->new_uuid },
{ "Partition", "Priority", config_parse_int32, 0, &p->priority },
{ "Partition", "Weight", config_parse_weight, 0, &p->weight },
{ "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
{ "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
{ "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
{ "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
{ "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
{ "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
{ "Partition", "Type", config_parse_type, 0, &p->type_uuid },
{ "Partition", "Label", config_parse_label, 0, &p->new_label },
{ "Partition", "UUID", config_parse_id128, 0, &p->new_uuid },
{ "Partition", "Priority", config_parse_int32, 0, &p->priority },
{ "Partition", "Weight", config_parse_weight, 0, &p->weight },
{ "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
{ "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
{ "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
{ "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
{ "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
{ "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
{ "Partition", "CopyBlocks", config_parse_path, 0, &p->copy_blocks_path },
{}
};
int r;
@@ -2126,6 +2140,48 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
return 0;
}
static int context_copy_blocks(Context *context) {
Partition *p;
int fd = -1, r;
assert(context);
/* Copy in file systems on the block level */
LIST_FOREACH(partitions, p, context->partitions) {
char buf[FORMAT_BYTES_MAX];
if (p->copy_blocks_fd < 0)
continue;
if (p->dropped)
continue;
if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
continue;
assert(p->new_size != UINT64_MAX);
assert(p->copy_blocks_size != UINT64_MAX);
assert(p->new_size >= p->copy_blocks_size);
if (fd < 0)
assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
log_info("Copying in '%s' (%s) on block level into partition %" PRIu64 ".", p->copy_blocks_path, format_bytes(buf, sizeof(buf), p->copy_blocks_size), p->partno);
r = copy_bytes_full(p->copy_blocks_fd, fd, p->copy_blocks_size, 0, NULL, NULL, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
}
return 0;
}
static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
struct {
sd_id128_t type_uuid;
@@ -2388,6 +2444,10 @@ static int context_write_partition_table(
if (r < 0)
return r;
r = context_copy_blocks(context);
if (r < 0)
return r;
LIST_FOREACH(partitions, p, context->partitions) {
if (p->dropped)
continue;
@@ -2633,6 +2693,87 @@ static int context_can_factory_reset(Context *context) {
return false;
}
static int context_open_copy_block_paths(Context *context) {
Partition *p;
int r;
assert(context);
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_close_ int source_fd = -1;
uint64_t size;
struct stat st;
assert(p->copy_blocks_fd < 0);
assert(p->copy_blocks_size == UINT64_MAX);
if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
continue;
if (!p->copy_blocks_path)
continue;
source_fd = open(p->copy_blocks_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (source_fd < 0)
return log_error_errno(errno, "Failed to open block copy file '%s': %m", p->copy_blocks_path);
if (fstat(source_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block copy file '%s': %m", p->copy_blocks_path);
if (S_ISDIR(st.st_mode)) {
_cleanup_free_ char *bdev = NULL;
/* If the file is a directory, automatically find the backing block device */
if (major(st.st_dev) != 0)
r = device_path_make_major_minor(S_IFBLK, st.st_dev, &bdev);
else {
dev_t devt;
/* Special support for btrfs */
r = btrfs_get_block_device_fd(source_fd, &devt);
if (r < 0)
return log_error_errno(r, "Unable to determine backing block device of '%s': %m", p->copy_blocks_path);
r = device_path_make_major_minor(S_IFBLK, devt, &bdev);
}
if (r < 0)
return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", p->copy_blocks_path);
safe_close(source_fd);
source_fd = open(bdev, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (source_fd < 0)
return log_error_errno(errno, "Failed to open block device '%s': %m", bdev);
if (fstat(source_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
if (!S_ISBLK(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Block device '%s' is not actually a block device, refusing.", bdev);
}
if (S_ISREG(st.st_mode))
size = st.st_size;
else if (S_ISBLK(st.st_mode)) {
if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", p->copy_blocks_path);
if (size <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", p->copy_blocks_path);
if (size % 512 != 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", p->copy_blocks_path);
p->copy_blocks_fd = TAKE_FD(source_fd);
p->copy_blocks_size = size;
}
return 0;
}
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -3213,6 +3354,11 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
/* Open all files to copy blocks from now, since we want to take their size into consideration */
r = context_open_copy_block_paths(context);
if (r < 0)
return r;
/* First try to fit new partitions in, dropping by priority until it fits */
for (;;) {
if (context_allocate_partitions(context))
+31
View File
@@ -113,3 +113,34 @@ $D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FB
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
EOF
dd if=/dev/urandom of=$D/block-copy bs=4096 count=10240
cat >$D/definitions/extra2.conf <<EOF
[Partition]
Type=linux-generic
Label=block-copy
UUID=2a1d97e1d0a346cca26eadc643926617
CopyBlocks=$D/block-copy
EOF
$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions
sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated4
cmp $D/populated4 - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 6291422
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-$UNAME"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy"
EOF
cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) $D/block-copy $D/zzz