]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libfdisk: improve collision reporting
authorKarel Zak <kzak@redhat.com>
Mon, 8 Sep 2025 09:44:16 +0000 (11:44 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 8 Sep 2025 09:44:16 +0000 (11:44 +0200)
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 <kzak@redhat.com>
disk-utils/fdisk.c
disk-utils/sfdisk.c
libfdisk/docs/libfdisk-sections.txt
libfdisk/src/context.c
libfdisk/src/fdiskP.h
libfdisk/src/libfdisk.h.in
libfdisk/src/libfdisk.sym
libfdisk/src/wipe.c

index 727178da4ab709cb772ccbb971c065c009a5b1fa..72feed5879f7f2e31c29ff5977d3f82c926bad7b 100644 (file)
@@ -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 "
index b754916efd73cbd83792aa942223b4351ddff119..b691cb0846a631e055aa21841d30ed051e973951 100644 (file)
@@ -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)
index b58c8809975c75bede98b40f3836e4655a757b1f..fd11df7939b209ee38c849b04b9614b6214a8f8a 100644 (file)
@@ -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
index 3b2a4d25fa02311b0d77cbb9b69bd6f4c6897892..bc46f1f6222dfb2fcf47cfcd749bfb2a97fbecf4 100644 (file)
@@ -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
index 1f44996afa8fbba65e8ff12729c1f20e4681f1d6..e3e57929a46465d933149a0686e44af8ab644713 100644 (file)
@@ -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_* */
index 7385755e4ec83785e709a71546fe66de21f00464..bc9ff603601aef3989d2d35ef87a2fe52a0a1a38 100644 (file)
@@ -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);
index 43eb0f251a5d20ad91b7f450a5f99ce9b4e536a2..9e82cfd40a8dbc77a2b07c283573ddc9f5e58906 100644 (file)
@@ -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;
index f389ed4194a03cb8186c7f1697d18ba05a4e6421..f8aa2405234b327460837fe677d0c193b59b55cd 100644 (file)
@@ -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);