]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: use /sys to read all block devices
authorKarel Zak <kzak@redhat.com>
Wed, 7 Oct 2020 11:49:45 +0000 (13:49 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 7 Oct 2020 11:49:45 +0000 (13:49 +0200)
The old implementation uses /proc/partitions where devices are
filtered by kernel (missing devices with ext_range=1 and removable
devices).

The problem with the old implementation is whole-disk heuristic based
on device name, order of devices, etc.

The new implementation use the same code to read also removable
devices.

Addresses: https://github.com/karelzak/util-linux/issues/1151
Signed-off-by: Karel Zak <kzak@redhat.com>
lib/sysfs.c
libblkid/src/blkidP.h
libblkid/src/devname.c

index 5b4de2cad9c361d37f2ad4860bf1a0e5a4274672..0c360ce1769304d2710a65e94bc23e0e2d69c578 100644 (file)
@@ -874,7 +874,7 @@ int sysfs_devname_is_hidden(const char *prefix, const char *name)
 dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent)
 {
        char buf[PATH_MAX];
-       char *_name = NULL    /* name as encoded in sysfs */
+       char *_name = NULL, *_parent = NULL;    /* name as encoded in sysfs */
        dev_t dev = 0;
        int len;
 
@@ -901,21 +901,22 @@ dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char
                goto done;
        sysfs_devname_dev_to_sys(_name);
 
-       if (parent && strncmp("dm-", name, 3) != 0) {
-               /*
-                * Create path to /sys/block/<parent>/<name>/dev
-                */
-               char *_parent = strdup(parent);
-
+       if (parent) {
+               _parent = strdup(parent);
                if (!_parent) {
                        free(_parent);
                        goto done;
                }
+       }
+
+       if (parent && strncmp("dm-", name, 3) != 0) {
+               /*
+                * Create path to /sys/block/<parent>/<name>/dev
+                */
                sysfs_devname_dev_to_sys(_parent);
                len = snprintf(buf, sizeof(buf),
                                "%s" _PATH_SYS_BLOCK "/%s/%s/dev",
                                prefix, _parent, _name);
-               free(_parent);
                if (len < 0 || (size_t) len >= sizeof(buf))
                        goto done;
 
@@ -934,10 +935,22 @@ dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char
                goto done;
        dev = read_devno(buf);
 
+       /*
+        * Read from /sys/block/<parent>/<partition>/dev
+        */
+       if (!dev && parent && startswith(name, parent)) {
+               len = snprintf(buf, sizeof(buf),
+                               "%s" _PATH_SYS_BLOCK "/%s/%s/dev",
+                               prefix, _parent, _name);
+               if (len < 0 || (size_t) len >= sizeof(buf))
+                       goto done;
+               dev = read_devno(buf);
+       }
+
+       /*
+        * Read from /sys/block/<sysname>/device/dev
+        */
        if (!dev) {
-               /*
-                * Read from /sys/block/<sysname>/device/dev
-                */
                len = snprintf(buf, sizeof(buf),
                                "%s" _PATH_SYS_BLOCK "/%s/device/dev",
                                prefix, _name);
@@ -947,6 +960,7 @@ dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char
        }
 done:
        free(_name);
+       free(_parent);
        return dev;
 }
 
index 802a1b3a6d22c79f7423edc15f45764a885dc188..fe3736f744ad8ff72755596a09bafdd209a99c66 100644 (file)
@@ -301,7 +301,7 @@ struct blkid_struct_cache
 #define BLKID_PROBE_NONE 1
 
 #define BLKID_ERR_IO    5
-#define BLKID_ERR_PROC  9
+#define BLKID_ERR_SYSFS         9
 #define BLKID_ERR_MEM  12
 #define BLKID_ERR_CACHE        14
 #define BLKID_ERR_DEV  19
index 8f2d89a31577c317619cb60008d4a201a60c77e7..4b9df5a9754469c41b1c691d3f06a197f3b58728 100644 (file)
@@ -39,6 +39,7 @@
 #include "canonicalize.h"              /* $(top_srcdir)/include */
 #include "pathnames.h"
 #include "sysfs.h"
+#include "fileutils.h"
 
 /*
  * Find a dev struct in the cache by device name, if available.
@@ -442,178 +443,146 @@ ubi_probe_all(blkid_cache cache, int only_if_new)
 }
 
 /*
- * Read the device data for all available block devices in the system.
+ * This function uses /sys to read all block devices in way compatible with
+ * /proc/partitions (like the original libblkid implementation)
  */
-static int probe_all(blkid_cache cache, int only_if_new)
+static int
+sysfs_probe_all(blkid_cache cache, int only_if_new, int only_removable)
 {
-       FILE *proc;
-       char line[1024];
-       char ptname0[128 + 1], ptname1[128 + 1], *ptname = NULL;
-       char *ptnames[2];
-       dev_t devs[2] = { 0, 0 };
-       int iswhole[2] = { 0, 0 };
-       int ma, mi;
-       unsigned long long sz;
-       int lens[2] = { 0, 0 };
-       int which = 0, last = 0;
-       struct list_head *p, *pnext;
+       DIR *sysfs;
+       struct dirent *dev;
 
-       ptnames[0] = ptname0;
-       ptnames[1] = ptname1;
+       sysfs = opendir(_PATH_SYS_BLOCK);
+       if (!sysfs)
+               return -BLKID_ERR_SYSFS;
 
-       if (!cache)
-               return -BLKID_ERR_PARAM;
+       /* scan /sys/block */
+       while ((dev = xreaddir(sysfs))) {
+               DIR *dir = NULL;
+               dev_t devno;
+               size_t nparts = 0;
+               unsigned int maxparts = 0, removable = 0;
+               struct dirent *part;
+               struct path_cxt *pc = NULL;
+               uint64_t size = 0;
 
-       if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
-           time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL)
-               return 0;
+               DBG(DEVNAME, ul_debug("checking %s", dev->d_name));
 
-       blkid_read_cache(cache);
-       evms_probe_all(cache, only_if_new);
-#ifdef VG_DIR
-       lvm_probe_all(cache, only_if_new);
-#endif
-       ubi_probe_all(cache, only_if_new);
+               devno = sysfs_devname_to_devno(dev->d_name);
+               if (!devno)
+                       goto next;
+               pc = ul_new_sysfs_path(devno, NULL, NULL);
+               if (!pc)
+                       goto next;
+
+               if (ul_path_read_u64(pc, &size, "size") != 0)
+                       size = 0;
+               if (ul_path_read_u32(pc, &removable, "removable") != 0)
+                       removable = 0;
+
+               /* ingnore empty devices */
+               if (!size)
+                       goto next;
+
+               /* accept removeable if only removable requested */
+               if (only_removable) {
+                       if (!removable)
+                               goto next;
+
+               /* emulate /proc/partitions
+                * -- ignore empty devices and non-partitionable removable devices */
+               } else {
+                       if (ul_path_read_u32(pc, &maxparts, "ext_range") != 0)
+                               maxparts = 0;
+                       if (!maxparts && removable)
+                               goto next;
+               }
 
-       proc = fopen(PROC_PARTITIONS, "r" UL_CLOEXECSTR);
-       if (!proc)
-               return -BLKID_ERR_PROC;
+               DBG(DEVNAME, ul_debug("read device name %s", dev->d_name));
 
-       while (fgets(line, sizeof(line), proc)) {
-               last = which;
-               which ^= 1;
-               ptname = ptnames[which];
+               dir = ul_path_opendir(pc, NULL);
+               if (!dir)
+                       goto next;
 
-               if (sscanf(line, " %d %d %llu %128[^\n ]",
-                          &ma, &mi, &sz, ptname) != 4)
-                       continue;
-               devs[which] = makedev(ma, mi);
-
-               DBG(DEVNAME, ul_debug("read device name %s", ptname));
-
-               /* Skip whole disk devs unless they have no partitions.
-                * If base name of device has changed, also
-                * check previous dev to see if it didn't have a partn.
-                * heuristic: partition name ends in a digit, & partition
-                * names contain whole device name as substring.
-                *
-                * Skip extended partitions.
-                * heuristic: size is 1
-                */
+               /* read /sys/block/<name>/ do get partitions */
+               while ((part = xreaddir(dir))) {
+                       dev_t partno;
 
-               lens[which] = strlen(ptname);
-               iswhole[which] = sysfs_devno_is_wholedisk(devs[which]);
+                       if (!sysfs_blkdev_is_partition_dirent(dir, part, dev->d_name))
+                               continue;
 
-               /* probably partition, so check */
-               if (!iswhole[which]) {
-                       DBG(DEVNAME, ul_debug(" Probe partition dev %s, devno 0x%04X",
-                                  ptname, (unsigned int) devs[which]));
+                       /* ignore extended partitions
+                        * -- recount size to blocks like /proc/partitions */
+                       if (ul_path_readf_u64(pc, &size, "%s/size", part->d_name) == 0
+                           && (size >> 1) == 1)
+                               continue;
+                       partno = __sysfs_devname_to_devno(NULL, part->d_name, dev->d_name);
+                       if (!partno)
+                               continue;
 
-                       if (sz > 1)
-                               probe_one(cache, ptname, devs[which], 0,
-                                         only_if_new, 0);
-                       lens[which] = 0;        /* mark as checked */
+                       DBG(DEVNAME, ul_debug(" Probe partition dev %s, devno 0x%04X",
+                                   part->d_name, (unsigned int) partno));
+                       nparts++;
+                       probe_one(cache, part->d_name, partno, 0, only_if_new, 0);
                }
 
-               /*
-                * If last was a whole disk and we just found a partition
-                * on it, remove the whole-disk dev from the cache if
-                * it exists.
-                */
-               if (lens[last] && iswhole[last]
-                   && !strncmp(ptnames[last], ptname, lens[last])) {
+               if (!nparts) {
+                       /* add non-partitioned whole disk to cache */
+                       DBG(DEVNAME, ul_debug(" Probe whole dev %s, devno 0x%04X",
+                                  dev->d_name, (unsigned int) devno));
+                       probe_one(cache, dev->d_name, devno, 0, only_if_new, 0);
+               } else {
+                       /* remove partitioned whole-disk from cache */
+                       struct list_head *p, *pnext;
 
                        list_for_each_safe(p, pnext, &cache->bic_devs) {
-                               blkid_dev tmp;
-
-                               /* find blkid dev for the whole-disk devno */
-                               tmp = list_entry(p, struct blkid_struct_dev,
-                                                bid_devs);
-                               if (tmp->bid_devno == devs[last]) {
-                                       DBG(DEVNAME, ul_debug(" freeing %s",
-                                                      tmp->bid_name));
+                               blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
+                                                       bid_devs);
+                               if (tmp->bid_devno == devno) {
+                                       DBG(DEVNAME, ul_debug(" freeing %s", tmp->bid_name));
                                        blkid_free_dev(tmp);
                                        cache->bic_flags |= BLKID_BIC_FL_CHANGED;
                                        break;
                                }
                        }
-                       lens[last] = 0;         /* mark as checked */
-               }
-               /*
-                * If last was not checked because it looked like a whole-disk
-                * dev, and the device's base name has changed,
-                * check last as well.
-                */
-               if (lens[last] && strncmp(ptnames[last], ptname, lens[last]) != 0) {
-                       DBG(DEVNAME, ul_debug(" Probe whole dev %s, devno 0x%04X",
-                                  ptnames[last], (unsigned int) devs[last]));
-                       probe_one(cache, ptnames[last], devs[last], 0,
-                                 only_if_new, 0);
-
-                       lens[last] = 0;         /* mark as checked */
                }
+       next:
+               if (dir)
+                       closedir(dir);
+               if (pc)
+                       ul_unref_path(pc);
        }
 
-       /* Handle the last device if it wasn't partitioned */
-       if (lens[which]) {
-               DBG(DEVNAME, ul_debug(" Probe whole dev %s, devno 0x%04X",
-                                       ptname, (unsigned int) devs[which]));
-               probe_one(cache, ptname, devs[which], 0, only_if_new, 0);
-       }
-
-       fclose(proc);
-       blkid_flush_cache(cache);
+       closedir(sysfs);
        return 0;
 }
 
-/* Don't use it by default -- it's pretty slow (because cdroms, floppy, ...)
+/*
+ * Read the device data for all available block devices in the system.
  */
-static int probe_all_removable(blkid_cache cache)
+static int probe_all(blkid_cache cache, int only_if_new)
 {
-       struct path_cxt *pc;
-       DIR *dir;
-       struct dirent *d;
-
        if (!cache)
                return -BLKID_ERR_PARAM;
 
-       dir = opendir(_PATH_SYS_BLOCK);
-       if (!dir)
-               return -BLKID_ERR_PROC;
-
-       pc = ul_new_path(NULL);
+       if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+           time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL)
+               return 0;
 
-       while((d = readdir(dir))) {
-               int removable = 0;
-               dev_t devno;
+       blkid_read_cache(cache);
 
-#ifdef _DIRENT_HAVE_D_TYPE
-               if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK)
-                       continue;
+       evms_probe_all(cache, only_if_new);
+#ifdef VG_DIR
+       lvm_probe_all(cache, only_if_new);
 #endif
-               if (d->d_name[0] == '.' &&
-                   ((d->d_name[1] == 0) ||
-                    ((d->d_name[1] == '.') && (d->d_name[2] == 0))))
-                       continue;
-
-               devno = sysfs_devname_to_devno(d->d_name);
-               if (!devno)
-                       continue;
-
-               if (sysfs_blkdev_init_path(pc, devno, NULL) == 0
-                   && ul_path_read_s32(pc, &removable, "removable") != 0)
-                               removable = 0;
+       ubi_probe_all(cache, only_if_new);
 
-               if (removable)
-                       probe_one(cache, d->d_name, devno, 0, 0, 1);
-       }
+       sysfs_probe_all(cache, only_if_new, 0);
 
-       ul_unref_path(pc);
-       closedir(dir);
+       blkid_flush_cache(cache);
        return 0;
 }
 
-
 /**
  * blkid_probe_all:
  * @cache: cache handler
@@ -677,7 +646,7 @@ int blkid_probe_all_removable(blkid_cache cache)
        int ret;
 
        DBG(PROBE, ul_debug("Begin blkid_probe_all_removable()"));
-       ret = probe_all_removable(cache);
+       ret = sysfs_probe_all(cache, 0, 1);
        DBG(PROBE, ul_debug("End blkid_probe_all_removable() [rc=%d]", ret));
        return ret;
 }