From 9d8f8d55ed15ec13601cfbb455d61dd4b344df5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 16 Jul 2026 18:11:31 +0200 Subject: [PATCH] sysinstall: don't ask whether to erase a disk that contains no partitions When the target disk carried no partitions, the installer still asked: Please type 'keep' to install the OS in addition to what the disk already contains, or 'erase' to erase all data on the disk: The question is meaningless in that case: there's nothing on the disk worth preserving, and both answers lead to the same result. Modify fsystemd-repart to report in the dry-run reply of io.systemd.Repart.Run() the number of partitions currently on the disk. The field is only included if the existing partition table was actually read, i.e. in the 'refuse' and 'allow' empty modes, and omitted otherwise. Make systemd-sysinstall skip the erase question if the reply positively indicates that there are no partitions, proceeding as if 'keep' was selected. --- src/repart/repart.c | 13 ++++++- src/shared/varlink-io.systemd.Repart.c | 2 ++ src/sysinstall/sysinstall.c | 50 +++++++++++++++++--------- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index 7a6c542c69..31277c8208 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -11608,10 +11608,21 @@ static int vl_method_run( if (r < 0) return r; + bool count_partitions = IN_SET(context->empty, EMPTY_REFUSE, EMPTY_ALLOW); + uint64_t n_partitions = 0; + + if (count_partitions) + LIST_FOREACH(partitions, pp, context->partitions) + n_partitions += PARTITION_EXISTS(pp); + + /* In 'force' and 'require' modes the existing partition table is not read, hence we don't + * know how many partitions the disk contains, and say nothing about it. */ return sd_varlink_replybo( link, SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", minimal_size), - SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", context->total)); + SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", context->total), + SD_JSON_BUILD_PAIR_CONDITION(count_partitions, + "partitionCount", SD_JSON_BUILD_UNSIGNED(n_partitions))); } r = context_write_partition_table(context); diff --git a/src/shared/varlink-io.systemd.Repart.c b/src/shared/varlink-io.systemd.Repart.c index 968d643f01..33b16525ae 100644 --- a/src/shared/varlink-io.systemd.Repart.c +++ b/src/shared/varlink-io.systemd.Repart.c @@ -59,6 +59,8 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_DEFINE_OUTPUT(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("In dry-run mode returns the size of the selected block device."), SD_VARLINK_DEFINE_OUTPUT(currentSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Returns the number of partitions currently on the selected block device. Omitted if not determined by the workflow, e.g. if the existing partition table was not read because empty mode 'force' or 'require' was selected."), + SD_VARLINK_DEFINE_OUTPUT(partitionCount, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, a phase identifier is sent in progress updates."), SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(phase, ProgressPhase, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, an object identifier string is sent in progress updates."), diff --git a/src/sysinstall/sysinstall.c b/src/sysinstall/sysinstall.c index 7a531edfcc..b3b730b914 100644 --- a/src/sysinstall/sysinstall.c +++ b/src/sysinstall/sysinstall.c @@ -689,11 +689,12 @@ static int sysinstall_context_invoke_repart_run(SysInstallContext *context) { return result.ret; } -static int read_space_metrics( +static int read_repart_metrics( sd_json_variant *v, uint64_t *min_size, uint64_t *current_size, - uint64_t *need_free) { + uint64_t *need_free, + uint64_t *n_partitions) { int r; @@ -701,16 +702,19 @@ static int read_space_metrics( uint64_t min_size; uint64_t current_size; uint64_t need_free; + uint64_t n_partitions; } p = { .min_size = UINT64_MAX, .current_size = UINT64_MAX, .need_free = UINT64_MAX, + .n_partitions = UINT64_MAX, }; static const sd_json_dispatch_field dispatch_table[] = { { "minimalSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, min_size), 0 }, { "currentSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, current_size), 0 }, { "needFreeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, need_free), 0 }, + { "partitionCount", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, n_partitions), 0 }, {} }; @@ -724,6 +728,8 @@ static int read_space_metrics( *current_size = p.current_size; if (need_free) *need_free = p.need_free; + if (n_partitions) + *n_partitions = p.n_partitions; return 0; } @@ -736,7 +742,8 @@ static int invoke_repart( char **definitions, uint64_t *min_size, /* initialized both on success and error */ uint64_t *current_size, /* ditto */ - uint64_t *need_free) { /* ditto */ + uint64_t *need_free, /* ditto */ + uint64_t *n_partitions) { /* ditto; UINT64_MAX if not determined */ int r; @@ -747,7 +754,7 @@ static int invoke_repart( r = connect_to_repart(link); if (r < 0) { - read_space_metrics(/* v= */ NULL, min_size, current_size, need_free); + read_repart_metrics(/* v= */ NULL, min_size, current_size, need_free, n_partitions); return r; } @@ -772,19 +779,19 @@ static int invoke_repart( SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsEmpty", true), SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsFactoryReset", true)); if (r < 0) { - read_space_metrics(/* v= */ NULL, min_size, current_size, need_free); + read_repart_metrics(/* v= */ NULL, min_size, current_size, need_free, n_partitions); return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %m"); } if (error_id) { if (streq(error_id, "io.systemd.Repart.InsufficientFreeSpace")) { - (void) read_space_metrics(reply, min_size, current_size, need_free); + (void) read_repart_metrics(reply, min_size, current_size, need_free, n_partitions); return log_full_errno( dry_run ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENOSPC), "Not enough free space on disk, cannot install."); } if (streq(error_id, "io.systemd.Repart.DiskTooSmall")) { - (void) read_space_metrics(reply, min_size, current_size, need_free); + (void) read_repart_metrics(reply, min_size, current_size, need_free, n_partitions); return log_full_errno( dry_run ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(E2BIG), @@ -792,7 +799,7 @@ static int invoke_repart( } /* For all other errors reset the metrics */ - read_space_metrics(/* v= */ NULL, min_size, current_size, need_free); + read_repart_metrics(/* v= */ NULL, min_size, current_size, need_free, n_partitions); if (streq(error_id, "io.systemd.Repart.ConflictingDiskLabelPresent")) return log_full_errno( @@ -807,7 +814,7 @@ static int invoke_repart( return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %s", error_id); } - (void) read_space_metrics(reply, min_size, current_size, need_free); + (void) read_repart_metrics(reply, min_size, current_size, need_free, n_partitions); return 0; } @@ -923,7 +930,8 @@ static int validate_run(sd_varlink **repart_link, const char *node) { /* First loop: either with explicitly configured --erase= value, or false. A second loop only if not configured explicitly. */ bool try_erase = arg_erase > 0, conflicting_disk_label = false; for (;;) { - uint64_t min_size = UINT64_MAX, current_size = UINT64_MAX, need_free = UINT64_MAX; + uint64_t min_size = UINT64_MAX, current_size = UINT64_MAX, need_free = UINT64_MAX, + n_partitions = UINT64_MAX; r = invoke_repart( repart_link, node, @@ -932,7 +940,8 @@ static int validate_run(sd_varlink **repart_link, const char *node) { arg_definitions, &min_size, ¤t_size, - &need_free); + &need_free, + &n_partitions); if (r == -ENOSPC) { /* The disk is large enough, but there's not enough unallocated space. Hence proceed, but require erasing */ if (try_erase || arg_erase >= 0) @@ -970,9 +979,16 @@ static int validate_run(sd_varlink **repart_link, const char *node) { log_warning("A conflicting disk label has been found, and must be erased for installation."); if (arg_erase < 0) { - r = prompt_erase(/* can_add= */ !try_erase, &arg_erase); - if (r < 0) - return r; + if (n_partitions == 0) { + /* If the disk contains no partitions there's nothing worth preserving on + * it, hence don't bother asking whether to erase it. */ + log_info("The selected disk contains no partitions, proceeding."); + arg_erase = false; + } else { + r = prompt_erase(/* can_add= */ !try_erase, &arg_erase); + if (r < 0) + return r; + } } return 0; @@ -1754,7 +1770,8 @@ static int fetch_candidate_devices_reply( context->definitions, &min_size, ¤t_size, - &need_free); + &need_free, + /* n_partitions= */ NULL); DeviceFit fit; if (r < 0) { @@ -2132,7 +2149,8 @@ static int run(int argc, char *argv[]) { arg_definitions, &min_size, /* current_size= */ NULL, - /* need_free= */ NULL); + /* need_free= */ NULL, + /* n_partitions= */ NULL); if (r < 0) return r;