summaryrefslogblamecommitdiffstats
path: root/misc-utils/lsblk-devtree.c
blob: 3c71d24e464a75b02800e1b4fdc961570cd0bc04 (plain) (tree)
































































































































































































































































                                                                                                         

#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;
}