]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsblk: add dependence between CD/DVD block and packet devices
authorKarel Zak <kzak@redhat.com>
Mon, 23 Nov 2020 14:16:39 +0000 (15:16 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 24 Nov 2020 09:44:46 +0000 (10:44 +0100)
This dependence is defined by

  $ cat /sys/class/pktcdvd/device_map
  pktcdvd0 252:0 11:0

Unfortunately, there is not any direct sysfs way how to refer this
relationship in /sys/{block,dev/block}. So, we have to read the
device_map file and then compare device numbers with the list.

$ lsblk /dev/sr0
NAME       MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sr0         11:0    1 1024M  0 rom
└─pktcdvd0 252:0    1    0B  0 disk

Addresses: https://github.com/karelzak/util-linux/issues/1185
Signed-off-by: Karel Zak <kzak@redhat.com>
misc-utils/lsblk-devtree.c
misc-utils/lsblk.c
misc-utils/lsblk.h

index 4d15f70c17d11f6c05f87bc13ec1c35a0dd11cae..7f39313cd14ea84d47461de2d8cc773587774029 100644 (file)
@@ -18,6 +18,7 @@
  */
 #include "lsblk.h"
 #include "sysfs.h"
+#include "pathnames.h"
 
 
 void lsblk_reset_iter(struct lsblk_iter *itr, int direction)
@@ -244,6 +245,7 @@ struct lsblk_devtree *lsblk_new_devtree()
 
        INIT_LIST_HEAD(&tr->roots);
        INIT_LIST_HEAD(&tr->devices);
+       INIT_LIST_HEAD(&tr->pktcdvd_map);
 
        DBG(TREE, ul_debugobj(tr, "alloc"));
        return tr;
@@ -268,6 +270,14 @@ void lsblk_unref_devtree(struct lsblk_devtree *tr)
                                                struct lsblk_device, ls_devices);
                        lsblk_devtree_remove_device(tr, dev);
                }
+
+               while (!list_empty(&tr->pktcdvd_map)) {
+                       struct lsblk_devnomap *map = list_entry(tr->pktcdvd_map.next,
+                                               struct lsblk_devnomap, ls_devnomap);
+                       list_del(&map->ls_devnomap);
+                       free(map);
+               }
+
                free(tr);
        }
 }
@@ -285,6 +295,15 @@ int lsblk_devtree_add_root(struct lsblk_devtree *tr, struct lsblk_device *dev)
        return 0;
 }
 
+int lsblk_devtree_remove_root(struct lsblk_devtree *tr __attribute__((unused)),
+                             struct lsblk_device *dev)
+{
+        DBG(TREE, ul_debugobj(tr, "remove root device 0x%p [%s]", dev, dev->name));
+        list_del_init(&dev->ls_roots);
+
+       return 0;
+}
+
 int lsblk_devtree_next_root(struct lsblk_devtree *tr,
                            struct lsblk_iter *itr,
                            struct lsblk_device **dev)
@@ -374,6 +393,62 @@ int lsblk_devtree_remove_device(struct lsblk_devtree *tr, struct lsblk_device *d
        return 0;
 }
 
+static void read_pktcdvd_map(struct lsblk_devtree *tr)
+{
+       char buf[PATH_MAX];
+       FILE *f;
+
+       assert(tr->pktcdvd_read == 0);
+
+       f = ul_path_fopen(NULL, "r", _PATH_SYS_CLASS "/pktcdvd/device_map");
+       if (!f)
+               goto done;
+
+       while (fgets(buf, sizeof(buf), f)) {
+               struct lsblk_devnomap *map;
+               int pkt_maj, pkt_min, blk_maj, blk_min;
+
+               if (sscanf(buf, "%*s %d:%d %d:%d\n",
+                                       &pkt_maj, &pkt_min,
+                                       &blk_maj, &blk_min) != 4)
+                       continue;
+
+               map = malloc(sizeof(*map));
+               if (!map)
+                       break;
+               map->holder = makedev(pkt_maj, pkt_min);
+               map->slave = makedev(blk_maj, blk_min);
+               INIT_LIST_HEAD(&map->ls_devnomap);
+               list_add_tail(&map->ls_devnomap, &tr->pktcdvd_map);
+       }
+
+       fclose(f);
+done:
+       tr->pktcdvd_read = 1;
+}
+
+/* returns opposite device of @devno for blk->pkt relation -- e.g. if devno
+ * is_slave (blk) then returns holder (pkt) and vice-versa */
+dev_t lsblk_devtree_pktcdvd_get_mate(struct lsblk_devtree *tr, dev_t devno, int is_slave)
+{
+       struct list_head *p;
+
+       if (!tr->pktcdvd_read)
+               read_pktcdvd_map(tr);
+       if (list_empty(&tr->pktcdvd_map))
+               return 0;
+
+       list_for_each(p, &tr->pktcdvd_map) {
+               struct lsblk_devnomap *x = list_entry(p, struct lsblk_devnomap, ls_devnomap);
+
+               if (is_slave && devno == x->slave)
+                       return x->holder;
+               if (!is_slave && devno == x->holder)
+                       return x->slave;
+       }
+       return 0;
+}
+
 static int device_dedupkey_is_equal(
                        struct lsblk_device *dev,
                        struct lsblk_device *pattern)
index 03456b81c894c4891ae68ae31b9e9fabd934b9bd..210a36633129d89812274f28bdeadb57ec236c40 100644 (file)
@@ -1267,6 +1267,26 @@ static struct lsblk_device *devtree_get_device_or_new(struct lsblk_devtree *tr,
        return dev;
 }
 
+static struct lsblk_device *devtree_pktcdvd_get_dep(
+                       struct lsblk_devtree *tr,
+                       struct lsblk_device *dev,
+                       int want_slave)
+{
+       char buf[PATH_MAX], *name;
+       dev_t devno;
+
+       devno = lsblk_devtree_pktcdvd_get_mate(tr,
+                       makedev(dev->maj, dev->min), !want_slave);
+       if (!devno)
+               return NULL;
+
+       name = sysfs_devno_to_devname(devno, buf, sizeof(buf));
+       if (!name)
+               return NULL;
+
+       return devtree_get_device_or_new(tr, NULL, name);
+}
+
 static int process_dependencies(
                        struct lsblk_devtree *tr,
                        struct lsblk_device *dev,
@@ -1357,6 +1377,7 @@ static int process_dependencies(
        DIR *dir;
        struct dirent *d;
        const char *depname;
+       struct lsblk_device *dep = NULL;
 
        assert(dev);
 
@@ -1371,21 +1392,20 @@ static int process_dependencies(
 
        if (!(lsblk->inverse ? dev->nslaves : dev->nholders)) {
                DBG(DEV, ul_debugobj(dev, " ignore (no slaves/holders)"));
-               return 0;
+               goto done;
        }
 
        depname = lsblk->inverse ? "slaves" : "holders";
        dir = ul_path_opendir(dev->sysfs, depname);
        if (!dir) {
                DBG(DEV, ul_debugobj(dev, " ignore (no slaves/holders directory)"));
-               return 0;
+               goto done;
        }
        ul_path_close_dirfd(dev->sysfs);
 
        DBG(DEV, ul_debugobj(dev, " %s: checking for '%s' dependence", dev->name, depname));
 
        while ((d = xreaddir(dir))) {
-               struct lsblk_device *dep = NULL;
                struct lsblk_device *disk = NULL;
 
                /* Is the dependency a partition? */
@@ -1436,8 +1456,14 @@ next:
                        ul_path_close_dirfd(disk->sysfs);
        }
        closedir(dir);
+done:
+       dep = devtree_pktcdvd_get_dep(tr, dev, lsblk->inverse);
+
+       if (dep && lsblk_device_new_dependence(dev, dep) == 0) {
+               lsblk_devtree_remove_root(tr, dep);
+               process_dependencies(tr, dep, lsblk->inverse ? 0 : 1);
+       }
 
-       DBG(DEV, ul_debugobj(dev, "%s: checking for '%s' -- done", dev->name, depname));
        return 0;
 }
 
index 87221271ed080cd8e852ba7a23a5fced2c8eea05..17708b4d51d735bc7e301ab81f2911d80ad568eb 100644 (file)
@@ -41,6 +41,7 @@ struct lsblk {
 
        int dedup_id;
 
+
        const char *sysroot;
        int flags;                      /* LSBLK_* */
 
@@ -140,6 +141,18 @@ struct lsblk_device {
 
 #define device_is_partition(_x)                ((_x)->wholedisk != NULL)
 
+/* Unfortunately, pktcdvd dependence on block device is not defined by
+ * slave/holder symlinks. The struct lsblk_devnomap represents one line in
+ * /sys/class/pktcdvd/device_map
+ */
+struct lsblk_devnomap {
+       dev_t slave;            /* packet device devno */
+       dev_t holder;           /* block device devno */
+
+       struct list_head ls_devnomap;
+};
+
+
 /*
  * Note that lsblk tree uses bottom devices (devices without slaves) as root
  * of the tree, and partitions are interpreted as a dependence too; it means:
@@ -153,8 +166,10 @@ struct lsblk_devtree {
 
        struct list_head        roots;          /* tree root devices */
        struct list_head        devices;        /* all devices */
+       struct list_head        pktcdvd_map;    /* devnomap->ls_devnomap */
 
-       unsigned int    is_inverse : 1;         /* inverse tree */
+       unsigned int    is_inverse : 1,         /* inverse tree */
+                       pktcdvd_read : 1;
 };
 
 
@@ -212,6 +227,8 @@ int lsblk_device_next_child(struct lsblk_device *dev,
                           struct lsblk_iter *itr,
                           struct lsblk_device **child);
 
+dev_t lsblk_devtree_pktcdvd_get_mate(struct lsblk_devtree *tr, dev_t devno, int is_slave);
+
 int lsblk_device_is_last_parent(struct lsblk_device *dev, struct lsblk_device *parent);
 int lsblk_device_next_parent(
                         struct lsblk_device *dev,
@@ -222,6 +239,7 @@ struct lsblk_devtree *lsblk_new_devtree(void);
 void lsblk_ref_devtree(struct lsblk_devtree *tr);
 void lsblk_unref_devtree(struct lsblk_devtree *tr);
 int lsblk_devtree_add_root(struct lsblk_devtree *tr, struct lsblk_device *dev);
+int lsblk_devtree_remove_root(struct lsblk_devtree *tr, struct lsblk_device *dev);
 int lsblk_devtree_next_root(struct lsblk_devtree *tr,
                             struct lsblk_iter *itr,
                             struct lsblk_device **dev);