OpenSolaris_b135/lib/cfgadm_plugins/scsi/common/cfga_cvt.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "cfga_scsi.h"

typedef struct {
	char *dyncomp;
	char *devlink;
	int l_errno;
	scfga_ret_t ret;
} dyn_t;

typedef struct {
	scfga_recur_t (*devlink_to_dyncomp_p)(dyn_t *dyntp);
	scfga_recur_t (*dyncomp_to_devlink_p)(dyn_t *dyntp);
} dynrules_t;

typedef struct {
	dyn_t *dynp;
	dynrules_t *rule_array;
	int nrules;
} dyncvt_t;

typedef struct {
	const char *hba_phys;
	const char *dyncomp;
	char *path;
	int l_errno;
	scfga_ret_t ret;
} devpath_t;



/* Function prototypes */

static int drv_to_hba_logid(di_node_t node, di_minor_t minor, void *arg);
static scfga_ret_t drv_dyn_to_devpath(const char *hba_phys,
    const char *dyncomp, char **pathpp, int *l_errnop);
static int do_drv_dyn_to_devpath(di_node_t node, void *arg);
static scfga_ret_t devlink_dyn_to_devpath(const char *hba_phys,
    const char *dyncomp, char **pathpp, int *l_errnop);

static scfga_recur_t disk_dyncomp_to_devlink(dyn_t *dyntp);
static scfga_recur_t tape_dyncomp_to_devlink(dyn_t *dyntp);
static scfga_recur_t def_dyncomp_to_devlink(dyn_t *dyntp);

static scfga_ret_t devlink_to_dyncomp(char *devlink,
    char **dyncompp, int *l_errnop);
static scfga_recur_t disk_devlink_to_dyncomp(dyn_t *dyntp);
static scfga_recur_t tape_devlink_to_dyncomp(dyn_t *dyntp);
static scfga_recur_t def_devlink_to_dyncomp(dyn_t *dyntp);
static scfga_ret_t drv_to_dyncomp(di_node_t node, const char *phys,
    char **dyncompp, int *l_errnop);
static scfga_ret_t get_hba_devlink(const char *hba_phys,
    char **hba_logpp, int *l_errnop);
static scfga_ret_t path_apid_dyn_to_path(const char *hba_phys, const char *dyn,
    char **pathpp, int *l_errnop);


/* Globals */

/*
 * Rules for converting between a devlink and logical ap_id and vice-versa
 * The default rules must be the last entry.
 */
static dynrules_t dyncvt_rules[] = {
	{disk_devlink_to_dyncomp,	disk_dyncomp_to_devlink},
	{tape_devlink_to_dyncomp,	tape_dyncomp_to_devlink},
	{def_devlink_to_dyncomp,	def_dyncomp_to_devlink}
};

#define	N_DYNRULES	(sizeof (dyncvt_rules)/sizeof (dyncvt_rules[0]))

/*
 * Numbering of disk slices is assumed to be 0 through n - 1
 */
typedef struct {
	char *prefix;
	int nslices;
} slice_t;

static slice_t disk_slices[] = {
	{"s", 16},
	{"p", 5},
};

#define	N_SLICE_TYPES	(sizeof (disk_slices) / sizeof (disk_slices[0]))

static const char *tape_modes[] = {
	"",
	"b", "bn",
	"c", "cb", "cbn", "cn",
	"h", "hb", "hbn", "hn",
	"l", "lb", "lbn", "ln",
	"m", "mb", "mbn", "mn",
	"n",
	"u", "ub", "ubn", "un"
};

#define	N_TAPE_MODES	(sizeof (tape_modes) / sizeof (tape_modes[0]))


/* Various conversions routines */

/*
 * Generates the HBA logical ap_id from physical ap_id.
 */
scfga_ret_t
make_hba_logid(const char *hba_phys, char **hba_logpp, int *l_errnop)
{
	walkarg_t u;
	pathm_t pmt = {NULL};
	scfga_ret_t ret;


	if (*hba_logpp != NULL) {
		return (SCFGA_ERR);
	}

	/* A devlink for the HBA may or may not exist */
	if (get_hba_devlink(hba_phys, hba_logpp, l_errnop) == SCFGA_OK) {
		assert(*hba_logpp != NULL);
		return (SCFGA_OK);
	}

	/*
	 * No devlink based logical ap_id.
	 * Try driver name and instance number.
	 */
	u.minor_args.nodetype = DDI_NT_SCSI_ATTACHMENT_POINT;
	u.minor_args.fcn = drv_to_hba_logid;

	pmt.phys = (char *)hba_phys;
	pmt.ret = SCFGA_APID_NOEXIST;

	errno = 0;
	ret = walk_tree(pmt.phys, &pmt, DINFOMINOR | DINFOPROP, &u,
	    SCFGA_WALK_MINOR, &pmt.l_errno);
	if (ret == SCFGA_OK && (ret = pmt.ret) == SCFGA_OK) {
		assert(pmt.log != NULL);
		*hba_logpp = pmt.log;
		return (SCFGA_OK);
	}

	/* failed to create logical ap_id */
	if (pmt.log != NULL) {
		S_FREE(pmt.log);
	}


	*l_errnop = pmt.l_errno;
	return (ret);
}

static scfga_ret_t
get_hba_devlink(const char *hba_phys, char **hba_logpp, int *l_errnop)
{
	size_t len;
	scfga_ret_t ret;
	int match_minor = 1;

	ret = physpath_to_devlink((char *)hba_phys, hba_logpp,
	    l_errnop, match_minor);
	if (ret != SCFGA_OK) {
		return (ret);
	}

	assert(*hba_logpp != NULL);

	/* Remove the "/dev/cfg/"  prefix */
	len = strlen(CFGA_DEV_DIR SLASH);

	(void) memmove(*hba_logpp, *hba_logpp + len,
	    strlen(*hba_logpp + len) + 1);

	return (SCFGA_OK);
}

/* Make logical name for HBA  based on driver and instance */
static int
drv_to_hba_logid(di_node_t node, di_minor_t minor, void *arg)
{
	int inst;
	char *drv, *mn, *log;
	pathm_t *ptp;
	const size_t loglen = MAXPATHLEN;

	ptp = (pathm_t *)arg;

	errno = 0;

	mn = di_minor_name(minor);
	drv = di_driver_name(node);
	inst = di_instance(node);
	log = calloc(1, loglen);

	if (mn != NULL && drv != NULL && inst != -1 && log != NULL) {
		/* Count does not include terminating NULL */
		if (snprintf(log, loglen, "%s%d:%s", drv, inst, mn) < loglen) {
			ptp->ret = SCFGA_OK;
			ptp->log = log;
			return (DI_WALK_TERMINATE);
		}
	}

	S_FREE(log);
	return (DI_WALK_CONTINUE);
}

/*
 * Given a bus or device ap_id <hba_phys, dyncomp>, returns the physical
 * path in pathpp.
 * Returns: SCFGA_APID_NOEXIST if the path does not exist.
 */

scfga_ret_t
apid_to_path(
	const char *hba_phys,
	const char *dyncomp,
	char **pathpp,
	int *l_errnop)
{
	scfga_ret_t ret;

	if (*pathpp != NULL) {
		return (SCFGA_LIB_ERR);
	}

	/* If a bus, the physical ap_id is the physical path */
	if (dyncomp == NULL) {
		if ((*pathpp = strdup(hba_phys)) == NULL) {
			*l_errnop = errno;
			return (SCFGA_LIB_ERR);
		}
		return (SCFGA_OK);
	}

	/* Dynamic component exists, we have a device */

	/*
	 * If the dynamic component has a '/', it was derived from a devlink
	 * Else it was derived from driver name and instance number.
	 * If it is pathinfo instance number based ap id, it will have a format
	 * path#.???.
	 */
	if (strchr(dyncomp, '/') != NULL) {
		ret = devlink_dyn_to_devpath(hba_phys, dyncomp, pathpp,
		    l_errnop);
	} else if (strstr(dyncomp, PATH_APID_DYN_SEP) != NULL) {
		ret = path_apid_dyn_to_path(hba_phys, dyncomp, pathpp,
		    l_errnop);
	} else {
		ret = drv_dyn_to_devpath(hba_phys, dyncomp, pathpp, l_errnop);
	}
	assert(ret != SCFGA_OK || *pathpp != NULL);


	return (ret);
}

/*
 * Get the devfs path of pathinfo node that is associated with
 * the given dynamic component.
 *
 * input
 *   hba_phys: physical path of HBA
 *   dyn : bus address of pathinfo node
 * output:
 *   pathpp: devfs path of the pathinfo node.
 */
static scfga_ret_t
path_apid_dyn_to_path(
	const char *hba_phys,
	const char *dyn,
	char **pathpp,
	int *l_errnop)
{

	di_node_t   root, walk_root;
	di_path_t   pi_node = DI_PATH_NIL;
	char	    *root_path, *devpath, *cp;
	int	    len;

	*l_errnop = 0;

	/* *pathpp should be NULL if pathpp is not NULL. */
	if ((hba_phys == NULL) || (pathpp != NULL) && (*pathpp != NULL)) {
		return (SCFGA_LIB_ERR);
	}

	if ((root_path = strdup(hba_phys)) == NULL) {
		*l_errnop = errno;
		return (SCFGA_LIB_ERR);
	}

	/* Fix up path for di_init() */
	len = strlen(DEVICES_DIR);
	if (strncmp(root_path, DEVICES_DIR SLASH,
	    len + strlen(SLASH)) == 0) {
		cp = root_path + len;
		(void) memmove(root_path, cp, strlen(cp) + 1);
	} else if (*root_path != '/') {
		*l_errnop = 0;
		S_FREE(root_path);
		return (SCFGA_ERR);
	}

	/* Remove dynamic component if any */
	if ((cp = GET_DYN(root_path)) != NULL) {
		*cp = '\0';
	}

	/* Remove minor name if any */
	if ((cp = strrchr(root_path, ':')) != NULL) {
		*cp = '\0';
	}

	/*
	 * Cached snapshots are always rooted at "/"
	 */

	/* Get a snapshot */
	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
		*l_errnop = errno;
		S_FREE(root_path);
		return (SCFGA_ERR);
	}

	/*
	 * Lookup the subtree of interest
	 */
	walk_root = di_lookup_node(root, root_path);

	if (walk_root == DI_NODE_NIL) {
		*l_errnop = errno;
		di_fini(root);
		S_FREE(root_path);
		return (SCFGA_LIB_ERR);
	}

	S_FREE(root_path);

	if ((pi_node = di_path_next_client(walk_root, pi_node)) ==
	    DI_PATH_NIL) {
		di_fini(root);
		return (SCFGA_APID_NOEXIST);
	}

	/*
	 * now parse the path info node.
	 */
	do {
		/* check the length first. */
		if (strlen(di_path_bus_addr(pi_node)) != strlen(dyn)) {
			continue;
		}

		if (strcmp(di_path_bus_addr(pi_node), dyn) == 0) {
			/* get the devfspath of pathinfo node. */
			devpath = di_path_devfs_path(pi_node);
			if (devpath == NULL) {
				*l_errnop = errno;
				di_fini(root);
				return (SCFGA_ERR);
			}

			len = strlen(DEVICES_DIR) + strlen(devpath) + 1;
			*pathpp = calloc(1, len);
			if (*pathpp == NULL) {
				*l_errnop = errno;
				di_devfs_path_free(devpath);
				di_fini(root);
				return (SCFGA_ERR);
			} else {
				(void) snprintf(*pathpp, len, "%s%s",
				    DEVICES_DIR, devpath);
				di_devfs_path_free(devpath);
				di_fini(root);
				return (SCFGA_OK);
			}
		}
		pi_node = di_path_next_client(walk_root, pi_node);
	} while (pi_node != DI_PATH_NIL);

	di_fini(root);
	return (SCFGA_APID_NOEXIST);
}

static scfga_ret_t
drv_dyn_to_devpath(
	const char *hba_phys,
	const char *dyncomp,
	char **pathpp,
	int *l_errnop)
{
	walkarg_t u;
	devpath_t dpt = {NULL};
	scfga_ret_t ret;

	/* A device MUST have a dynamic component */
	if (dyncomp == NULL || *pathpp != NULL) {
		return (SCFGA_LIB_ERR);
	}

	u.node_args.flags = DI_WALK_CLDFIRST;
	u.node_args.fcn = do_drv_dyn_to_devpath;

	dpt.hba_phys = hba_phys;
	dpt.dyncomp = dyncomp;
	dpt.ret = SCFGA_APID_NOEXIST;

	ret = walk_tree(hba_phys, &dpt, DINFOCPYALL, &u,
	    SCFGA_WALK_NODE, &dpt.l_errno);

	if (ret == SCFGA_OK && (ret = dpt.ret) == SCFGA_OK) {
		assert(dpt.path != NULL);
		*pathpp = dpt.path;
		return (SCFGA_OK);
	}

	if (dpt.path != NULL) {
		S_FREE(dpt.path);
	}


	*l_errnop = dpt.l_errno;
	return (ret);
}

/* Converts a driver and instance number based logid into a physical path */
static int
do_drv_dyn_to_devpath(di_node_t node, void *arg)
{
	int inst, rv, match_minor;
	devpath_t *dptp;
	char *physpath, *drv;
	char *drvinst, *devpath;
	const size_t drvlen = MAXPATHLEN;
	size_t devlen;

	dptp = (devpath_t *)arg;

	assert(dptp->hba_phys != NULL && dptp->dyncomp != NULL);
	assert(dptp->path == NULL);

	/*
	 * Skip stub nodes
	 */
	if (IS_STUB_NODE(node)) {
		return (DI_WALK_CONTINUE);
	}

	errno = 0;

	drv = di_driver_name(node);
	inst = di_instance(node);
	physpath = di_devfs_path(node);
	if (drv == NULL || inst == -1 || physpath == NULL) {
		rv = DI_WALK_CONTINUE;
		goto out;
	}

	devlen = strlen(DEVICES_DIR) + strlen(physpath) + 1;

	devpath = calloc(1, devlen);
	drvinst = calloc(1, drvlen);
	if (devpath == NULL || drvinst == NULL) {
		dptp->l_errno = errno;
		dptp->ret = SCFGA_LIB_ERR;
		rv = DI_WALK_TERMINATE;
		goto out;
	}

	(void) snprintf(drvinst, drvlen, "%s%d", drv, inst);

	/* Create the physical path */
	(void) snprintf(devpath, devlen, "%s%s", DEVICES_DIR, physpath);

	/* Skip node if it is the HBA */
	match_minor = 0;
	if (!dev_cmp(dptp->hba_phys, devpath, match_minor)) {
		rv = DI_WALK_CONTINUE;
		goto out;
	}

	/* Compare the base and dynamic components */
	if (!hba_dev_cmp(dptp->hba_phys, devpath) &&
	    strcmp(dptp->dyncomp, drvinst) == 0) {
		dptp->ret = SCFGA_OK;
		dptp->path = devpath;
		rv = DI_WALK_TERMINATE;
	} else {
		rv =  DI_WALK_CONTINUE;
	}

	/*FALLTHRU*/
out:
	S_FREE(drvinst);
	if (physpath != NULL) di_devfs_path_free(physpath);
	if (dptp->ret != SCFGA_OK) S_FREE(devpath);
	return (rv);
}

/* readlink wrapper to ensure proper null termination of the results */
static int
s_readlink(char *link, char *buf, int len)
{
	int count;

	count = readlink(link, buf, len - 1);
	if (count != -1)
		buf[count] = '\0';
	return (count);
}

/* Converts a devlink based dynamic component to a path */
static scfga_ret_t
devlink_dyn_to_devpath(
	const char *hba_phys,
	const char *dyncomp,
	char **pathpp,
	int *l_errnop)
{
	dyn_t dynt = {NULL};
	int i;
	scfga_ret_t ret;
	char buf[PATH_MAX], *path;

	if (*pathpp != NULL) {
		return (SCFGA_LIB_ERR);
	}

	/* Convert the dynamic component to the corresponding devlink */
	dynt.dyncomp = (char *)dyncomp;
	dynt.ret = SCFGA_APID_NOEXIST;

	for (i = 0; i < N_DYNRULES; i++) {
		if (dyncvt_rules[i].dyncomp_to_devlink_p(&dynt)
		    != SCFGA_CONTINUE) {
			break;
		}
	}

	if (i >= N_DYNRULES) {
		dynt.ret = SCFGA_APID_NOEXIST;
	}

	if (dynt.ret != SCFGA_OK) {
		/* No symlink or error */
		return (dynt.ret);
	}

	assert(dynt.devlink != NULL);

	/*
	 * Follow devlink to get the physical path
	 * Note: Do not use realpath().	It will stat() device
	 *	and stat() fails under devfs if device is offline.
	 */
	errno = 0;
	if ((s_readlink(dynt.devlink, buf, PATH_MAX) == -1) ||
	    ((path = strstr(buf, "/devices/")) == NULL) ||
	    ((*pathpp = strdup(path)) == NULL)) {
		*l_errnop = errno;
		ret = SCFGA_LIB_ERR;
		goto out;
	}

	/* Compare base components as well */
	if (!hba_dev_cmp(hba_phys, path)) {
		ret = SCFGA_OK;
	} else {
		/* Mismatched base and dynamic component */
		*l_errnop = 0;
		ret = SCFGA_APID_NOEXIST;
	}

	/*FALLTHRU*/
out:
	S_FREE(dynt.devlink);
	if (ret != SCFGA_OK) S_FREE(*pathpp);
	return (ret);
}

scfga_ret_t
make_dyncomp(
	di_node_t node,
	const char *physpath,
	char **dyncompp,
	int *l_errnop)
{
	char *devlink = NULL;
	scfga_ret_t ret;
	di_minor_t minor;
	char *path;
	char pathbuf[MAXPATHLEN];
	int match_minor;

	if (*dyncompp != NULL) {
		return (SCFGA_LIB_ERR);
	}

	/* tag on minor name */
	minor = di_minor_next(node, DI_MINOR_NIL);
	if (minor == DI_MINOR_NIL) {
		match_minor = 0;
		path = (char *)physpath;
	} else {
		match_minor = 1;
		(void) snprintf(pathbuf, MAXPATHLEN, "%s:%s", physpath,
		    di_minor_name(minor));
		path = pathbuf;
	}

	/* Get the corresponding devlink from the physical path */
	ret = physpath_to_devlink(path, &devlink, l_errnop, match_minor);
	if (ret == SCFGA_OK) {
		assert(devlink != NULL);

		/* Create dynamic component. */
		ret = devlink_to_dyncomp(devlink, dyncompp, l_errnop);
		S_FREE(devlink);
		if (ret == SCFGA_OK) {
			assert(*dyncompp != NULL);
			return (SCFGA_OK);
		}

		/*
		 * Failed to get devlink based dynamic component.
		 * Try driver and instance
		 */
	}

	ret = drv_to_dyncomp(node, physpath, dyncompp, l_errnop);
	assert(ret != SCFGA_OK || *dyncompp != NULL);

	return (ret);
}

/*
 * Create a dynamic component of path ap_id for the given path info node.
 * The caller should free the buffer for the dynamic component.
 */
scfga_ret_t
make_path_dyncomp(
	di_path_t path,
	char **dyncompp,
	int *l_errnop)
{
	char *pi_addr;

	if ((path == DI_PATH_NIL) || (*dyncompp != NULL)) {
		return (SCFGA_LIB_ERR);
	}

	if ((pi_addr = di_path_bus_addr(path)) != NULL) {
		*dyncompp = calloc(1, strlen(pi_addr) + 1);
		if (*dyncompp == NULL) {
			*l_errnop = errno;
			return (SCFGA_LIB_ERR);
		}
		(void) strncpy(*dyncompp, pi_addr, strlen(pi_addr));
	} else {
		return (SCFGA_LIB_ERR);
	}

	return (SCFGA_OK);
}

/*ARGSUSED*/
static scfga_ret_t
drv_to_dyncomp(di_node_t node, const char *phys, char **dyncompp, int *l_errnop)
{
	char *drv;
	int inst;
	const int dynlen = MAXPATHLEN;
	scfga_ret_t ret;

	*l_errnop = 0;

	if ((*dyncompp = calloc(1, dynlen)) == NULL) {
		*l_errnop = errno;
		return (SCFGA_LIB_ERR);
	}

	drv = di_driver_name(node);
	inst = di_instance(node);
	if (drv != NULL && inst != -1) {
		if (snprintf(*dyncompp, dynlen, "%s%d", drv, inst) < dynlen) {
			return (SCFGA_OK);
		} else {
			ret = SCFGA_LIB_ERR;
		}
	} else {
		ret = SCFGA_APID_NOEXIST;
	}

	S_FREE(*dyncompp);
	return (ret);
}

/* Get a dynamic component from a physical path if possible */
static scfga_ret_t
devlink_to_dyncomp(char *devlink, char **dyncompp, int *l_errnop)
{
	int i;
	dyn_t dynt = {NULL};

	*l_errnop = 0;

	if (*dyncompp != NULL) {
		return (SCFGA_LIB_ERR);
	}

	/* Convert devlink to dynamic component */
	dynt.devlink = devlink;
	dynt.ret = SCFGA_APID_NOEXIST;

	for (i = 0; i < N_DYNRULES; i++) {
		if (dyncvt_rules[i].devlink_to_dyncomp_p(&dynt)
		    != SCFGA_CONTINUE) {
			break;
		}
	}

	if (i >= N_DYNRULES) {
		dynt.ret = SCFGA_APID_NOEXIST;
	}

	if (dynt.ret == SCFGA_OK) {
		assert(dynt.dyncomp != NULL);
		*dyncompp = dynt.dyncomp;
	}

	return (dynt.ret);
}

/* For disks remove partition information, (s or p) */
static scfga_recur_t
disk_devlink_to_dyncomp(dyn_t *dyntp)
{
	char *cp = NULL, *cp1 = NULL;

	assert(dyntp->devlink != NULL);

	dyntp->l_errno = 0;

	if (dyntp->dyncomp != NULL) {
		goto lib_err;
	}

	/* Check if a disk devlink */
	if (strncmp(dyntp->devlink, DEV_DSK SLASH, strlen(DEV_DSK SLASH)) &&
	    strncmp(dyntp->devlink, DEV_RDSK SLASH, strlen(DEV_RDSK SLASH))) {
		return (SCFGA_CONTINUE);
	}

	cp = dyntp->devlink + strlen(DEV_DIR SLASH);

	if ((dyntp->dyncomp = strdup(cp)) == NULL) {
		dyntp->l_errno = errno;
		goto lib_err;
	}

	/* Get the leaf component from dsk/cXtYdZsN */
	cp1 = strrchr(dyntp->dyncomp, '/');

	/* Blank out partition information */
	dyntp->ret = SCFGA_OK;
	if ((cp = strchr(cp1 + 1, 's')) != NULL) {
		*cp = '\0';
	} else if ((cp = strchr(cp1 + 1, 'p')) != NULL) {
		*cp = '\0';
	} else {
		S_FREE(dyntp->dyncomp);
		dyntp->ret = SCFGA_ERR;
	}

	return (SCFGA_TERMINATE);

lib_err:
	dyntp->ret = SCFGA_LIB_ERR;
	return (SCFGA_TERMINATE);
}


static scfga_recur_t
disk_dyncomp_to_devlink(dyn_t *dyntp)
{
	char buf[MAXPATHLEN], *cp = NULL;
	int i, j;
	size_t len;
	struct stat sbuf;

	assert(dyntp->dyncomp != NULL);

	dyntp->l_errno = 0;

	if (dyntp->devlink != NULL) {
		dyntp->ret = SCFGA_LIB_ERR;
		return (SCFGA_TERMINATE);
	}

	/* A disk link can only be from DEV_DSK (ignore /dev/rdsk) */
	if (strncmp(dyntp->dyncomp, DSK_DIR SLASH, strlen(DSK_DIR SLASH)) != 0)
		return (SCFGA_CONTINUE);	/* not a disk link */

	(void) snprintf(buf, sizeof (buf), "%s%s", DEV_DIR SLASH,
	    dyntp->dyncomp);

	len = strlen(buf);
	cp = buf + len;
	len = sizeof (buf) - len;

	for (i = 0; i < N_SLICE_TYPES; i++) {
		for (j = 0; j < disk_slices[i].nslices; j++) {
			if (snprintf(cp, len, "%s%d", disk_slices[i].prefix, j)
			    >= len) {
				continue;
			}

			if (lstat(buf, &sbuf) != -1 && S_ISLNK(sbuf.st_mode)) {
				if ((dyntp->devlink = strdup(buf)) == NULL) {
					dyntp->l_errno = errno;
					dyntp->ret = SCFGA_LIB_ERR;
					return (SCFGA_TERMINATE);
				}
				dyntp->ret = SCFGA_OK;
				return (SCFGA_TERMINATE);
			}
		}
	}

	dyntp->ret = SCFGA_APID_NOEXIST;
	return (SCFGA_TERMINATE);
}

/* For tapes, remove mode(minor) information from link */
static scfga_recur_t
tape_devlink_to_dyncomp(dyn_t *dyntp)
{
	char *cp = NULL;

	assert(dyntp->devlink != NULL);

	dyntp->l_errno = 0;

	if (dyntp->dyncomp != NULL) {
		goto lib_err;
	}

	if (strncmp(dyntp->devlink, DEV_RMT SLASH, strlen(DEV_RMT SLASH))) {
		return (SCFGA_CONTINUE);	/* not a tape */
	}

	cp = dyntp->devlink + strlen(DEV_DIR SLASH);
	if ((dyntp->dyncomp = strdup(cp)) == NULL) {
		dyntp->l_errno = errno;
		goto lib_err;
	}

	/* Get the leaf component from rmt/xyz */
	cp = strrchr(dyntp->dyncomp, '/');

	/* Remove the mode part */
	while (isdigit(*(++cp))) {
	};
	*cp = '\0';


	dyntp->ret = SCFGA_OK;
	return (SCFGA_TERMINATE);

lib_err:
	dyntp->ret = SCFGA_LIB_ERR;
	return (SCFGA_TERMINATE);
}

static scfga_recur_t
tape_dyncomp_to_devlink(dyn_t *dyntp)
{
	char buf[MAXPATHLEN], *cp = NULL;
	int i;
	size_t len = 0;
	struct stat sbuf;

	assert(dyntp->dyncomp != NULL);

	dyntp->l_errno = 0;

	if (dyntp->devlink != NULL) {
		goto lib_err;
	}

	if (strncmp(dyntp->dyncomp, RMT_DIR SLASH, strlen(RMT_DIR SLASH))) {
		return (SCFGA_CONTINUE);	/* not a tape */
	}

	/* A tape device */
	(void) snprintf(buf, sizeof (buf), "%s%s", DEV_DIR SLASH,
	    dyntp->dyncomp);

	len = strlen(buf);
	cp = buf + len;
	len = sizeof (buf) - len;

	for (i = 0; i < N_TAPE_MODES; i++) {
		(void) snprintf(cp, len, "%s", tape_modes[i]);

		if (lstat(buf, &sbuf) != -1 && S_ISLNK(sbuf.st_mode)) {
			if ((dyntp->devlink = strdup(buf)) == NULL) {
				dyntp->l_errno = errno;
				goto lib_err;
			}
			dyntp->ret = SCFGA_OK;
			return (SCFGA_TERMINATE);
		}
	}

	dyntp->ret = SCFGA_APID_NOEXIST;
	return (SCFGA_TERMINATE);

lib_err:
	dyntp->ret = SCFGA_LIB_ERR;
	return (SCFGA_TERMINATE);

}

/*
 * Default rules
 */
static scfga_recur_t
def_devlink_to_dyncomp(dyn_t *dyntp)
{
	size_t len = 0;
	char *cp = NULL;

	assert(dyntp->devlink != NULL);

	dyntp->l_errno = 0;

	if (dyntp->dyncomp != NULL) {
		dyntp->ret = SCFGA_LIB_ERR;
		return (SCFGA_TERMINATE);
	}

	/* Is it a link in DEV_DIR directory ? */
	len = strlen(DEV_DIR SLASH);
	if (strncmp(dyntp->devlink, DEV_DIR SLASH, len)) {
		return (SCFGA_CONTINUE);
	}

	/* Check if this is a top level devlink */
	if (strchr(dyntp->devlink + len, '/') != NULL) {
		/* not top level - Remove DEV_DIR SLASH prefix */
		cp = dyntp->devlink + len;
	} else {
		/* top level, leave DEV_DIR SLASH part in */
		cp = dyntp->devlink;
	}

	if ((dyntp->dyncomp = strdup(cp)) == NULL) {
		dyntp->l_errno = errno;
		dyntp->ret = SCFGA_LIB_ERR;
	} else {
		dyntp->ret = SCFGA_OK;
	}

	return (SCFGA_TERMINATE);

}

static scfga_recur_t
def_dyncomp_to_devlink(dyn_t *dyntp)
{
	struct stat sbuf;
	int top;
	size_t prelen, linklen;

	assert(dyntp->dyncomp != NULL);

	dyntp->l_errno = 0;

	if (dyntp->devlink != NULL) {
		goto lib_err;
	}

	prelen = strlen(DEV_DIR SLASH);
	linklen = strlen(dyntp->dyncomp) + 1;

	/*
	 * Check if the dynamic component was derived from a top level entry
	 * in "/dev"
	 */
	if (strncmp(dyntp->dyncomp, DEV_DIR SLASH, prelen) == 0) {
		top = 1;
	} else if (*dyntp->dyncomp != '/' && linklen > 1 &&
	    strchr(dyntp->dyncomp + 1, '/') != NULL) {
		top = 0;
		linklen += prelen;  /* The "/dev/" needs to be prepended */
	} else {
		/* Not a dynamic component we handle */
		return (SCFGA_CONTINUE);
	}

	if ((dyntp->devlink = calloc(1, linklen)) == NULL) {
		dyntp->l_errno = errno;
		goto lib_err;
	}

	*dyntp->devlink = '\0';
	if (!top) {
		(void) strcpy(dyntp->devlink, DEV_DIR SLASH);
	}
	(void) strcat(dyntp->devlink, dyntp->dyncomp);

	if (lstat(dyntp->devlink, &sbuf) != -1 && S_ISLNK(sbuf.st_mode)) {
		dyntp->ret = SCFGA_OK;
		return (SCFGA_TERMINATE);
	}


	S_FREE(dyntp->devlink);
	return (SCFGA_CONTINUE);

lib_err:
	dyntp->ret = SCFGA_LIB_ERR;
	return (SCFGA_TERMINATE);
}