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 <devfsadm.h>
27 #include <stdio.h>
28 #include <strings.h>
29 #include <stdlib.h>
30 #include <limits.h>
31 #include <ctype.h>
32 #include <sys/int_fmtio.h>
33 #include <sys/stat.h>
34 #include <bsm/devalloc.h>
35 #include <sys/scsi/scsi_address.h>
36 #include <sys/libdevid.h>
37
38 #define DISK_SUBPATH_MAX 100
39 #define RM_STALE 0x01
40 #define DISK_LINK_RE "^r?dsk/c[0-9]+(t[0-9A-F]+)?d[0-9]+(((s|p))[0-9]+)?$"
41 #define DISK_LINK_TO_UPPER(ch)\
42 (((ch) >= 'a' && (ch) <= 'z') ? (ch - 'a' + 'A') : ch)
43
44 #define SLICE_SMI "s7"
45 #define SLICE_EFI ""
46
47 #define MN_SMI "h"
48 #define MN_EFI "wd"
49 #define ASCIIWWNSIZE 255
50 #if defined(__i386) || defined(__amd64)
51 /*
52 * The number of minor nodes per LUN is defined by the disk drivers.
53 * Currently it is set to 64. Refer CMLBUNIT_SHIFT (cmlb_impl.h)
54 */
55 #define NUM_MINORS_PER_INSTANCE 64
56 #endif
57
58
59 extern int system_labeled;
60
61 static int disk_callback_chan(di_minor_t minor, di_node_t node);
62 static int disk_callback_nchan(di_minor_t minor, di_node_t node);
63 static int disk_callback_wwn(di_minor_t minor, di_node_t node);
64 static int disk_callback_xvmd(di_minor_t minor, di_node_t node);
65 static int disk_callback_fabric(di_minor_t minor, di_node_t node);
66 static int disk_callback_sas(di_minor_t minor, di_node_t node);
67 static void disk_common(di_minor_t minor, di_node_t node, char *disk,
68 int flags);
69 static char *diskctrl(di_node_t node, di_minor_t minor);
70 static int reserved_links_exist(di_node_t node, di_minor_t minor, int nflags);
71
72
73 static devfsadm_create_t disk_cbt[] = {
74 { "disk", DDI_NT_BLOCK, NULL,
75 TYPE_EXACT, ILEVEL_0, disk_callback_nchan
76 },
77 { "disk", DDI_NT_BLOCK_CHAN, NULL,
78 TYPE_EXACT, ILEVEL_0, disk_callback_chan
79 },
80 { "disk", DDI_NT_BLOCK_FABRIC, NULL,
81 TYPE_EXACT, ILEVEL_0, disk_callback_fabric
82 },
83 { "disk", DDI_NT_BLOCK_WWN, NULL,
84 TYPE_EXACT, ILEVEL_0, disk_callback_wwn
85 },
86 { "disk", DDI_NT_BLOCK_SAS, NULL,
87 TYPE_EXACT, ILEVEL_0, disk_callback_sas
88 },
89 { "disk", DDI_NT_CD, NULL,
90 TYPE_EXACT, ILEVEL_0, disk_callback_nchan
91 },
92 { "disk", DDI_NT_CD_CHAN, NULL,
93 TYPE_EXACT, ILEVEL_0, disk_callback_chan
94 },
95 { "disk", DDI_NT_BLOCK_XVMD, NULL,
96 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd
97 },
98 { "disk", DDI_NT_CD_XVMD, NULL,
99 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd
100 },
101 };
102
103 DEVFSADM_CREATE_INIT_V0(disk_cbt);
104
105 /*
106 * HOT auto cleanup of disks not desired.
107 */
108 static devfsadm_remove_t disk_remove_cbt[] = {
109 { "disk", DISK_LINK_RE, RM_POST,
110 ILEVEL_0, devfsadm_rm_all
111 }
112 };
113
114 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt);
115
116 static devlink_re_t disks_re_array[] = {
117 {"^r?dsk/c([0-9]+)", 1},
118 {"^cfg/c([0-9]+)$", 1},
119 {"^scsi/.+/c([0-9]+)", 1},
120 {NULL}
121 };
122
123 static char *disk_mid = "disk_mid";
124 static char *modname = "disk_link";
125
126 int
minor_init()127 minor_init()
128 {
129 devfsadm_print(disk_mid,
130 "%s: minor_init(): Creating disks reserved ID cache\n",
131 modname);
132 return (devfsadm_reserve_id_cache(disks_re_array, NULL));
133 }
134
135 static int
disk_callback_chan(di_minor_t minor,di_node_t node)136 disk_callback_chan(di_minor_t minor, di_node_t node)
137 {
138 char *addr;
139 char disk[20];
140 uint_t targ;
141 uint_t lun;
142
143 addr = di_bus_addr(node);
144 (void) sscanf(addr, "%X,%X", &targ, &lun);
145 (void) sprintf(disk, "t%dd%d", targ, lun);
146 disk_common(minor, node, disk, 0);
147 return (DEVFSADM_CONTINUE);
148
149 }
150
151 static int
disk_callback_nchan(di_minor_t minor,di_node_t node)152 disk_callback_nchan(di_minor_t minor, di_node_t node)
153 {
154 char *addr;
155 char disk[10];
156 uint_t lun;
157
158 addr = di_bus_addr(node);
159 (void) sscanf(addr, "%X", &lun);
160 (void) sprintf(disk, "d%d", lun);
161 disk_common(minor, node, disk, 0);
162 return (DEVFSADM_CONTINUE);
163
164 }
165
166 static int
disk_callback_wwn(di_minor_t minor,di_node_t node)167 disk_callback_wwn(di_minor_t minor, di_node_t node)
168 {
169 char disk[10];
170 int lun;
171 int targ;
172 int *intp;
173
174 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_TARGET,
175 &intp) <= 0) {
176 return (DEVFSADM_CONTINUE);
177 }
178 targ = *intp;
179 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_LUN,
180 &intp) <= 0) {
181 lun = 0;
182 } else {
183 lun = *intp;
184 }
185 (void) sprintf(disk, "t%dd%d", targ, lun);
186
187 disk_common(minor, node, disk, RM_STALE);
188
189 return (DEVFSADM_CONTINUE);
190 }
191
192 static int
disk_callback_fabric(di_minor_t minor,di_node_t node)193 disk_callback_fabric(di_minor_t minor, di_node_t node)
194 {
195 char disk[DISK_SUBPATH_MAX];
196 int lun;
197 int count;
198 int *intp;
199 uchar_t *str;
200 uchar_t *wwn;
201 uchar_t ascii_wwn[ASCIIWWNSIZE];
202
203 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
204 "client-guid", (char **)&wwn) > 0) {
205 if (strlcpy((char *)ascii_wwn, (char *)wwn,
206 sizeof (ascii_wwn)) >= sizeof (ascii_wwn)) {
207 devfsadm_errprint("SUNW_disk_link: GUID too long:%d",
208 strlen((char *)wwn));
209 return (DEVFSADM_CONTINUE);
210 }
211 lun = 0;
212 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
213 "port-wwn", &wwn) > 0) {
214 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
215 SCSI_ADDR_PROP_LUN, &intp) > 0) {
216 lun = *intp;
217 } else {
218 lun = 0;
219 }
220
221 for (count = 0, str = ascii_wwn; count < 8; count++, str += 2) {
222 (void) sprintf((caddr_t)str, "%02x", wwn[count]);
223 }
224 *str = '\0';
225 } else {
226 return (DEVFSADM_CONTINUE);
227 }
228
229 for (str = ascii_wwn; *str != '\0'; str++) {
230 *str = DISK_LINK_TO_UPPER(*str);
231 }
232
233 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", ascii_wwn, lun);
234
235 disk_common(minor, node, disk, RM_STALE);
236
237 return (DEVFSADM_CONTINUE);
238 }
239
240 static int
disk_callback_sas(di_minor_t minor,di_node_t node)241 disk_callback_sas(di_minor_t minor, di_node_t node)
242 {
243 char disk[DISK_SUBPATH_MAX];
244 int lun64_found = 0;
245 scsi_lun64_t lun64, sl;
246 scsi_lun_t lun;
247 int64_t *lun64p;
248 uint64_t wwn;
249 int *intp;
250 char *tgt_port;
251 uchar_t addr_method;
252
253 /* Get lun property */
254 if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
255 SCSI_ADDR_PROP_LUN64, &lun64p) > 0) {
256 if (*lun64p != SCSI_LUN64_ILLEGAL) {
257 lun64_found = 1;
258 lun64 = (uint64_t)*lun64p;
259 }
260 }
261 if ((!lun64_found) && (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
262 SCSI_ADDR_PROP_LUN, &intp) > 0)) {
263 lun64 = (uint64_t)*intp;
264 }
265
266 lun = scsi_lun64_to_lun(lun64);
267
268 addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK);
269
270 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
271 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) {
272 (void) scsi_wwnstr_to_wwn(tgt_port, &wwn);
273 if ((addr_method == SCSI_LUN_AM_PDEV) &&
274 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
275 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
276 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
277 (void) snprintf(disk, DISK_SUBPATH_MAX,
278 "t%"PRIX64"d%"PRId64, wwn, lun64);
279 } else if ((addr_method == SCSI_LUN_AM_FLAT) &&
280 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
281 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
282 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
283 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
284 (void) snprintf(disk, DISK_SUBPATH_MAX,
285 "t%"PRIX64"d%"PRIX16, wwn, sl);
286 } else {
287 (void) snprintf(disk, DISK_SUBPATH_MAX,
288 "t%"PRIX64"d%"PRIX64, wwn, lun64);
289 }
290 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
291 SCSI_ADDR_PROP_SATA_PHY, &intp) > 0) {
292 /* Use phy format naming, for SATA devices without wwn */
293 if ((addr_method == SCSI_LUN_AM_PDEV) &&
294 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
295 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
296 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
297 (void) snprintf(disk, DISK_SUBPATH_MAX,
298 "t%dd%"PRId64, *intp, lun64);
299 } else if ((addr_method == SCSI_LUN_AM_FLAT) &&
300 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
301 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
302 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
303 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
304 (void) snprintf(disk, DISK_SUBPATH_MAX,
305 "t%dd%"PRIX16, *intp, sl);
306 } else {
307 (void) snprintf(disk, DISK_SUBPATH_MAX,
308 "t%dd%"PRIX64, *intp, lun64);
309 }
310 } else {
311 return (DEVFSADM_CONTINUE);
312 }
313
314 disk_common(minor, node, disk, RM_STALE);
315
316 return (DEVFSADM_CONTINUE);
317 }
318
319 /*
320 * xVM virtual block device
321 *
322 * VBDs are enumerated into xenstore by xend and named using
323 * the linux dev_t values for 'hd' and 'xvd' devices. Linux
324 * dev_t's are 16-bit values. The upper 8 bits identify the major #
325 * of the device (hd, xvd) and the lower 8 bits the instance and partition
326 *
327 * For PV guests, VBDs are named by the virt-tools using
328 * the form xvd[a-p][1-15]. The corresponding Solaris /dev/dsk name
329 * created by this generator will be c0t[0-15]d[0-15]sN,
330 * were the target (t) value represents [a-p] and the
331 * disk (d) value is either 0 (e.g. xvda) or contains the partition
332 * information if it has been specified [1-15] (e.g. xvda1)
333 *
334 * For PV guests using the legacy naming (0, 1, 2, ...)
335 * the Solaris disk names created will be c0d[0..767]sN
336 * The Solaris version of virt-install based on virtinst.101
337 * named PV disks as sequential integers. With virtinst.300_1 and
338 * beyond, the virt-* tools will no longer create legacy disk
339 * names.
340 */
341 static int
disk_callback_xvmd(di_minor_t minor,di_node_t node)342 disk_callback_xvmd(di_minor_t minor, di_node_t node)
343 {
344 #define HD_BASE (3 << 8)
345 #define XVBDMAJ 202
346
347 char *addr;
348 char disk[16];
349 uint_t targ;
350 uint_t lun = 0;
351 uint_t fmaj;
352
353 addr = di_bus_addr(node);
354 targ = strtol(addr, (char **)NULL, 10);
355 fmaj = targ >> 8;
356
357 /* legacy device address */
358 if (targ < HD_BASE)
359 (void) snprintf(disk, sizeof (disk), "d%d", targ);
360 /* PV VBD */
361 else if (fmaj == XVBDMAJ) {
362 lun = targ & 0xf;
363 targ = (targ & 0xff) >> 4;
364 (void) snprintf(disk, sizeof (disk), "t%dd%d", targ, lun);
365 /* HVM device names are generated using the standard generator */
366 } else {
367 devfsadm_errprint("%s: invalid disk device number (%s)\n",
368 modname, addr);
369 return (DEVFSADM_CONTINUE);
370 }
371 disk_common(minor, node, disk, 0);
372 return (DEVFSADM_CONTINUE);
373
374 }
375
376 /*
377 * This function is called for every disk minor node.
378 * Calls enumerate to assign a logical controller number, and
379 * then devfsadm_mklink to make the link.
380 */
381 static void
disk_common(di_minor_t minor,di_node_t node,char * disk,int flags)382 disk_common(di_minor_t minor, di_node_t node, char *disk, int flags)
383 {
384 char l_path[PATH_MAX + 1];
385 char sec_path[PATH_MAX + 1];
386 char stale_re[DISK_SUBPATH_MAX];
387 char *dir;
388 char slice[4];
389 char *mn;
390 char *ctrl;
391 char *nt = NULL;
392 int *int_prop;
393 int nflags = 0;
394 #if defined(__i386) || defined(__amd64)
395 char mn_copy[4];
396 char *part;
397 int part_num;
398 #endif
399
400 mn = di_minor_name(minor);
401 if (strstr(mn, ",raw")) {
402 dir = "rdsk";
403 #if defined(__i386) || defined(__amd64)
404 (void) strncpy(mn_copy, mn, 4);
405 part = strtok(mn_copy, ",");
406 #endif
407 } else {
408 dir = "dsk";
409 #if defined(__i386) || defined(__amd64)
410 part = mn;
411 #endif
412 }
413
414 #if defined(__i386) || defined(__amd64)
415 /*
416 * The following is a table describing the allocation of
417 * minor numbers, minor names and /dev/dsk names for partitions
418 * and slices on x86 systems.
419 *
420 * Minor Number Minor Name /dev/dsk name
421 * ---------------------------------------------
422 * 0 to 15 "a" to "p" s0 to s15
423 * 16 "q" p0
424 * 17 to 20 "r" to "u" p1 to p4
425 * 21 to 52 "p5" to "p36" p5 to p36
426 *
427 */
428 part_num = atoi(part + 1);
429
430 if ((mn[0] == 'p') && (part_num >= 5)) {
431 /* logical drive */
432 (void) snprintf(slice, 4, "%s", part);
433 } else {
434 #endif
435 if (mn[0] < 'q') {
436 (void) sprintf(slice, "s%d", mn[0] - 'a');
437 } else if (strncmp(mn, MN_EFI, 2) != 0) {
438 (void) sprintf(slice, "p%d", mn[0] - 'q');
439 } else {
440 /* For EFI label */
441 (void) sprintf(slice, SLICE_EFI);
442 }
443 #if defined(__i386) || defined(__amd64)
444 }
445 #endif
446
447 nflags = 0;
448 if (system_labeled) {
449 nt = di_minor_nodetype(minor);
450 if ((nt != NULL) &&
451 ((strcmp(nt, DDI_NT_CD) == 0) ||
452 (strcmp(nt, DDI_NT_CD_CHAN) == 0) ||
453 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) {
454 nflags = DA_ADD|DA_CD;
455 }
456 }
457
458 if (reserved_links_exist(node, minor, nflags) == DEVFSADM_SUCCESS) {
459 devfsadm_print(disk_mid, "Reserved link exists. Not "
460 "creating links for slice %s\n", slice);
461 return;
462 }
463
464 if (NULL == (ctrl = diskctrl(node, minor)))
465 return;
466
467 (void) strcpy(l_path, dir);
468 (void) strcat(l_path, "/c");
469 (void) strcat(l_path, ctrl);
470 (void) strcat(l_path, disk);
471
472 /*
473 * If switching between SMI and EFI label or vice versa
474 * cleanup the previous label's devlinks.
475 */
476 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) {
477 char *s, tpath[PATH_MAX + 1];
478 struct stat sb;
479
480 s = l_path + strlen(l_path);
481 (void) strcat(l_path, (*mn == *(MN_SMI))
482 ? SLICE_EFI : SLICE_SMI);
483 /*
484 * Attempt the remove only if the stale link exists
485 */
486 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s",
487 devfsadm_root_path(), l_path);
488 if (lstat(tpath, &sb) != -1)
489 devfsadm_rm_all(l_path);
490 *s = '\0';
491 }
492 (void) strcat(l_path, slice);
493
494 (void) devfsadm_mklink(l_path, node, minor, nflags);
495
496 /* secondary links for removable and hotpluggable devices */
497 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media",
498 &int_prop) >= 0) {
499 (void) strcpy(sec_path, "removable-media/");
500 (void) strcat(sec_path, l_path);
501 (void) devfsadm_secondary_link(sec_path, l_path, 0);
502 }
503 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable",
504 &int_prop) >= 0) {
505 (void) strcpy(sec_path, "hotpluggable/");
506 (void) strcat(sec_path, l_path);
507 (void) devfsadm_secondary_link(sec_path, l_path, 0);
508 }
509
510 if ((flags & RM_STALE) == RM_STALE) {
511 (void) strcpy(stale_re, "^");
512 (void) strcat(stale_re, dir);
513 (void) strcat(stale_re, "/c");
514 (void) strcat(stale_re, ctrl);
515 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$");
516 /*
517 * optimizations are made inside of devfsadm_rm_stale_links
518 * instead of before calling the function, as it always
519 * needs to add the valid link to the cache.
520 */
521 devfsadm_rm_stale_links(stale_re, l_path, node, minor);
522 }
523
524 free(ctrl);
525 }
526
527
528 /* index of enumeration rule applicable to this module */
529 #define RULE_INDEX 0
530
531 static char *
diskctrl(di_node_t node,di_minor_t minor)532 diskctrl(di_node_t node, di_minor_t minor)
533 {
534 char path[PATH_MAX + 1];
535 char *devfspath;
536 char *buf, *mn;
537
538 devfsadm_enumerate_t rules[3] = {
539 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
540 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
541 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
542 };
543
544 mn = di_minor_name(minor);
545
546 if ((devfspath = di_devfs_path(node)) == NULL) {
547 return (NULL);
548 }
549 (void) strcpy(path, devfspath);
550 (void) strcat(path, ":");
551 (void) strcat(path, mn);
552 di_devfs_path_free(devfspath);
553
554 /*
555 * Use controller component of disk path
556 */
557 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) ==
558 DEVFSADM_MULTIPLE) {
559
560 /*
561 * We failed because there are multiple logical controller
562 * numbers for a single physical controller. If we use node
563 * name also in the match it should fix this and only find one
564 * logical controller. (See 4045879).
565 * NOTE: Rules for controllers are not changed, as there is
566 * no unique controller number for them in this case.
567 *
568 * MATCH_UNCACHED flag is private to the "disks" and "sgen"
569 * modules. NOT to be used by other modules.
570 */
571
572 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */
573 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */
574 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) {
575 return (NULL);
576 }
577 }
578
579 return (buf);
580 }
581
582 typedef struct dvlist {
583 char *dv_link;
584 struct dvlist *dv_next;
585 } dvlist_t;
586
587 static void
free_dvlist(dvlist_t ** pp)588 free_dvlist(dvlist_t **pp)
589 {
590 dvlist_t *entry;
591
592 while (*pp) {
593 entry = *pp;
594 *pp = entry->dv_next;
595 assert(entry->dv_link);
596 free(entry->dv_link);
597 free(entry);
598 }
599 }
600 static int
dvlink_cb(di_devlink_t devlink,void * arg)601 dvlink_cb(di_devlink_t devlink, void *arg)
602 {
603 char *path;
604 char *can_path;
605 dvlist_t **pp = (dvlist_t **)arg;
606 dvlist_t *entry = NULL;
607
608 entry = calloc(1, sizeof (dvlist_t));
609 if (entry == NULL) {
610 devfsadm_errprint("%s: calloc failed\n", modname);
611 goto error;
612 }
613
614 path = (char *)di_devlink_path(devlink);
615 assert(path);
616 if (path == NULL) {
617 devfsadm_errprint("%s: di_devlink_path() returned NULL\n",
618 modname);
619 goto error;
620 }
621
622 devfsadm_print(disk_mid, "%s: found link %s in reverse link cache\n",
623 modname, path);
624
625 /*
626 * Return linkname in canonical form i.e. without the
627 * "/dev/" prefix
628 */
629 can_path = strstr(path, "/dev/");
630 if (can_path == NULL) {
631 devfsadm_errprint("%s: devlink path %s has no /dev/\n",
632 modname, path);
633 goto error;
634 }
635
636 entry->dv_link = s_strdup(can_path + strlen("/dev/"));
637 entry->dv_next = *pp;
638 *pp = entry;
639
640 return (DI_WALK_CONTINUE);
641
642 error:
643 free(entry);
644 free_dvlist(pp);
645 *pp = NULL;
646 return (DI_WALK_TERMINATE);
647 }
648
649 /*
650 * Returns success only if all goes well. If there is no matching reserved link
651 * or if there is an error, we assume no match. It is better to err on the side
652 * of caution by creating extra links than to miss out creating a required link.
653 */
654 static int
reserved_links_exist(di_node_t node,di_minor_t minor,int nflags)655 reserved_links_exist(di_node_t node, di_minor_t minor, int nflags)
656 {
657 di_devlink_handle_t dvlink_cache = devfsadm_devlink_cache();
658 char phys_path[PATH_MAX];
659 char *minor_path;
660 dvlist_t *head;
661 dvlist_t *entry;
662 char *s;
663 char l[PATH_MAX];
664 int switch_link = 0;
665 char *mn = di_minor_name(minor);
666
667 if (dvlink_cache == NULL || mn == NULL) {
668 devfsadm_errprint("%s: No minor or devlink cache\n", modname);
669 return (DEVFSADM_FAILURE);
670 }
671
672 if (!devfsadm_have_reserved()) {
673 devfsadm_print(disk_mid, "%s: No reserved links\n", modname);
674 return (DEVFSADM_FAILURE);
675 }
676
677 minor_path = di_devfs_minor_path(minor);
678 if (minor_path == NULL) {
679 devfsadm_errprint("%s: di_devfs_minor_path failed\n", modname);
680 return (DEVFSADM_FAILURE);
681 }
682
683 (void) strlcpy(phys_path, minor_path, sizeof (phys_path));
684
685 di_devfs_path_free(minor_path);
686
687 head = NULL;
688 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, phys_path,
689 DI_PRIMARY_LINK, &head, dvlink_cb);
690
691 /*
692 * We may be switching between EFI label and SMI label in which case
693 * we only have minors of the other type.
694 */
695 if (head == NULL && (*mn == *(MN_SMI) ||
696 (strncmp(mn, MN_EFI, 2) == 0))) {
697 devfsadm_print(disk_mid, "%s: No links for minor %s in /dev. "
698 "Trying another label\n", modname, mn);
699 s = strrchr(phys_path, ':');
700 if (s == NULL) {
701 devfsadm_errprint("%s: invalid minor path: %s\n",
702 modname, phys_path);
703 return (DEVFSADM_FAILURE);
704 }
705 (void) snprintf(s+1, sizeof (phys_path) - (s + 1 - phys_path),
706 "%s%s", *mn == *(MN_SMI) ? MN_EFI : MN_SMI,
707 strstr(s, ",raw") ? ",raw" : "");
708 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE,
709 phys_path, DI_PRIMARY_LINK, &head, dvlink_cb);
710 }
711
712 if (head == NULL) {
713 devfsadm_print(disk_mid, "%s: minor %s has no links in /dev\n",
714 modname, phys_path);
715 /* no links on disk */
716 return (DEVFSADM_FAILURE);
717 }
718
719 /*
720 * It suffices to use 1 link to this minor, since
721 * we are matching with reserved IDs on the basis of
722 * the controller number which will be the same for
723 * all links to this minor.
724 */
725 if (!devfsadm_is_reserved(disks_re_array, head->dv_link)) {
726 /* not reserved links */
727 devfsadm_print(disk_mid, "%s: devlink %s and its minor "
728 "are NOT reserved\n", modname, head->dv_link);
729 free_dvlist(&head);
730 return (DEVFSADM_FAILURE);
731 }
732
733 devfsadm_print(disk_mid, "%s: devlink %s and its minor are on "
734 "reserved list\n", modname, head->dv_link);
735
736 /*
737 * Switch between SMI and EFI labels if required
738 */
739 switch_link = 0;
740 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) {
741 for (entry = head; entry; entry = entry->dv_next) {
742 s = strrchr(entry->dv_link, '/');
743 assert(s);
744 if (s == NULL) {
745 devfsadm_errprint("%s: disk link %s has no "
746 "directory\n", modname, entry->dv_link);
747 continue;
748 }
749 if (*mn == *(MN_SMI) && strchr(s, 's') == NULL) {
750 (void) snprintf(l, sizeof (l), "%s%s",
751 entry->dv_link, SLICE_SMI);
752 switch_link = 1;
753 devfsadm_print(disk_mid, "%s: switching "
754 "reserved link from EFI to SMI label. "
755 "New link is %s\n", modname, l);
756 } else if (strncmp(mn, MN_EFI, 2) == 0 &&
757 (s = strchr(s, 's'))) {
758 *s = '\0';
759 (void) snprintf(l, sizeof (l), "%s",
760 entry->dv_link);
761 *s = 's';
762 switch_link = 1;
763 devfsadm_print(disk_mid, "%s: switching "
764 "reserved link from SMI to EFI label. "
765 "New link is %s\n", modname, l);
766 }
767 if (switch_link) {
768 devfsadm_print(disk_mid, "%s: switching "
769 "link: deleting %s and creating %s\n",
770 modname, entry->dv_link, l);
771 devfsadm_rm_link(entry->dv_link);
772 (void) devfsadm_mklink(l, node, minor, nflags);
773 }
774 }
775 }
776 free_dvlist(&head);
777
778 /*
779 * return SUCCESS to indicate that new links to this minor should not
780 * be created so that only compatibility links to this minor remain.
781 */
782 return (DEVFSADM_SUCCESS);
783 }
784