1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1999, 2001-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "cfga_scsi.h"
30 
31 typedef struct {
32 	char *dyncomp;
33 	char *devlink;
34 	int l_errno;
35 	scfga_ret_t ret;
36 } dyn_t;
37 
38 typedef struct {
39 	scfga_recur_t (*devlink_to_dyncomp_p)(dyn_t *dyntp);
40 	scfga_recur_t (*dyncomp_to_devlink_p)(dyn_t *dyntp);
41 } dynrules_t;
42 
43 typedef struct {
44 	dyn_t *dynp;
45 	dynrules_t *rule_array;
46 	int nrules;
47 } dyncvt_t;
48 
49 typedef struct {
50 	const char *hba_phys;
51 	const char *dyncomp;
52 	char *path;
53 	int l_errno;
54 	scfga_ret_t ret;
55 } devpath_t;
56 
57 
58 
59 /* Function prototypes */
60 
61 static int drv_to_hba_logid(di_node_t node, di_minor_t minor, void *arg);
62 static scfga_ret_t drv_dyn_to_devpath(const char *hba_phys,
63     const char *dyncomp, char **pathpp, int *l_errnop);
64 static int do_drv_dyn_to_devpath(di_node_t node, void *arg);
65 static scfga_ret_t devlink_dyn_to_devpath(const char *hba_phys,
66     const char *dyncomp, char **pathpp, int *l_errnop);
67 
68 static scfga_recur_t disk_dyncomp_to_devlink(dyn_t *dyntp);
69 static scfga_recur_t tape_dyncomp_to_devlink(dyn_t *dyntp);
70 static scfga_recur_t def_dyncomp_to_devlink(dyn_t *dyntp);
71 
72 static scfga_ret_t devlink_to_dyncomp(char *devlink,
73     char **dyncompp, int *l_errnop);
74 static scfga_recur_t disk_devlink_to_dyncomp(dyn_t *dyntp);
75 static scfga_recur_t tape_devlink_to_dyncomp(dyn_t *dyntp);
76 static scfga_recur_t def_devlink_to_dyncomp(dyn_t *dyntp);
77 static scfga_ret_t drv_to_dyncomp(di_node_t node, const char *phys,
78     char **dyncompp, int *l_errnop);
79 static scfga_ret_t get_hba_devlink(const char *hba_phys,
80     char **hba_logpp, int *l_errnop);
81 
82 
83 /* Globals */
84 
85 /*
86  * Rules for converting between a devlink and logical ap_id and vice-versa
87  * The default rules must be the last entry.
88  */
89 static dynrules_t dyncvt_rules[] = {
90 	{disk_devlink_to_dyncomp,	disk_dyncomp_to_devlink},
91 	{tape_devlink_to_dyncomp,	tape_dyncomp_to_devlink},
92 	{def_devlink_to_dyncomp,	def_dyncomp_to_devlink}
93 };
94 
95 #define	N_DYNRULES	(sizeof (dyncvt_rules)/sizeof (dyncvt_rules[0]))
96 
97 /*
98  * Numbering of disk slices is assumed to be 0 through n - 1
99  */
100 typedef struct {
101 	char *prefix;
102 	int nslices;
103 } slice_t;
104 
105 static slice_t disk_slices[] = {
106 	{"s", 16},
107 	{"p", 5},
108 };
109 
110 #define	N_SLICE_TYPES	(sizeof (disk_slices) / sizeof (disk_slices[0]))
111 
112 static const char *tape_modes[] = {
113 	"",
114 	"b", "bn",
115 	"c", "cb", "cbn", "cn",
116 	"h", "hb", "hbn", "hn",
117 	"l", "lb", "lbn", "ln",
118 	"m", "mb", "mbn", "mn",
119 	"n",
120 	"u", "ub", "ubn", "un"
121 };
122 
123 #define	N_TAPE_MODES	(sizeof (tape_modes) / sizeof (tape_modes[0]))
124 
125 
126 /* Various conversions routines */
127 
128 /*
129  * Generates the HBA logical ap_id from physical ap_id.
130  */
131 scfga_ret_t
132 make_hba_logid(const char *hba_phys, char **hba_logpp, int *l_errnop)
133 {
134 	walkarg_t u;
135 	pathm_t pmt = {NULL};
136 	scfga_ret_t ret;
137 
138 
139 	if (*hba_logpp != NULL) {
140 		return (SCFGA_ERR);
141 	}
142 
143 	/* A devlink for the HBA may or may not exist */
144 	if (get_hba_devlink(hba_phys, hba_logpp, l_errnop) == SCFGA_OK) {
145 		assert(*hba_logpp != NULL);
146 		return (SCFGA_OK);
147 	}
148 
149 	/*
150 	 * No devlink based logical ap_id.
151 	 * Try driver name and instance number.
152 	 */
153 	u.minor_args.nodetype = DDI_NT_SCSI_ATTACHMENT_POINT;
154 	u.minor_args.fcn = drv_to_hba_logid;
155 
156 	pmt.phys = (char *)hba_phys;
157 	pmt.ret = SCFGA_APID_NOEXIST;
158 
159 	errno = 0;
160 	ret = walk_tree(pmt.phys, &pmt, DINFOMINOR | DINFOPROP, &u,
161 	    SCFGA_WALK_MINOR, &pmt.l_errno);
162 	if (ret == SCFGA_OK && (ret = pmt.ret) == SCFGA_OK) {
163 		assert(pmt.log != NULL);
164 		*hba_logpp = pmt.log;
165 		return (SCFGA_OK);
166 	}
167 
168 	/* failed to create logical ap_id */
169 	if (pmt.log != NULL) {
170 		S_FREE(pmt.log);
171 	}
172 
173 
174 	*l_errnop = pmt.l_errno;
175 	return (ret);
176 }
177 
178 static scfga_ret_t
179 get_hba_devlink(const char *hba_phys, char **hba_logpp, int *l_errnop)
180 {
181 	size_t len;
182 	scfga_ret_t ret;
183 	int match_minor = 1;
184 
185 	ret = physpath_to_devlink((char *)hba_phys, hba_logpp,
186 	    l_errnop, match_minor);
187 	if (ret != SCFGA_OK) {
188 		return (ret);
189 	}
190 
191 	assert(*hba_logpp != NULL);
192 
193 	/* Remove the "/dev/cfg/"  prefix */
194 	len = strlen(CFGA_DEV_DIR SLASH);
195 
196 	(void) memmove(*hba_logpp, *hba_logpp + len,
197 	    strlen(*hba_logpp + len) + 1);
198 
199 	return (SCFGA_OK);
200 }
201 
202 /* Make logical name for HBA  based on driver and instance */
203 static int
204 drv_to_hba_logid(di_node_t node, di_minor_t minor, void *arg)
205 {
206 	int inst;
207 	char *drv, *mn, *log;
208 	pathm_t *ptp;
209 	const size_t loglen = MAXPATHLEN;
210 
211 	ptp = (pathm_t *)arg;
212 
213 	errno = 0;
214 
215 	mn = di_minor_name(minor);
216 	drv = di_driver_name(node);
217 	inst = di_instance(node);
218 	log = calloc(1, loglen);
219 
220 	if (mn != NULL && drv != NULL && inst != -1 && log != NULL) {
221 		/* Count does not include terminating NULL */
222 		if (snprintf(log, loglen, "%s%d:%s", drv, inst, mn) < loglen) {
223 			ptp->ret = SCFGA_OK;
224 			ptp->log = log;
225 			return (DI_WALK_TERMINATE);
226 		}
227 	}
228 
229 	S_FREE(log);
230 	return (DI_WALK_CONTINUE);
231 }
232 
233 /*
234  * Given a bus or device ap_id <hba_phys, dyncomp>, returns the physical
235  * path in pathpp.
236  * Returns: SCFGA_APID_NOEXIST if the path does not exist.
237  */
238 
239 scfga_ret_t
240 apid_to_path(
241 	const char *hba_phys,
242 	const char *dyncomp,
243 	char **pathpp,
244 	int *l_errnop)
245 {
246 	scfga_ret_t ret;
247 
248 	if (*pathpp != NULL) {
249 		return (SCFGA_LIB_ERR);
250 	}
251 
252 	/* If a bus, the physical ap_id is the physical path */
253 	if (dyncomp == NULL) {
254 		if ((*pathpp = strdup(hba_phys)) == NULL) {
255 			*l_errnop = errno;
256 			return (SCFGA_LIB_ERR);
257 		}
258 		return (SCFGA_OK);
259 	}
260 
261 	/* Dynamic component exists, we have a device */
262 
263 	/*
264 	 * If the dynamic component has a '/', it was derived from a devlink
265 	 * Else it was derived from driver name and instance number.
266 	 */
267 	if (strchr(dyncomp, '/') != NULL) {
268 		ret = devlink_dyn_to_devpath(hba_phys, dyncomp, pathpp,
269 		    l_errnop);
270 	} else {
271 		ret = drv_dyn_to_devpath(hba_phys, dyncomp, pathpp, l_errnop);
272 	}
273 
274 	assert(ret != SCFGA_OK || *pathpp != NULL);
275 
276 	return (ret);
277 }
278 
279 static scfga_ret_t
280 drv_dyn_to_devpath(
281 	const char *hba_phys,
282 	const char *dyncomp,
283 	char **pathpp,
284 	int *l_errnop)
285 {
286 	walkarg_t u;
287 	devpath_t dpt = {NULL};
288 	scfga_ret_t ret;
289 
290 	/* A device MUST have a dynamic component */
291 	if (dyncomp == NULL || *pathpp != NULL) {
292 		return (SCFGA_LIB_ERR);
293 	}
294 
295 	u.node_args.flags = DI_WALK_CLDFIRST;
296 	u.node_args.fcn = do_drv_dyn_to_devpath;
297 
298 	dpt.hba_phys = hba_phys;
299 	dpt.dyncomp = dyncomp;
300 	dpt.ret = SCFGA_APID_NOEXIST;
301 
302 	ret = walk_tree(hba_phys, &dpt, DINFOCPYALL, &u,
303 	    SCFGA_WALK_NODE, &dpt.l_errno);
304 
305 	if (ret == SCFGA_OK && (ret = dpt.ret) == SCFGA_OK) {
306 		assert(dpt.path != NULL);
307 		*pathpp = dpt.path;
308 		return (SCFGA_OK);
309 	}
310 
311 	if (dpt.path != NULL) {
312 		S_FREE(dpt.path);
313 	}
314 
315 
316 	*l_errnop = dpt.l_errno;
317 	return (ret);
318 }
319 
320 /* Converts a driver and instance number based logid into a physical path */
321 static int
322 do_drv_dyn_to_devpath(di_node_t node, void *arg)
323 {
324 	int inst, rv, match_minor;
325 	devpath_t *dptp;
326 	char *physpath, *drv;
327 	char *drvinst, *devpath;
328 	const size_t drvlen = MAXPATHLEN;
329 	size_t devlen;
330 
331 	dptp = (devpath_t *)arg;
332 
333 	assert(dptp->hba_phys != NULL && dptp->dyncomp != NULL);
334 	assert(dptp->path == NULL);
335 
336 	/*
337 	 * Skip stub nodes
338 	 */
339 	if (IS_STUB_NODE(node)) {
340 		return (DI_WALK_CONTINUE);
341 	}
342 
343 	errno = 0;
344 
345 	drv = di_driver_name(node);
346 	inst = di_instance(node);
347 	physpath = di_devfs_path(node);
348 	if (drv == NULL || inst == -1 || physpath == NULL) {
349 		rv = DI_WALK_CONTINUE;
350 		goto out;
351 	}
352 
353 	devlen = strlen(DEVICES_DIR) + strlen(physpath) + 1;
354 
355 	devpath = calloc(1, devlen);
356 	drvinst = calloc(1, drvlen);
357 	if (devpath == NULL || drvinst == NULL) {
358 		dptp->l_errno = errno;
359 		dptp->ret = SCFGA_LIB_ERR;
360 		rv = DI_WALK_TERMINATE;
361 		goto out;
362 	}
363 
364 	(void) snprintf(drvinst, drvlen, "%s%d", drv, inst);
365 
366 	/* Create the physical path */
367 	(void) snprintf(devpath, devlen, "%s%s", DEVICES_DIR, physpath);
368 
369 	/* Skip node if it is the HBA */
370 	match_minor = 0;
371 	if (!dev_cmp(dptp->hba_phys, devpath, match_minor)) {
372 		rv = DI_WALK_CONTINUE;
373 		goto out;
374 	}
375 
376 	/* Compare the base and dynamic components */
377 	if (!hba_dev_cmp(dptp->hba_phys, devpath) &&
378 	    strcmp(dptp->dyncomp, drvinst) == 0) {
379 		dptp->ret = SCFGA_OK;
380 		dptp->path = devpath;
381 		rv = DI_WALK_TERMINATE;
382 	} else {
383 		rv =  DI_WALK_CONTINUE;
384 	}
385 
386 	/*FALLTHRU*/
387 out:
388 	S_FREE(drvinst);
389 	if (physpath != NULL) di_devfs_path_free(physpath);
390 	if (dptp->ret != SCFGA_OK) S_FREE(devpath);
391 	return (rv);
392 }
393 
394 /* readlink wrapper to ensure proper null termination of the results */
395 static int
396 s_readlink(char *link, char *buf, int len)
397 {
398 	int count;
399 
400 	count = readlink(link, buf, len - 1);
401 	if (count != -1)
402 		buf[count] = '\0';
403 	return (count);
404 }
405 
406 /* Converts a devlink based dynamic component to a path */
407 static scfga_ret_t
408 devlink_dyn_to_devpath(
409 	const char *hba_phys,
410 	const char *dyncomp,
411 	char **pathpp,
412 	int *l_errnop)
413 {
414 	dyn_t dynt = {NULL};
415 	int i;
416 	scfga_ret_t ret;
417 	char buf[PATH_MAX], *path;
418 
419 	if (*pathpp != NULL) {
420 		return (SCFGA_LIB_ERR);
421 	}
422 
423 	/* Convert the dynamic component to the corresponding devlink */
424 	dynt.dyncomp = (char *)dyncomp;
425 	dynt.ret = SCFGA_APID_NOEXIST;
426 
427 	for (i = 0; i < N_DYNRULES; i++) {
428 		if (dyncvt_rules[i].dyncomp_to_devlink_p(&dynt)
429 		    != SCFGA_CONTINUE) {
430 			break;
431 		}
432 	}
433 
434 	if (i >= N_DYNRULES) {
435 		dynt.ret = SCFGA_APID_NOEXIST;
436 	}
437 
438 	if (dynt.ret != SCFGA_OK) {
439 		/* No symlink or error */
440 		return (dynt.ret);
441 	}
442 
443 	assert(dynt.devlink != NULL);
444 
445 	/*
446 	 * Follow devlink to get the physical path
447 	 * Note: Do not use realpath().	It will stat() device
448 	 *	and stat() fails under devfs if device is offline.
449 	 */
450 	errno = 0;
451 	if ((s_readlink(dynt.devlink, buf, PATH_MAX) == -1) ||
452 	    ((path = strstr(buf, "/devices/")) == NULL) ||
453 	    ((*pathpp = strdup(path)) == NULL)) {
454 		*l_errnop = errno;
455 		ret = SCFGA_LIB_ERR;
456 		goto out;
457 	}
458 
459 	/* Compare base components as well */
460 	if (!hba_dev_cmp(hba_phys, path)) {
461 		ret = SCFGA_OK;
462 	} else {
463 		/* Mismatched base and dynamic component */
464 		*l_errnop = 0;
465 		ret = SCFGA_APID_NOEXIST;
466 	}
467 
468 	/*FALLTHRU*/
469 out:
470 	S_FREE(dynt.devlink);
471 	if (ret != SCFGA_OK) S_FREE(*pathpp);
472 	return (ret);
473 }
474 
475 scfga_ret_t
476 make_dyncomp(
477 	di_node_t node,
478 	const char *physpath,
479 	char **dyncompp,
480 	int *l_errnop)
481 {
482 	char *devlink = NULL;
483 	scfga_ret_t ret;
484 	di_minor_t minor;
485 	char *path;
486 	char pathbuf[MAXPATHLEN];
487 	int match_minor;
488 
489 	if (*dyncompp != NULL) {
490 		return (SCFGA_LIB_ERR);
491 	}
492 
493 	/* tag on minor name */
494 	minor = di_minor_next(node, DI_MINOR_NIL);
495 	if (minor == DI_MINOR_NIL) {
496 		match_minor = 0;
497 		path = (char *)physpath;
498 	} else {
499 		match_minor = 1;
500 		snprintf(pathbuf, MAXPATHLEN, "%s:%s", physpath,
501 		    di_minor_name(minor));
502 		path = pathbuf;
503 	}
504 
505 	/* Get the corresponding devlink from the physical path */
506 	ret = physpath_to_devlink(path, &devlink, l_errnop, match_minor);
507 	if (ret == SCFGA_OK) {
508 		assert(devlink != NULL);
509 
510 		/* Create dynamic component. */
511 		ret = devlink_to_dyncomp(devlink, dyncompp, l_errnop);
512 		S_FREE(devlink);
513 		if (ret == SCFGA_OK) {
514 			assert(*dyncompp != NULL);
515 			return (SCFGA_OK);
516 		}
517 
518 		/*
519 		 * Failed to get devlink based dynamic component.
520 		 * Try driver and instance
521 		 */
522 	}
523 
524 	ret = drv_to_dyncomp(node, physpath, dyncompp, l_errnop);
525 	assert(ret != SCFGA_OK || *dyncompp != NULL);
526 
527 	return (ret);
528 }
529 
530 /*ARGSUSED*/
531 static scfga_ret_t
532 drv_to_dyncomp(di_node_t node, const char *phys, char **dyncompp, int *l_errnop)
533 {
534 	char *drv;
535 	int inst;
536 	const int dynlen = MAXPATHLEN;
537 	scfga_ret_t ret;
538 
539 	*l_errnop = 0;
540 
541 	if ((*dyncompp = calloc(1, dynlen)) == NULL) {
542 		*l_errnop = errno;
543 		return (SCFGA_LIB_ERR);
544 	}
545 
546 	drv = di_driver_name(node);
547 	inst = di_instance(node);
548 	if (drv != NULL && inst != -1) {
549 		if (snprintf(*dyncompp, dynlen, "%s%d", drv, inst) < dynlen) {
550 			return (SCFGA_OK);
551 		} else {
552 			ret = SCFGA_LIB_ERR;
553 		}
554 	} else {
555 		ret = SCFGA_APID_NOEXIST;
556 	}
557 
558 	S_FREE(*dyncompp);
559 	return (ret);
560 }
561 
562 /* Get a dynamic component from a physical path if possible */
563 static scfga_ret_t
564 devlink_to_dyncomp(char *devlink, char **dyncompp, int *l_errnop)
565 {
566 	int i;
567 	dyn_t dynt = {NULL};
568 
569 	*l_errnop = 0;
570 
571 	if (*dyncompp != NULL) {
572 		return (SCFGA_LIB_ERR);
573 	}
574 
575 	/* Convert devlink to dynamic component */
576 	dynt.devlink = devlink;
577 	dynt.ret = SCFGA_APID_NOEXIST;
578 
579 	for (i = 0; i < N_DYNRULES; i++) {
580 		if (dyncvt_rules[i].devlink_to_dyncomp_p(&dynt)
581 		    != SCFGA_CONTINUE) {
582 			break;
583 		}
584 	}
585 
586 	if (i >= N_DYNRULES) {
587 		dynt.ret = SCFGA_APID_NOEXIST;
588 	}
589 
590 	if (dynt.ret == SCFGA_OK) {
591 		assert(dynt.dyncomp != NULL);
592 		*dyncompp = dynt.dyncomp;
593 	}
594 
595 	return (dynt.ret);
596 }
597 
598 /* For disks remove partition information, (s or p) */
599 static scfga_recur_t
600 disk_devlink_to_dyncomp(dyn_t *dyntp)
601 {
602 	char *cp = NULL, *cp1 = NULL;
603 
604 	assert(dyntp->devlink != NULL);
605 
606 	dyntp->l_errno = 0;
607 
608 	if (dyntp->dyncomp != NULL) {
609 		goto lib_err;
610 	}
611 
612 	/* Check if a disk devlink */
613 	if (strncmp(dyntp->devlink, DEV_DSK SLASH, strlen(DEV_DSK SLASH)) &&
614 	    strncmp(dyntp->devlink, DEV_RDSK SLASH, strlen(DEV_RDSK SLASH))) {
615 		return (SCFGA_CONTINUE);
616 	}
617 
618 	cp = dyntp->devlink + strlen(DEV_DIR SLASH);
619 
620 	if ((dyntp->dyncomp = strdup(cp)) == NULL) {
621 		dyntp->l_errno = errno;
622 		goto lib_err;
623 	}
624 
625 	/* Get the leaf component from dsk/cXtYdZsN */
626 	cp1 = strrchr(dyntp->dyncomp, '/');
627 
628 	/* Blank out partition information */
629 	dyntp->ret = SCFGA_OK;
630 	if ((cp = strchr(cp1 + 1, 's')) != NULL) {
631 		*cp = '\0';
632 	} else if ((cp = strchr(cp1 + 1, 'p')) != NULL) {
633 		*cp = '\0';
634 	} else {
635 		S_FREE(dyntp->dyncomp);
636 		dyntp->ret = SCFGA_ERR;
637 	}
638 
639 	return (SCFGA_TERMINATE);
640 
641 lib_err:
642 	dyntp->ret = SCFGA_LIB_ERR;
643 	return (SCFGA_TERMINATE);
644 }
645 
646 
647 static scfga_recur_t
648 disk_dyncomp_to_devlink(dyn_t *dyntp)
649 {
650 	char buf[MAXPATHLEN], *cp = NULL;
651 	int i, j;
652 	size_t len;
653 	struct stat sbuf;
654 
655 	assert(dyntp->dyncomp != NULL);
656 
657 	dyntp->l_errno = 0;
658 
659 	if (dyntp->devlink != NULL) {
660 		dyntp->ret = SCFGA_LIB_ERR;
661 		return (SCFGA_TERMINATE);
662 	}
663 
664 	/* A disk link can only be from DEV_DSK (ignore /dev/rdsk) */
665 	if (strncmp(dyntp->dyncomp, DSK_DIR SLASH, strlen(DSK_DIR SLASH)) != 0)
666 		return (SCFGA_CONTINUE);	/* not a disk link */
667 
668 	(void) snprintf(buf, sizeof (buf), "%s%s", DEV_DIR SLASH,
669 	    dyntp->dyncomp);
670 
671 	len = strlen(buf);
672 	cp = buf + len;
673 	len = sizeof (buf) - len;
674 
675 	for (i = 0; i < N_SLICE_TYPES; i++) {
676 		for (j = 0; j < disk_slices[i].nslices; j++) {
677 			if (snprintf(cp, len, "%s%d", disk_slices[i].prefix, j)
678 			    >= len) {
679 				continue;
680 			}
681 
682 			if (lstat(buf, &sbuf) != -1 && S_ISLNK(sbuf.st_mode)) {
683 				if ((dyntp->devlink = strdup(buf)) == NULL) {
684 					dyntp->l_errno = errno;
685 					dyntp->ret = SCFGA_LIB_ERR;
686 					return (SCFGA_TERMINATE);
687 				}
688 				dyntp->ret = SCFGA_OK;
689 				return (SCFGA_TERMINATE);
690 			}
691 		}
692 	}
693 
694 	dyntp->ret = SCFGA_APID_NOEXIST;
695 	return (SCFGA_TERMINATE);
696 }
697 
698 /* For tapes, remove mode(minor) information from link */
699 static scfga_recur_t
700 tape_devlink_to_dyncomp(dyn_t *dyntp)
701 {
702 	char *cp = NULL;
703 
704 	assert(dyntp->devlink != NULL);
705 
706 	dyntp->l_errno = 0;
707 
708 	if (dyntp->dyncomp != NULL) {
709 		goto lib_err;
710 	}
711 
712 	if (strncmp(dyntp->devlink, DEV_RMT SLASH, strlen(DEV_RMT SLASH))) {
713 		return (SCFGA_CONTINUE);	/* not a tape */
714 	}
715 
716 	cp = dyntp->devlink + strlen(DEV_DIR SLASH);
717 	if ((dyntp->dyncomp = strdup(cp)) == NULL) {
718 		dyntp->l_errno = errno;
719 		goto lib_err;
720 	}
721 
722 	/* Get the leaf component from rmt/xyz */
723 	cp = strrchr(dyntp->dyncomp, '/');
724 
725 	/* Remove the mode part */
726 	while (isdigit(*(++cp)));
727 	*cp = '\0';
728 
729 
730 	dyntp->ret = SCFGA_OK;
731 	return (SCFGA_TERMINATE);
732 
733 lib_err:
734 	dyntp->ret = SCFGA_LIB_ERR;
735 	return (SCFGA_TERMINATE);
736 }
737 
738 static scfga_recur_t
739 tape_dyncomp_to_devlink(dyn_t *dyntp)
740 {
741 	char buf[MAXPATHLEN], *cp = NULL;
742 	int i;
743 	size_t len = 0;
744 	struct stat sbuf;
745 
746 	assert(dyntp->dyncomp != NULL);
747 
748 	dyntp->l_errno = 0;
749 
750 	if (dyntp->devlink != NULL) {
751 		goto lib_err;
752 	}
753 
754 	if (strncmp(dyntp->dyncomp, RMT_DIR SLASH, strlen(RMT_DIR SLASH))) {
755 		return (SCFGA_CONTINUE);	/* not a tape */
756 	}
757 
758 	/* A tape device */
759 	(void) snprintf(buf, sizeof (buf), "%s%s", DEV_DIR SLASH,
760 	    dyntp->dyncomp);
761 
762 	len = strlen(buf);
763 	cp = buf + len;
764 	len = sizeof (buf) - len;
765 
766 	for (i = 0; i < N_TAPE_MODES; i++) {
767 		(void) snprintf(cp, len, "%s", tape_modes[i]);
768 
769 		if (lstat(buf, &sbuf) != -1 && S_ISLNK(sbuf.st_mode)) {
770 			if ((dyntp->devlink = strdup(buf)) == NULL) {
771 				dyntp->l_errno = errno;
772 				goto lib_err;
773 			}
774 			dyntp->ret = SCFGA_OK;
775 			return (SCFGA_TERMINATE);
776 		}
777 	}
778 
779 	dyntp->ret = SCFGA_APID_NOEXIST;
780 	return (SCFGA_TERMINATE);
781 
782 lib_err:
783 	dyntp->ret = SCFGA_LIB_ERR;
784 	return (SCFGA_TERMINATE);
785 
786 }
787 
788 /*
789  * Default rules
790  */
791 static scfga_recur_t
792 def_devlink_to_dyncomp(dyn_t *dyntp)
793 {
794 	size_t len = 0;
795 	char *cp = NULL;
796 
797 	assert(dyntp->devlink != NULL);
798 
799 	dyntp->l_errno = 0;
800 
801 	if (dyntp->dyncomp != NULL) {
802 		dyntp->ret = SCFGA_LIB_ERR;
803 		return (SCFGA_TERMINATE);
804 	}
805 
806 	/* Is it a link in DEV_DIR directory ? */
807 	len = strlen(DEV_DIR SLASH);
808 	if (strncmp(dyntp->devlink, DEV_DIR SLASH, len)) {
809 		return (SCFGA_CONTINUE);
810 	}
811 
812 	/* Check if this is a top level devlink */
813 	if (strchr(dyntp->devlink + len, '/') != NULL) {
814 		/* not top level - Remove DEV_DIR SLASH prefix */
815 		cp = dyntp->devlink + len;
816 	} else {
817 		/* top level, leave DEV_DIR SLASH part in */
818 		cp = dyntp->devlink;
819 	}
820 
821 	if ((dyntp->dyncomp = strdup(cp)) == NULL) {
822 		dyntp->l_errno = errno;
823 		dyntp->ret = SCFGA_LIB_ERR;
824 	} else {
825 		dyntp->ret = SCFGA_OK;
826 	}
827 
828 	return (SCFGA_TERMINATE);
829 
830 }
831 
832 static scfga_recur_t
833 def_dyncomp_to_devlink(dyn_t *dyntp)
834 {
835 	struct stat sbuf;
836 	int top;
837 	size_t prelen, linklen;
838 
839 	assert(dyntp->dyncomp != NULL);
840 
841 	dyntp->l_errno = 0;
842 
843 	if (dyntp->devlink != NULL) {
844 		goto lib_err;
845 	}
846 
847 	prelen = strlen(DEV_DIR SLASH);
848 	linklen = strlen(dyntp->dyncomp) + 1;
849 
850 	/*
851 	 * Check if the dynamic component was derived from a top level entry
852 	 * in "/dev"
853 	 */
854 	if (strncmp(dyntp->dyncomp, DEV_DIR SLASH, prelen) == 0) {
855 		top = 1;
856 	} else if (*dyntp->dyncomp != '/' && linklen > 1 &&
857 	    strchr(dyntp->dyncomp + 1, '/') != NULL) {
858 		top = 0;
859 		linklen += prelen;  /* The "/dev/" needs to be prepended */
860 	} else {
861 		/* Not a dynamic component we handle */
862 		return (SCFGA_CONTINUE);
863 	}
864 
865 	if ((dyntp->devlink = calloc(1, linklen)) == NULL) {
866 		dyntp->l_errno = errno;
867 		goto lib_err;
868 	}
869 
870 	*dyntp->devlink = '\0';
871 	if (!top) {
872 		(void) strcpy(dyntp->devlink, DEV_DIR SLASH);
873 	}
874 	(void) strcat(dyntp->devlink, dyntp->dyncomp);
875 
876 	if (lstat(dyntp->devlink, &sbuf) != -1 && S_ISLNK(sbuf.st_mode)) {
877 		dyntp->ret = SCFGA_OK;
878 		return (SCFGA_TERMINATE);
879 	}
880 
881 
882 	S_FREE(dyntp->devlink);
883 	return (SCFGA_CONTINUE);
884 
885 lib_err:
886 	dyntp->ret = SCFGA_LIB_ERR;
887 	return (SCFGA_TERMINATE);
888 }
889