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
make_hba_logid(const char * hba_phys,char ** hba_logpp,int * l_errnop)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
get_hba_devlink(const char * hba_phys,char ** hba_logpp,int * l_errnop)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
drv_to_hba_logid(di_node_t node,di_minor_t minor,void * arg)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
apid_to_path(const char * hba_phys,const char * dyncomp,char ** pathpp,int * l_errnop)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
path_apid_dyn_to_path(const char * hba_phys,const char * dyn,char ** pathpp,int * l_errnop)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
drv_dyn_to_devpath(const char * hba_phys,const char * dyncomp,char ** pathpp,int * l_errnop)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
do_drv_dyn_to_devpath(di_node_t node,void * arg)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
s_readlink(char * link,char * buf,int len)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
devlink_dyn_to_devpath(const char * hba_phys,const char * dyncomp,char ** pathpp,int * l_errnop)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
make_dyncomp(di_node_t node,const char * physpath,char ** dyncompp,int * l_errnop)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
make_path_dyncomp(di_path_t path,char ** dyncompp,int * l_errnop)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
drv_to_dyncomp(di_node_t node,const char * phys,char ** dyncompp,int * l_errnop)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
devlink_to_dyncomp(char * devlink,char ** dyncompp,int * l_errnop)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
disk_devlink_to_dyncomp(dyn_t * dyntp)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
disk_dyncomp_to_devlink(dyn_t * dyntp)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
tape_devlink_to_dyncomp(dyn_t * dyntp)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
tape_dyncomp_to_devlink(dyn_t * dyntp)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
def_devlink_to_dyncomp(dyn_t * dyntp)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
def_dyncomp_to_devlink(dyn_t * dyntp)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