Skip to content

Commit

Permalink
lsblk: add basic function to build devices tree
Browse files Browse the repository at this point in the history
Signed-off-by: Karel Zak <[email protected]>
  • Loading branch information
karelzak committed Dec 7, 2018
1 parent a0a76f4 commit 5bb395f
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 3 deletions.
1 change: 1 addition & 0 deletions misc-utils/Makemodule.am
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
257 changes: 257 additions & 0 deletions misc-utils/lsblk-devtree.c
Original file line number Diff line number Diff line change
@@ -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;
}


99 changes: 96 additions & 3 deletions misc-utils/lsblk.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
#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);
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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 */

0 comments on commit 5bb395f

Please sign in to comment.