415 lines
12 KiB
Diff
415 lines
12 KiB
Diff
From 8d3f9430c59416e4c1eddc899578158a7a1ed414 Mon Sep 17 00:00:00 2001
|
|
From: Karel Zak <kzak@redhat.com>
|
|
Date: Wed, 7 Oct 2020 13:49:45 +0200
|
|
Subject: [PATCH] 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 <kzak@redhat.com>
|
|
---
|
|
lib/sysfs.c | 36 +++++---
|
|
libblkid/src/blkidP.h | 2 +-
|
|
libblkid/src/devname.c | 235 +++++++++++++++++++++----------------------------
|
|
3 files changed, 128 insertions(+), 145 deletions(-)
|
|
|
|
diff --git a/lib/sysfs.c b/lib/sysfs.c
|
|
index 5b4de2c..0c360ce 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,19 +901,20 @@ 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)
|
|
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;
|
|
}
|
|
|
|
diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h
|
|
index 802a1b3..fe3736f 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 8f2d89a..4b9df5a 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/<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;
|
|
}
|
|
--
|
|
1.8.3.1
|
|
|