1
0
mirror of https://github.com/systemd/systemd synced 2024-06-29 06:34:30 +00:00

Merge pull request #33003 from poettering/repart-progress

repart: draw progress bar during CopyBlocks= operation and other tweaks
This commit is contained in:
Lennart Poettering 2024-06-25 14:08:04 +02:00 committed by GitHub
commit 7f1c31829b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 15 deletions

View File

@ -362,12 +362,14 @@
<varlistentry>
<term><varname>CopyBlocks=</varname></term>
<listitem><para>Takes a path to a regular file, block device node or directory, or the special value
<literal>auto</literal>. 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.</para>
<listitem><para>Takes a path to a regular file, block device node, char device node or directory, or
the special value <literal>auto</literal>. 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 <filename>/dev/urandom</filename> as value to initialize a partition with random
data.</para>
<para>If the special value <literal>auto</literal> is specified, the source to copy from is
automatically picked up from the running system (or the image specified with

View File

@ -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;
@ -4522,8 +4543,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);
@ -4550,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);
@ -6350,13 +6377,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;

View File

@ -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);

View File

@ -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