From ad25ede4887875a42a16d3e8336c9119b6877a6d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 May 2024 13:56:47 +0200 Subject: [PATCH 1/4] repart: make prefix argument to clear_progress_bar() optional In this case, let's try to override the whole line, not just the first few chars. --- src/shared/pretty-print.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index 8eba052f2c..f361c4760a 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -532,7 +532,9 @@ void clear_progress_bar(const char *prefix) { fputc('\r', stderr); if (terminal_is_dumb()) - fputs(strrepa(" ", utf8_console_width(prefix) + 4), /* 4: %3.0f%% */ + fputs(strrepa(" ", + prefix ? utf8_console_width(prefix) + 4 : + LESS_BY(columns(), 1U)), /* 4: %3.0f%% */ stderr); else fputs(ANSI_ERASE_TO_END_OF_LINE, stderr); From 468d09c3196a7a4c68b2c3f75b4dd8dcbed8650f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 May 2024 13:57:56 +0200 Subject: [PATCH 2/4] repart: allow reading from char device for CopyBlocks= Sometimes it is useful to allow initializing a partition with randomized data, hence allow reading from a char device as source for CopyBlocks= --- man/repart.d.xml | 14 ++++++++------ src/partition/repart.c | 23 ++++++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/man/repart.d.xml b/man/repart.d.xml index 52e6b97240..b107b20c1c 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -362,12 +362,14 @@ CopyBlocks= - Takes a path to a regular file, block device node or directory, or the special value - auto. If specified and the partition is newly created, the data from the specified - path is written to the newly created partition, on the block level. If a directory is specified, the - backing block device of the file system the directory is on is determined, and the data read directly - from that. This option is useful to efficiently replicate existing file systems onto new partitions - on the block level — for example to build a simple OS installer or an OS image builder. + Takes a path to a regular file, block device node, char device node or directory, or + the special value auto. If specified and the partition is newly created, the data + from the specified path is written to the newly created partition, on the block level. If a directory + is specified, the backing block device of the file system the directory is on is determined, and the + data read directly from that. This option is useful to efficiently replicate existing file systems + onto new partitions on the block level — for example to build a simple OS installer or an OS image + builder. Specify /dev/urandom as value to initialize a partition with random + data. If the special value auto is specified, the source to copy from is automatically picked up from the running system (or the image specified with diff --git a/src/partition/repart.c b/src/partition/repart.c index bee48b770c..17ebcbe9ef 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -4522,8 +4522,13 @@ static int context_copy_blocks(Context *context) { continue; assert(p->new_size != UINT64_MAX); - assert(p->copy_blocks_size != UINT64_MAX); - assert(p->new_size >= p->copy_blocks_size + (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0)); + + size_t extra = p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0; + + if (p->copy_blocks_size == UINT64_MAX) + p->copy_blocks_size = LESS_BY(p->new_size, extra); + + assert(p->new_size >= p->copy_blocks_size + extra); usec_t start_timestamp = now(CLOCK_MONOTONIC); @@ -6350,13 +6355,17 @@ static int context_open_copy_block_paths( r = blockdev_get_device_size(source_fd, &size); if (r < 0) return log_error_errno(r, "Failed to determine size of block device to copy from: %m"); - } else + } else if (S_ISCHR(st.st_mode)) + size = UINT64_MAX; + 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.", opened); - if (size <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", opened); - 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.", opened); + if (size != UINT64_MAX) { + if (size <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", opened); + 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.", opened); + } p->copy_blocks_fd = TAKE_FD(source_fd); p->copy_blocks_size = size; From add090ea826f20b8c3ced330cef65fac2d6eb186 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 May 2024 14:00:21 +0200 Subject: [PATCH 3/4] repart: show progress bar when initializing partition via CopyBlocks= This might take a while, hence make it pretty. --- src/partition/repart.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 17ebcbe9ef..d8253089a1 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -295,6 +295,7 @@ typedef struct Partition { int copy_blocks_fd; uint64_t copy_blocks_offset; uint64_t copy_blocks_size; + uint64_t copy_blocks_done; char *format; char **copy_files; @@ -4499,6 +4500,26 @@ static int partition_format_verity_sig(Context *context, Partition *p) { return 0; } +static int progress_bytes(uint64_t n_bytes, void *userdata) { + Partition *p = ASSERT_PTR(userdata); + _cleanup_free_ char *s = NULL; + + p->copy_blocks_done += n_bytes; + + if (asprintf(&s, "%s %s %s %s/%s ", + strna(p->copy_blocks_path), + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), + strna(p->definition_path), + FORMAT_BYTES(p->copy_blocks_done), + FORMAT_BYTES(p->copy_blocks_size)) < 0) + return log_oom(); + + draw_progress_bar(s, + p->copy_blocks_done >= p->copy_blocks_size ? 100.0 : /* catch division be zero */ + 100.0 * (double) p->copy_blocks_done / (double) p->copy_blocks_size); + return 0; +} + static int context_copy_blocks(Context *context) { int r; @@ -4555,7 +4576,8 @@ static int context_copy_blocks(Context *context) { 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); + r = copy_bytes_full(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK, /* ret_remains= */ NULL, /* ret_remains_size= */ NULL, progress_bytes, p); + clear_progress_bar(/* prefix= */ NULL); if (r < 0) return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path); From 9a9cc27761c2ce5a5c5ff7e3c57b7d96c162da63 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Jun 2024 10:24:47 +0200 Subject: [PATCH 4/4] ci: add small test case for /dev/urandom as source for CopyBlocks= --- test/units/TEST-58-REPART.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index 8a014ac9fb..fc06fde0ee 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -1287,6 +1287,26 @@ testcase_dropped_partitions() { [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]] } +testcase_urandom() { + local workdir image defs + + workdir="$(mktemp --directory "/tmp/test-repart.urandom.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '${workdir:?}'" RETURN + + image="$workdir/image.img" + truncate -s 32M "$image" + + defs="$workdir/defs" + mkdir "$defs" + echo -ne "[Partition]\nType=swap\nCopyBlocks=/dev/urandom\n" >"$defs/10-urandom.conf" + + systemd-repart --empty=force --pretty=yes --dry-run=no --definitions="$defs" "$image" + + sfdisk -q -l "$image" + [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 1 ]] +} + OFFLINE="yes" run_testcases