From: Lennart Poettering Date: Fri, 29 Aug 2025 08:40:24 +0000 (+0200) Subject: repart: if device node is specified as "-", calculate needed disk space X-Git-Tag: v259-rc1~193^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2ecfea7491d8be9adaf24644e8e00f743444a96b;p=thirdparty%2Fsystemd.git repart: if device node is specified as "-", calculate needed disk space So far repart always required specification of a device node. And if none was specified, then we'd fine the node backing the root fs. Let's optionally allow that the device node is explicitly not specified (i.e. specified as "-" or ""), in which case we'll just print the size of the minimal image given the definitions. --- diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index 317ae05826a..41e386212ce 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -26,7 +26,7 @@ systemd-repart OPTIONS - BLOCKDEVICE + BLOCKDEVICE systemd-repart.service @@ -35,10 +35,10 @@ Description - systemd-repart creates partition tables, and adds or grows partitions, - based on the configuration files described in - repart.d5. - + systemd-repart creates partition tables, and adds or grows partitions, based on + the configuration files described in + repart.d5. It operates + on the block device or file image specified on the command line. systemd-repart is used when building OS images, and also when deploying images to automatically adjust them, during boot, to the system they @@ -53,6 +53,11 @@ systemd-repart.service service is generally run at boot in the initrd, in order to augment the partition table of the OS before its partitions are mounted. + If the block device is specified as - (or as an empty string), + systemd-repart will not operate on any block device or image file, and instead + determine and output the minimum disk/image size for the specified partition configuration, taking all + configured size constraints into account. + systemd-repart operations are mostly incremental: it grows existing partitions or adds new ones, but does not shrink, delete, or move existing partitions. The service is intended to be run on every boot, but when it detects that the partition table already matches the installed diff --git a/src/repart/repart.c b/src/repart/repart.c index 4d3984f815d..fcd46702a15 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -159,6 +159,7 @@ typedef enum AppendMode { static EmptyMode arg_empty = EMPTY_UNSET; static bool arg_dry_run = true; static char *arg_node = NULL; +static bool arg_node_none = false; static char *arg_root = NULL; static char *arg_image = NULL; static char **arg_definitions = NULL; @@ -830,6 +831,7 @@ static Context* context_new( .private_key = private_key, .empty = empty, .dry_run = dry_run, + .backing_fd = -EBADF, }; return context; @@ -3410,6 +3412,15 @@ static void derive_salt(sd_id128_t base, const char *token, uint8_t ret[static S hmac_sha256(base.bytes, sizeof(base.bytes), token, strlen(token), ret); } +static int context_load_fallback_metrics(Context *context) { + assert(context); + + context->sector_size = arg_sector_size > 0 ? arg_sector_size : 512; + context->grain_size = MAX(context->sector_size, 4096U); + context->default_fs_sector_size = arg_sector_size > 0 ? arg_sector_size : DEFAULT_FILESYSTEM_SECTOR_SIZE; + return 1; /* Starting from scratch */ +} + 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; @@ -3422,6 +3433,7 @@ static int context_load_partition_table(Context *context) { int r; assert(context); + assert(context->node); assert(!context->fdisk_context); assert(!context->free_areas); assert(context->start == UINT64_MAX); @@ -4685,14 +4697,16 @@ static int prepare_temporary_file(Context *context, PartitionTarget *t, uint64_t if (fd < 0) return log_error_errno(fd, "Failed to create temporary file: %m"); - r = read_attr_fd(fdisk_get_devfd(context->fdisk_context), &attrs); - if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r)) - log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node); - - if (FLAGS_SET(attrs, FS_NOCOW_FL)) { - r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r)) - return log_error_errno(r, "Failed to disable copy-on-write on %s: %m", temp); + if (context->fdisk_context) { + r = read_attr_fd(fdisk_get_devfd(context->fdisk_context), &attrs); + if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r)) + log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node); + + if (FLAGS_SET(attrs, FS_NOCOW_FL)) { + r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r)) + return log_error_errno(r, "Failed to disable copy-on-write on %s: %m", temp); + } } if (ftruncate(fd, size) < 0) @@ -8242,9 +8256,11 @@ static int context_minimize(Context *context) { assert(context); - r = read_attr_fd(context->backing_fd, &attrs); - if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r)) - log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node); + if (context->backing_fd >= 0) { + r = read_attr_fd(context->backing_fd, &attrs); + if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r)) + log_warning_errno(r, "Failed to read file attributes of %s, ignoring: %m", context->node); + } LIST_FOREACH(partitions, p, context->partitions) { _cleanup_(rm_rf_physical_and_freep) char *root = NULL; @@ -9323,9 +9339,14 @@ static int parse_argv( } if (argc > optind) { - arg_node = strdup(argv[optind]); - if (!arg_node) - return log_oom(); + if (empty_or_dash(argv[optind])) + arg_node_none = true; + else { + arg_node = strdup(argv[optind]); + if (!arg_node) + return log_oom(); + arg_node_none = false; + } } if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image) @@ -9579,6 +9600,9 @@ static int find_root(Context *context) { assert(context); + if (arg_node_none) + return 0; + if (arg_node) { if (context->empty == EMPTY_CREATE) { _cleanup_close_ int fd = -EBADF; @@ -9806,7 +9830,7 @@ done: return 1; } -static int determine_auto_size(Context *c) { +static int determine_auto_size(Context *c, uint64_t *ret) { uint64_t sum; assert(c); @@ -9828,14 +9852,15 @@ static int determine_auto_size(Context *c) { if (c->total != UINT64_MAX) /* Image already allocated? Then show its size. */ - log_info("Automatically determined minimal disk image size as %s, current image size is %s.", + log_info("Automatically determined minimal disk image size as %s, current block device/image size is %s.", FORMAT_BYTES(sum), FORMAT_BYTES(c->total)); else /* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */ log_info("Automatically determined minimal disk image size as %s.", FORMAT_BYTES(sum)); - arg_size = sum; + if (ret) + *ret = sum; return 0; } @@ -9858,11 +9883,9 @@ static int context_ponder(Context *context) { continue; /* Still no luck. Let's drop a priority and try again. */ /* No more priorities left to drop. This configuration just doesn't fit on this disk... */ - r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC), - "Can't fit requested partitions into available free space (%s), refusing.", - FORMAT_BYTES(largest_free_area)); - (void) determine_auto_size(context); - return r; + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), + "Can't fit requested partitions into available free space (%s), refusing.", + FORMAT_BYTES(largest_free_area)); } LIST_FOREACH(partitions, p, context->partitions) { @@ -10050,7 +10073,7 @@ static int run(int argc, char *argv[]) { if (!arg_root) return log_oom(); - if (!arg_node) { + if (!arg_node && !arg_node_none) { arg_node = strdup(loop_device->node); if (!arg_node) return log_oom(); @@ -10117,21 +10140,24 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - if (arg_size != UINT64_MAX) { - r = resize_backing_fd( - context->node, - &context->backing_fd, - node_is_our_loop ? arg_image : NULL, - node_is_our_loop ? loop_device : NULL, - context->sector_size); - if (r < 0) - return r; - } + if (context->node) { + if (arg_size != UINT64_MAX) { + r = resize_backing_fd( + context->node, + &context->backing_fd, + node_is_our_loop ? arg_image : NULL, + node_is_our_loop ? loop_device : NULL, + context->sector_size); + if (r < 0) + return r; + } - r = context_load_partition_table(context); - if (r == -EHWPOISON) - return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't - * really an error when called at boot. */ + r = context_load_partition_table(context); + if (r == -EHWPOISON) + return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't + * really an error when called at boot. */ + } else + r = context_load_fallback_metrics(context); if (r < 0) return r; context->from_scratch = r > 0; /* Starting from scratch */ @@ -10194,8 +10220,13 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + if (arg_node_none) { + (void) determine_auto_size(context, /* ret= */ NULL); + return 0; + } + if (arg_size_auto) { - r = determine_auto_size(context); + r = determine_auto_size(context, &arg_size); if (r < 0) return r; @@ -10218,6 +10249,11 @@ static int run(int argc, char *argv[]) { } r = context_ponder(context); + if (r == -ENOSPC) { + /* When we hit space issues, tell the user the minimal size. */ + (void) determine_auto_size(context, /* ret= */ NULL); + return r; + } if (r < 0) return r;