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