]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
wipefs: postpone BLKRRPART until all is done
authorKarel Zak <kzak@redhat.com>
Mon, 11 Jun 2018 10:21:56 +0000 (12:21 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 11 Jun 2018 10:36:32 +0000 (12:36 +0200)
It's possible we erase from the whole device before we erase from the
partition on the same disk:

 # wipefs -a /dev/sdc /dev/sdc1

the current code calls re-read PT ioctl immediately after erase (so,
before sdc1 is processed). The result is that sdc1 node is no more
accessible:

  # wipefs -a /dev/sdc /dev/sdc1
  /dev/sdc: 2 bytes were erased at offset 0x000001fe (dos): 55 aa
  /dev/sdc: calling ioctl to re-read partition table: Success
  wipefs: error: /dev/sdc1: probing initialization failed: No such file or directory

It seems the most simple solution is to postpone the re-read ioctl and
do it as the last thing.

  # wipefs -a  /dev/sdc /dev/sdc1
  /dev/sdc: 2 bytes were erased at offset 0x000001fe (dos): 55 aa
  /dev/sdc1: 2 bytes were erased at offset 0x00000438 (ext4): 53 ef
  /dev/sdc: calling ioctl to re-read partition table: Success

The patch also adds a small delay before the re-read ioctl call. It's
not elegant, but without the usleep(25000) the first attempt returns
EBUSY.

Addresses: https://github.com/karelzak/util-linux/issues/598
Signed-off-by: Karel Zak <kzak@redhat.com>
misc-utils/wipefs.8
misc-utils/wipefs.c

index c26b7890f458cae7886809342e50f8a75b7dda98..3874e1a7f3a15fa99ba4bdb71b4d6fd19ac53d1f 100644 (file)
@@ -38,7 +38,8 @@ in environments where a stable output is required.
 
 .B wipefs
 calls the BLKRRPART ioctl when it has erased a partition-table signature
-to inform the kernel about the change.
+to inform the kernel about the change. The ioctl is called as the last step
+and when all specified signatures from all specified devices are already erased.
 
 Note that some filesystems and some partition tables store more magic strings on
 the device (e.g. FAT, ZFS, GPT).  The
index 76149d9fc24cc643eda3976068f7adc70bee4bbd..49c5c8ca11e1040575eac33862a328fedad97093 100644 (file)
@@ -62,12 +62,17 @@ struct wipe_desc {
 };
 
 struct wipe_control {
-       const char      *devname;
+       char            *devname;
        const char      *type_pattern;          /* -t <pattern> */
 
        struct libscols_table *outtab;
        struct wipe_desc *offsets;              /* -o <offset> -o <offset> ... */
 
+       size_t          ndevs;                  /* number of devices to probe */
+
+       char            **reread;               /* devices to BLKRRPART */
+       size_t          nrereads;               /* size of reread */
+
        unsigned int    noact : 1,
                        all : 1,
                        quiet : 1,
@@ -504,12 +509,25 @@ err:
 static void rereadpt(int fd, const char *devname)
 {
        struct stat st;
+       int try = 0;
 
        if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
                return;
 
-       errno = 0;
-       ioctl(fd, BLKRRPART);
+       do {
+               /*
+                * Unfortunately, it's pretty common that the first re-read
+                * without delay is uncuccesful. The reason is probably kernel
+                * and/or udevd.  Let's wait a moment and try more attempts.
+                */
+               xusleep(25000);
+
+               errno = 0;
+               ioctl(fd, BLKRRPART);
+               if (errno != EBUSY)
+                       break;
+       } while (try++ < 4);
+
        printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname);
 }
 #endif
@@ -590,8 +608,21 @@ static int do_wipe(struct wipe_control *ctl)
        fsync(blkid_probe_get_fd(pr));
 
 #ifdef BLKRRPART
-       if (reread && (mode & O_EXCL))
-               rereadpt(blkid_probe_get_fd(pr), ctl->devname);
+       if (reread && (mode & O_EXCL)) {
+               if (ctl->ndevs > 1) {
+                       /*
+                        * We're going to probe more device, let's postpone
+                        * re-read PT ioctl until all is erased to avoid
+                        * situation we erase PT on /dev/sda before /dev/sdaN
+                        * devices are processed.
+                        */
+                       if (!ctl->reread)
+                               ctl->reread = xcalloc(ctl->ndevs, sizeof(char *));
+
+                       ctl->reread[ctl->nrereads++] = ctl->devname;
+               } else
+                       rereadpt(blkid_probe_get_fd(pr), ctl->devname);
+       }
 #endif
 
        close(blkid_probe_get_fd(pr));
@@ -771,11 +802,28 @@ main(int argc, char **argv)
                /*
                 * Erase
                 */
+               size_t i;
+               ctl.ndevs = argc - optind;
+
                while (optind < argc) {
                        ctl.devname = argv[optind++];
                        do_wipe(&ctl);
+                       ctl.ndevs--;
                }
-       }
 
+               /* Re-read partition tables on whole-disk devices. This is
+                * postponed until all is done to avoid conflicts.
+                */
+               for (i = 0; i < ctl.nrereads; i++) {
+                       char *devname = ctl.reread[i];
+                       int fd = open(devname, O_RDONLY);
+
+                       if (fd >= 0) {
+                               rereadpt(fd, devname);
+                               close(fd);
+                       }
+               }
+               free(ctl.reread);
+       }
        return EXIT_SUCCESS;
 }