--- /dev/null
+
+#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;
+}
+
+
#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);
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 */
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);
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 */