diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am index c44cdda6708..3043687e6b0 100644 --- a/misc-utils/Makemodule.am +++ b/misc-utils/Makemodule.am @@ -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 index 00000000000..3c71d24e464 --- /dev/null +++ b/misc-utils/lsblk-devtree.c @@ -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; +} + + diff --git a/misc-utils/lsblk.h b/misc-utils/lsblk.h index 935c9de1510..05740ec5db0 100644 --- a/misc-utils/lsblk.h +++ b/misc-utils/lsblk.h @@ -14,12 +14,14 @@ #include #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 */