lib/loopdev: make is_loopdev() more robust

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>
This commit is contained in:
Karel Zak 2021-01-12 11:43:31 +01:00
parent a7d5efbb9a
commit fbc2fe824d
1 changed files with 23 additions and 6 deletions

View 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;
errno = ENODEV;
return 0;
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);
}
if (rc == 0)
errno = ENODEV;
return rc;
}
/*