From: Karel Zak Date: Wed, 7 Oct 2020 11:49:45 +0000 (+0200) Subject: libblkid: use /sys to read all block devices X-Git-Tag: v2.37-rc1~434 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8d3f9430c59416e4c1eddc899578158a7a1ed414;p=thirdparty%2Futil-linux.git libblkid: use /sys to read all block devices 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 --- diff --git a/lib/sysfs.c b/lib/sysfs.c index 5b4de2cad9..0c360ce176 100644 --- a/lib/sysfs.c +++ b/lib/sysfs.c @@ -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///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///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///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//device/dev + */ if (!dev) { - /* - * Read from /sys/block//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; } diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h index 802a1b3a6d..fe3736f744 100644 --- a/libblkid/src/blkidP.h +++ b/libblkid/src/blkidP.h @@ -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 diff --git a/libblkid/src/devname.c b/libblkid/src/devname.c index 8f2d89a315..4b9df5a975 100644 --- a/libblkid/src/devname.c +++ b/libblkid/src/devname.c @@ -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// 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; }