From: Karel Zak Date: Mon, 8 Sep 2025 09:44:16 +0000 (+0200) Subject: libfdisk: improve collision reporting X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e873aa0322f42167a402e95dd398fcc4eb256119;p=thirdparty%2Futil-linux.git libfdisk: improve collision reporting In some cases, a collision occurs in the first sector. Creating a new partition table overwrites this sector, but updating an existing table does not. In the latter case, promising to wipe the collision is a bug. Because we cannot know whether the collision is expected (e.g., hybrid or boot disks) or unintended, we inform the user and let them decide. libfdisk can wipe the unwanted signature only when creating a new partition table; otherwise, the user can use wipefs. This commit introduces fdisk_is_collision_area(), an API to detect where the collision occurs. The function is generic and not limited to the first sector. Fixes: https://github.com/util-linux/util-linux/issues/3659 Signed-off-by: Karel Zak --- diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c index 727178da4..72feed587 100644 --- a/disk-utils/fdisk.c +++ b/disk-utils/fdisk.c @@ -1056,12 +1056,21 @@ void follow_wipe_mode(struct fdisk_context *cxt) dowipe = 1; /* always remove old PT */ fdisk_enable_wipe(cxt, dowipe); - if (dowipe) - fdisk_warnx(cxt, _( - "The device contains '%s' signature and it will be removed by a write command. " - "See fdisk(8) man page and --wipe option for more details."), - fdisk_get_collision(cxt)); - else + + if (dowipe) { + /* check if collision in first sector */ + if (fdisk_has_label(cxt) && fdisk_is_collision_area(cxt, 0, + fdisk_get_sector_size(cxt))) + fdisk_warnx(cxt, _( + "The device contains a '%s' signature in the first sector; it will not be wiped " + "unless you create a new partition table. Alternatively, use wipefs(8)."), + fdisk_get_collision(cxt)); + else + fdisk_warnx(cxt, _( + "The device contains '%s' signature and it will be removed by a write command. " + "See fdisk(8) man page and --wipe option for more details."), + fdisk_get_collision(cxt)); + } else fdisk_warnx(cxt, _( "The device contains '%s' signature and it may remain on the device. " "It is recommended to wipe the device with wipefs(8) or " diff --git a/disk-utils/sfdisk.c b/disk-utils/sfdisk.c index b754916ef..b691cb084 100644 --- a/disk-utils/sfdisk.c +++ b/disk-utils/sfdisk.c @@ -1737,22 +1737,32 @@ static void follow_wipe_mode(struct sfdisk *sf) if (sf->quiet) return; - if (dowipe) { - if (!fdisk_is_ptcollision(sf->cxt)) { - fdisk_warnx(sf->cxt, _( - "The device contains '%s' signature and it may be removed by a write command. " - "See sfdisk(8) man page and --wipe option for more details."), - fdisk_get_collision(sf->cxt)); - fputc('\n', stdout); - } - } else { + if (!dowipe) { fdisk_warnx(sf->cxt, _( "The device contains '%s' signature and it may remain on the device. " "It is recommended to wipe the device with wipefs(8) or " "sfdisk --wipe, in order to avoid possible collisions."), fdisk_get_collision(sf->cxt)); fputc('\n', stderr); + return; } + + if (fdisk_is_ptcollision(sf->cxt)) + return; /* PT will be replaced */ + + if (sf->append && fdisk_has_label(sf->cxt) + && fdisk_is_collision_area(sf->cxt, 0, fdisk_get_sector_size(sf->cxt))) + fdisk_warnx(sf->cxt, _( + "The device contains a '%s' signature in the first sector; it will not be wiped " + "unless you create a new partition table. Alternatively, use wipefs(8)."), + fdisk_get_collision(sf->cxt)); + else + fdisk_warnx(sf->cxt, _( + "The device contains '%s' signature and it may be removed by a write command. " + "See sfdisk(8) man page and --wipe option for more details."), + fdisk_get_collision(sf->cxt)); + + fputc('\n', stdout); } static int wipe_partition(struct sfdisk *sf, size_t partno) diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index b58c88099..fd11df793 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -336,6 +336,7 @@ fdisk_has_dialogs fdisk_has_label fdisk_has_protected_bootbits fdisk_has_wipe +fdisk_is_collision_area fdisk_is_details fdisk_is_labeltype fdisk_is_listonly diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 3b2a4d25f..bc46f1f62 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -449,6 +449,27 @@ int fdisk_is_ptcollision(struct fdisk_context *cxt) return cxt->pt_collision; } +/** + * fdisk_is_collision_area: + * @cxt: fdisk context + * + * If there is a collision with the filesystem or another partition table, + * verify that the detected magic string is within the specified area. + * + * Returns: 0 or 1 + * + * Since: v2.42 + */ +int fdisk_is_collision_area(struct fdisk_context *cxt, + uint64_t start, uint64_t size) +{ + if (cxt->collision && + start <= cxt->collision_offset && cxt->collision_offset <= start + size) + return 1; + + return 0; +} + /** * fdisk_get_npartitions: * @cxt: context diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index 1f44996af..e3e57929a 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -410,6 +410,7 @@ struct fdisk_context { listonly : 1; /* list partition, nothing else */ char *collision; /* name of already existing FS/PT */ + uint64_t collision_offset; struct list_head wipes; /* list of areas to wipe before write */ int sizeunit; /* SIZE fields, FDISK_SIZEUNIT_* */ diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index 7385755e4..bc9ff6036 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -216,6 +216,7 @@ int fdisk_enable_wipe(struct fdisk_context *cxt, int enable); int fdisk_has_wipe(struct fdisk_context *cxt); const char *fdisk_get_collision(struct fdisk_context *cxt); int fdisk_is_ptcollision(struct fdisk_context *cxt); +int fdisk_is_collision_area(struct fdisk_context *cxt, uint64_t start, uint64_t size); int fdisk_set_unit(struct fdisk_context *cxt, const char *str); const char *fdisk_get_unit(struct fdisk_context *cxt, int n); diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index 43eb0f251..9e82cfd40 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -328,3 +328,7 @@ FDISK_2.40 { FDISK_2_41 { fdisk_ask_menu; } FDISK_2.40; + +FDISK_2_42 { + fdisk_is_collision_area; +} FDISK_2_41; diff --git a/libfdisk/src/wipe.c b/libfdisk/src/wipe.c index f389ed419..f8aa24052 100644 --- a/libfdisk/src/wipe.c +++ b/libfdisk/src/wipe.c @@ -181,25 +181,34 @@ int fdisk_check_collisions(struct fdisk_context *cxt) blkid_probe_enable_superblocks(pr, 1); blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE | + BLKID_SUBLKS_MAGIC | BLKID_SUBLKS_BADCSUM); blkid_probe_enable_partitions(pr, 1); - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_FORCE_GPT); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_FORCE_GPT | + BLKID_PARTS_MAGIC); /* we care about the first found FS/raid, so don't call blkid_do_probe() * in loop or don't use blkid_do_fullprobe() ... */ rc = blkid_do_probe(pr); if (rc == 0) { const char *name = NULL; + const char *off = NULL; - if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0) - cxt->collision = strdup(name); - else if (blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) { - cxt->collision = strdup(name); + if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0) { + blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); + + } else if (blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) { + blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL); cxt->pt_collision = 1; } - if (name && !cxt->collision) - rc = -ENOMEM; + if (name) { + cxt->collision = strdup(name); + if (!cxt->collision) + rc = -ENOMEM; + } + if (!rc && off) + cxt->collision_offset = strtoumax(off, NULL, 10); } blkid_free_probe(pr);