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