]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/loopdev: make is_loopdev() more robust
authorKarel Zak <kzak@redhat.com>
Tue, 12 Jan 2021 10:43:31 +0000 (11:43 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 12 Jan 2021 10:43:31 +0000 (11:43 +0100)
It seems the current kernel can create a loop devices with a different
major number. For example

  # losetup /dev/loop12345678 file.img
  # lsblk /dev/loop12345678
  NAME          MAJ:MIN    RM SIZE RO TYPE MOUNTPOINT
  loop12345678   15:811342  0   5M  0 loop

We need a way how to verify the device is loopdev also when the device is
not associated with any backing file -- in this case there is no "loop"
directory in /sys/dev/block/<maj:min>/, but we can cannonicalize this sysfs
symlink as it points to /sys/devices/virtual/block/loop<n> (see "loop" in
the path).

Note that without this change losetup is not able to list and delete
the loop device.

Addresses: https://github.com/karelzak/util-linux/issues/1202
Signed-off-by: Karel Zak <kzak@redhat.com>
lib/loopdev.c

index 2b909084e7d18b31a09fa46d8f88c4aeef335d05..b946acf3190d83e724ddd28a0b676dfad185aa4d 100644 (file)
@@ -41,6 +41,7 @@
 #include "canonicalize.h"
 #include "blkdev.h"
 #include "debug.h"
+#include "fileutils.h"
 
 /*
  * Debug stuff (based on include/debug.h)
@@ -634,14 +635,30 @@ done:
 int is_loopdev(const char *device)
 {
        struct stat st;
+       int rc = 0;
 
-       if (device && stat(device, &st) == 0 &&
-               S_ISBLK(st.st_mode) &&
-               major(st.st_rdev) == LOOPDEV_MAJOR)
-               return 1;
+       if (!device || stat(device, &st) != 0 || !S_ISBLK(st.st_mode))
+               rc = 0;
+       else if (major(st.st_rdev) == LOOPDEV_MAJOR)
+               rc = 1;
+       else {
+               /* It's possible that kernel creates a device with a different
+                * major number ... check by /sys it's really loop device.
+                */
+               char name[PATH_MAX], *cn, *p = NULL;
+
+               snprintf(name, sizeof(name), _PATH_SYS_DEVBLOCK "/%d:%d",
+                               major(st.st_rdev), minor(st.st_rdev));
+               cn = canonicalize_path(name);
+               if (cn)
+                       p = stripoff_last_component(cn);
+               rc = p && startswith(p, "loop");
+               free(cn);
+       }
 
-       errno = ENODEV;
-       return 0;
+       if (rc == 0)
+               errno = ENODEV;
+       return rc;
 }
 
 /*