From 5036c9f59634326039f06f70e0b0c89890943884 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 19 Nov 2025 18:30:04 +0100 Subject: [PATCH 1/3] repart: Sum partitions size to get current disk size instead of using total size When working on disks the disk may have a total size bigger then the actual allocated size, therefore sum up the current partitions to calculate the current disk size instead of asuming that the entire disk is currently allocated. --- src/repart/repart.c | 79 ++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index ea44775eec..31e6413f23 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -10065,44 +10065,52 @@ done: static int determine_auto_size( Context *c, int level, - bool ignore_allocated, /* If true, determines unallocated space needed */ - uint64_t *ret) { + uint64_t *ret_current_size, + uint64_t *ret_minimal_size) { - uint64_t sum; + uint64_t current_size, minimal_size; assert(c); - sum = round_up_size(GPT_METADATA_SIZE, 4096); + minimal_size = round_up_size(GPT_METADATA_SIZE, 4096); + + if (c->from_scratch) + current_size = 0; + else + current_size = round_up_size(GPT_METADATA_SIZE, 4096); LIST_FOREACH(partitions, p, c->partitions) { uint64_t m; + if (PARTITION_EXISTS(p)) + current_size += p->current_size; + if (p->dropped || PARTITION_SUPPRESSED(p)) continue; m = partition_min_size_with_padding(c, p); - if (m > UINT64_MAX - sum) + if (m > UINT64_MAX - minimal_size) return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing."); - if (ignore_allocated && PARTITION_EXISTS(p)) - m = LESS_BY(m, p->current_size + p->current_padding); - - sum += m; + minimal_size += m; } - if (c->total != UINT64_MAX) + if (current_size != 0) /* Image already allocated? Then show its size. */ log_full(level, "Automatically determined minimal disk image size as %s, current block device/image size is %s.", - FORMAT_BYTES(sum), FORMAT_BYTES(c->total)); + FORMAT_BYTES(minimal_size), FORMAT_BYTES(current_size)); else /* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */ log_full(level, "Automatically determined minimal disk image size as %s.", - FORMAT_BYTES(sum)); + FORMAT_BYTES(minimal_size)); + + if (ret_current_size) + *ret_current_size = current_size; + if (ret_minimal_size) + *ret_minimal_size = minimal_size; - if (ret) - *ret = sum; return 0; } @@ -10341,53 +10349,56 @@ static int vl_method_run( /* If we have no node, just sum up how much space we need */ if (!context->node) { /* Check if space issue is caused by the whole disk being too small */ - uint64_t size; - r = determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ false, &size); + uint64_t minimal_size; + r = determine_auto_size(context, LOG_DEBUG, /* ret_current_size= */ NULL, &minimal_size); if (r < 0) return r; return sd_varlink_replybo( link, - SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", size)); + SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", minimal_size)); } r = context_ponder(context); if (r == -ENOSPC) { + uint64_t current_size, minimal_size; + + r = determine_auto_size(context, LOG_DEBUG, ¤t_size, &minimal_size); + if (r < 0) + return r; + /* Check if space issue is caused by the whole disk being too small */ - uint64_t size = UINT64_MAX; - (void) determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ false, &size); - if (size != UINT64_MAX && context->total != UINT64_MAX && size > context->total) + if (minimal_size > context->total) return sd_varlink_errorbo( link, "io.systemd.Repart.DiskTooSmall", - SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", size), - SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", context->total)); + SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", current_size), + SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", minimal_size)); /* Or if the disk would fit, but theres's not enough unallocated space */ - uint64_t need_free = UINT64_MAX; - (void) determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ true, &need_free); + uint64_t need_free = LESS_BY(minimal_size, current_size); return sd_varlink_errorbo( link, "io.systemd.Repart.InsufficientFreeSpace", - JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("minimalSizeBytes", size, UINT64_MAX), - JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("needFreeBytes", need_free, UINT64_MAX), - JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", context->total, UINT64_MAX)); + SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", current_size), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("needFreeBytes", need_free), + SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", minimal_size)); } if (r < 0) return r; if (p.dry_run) { - uint64_t size; + uint64_t current_size, minimal_size; /* If we are doing a dry-run, report the minimal size. */ - r = determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ false, &size); + r = determine_auto_size(context, LOG_DEBUG, ¤t_size, &minimal_size); if (r < 0) return r; return sd_varlink_replybo( link, - SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", size), - JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", context->total, UINT64_MAX)); + SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", minimal_size), + SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", current_size)); } r = context_write_partition_table(context); @@ -10633,12 +10644,12 @@ static int run(int argc, char *argv[]) { return r; if (arg_node_none) { - (void) determine_auto_size(context, LOG_INFO, /* ignore_allocated= */ false, /* ret= */ NULL); + (void) determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, /* ret_minimal_size= */ NULL); return 0; } if (arg_size_auto) { - r = determine_auto_size(context, LOG_INFO, /* ignore_allocated= */ false, &arg_size); + r = determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, &arg_size); if (r < 0) return r; @@ -10663,7 +10674,7 @@ 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, LOG_INFO, /* ignore_allocated= */ false, /* ret= */ NULL); + (void) determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, /* ret_minimal_size= */ NULL); return r; } if (r < 0) From 9ee029c8f6e90985c417e7a6c0e0d69769777553 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Thu, 12 Feb 2026 17:28:43 +0100 Subject: [PATCH 2/3] repart-varlink: Calculate the size of foreign partitions To decide whether the disk is to small or has insufficient free space we need to know how much of the disk is filled with foreign partitions. The calculated size is used in a future commit. --- src/repart/repart.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index 31e6413f23..a3083ee1e1 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -10066,9 +10066,10 @@ static int determine_auto_size( Context *c, int level, uint64_t *ret_current_size, + uint64_t *ret_foreign_size, uint64_t *ret_minimal_size) { - uint64_t current_size, minimal_size; + uint64_t current_size, foreign_size, minimal_size; assert(c); @@ -10079,9 +10080,14 @@ static int determine_auto_size( else current_size = round_up_size(GPT_METADATA_SIZE, 4096); + foreign_size = 0; + LIST_FOREACH(partitions, p, c->partitions) { uint64_t m; + if (PARTITION_IS_FOREIGN(p)) + foreign_size += p->current_size; + if (PARTITION_EXISTS(p)) current_size += p->current_size; @@ -10108,6 +10114,8 @@ static int determine_auto_size( if (ret_current_size) *ret_current_size = current_size; + if (ret_foreign_size) + *ret_foreign_size = foreign_size; if (ret_minimal_size) *ret_minimal_size = minimal_size; @@ -10350,7 +10358,7 @@ static int vl_method_run( if (!context->node) { /* Check if space issue is caused by the whole disk being too small */ uint64_t minimal_size; - r = determine_auto_size(context, LOG_DEBUG, /* ret_current_size= */ NULL, &minimal_size); + r = determine_auto_size(context, LOG_DEBUG, /* ret_current_size= */ NULL, /* ret_foreign_size= */ NULL, &minimal_size); if (r < 0) return r; @@ -10363,7 +10371,7 @@ static int vl_method_run( if (r == -ENOSPC) { uint64_t current_size, minimal_size; - r = determine_auto_size(context, LOG_DEBUG, ¤t_size, &minimal_size); + r = determine_auto_size(context, LOG_DEBUG, ¤t_size, /* ret_foreign_size= */ NULL, &minimal_size); if (r < 0) return r; @@ -10391,7 +10399,7 @@ static int vl_method_run( uint64_t current_size, minimal_size; /* If we are doing a dry-run, report the minimal size. */ - r = determine_auto_size(context, LOG_DEBUG, ¤t_size, &minimal_size); + r = determine_auto_size(context, LOG_DEBUG, ¤t_size, /* ret_foreign_size= */ NULL, &minimal_size); if (r < 0) return r; @@ -10644,12 +10652,12 @@ static int run(int argc, char *argv[]) { return r; if (arg_node_none) { - (void) determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, /* ret_minimal_size= */ NULL); + (void) determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, /* ret_foreign_size= */ NULL, /* ret_minimal_size= */ NULL); return 0; } if (arg_size_auto) { - r = determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, &arg_size); + r = determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, /* ret_foreign_size= */ NULL, &arg_size); if (r < 0) return r; @@ -10674,7 +10682,7 @@ 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, LOG_INFO, /* ret_current_size= */ NULL, /* ret_minimal_size= */ NULL); + (void) determine_auto_size(context, LOG_INFO, /* ret_current_size= */ NULL, /* ret_foreign_size= */ NULL, /* ret_minimal_size= */ NULL); return r; } if (r < 0) From f94d3180b05c3fb7da0dae900b8caa2017f8e21c Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Thu, 12 Feb 2026 17:32:32 +0100 Subject: [PATCH 3/3] repart-varlink: Consider only managed parititions for size errors Report DiskTooSmall only if partitions managed by repart don't fit the disk. Because if the disk is already full with forigin partitions we would always report DiskTooSmall instead of InsufficentFreeSpace. --- src/repart/repart.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index a3083ee1e1..8050427eea 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -10369,14 +10369,16 @@ static int vl_method_run( r = context_ponder(context); if (r == -ENOSPC) { - uint64_t current_size, minimal_size; + uint64_t current_size, foreign_size, minimal_size; - r = determine_auto_size(context, LOG_DEBUG, ¤t_size, /* ret_foreign_size= */ NULL, &minimal_size); + r = determine_auto_size(context, LOG_DEBUG, ¤t_size, &foreign_size, &minimal_size); if (r < 0) return r; + uint64_t needed_size = LESS_BY(minimal_size, foreign_size); + /* Check if space issue is caused by the whole disk being too small */ - if (minimal_size > context->total) + if (needed_size > context->total) return sd_varlink_errorbo( link, "io.systemd.Repart.DiskTooSmall",