]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsblk: add basic function to build devices tree
authorKarel Zak <kzak@redhat.com>
Fri, 12 Oct 2018 10:50:03 +0000 (12:50 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 7 Dec 2018 11:32:57 +0000 (12:32 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
misc-utils/Makemodule.am
misc-utils/lsblk-devtree.c [new file with mode: 0644]
misc-utils/lsblk.h

index c44cdda67086a2641870eb26e2c0d6a99d7ce2e5..3043687e6b04c8e1c5c1f1afc140563d02053a13 100644 (file)
@@ -81,6 +81,7 @@ lsblk_SOURCES = \
        misc-utils/lsblk.c \
        misc-utils/lsblk-mnt.c \
        misc-utils/lsblk-properties.c \
+       misc-utils/lsblk-devtree.c \
        misc-utils/lsblk.h
 lsblk_LDADD = $(LDADD) libblkid.la libmount.la libcommon.la libsmartcols.la
 lsblk_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir) -I$(ul_libmount_incdir) -I$(ul_libsmartcols_incdir)
diff --git a/misc-utils/lsblk-devtree.c b/misc-utils/lsblk-devtree.c
new file mode 100644 (file)
index 0000000..3c71d24
--- /dev/null
@@ -0,0 +1,257 @@
+
+#include "lsblk.h"
+#include "sysfs.h"
+
+
+void lsblk_reset_iter(struct lsblk_iter *itr, int direction)
+{
+       if (direction == -1)
+               direction = itr->direction;
+
+       memset(itr, 0, sizeof(*itr));
+       itr->direction = direction;
+}
+
+struct lsblk_device *lsblk_new_device(struct lsblk_devtree *tree)
+{
+       struct lsblk_device *dev;
+
+       dev = calloc(1, sizeof(*dev));
+       if (!dev)
+               return NULL;
+
+       dev->refcount = 1;
+
+       dev->tree = tree;
+       lsblk_ref_devtree(dev->tree);
+
+        INIT_LIST_HEAD(&dev->deps);
+       INIT_LIST_HEAD(&dev->ls_roots);
+       INIT_LIST_HEAD(&dev->ls_devices);
+
+       DBG(DEV, ul_debugobj(dev, "alloc"));
+       return dev;
+}
+
+void lsblk_ref_device(struct lsblk_device *dev)
+{
+       if (dev)
+               dev->refcount++;
+}
+
+
+static int device_remove_dependence(struct lsblk_device *dev, struct lsblk_devdep *dep)
+{
+       if (!dev || !dep || !list_empty(&dev->deps))
+               return -EINVAL;
+
+       DBG(DEV, ul_debugobj(dev, "  remove-deallocate dependence 0x%p", dep));
+       list_del_init(&dep->ls_deps);
+       lsblk_unref_device(dep->child);
+       free(dep);
+       return 0;
+}
+
+static int device_remove_dependences(struct lsblk_device *dev)
+{
+       if (!dev)
+               return -EINVAL;
+
+       DBG(DEV, ul_debugobj(dev, "remove all depencences"));
+       while (!list_empty(&dev->deps)) {
+               struct lsblk_devdep *dp = list_entry(dev->deps.next,
+                                       struct lsblk_devdep, ls_deps);
+               device_remove_dependence(dev, dp);
+       }
+       return 0;
+}
+
+void lsblk_unref_device(struct lsblk_device *dev)
+{
+       if (dev)
+               return;
+
+       if (--dev->refcount <= 0) {
+               DBG(DEV, ul_debugobj(dev, "dealloc"));
+
+               device_remove_dependences(dev);
+               lsblk_device_free_properties(dev->properties);
+
+               list_del_init(&dev->ls_roots);
+               list_del_init(&dev->ls_devices);
+
+               free(dev->name);
+               free(dev->dm_name);
+               free(dev->filename);
+               free(dev->mountpoint);
+
+               ul_unref_path(dev->sysfs);
+               lsblk_ref_devtree(dev->tree);
+
+               free(dev);
+       }
+}
+
+struct lsblk_devdep *lsblk_device_new_dependence(struct lsblk_device *parent, struct lsblk_device *child)
+{
+       struct lsblk_devdep *dp;
+
+       if (!parent || !child) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       dp = calloc(1, sizeof(*dp));
+       if (!dp)
+               return NULL;
+
+       INIT_LIST_HEAD(&dp->ls_deps);
+
+       lsblk_ref_device(child);
+       dp->child = child;
+
+        DBG(DEV, ul_debugobj(parent, "add dependence 0x%p [%s->%s]", dp, parent->name, child->name));
+        list_add_tail(&dp->ls_deps, &parent->deps);
+
+       return dp;
+}
+
+int lsblk_device_next_child(struct lsblk_device *dev,
+                         struct lsblk_iter *itr,
+                         struct lsblk_device **child)
+{
+       int rc = 1;
+
+       if (!dev || !itr || !child)
+               return -EINVAL;
+       *child = NULL;
+
+       if (!itr->head)
+               LSBLK_ITER_INIT(itr, &dev->deps);
+       if (itr->p != itr->head) {
+               struct lsblk_devdep *dp = NULL;
+
+               LSBLK_ITER_ITERATE(itr, dp, struct lsblk_devdep, ls_deps);
+
+               *child = dp->child;
+               rc = 0;
+       }
+
+       return rc;
+}
+
+struct lsblk_devtree *lsblk_new_devtree()
+{
+       struct lsblk_devtree *tr;
+
+       tr = calloc(1, sizeof(*tr));
+       if (!tr)
+               return NULL;
+
+       tr->refcount = 1;
+
+       INIT_LIST_HEAD(&tr->roots);
+       INIT_LIST_HEAD(&tr->devices);
+
+       DBG(TREE, ul_debugobj(tr, "alloc"));
+       return tr;
+}
+
+void lsblk_ref_devtree(struct lsblk_devtree *tr)
+{
+       if (tr)
+               tr->refcount++;
+}
+
+void lsblk_unref_devtree(struct lsblk_devtree *tr)
+{
+       if (tr)
+               return;
+
+       if (--tr->refcount <= 0) {
+               DBG(TREE, ul_debugobj(tr, "dealloc"));
+
+               while (!list_empty(&tr->roots)) {
+                       struct lsblk_device *dev = list_entry(tr->roots.next,
+                                               struct lsblk_device, ls_roots);
+                       lsblk_unref_device(dev);
+               }
+               while (!list_empty(&tr->devices)) {
+                       struct lsblk_device *dev = list_entry(tr->devices.next,
+                                               struct lsblk_device, ls_devices);
+                       lsblk_unref_device(dev);
+               }
+               free(tr);
+       }
+}
+
+int lsblk_devtree_add_root(struct lsblk_devtree *tr, struct lsblk_device *dev)
+{
+       lsblk_ref_device(dev);
+
+        DBG(TREE, ul_debugobj(tr, "add root device 0x%p [%s]", dev, dev->name));
+        list_add_tail(&dev->ls_roots, &tr->roots);
+       return 0;
+}
+
+int lsblk_devtree_next_root(struct lsblk_devtree *tr,
+                           struct lsblk_iter *itr,
+                           struct lsblk_device **dev)
+{
+       int rc = 1;
+
+       if (!tr || !itr || !dev)
+               return -EINVAL;
+       *dev = NULL;
+       if (!itr->head)
+               LSBLK_ITER_INIT(itr, &tr->roots);
+       if (itr->p != itr->head) {
+               LSBLK_ITER_ITERATE(itr, *dev, struct lsblk_device, ls_roots);
+               rc = 0;
+       }
+       return rc;
+}
+
+int lsblk_devtree_add_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
+{
+       lsblk_ref_device(dev);
+
+        DBG(TREE, ul_debugobj(tr, "add device 0x%p [%s]", dev, dev->name));
+        list_add_tail(&dev->ls_devices, &tr->devices);
+       return 0;
+}
+
+int lsblk_devtree_next_device(struct lsblk_devtree *tr,
+                           struct lsblk_iter *itr,
+                           struct lsblk_device **dev)
+{
+       int rc = 1;
+
+       if (!tr || !itr || !dev)
+               return -EINVAL;
+       *dev = NULL;
+       if (!itr->head)
+               LSBLK_ITER_INIT(itr, &tr->devices);
+       if (itr->p != itr->head) {
+               LSBLK_ITER_ITERATE(itr, *dev, struct lsblk_device, ls_devices);
+               rc = 0;
+       }
+       return rc;
+}
+
+struct lsblk_device *lsblk_devtree_get_device(struct lsblk_devtree *tr, const char *name)
+{
+       struct lsblk_device *dev = NULL;
+       struct lsblk_iter itr;
+
+       lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+       while (lsblk_devtree_next_device(tr, &itr, &dev) == 0) {
+               if (strcmp(name, dev->name) == 0)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+
index 935c9de1510fd9a2fc6b8e93141183b2069f8d39..05740ec5db057eabcc558c1da52c5d6fa89bf429 100644 (file)
 #include <libsmartcols.h>
 
 #include "c.h"
+#include "list.h"
 #include "debug.h"
 
 #define LSBLK_DEBUG_INIT       (1 << 1)
 #define LSBLK_DEBUG_FILTER     (1 << 2)
 #define LSBLK_DEBUG_DEV                (1 << 3)
 #define LSBLK_DEBUG_CXT                (1 << 4)
+#define LSBLK_DEBUG_TREE       (1 << 5)
 #define LSBLK_DEBUG_ALL                0xFFFF
 
 UL_DEBUG_DECLARE_MASK(lsblk);
@@ -64,11 +66,29 @@ struct lsblk_devprop {
        char *model;            /* disk model */
 };
 
+/* Device dependence
+ *
+ * Note that the same device may be slave/holder for more another devices. It
+ * means we need to allocate list member rather than use @child directly.
+ */
+struct lsblk_devdep {
+       struct list_head        ls_deps;        /* item in parent->deps */
+       struct lsblk_device     *child;
+};
+
 struct lsblk_device {
-       struct lsblk_device *parent;
-       struct lsblk_devprop *properties;
+       int     refcount;
+
+       struct list_head        deps;           /* list with lsblk_devdep */
+       struct list_head        ls_roots;       /* item in devtree->roots list */
+       struct list_head        ls_devices;     /* item in devtree->devices list */
 
-       struct libscols_line *scols_line;
+       struct lsblk_devtree    *tree;
+
+       struct lsblk_devprop    *properties;
+       struct libscols_line    *scols_line;
+
+       struct lsblk_device     *parent;        /* obsolete */
        struct stat     st;
 
        char *name;             /* kernel name in /sys/block */
@@ -98,6 +118,55 @@ struct lsblk_device {
                        blkid_requested : 1;
 };
 
+
+/*
+ * Note that lsblk tree uses botton devices (devices without slaves) as root
+ * of the tree, and partitions are interpreted as a dependence too; it means:
+ *    sda -> sda1 -> md0
+ *
+ * The flag 'is_inverted' turns the tree over (root is device without holders):
+ *    md0 -> sda1 -> sda
+ */
+struct lsblk_devtree {
+       int     refcount;
+
+       struct list_head        roots;          /* tree root devices */
+       struct list_head        devices;        /* all devices */
+
+       unsigned int    is_inverse : 1;         /* inverse tree */
+};
+
+
+/*
+ * Generic iterator
+ */
+struct lsblk_iter {
+       struct list_head        *p;             /* current position */
+       struct list_head        *head;          /* start position */
+       int                     direction;      /* LSBLK_ITER_{FOR,BACK}WARD */
+};
+
+#define LSBLK_ITER_FORWARD     0
+#define LSBLK_ITER_BACKWARD    1
+
+#define IS_ITER_FORWARD(_i)    ((_i)->direction == LSBLK_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i)   ((_i)->direction == LSBLK_ITER_BACKWARD)
+
+#define LSBLK_ITER_INIT(itr, list) \
+       do { \
+               (itr)->p = IS_ITER_FORWARD(itr) ? \
+                               (list)->next : (list)->prev; \
+               (itr)->head = (list); \
+       } while(0)
+
+#define LSBLK_ITER_ITERATE(itr, res, restype, member) \
+       do { \
+               res = list_entry((itr)->p, restype, member); \
+               (itr)->p = IS_ITER_FORWARD(itr) ? \
+                               (itr)->p->next : (itr)->p->prev; \
+       } while(0)
+
+
 /* lsblk-mnt.c */
 extern void lsblk_mnt_init(void);
 extern void lsblk_mnt_deinit(void);
@@ -109,4 +178,28 @@ extern void lsblk_device_free_properties(struct lsblk_devprop *p);
 extern struct lsblk_devprop *lsblk_device_get_properties(struct lsblk_device *dev);
 extern void lsblk_properties_deinit(void);
 
+/* lsblk-devtree.c */
+void lsblk_reset_iter(struct lsblk_iter *itr, int direction);
+struct lsblk_device *lsblk_new_device(struct lsblk_devtree *tree);
+void lsblk_ref_device(struct lsblk_device *dev);
+void lsblk_unref_device(struct lsblk_device *dev);
+struct lsblk_devdep *lsblk_device_new_dependence(struct lsblk_device *parent, struct lsblk_device *child);
+int lsblk_device_next_child(struct lsblk_device *dev,
+                          struct lsblk_iter *itr,
+                          struct lsblk_device **child);
+
+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_next_root(struct lsblk_devtree *tr,
+                            struct lsblk_iter *itr,
+                            struct lsblk_device **dev);
+int lsblk_devtree_add_device(struct lsblk_devtree *tr, struct lsblk_device *dev);
+int lsblk_devtree_next_device(struct lsblk_devtree *tr,
+                            struct lsblk_iter *itr,
+                            struct lsblk_device **dev);
+struct lsblk_device *lsblk_devtree_get_device(struct lsblk_devtree *tr, const char *name);
+
+
 #endif /* UTIL_LINUX_LSBLK_H */