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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * ucblinks - create 4.x /dev compatibility names 30 * 31 * The basic algorithm is: 32 * 33 * find block and character special files in /devices with major 34 * numbers of devices that need compatibility names 35 * 36 * determine compatibility names from minor number, driver name, and 37 * /devices name 38 * 39 * create symlinks for the compatibility names to 5.x /dev 40 * entries if possible or /devices entries if necessary 41 * 42 * The name space that ucblinks creates has a number of problems. 43 * Unfortunately people have, to an unknown extent, come to depend 44 * on the broken name space. Fixing ucblinks to be more compatible 45 * with 4.x would make it less compatible with previous releases of 46 * 5.x. The places were it is broken are noted throughout the code 47 * and summarized here: 48 * 49 * 1157501 ucblinks creates completely broken 4.x links for IPI 50 * 1157616 ucblinks does 0 <-> 3 swap for disks when it shouldn't 51 * 1157617 ucblinks creates /dev/rxt* names instead of /dev/rmt* 52 * 1157970 ucblinks creates incompatible and overlapping links for tapes 53 */ 54 55 #include <stdlib.h> 56 #include <stdio.h> 57 #include <unistd.h> 58 #include <string.h> 59 #include <ftw.h> 60 #include <sys/types.h> 61 #include <sys/mkdev.h> 62 #include <sys/param.h> 63 #include <locale.h> 64 #include <libdevinfo.h> 65 66 static char *progname; 67 static char *rootdir = NULL; /* an alternate root to / */ 68 static int debug = 0; /* only print what is right and wrong */ 69 static int depth; /* num descriptors to use for nftw() */ 70 71 /* 72 * Each block or character device entry in /devices is 73 * represented by one of these structures. 74 */ 75 struct devices_ent { 76 char *devicename; /* /devices name */ 77 char *min_comp; /* minor component of name */ 78 int minor; /* minor number */ 79 int israw; /* character device? */ 80 int iscd; /* cdrom? */ 81 int issd; /* simple sd use? */ 82 int csum; /* checksum of name */ 83 struct symlink *linksto; /* symlinks to this name */ 84 struct drvinfo *drp; /* driver for this name */ 85 struct devices_ent *next; /* for hash table */ 86 }; 87 88 /* 89 * There are a set of devices for which we'll create 90 * compatibility names. Each driver for the devices 91 * is represented by a drvinfo structure. The rule_func 92 * field points to a rule function for that driver. 93 */ 94 typedef void (rule_func_t)(struct devices_ent *); 95 96 struct drvinfo { 97 char *name; /* driver name from name_to_major */ 98 int major; /* major number */ 99 int index; /* index, for sorting */ 100 rule_func_t *rule_func; /* rule for this driver */ 101 }; 102 103 /* 104 * The rules for the drivers. 105 */ 106 static rule_func_t rule_ar; /* Archive tapes */ 107 static rule_func_t rule_atapicd; /* PCI cdrom drive */ 108 static rule_func_t rule_fbs; /* frame buffers */ 109 static rule_func_t rule_fd; /* floppy disk */ 110 static rule_func_t rule_id; /* IPI disks */ 111 static rule_func_t rule_mt; /* mt tapes */ 112 static rule_func_t rule_sd; /* scsi disks */ 113 static rule_func_t rule_stxt; /* scsi and xt tapes */ 114 static rule_func_t rule_xdxy; /* xd and xy disks */ 115 static rule_func_t rule_zs; /* zs serial */ 116 117 #define NOMAJ (-1) /* no entry in /etc/name_to_major */ 118 119 /* 120 * Below are the devices for which we create compatibility 121 * links. Some are obsolete as they have no /etc/name_to_major 122 * entry, but they're here to be compatible with the awk-based 123 * version of ucblinks. This list should be in alphabetical 124 * order with the index field set for the position in the array 125 * (we could compute it at runtime, but we know it so we set 126 * it here). See dcomp() for more about sort order. 127 */ 128 static struct drvinfo drvs[] = { 129 { "ar", NOMAJ, 0, rule_ar }, /* obsolete */ 130 { "atapicd", NOMAJ, 1, rule_atapicd }, 131 { "bwtwo", NOMAJ, 1, rule_fbs }, 132 { "cgeight", NOMAJ, 2, rule_fbs }, 133 { "cgfour", NOMAJ, 3, rule_fbs }, /* obsolete */ 134 { "cgfourteen", NOMAJ, 4, rule_fbs }, /* obsolete */ 135 { "cgnine", NOMAJ, 5, rule_fbs }, 136 { "cgsix", NOMAJ, 6, rule_fbs }, 137 { "cgthree", NOMAJ, 7, rule_fbs }, 138 { "cgtwelve", NOMAJ, 8, rule_fbs }, /* obsolete */ 139 { "fd", NOMAJ, 9, rule_fd }, 140 { "id", NOMAJ, 10, rule_id }, 141 { "mt", NOMAJ, 11, rule_mt }, /* obsolete */ 142 { "sd", NOMAJ, 12, rule_sd }, 143 { "st", NOMAJ, 13, rule_stxt }, 144 { "xd", NOMAJ, 14, rule_xdxy }, 145 { "xt", NOMAJ, 15, rule_stxt }, 146 { "xy", NOMAJ, 16, rule_xdxy }, 147 { "zs", NOMAJ, 17, rule_zs }, 148 { "se", NOMAJ, 18, rule_zs }, /* Fast serial */ 149 { "su", NOMAJ, 19, rule_zs }, /* PC/16550 serial */ 150 { NULL }, 151 }; 152 153 /* 154 * Each symlink in /dev is represented by a symlink structure. 155 * We record all of them, not just those that point to interesting 156 * /devices entries, because when we have determined what a 157 * compatibility link should point to we want to know if it 158 * already points to the correct target and it is much cheaper to 159 * look it up in our list than to make a system call. 160 */ 161 struct symlink { 162 char *linkname; /* name of link */ 163 char *target; /* what the link points to */ 164 int csum; /* checksum of linkname */ 165 int already; /* link already made */ 166 struct symlink *hashnext; /* next on hash list */ 167 struct symlink *deventnext; /* next on /devices ent list */ 168 }; 169 170 /* 171 * The /devices entries and the /dev symlinks are kept in 172 * (separate) hash tables. HASHSIZE was pulled out of the 173 * air although it seems to work ok and we get a good 174 * distribution. Some theory says this should be prime, 175 * but I don't understand why and that would make "% HASHSIZE" 176 * a call to mod routine rather than a simple bit-wise and. 177 */ 178 #define HASHSIZE 256 179 static struct devices_ent *de_hashtab[HASHSIZE]; 180 static struct devices_ent **devices_list; 181 static int num_devices_ents; 182 183 static struct symlink *link_hashtab[HASHSIZE]; 184 185 /* 186 * Buffers used by the rule functions to construct link names. 187 */ 188 static char namebuf[MAXPATHLEN + 1]; 189 static char namebuf2[MAXPATHLEN + 1]; 190 191 /* 192 * Handle used for the link database 193 */ 194 static di_devlink_handle_t link_handle; 195 196 static void exec_script(char **argv); 197 static void get_major_nums(void); 198 static void set_depth(void); 199 static void get_devices(void); 200 static void get_dev_links(void); 201 static void call_device_rules(void); 202 static int is_blank(char *); 203 204 /* 205 * The command-line arguments to ucblinks are: 206 * 207 * -r specify a root relative to which ./devices and ./dev 208 * are used to create links. 209 * 210 * -e the awk-based ucblinks had a default rule-base and 211 * allowed alternate rule-bases with -e. If the user 212 * specifies a rule-base we run the awk-based ucblinks 213 * and pass all the args to it. 214 * 215 * -d undocumented debug option (like the awk-based version); 216 * print what would be created, fixed, or is already correct. 217 */ 218 int 219 main(int argc, char **argv) 220 { 221 int c; 222 int err = 0; 223 224 (void) setlocale(LC_ALL, ""); 225 #if !defined(TEXT_DOMAIN) 226 #define TEXT_DOMAIN "SYS_TEST" 227 #endif 228 (void) textdomain(TEXT_DOMAIN); 229 230 progname = argv[0]; /* save program name for error messages */ 231 232 while ((c = getopt(argc, argv, "r:e:d")) != EOF) { 233 switch (c) { 234 case 'r': 235 rootdir = optarg; 236 break; 237 case 'e': 238 exec_script(argv); 239 /* exec_script doesn't return */ 240 break; 241 case 'd': 242 debug = 1; 243 break; 244 case '?': 245 default: 246 err = 1; 247 break; 248 } 249 } 250 251 if (err || (optind != argc)) { 252 (void) fprintf(stderr, gettext("usage: %s [ -r rootdir ] " 253 "[ -e rulebase ]\n"), progname); 254 exit(1); 255 } 256 257 get_major_nums(); 258 259 set_depth(); 260 261 get_devices(); 262 263 get_dev_links(); 264 265 call_device_rules(); 266 267 return (0); 268 } 269 270 /* 271 * A utility function so we don't have to check the return 272 * value of malloc for NULL all over the place. 273 */ 274 static void * 275 xmalloc(size_t size) 276 { 277 void *p; 278 279 p = malloc(size); 280 if (p != NULL) 281 return (p); 282 else { 283 (void) fprintf(stderr, gettext("%s: malloc failed, " 284 "out of memory\n"), progname); 285 exit(1); 286 #ifdef lint 287 return (NULL); 288 #endif 289 } 290 } 291 292 /* 293 * A utility function so we don't have to check the return 294 * value of strdup (which gets space from malloc) for NULL 295 * all over the place. 296 */ 297 static char * 298 xstrdup(const char *s1) 299 { 300 char *s2; 301 302 s2 = strdup(s1); 303 if (s2 != NULL) 304 return (s2); 305 else { 306 (void) fprintf(stderr, gettext("%s: malloc failed, " 307 "out of memory\n"), progname); 308 exit(1); 309 #ifdef lint 310 return (NULL); 311 #endif 312 } 313 } 314 315 /* 316 * A utility function that prepends the program name to 317 * perror output. 318 */ 319 static void 320 xperror(const char *errstr) 321 { 322 int len1, len2; 323 char *msg; 324 325 len1 = strlen(progname); 326 len2 = strlen(errstr); 327 328 msg = xmalloc(len1 + 2 + len2 + 1); 329 (void) sprintf(msg, "%s: %s", progname, errstr); 330 perror(msg); 331 free(msg); 332 } 333 334 /* 335 * The awk-based ucblinks allowed an alternate rule-base with 336 * the -e option. Obviously we don't do awk, so pass off all 337 * our command-line arguments to the awk-based version which 338 * was moved from /usr/ucb to /usr/ucblib. 339 */ 340 #define SCRIPT "/usr/ucblib/ucblinks.sh" 341 342 static void 343 exec_script(char **argv) 344 { 345 argv[0] = SCRIPT; 346 if (execv(SCRIPT, argv) == -1) 347 xperror(gettext("cannot execute " SCRIPT)); 348 exit(1); 349 } 350 351 /* 352 * Construct a name with the rootdir, if specified with -r, 353 * prepended. Don't free this because when rootdir isn't 354 * set we return what was passed in (this is called at most 355 * four times so it's no big deal to not free it). 356 */ 357 static char * 358 root_name(char *name) 359 { 360 int len1, len2; 361 char *buf; 362 363 if (rootdir == NULL) 364 return (name); 365 else { 366 len1 = strlen(rootdir); 367 len2 = strlen(name); 368 buf = xmalloc(len1 + len2 + 1); 369 370 (void) strcpy(buf, rootdir); 371 (void) strcpy(buf + len1, name); 372 return (buf); 373 } 374 } 375 376 /* 377 * Read /etc/name_to_major to find the major numbers associated 378 * with the names in the drvinfo array. We silently ignore 379 * what we don't understand. 380 */ 381 static void 382 get_major_nums(void) 383 { 384 FILE *fp; 385 char line[FILENAME_MAX*2 + 1]; /* use the same size as add_drv does */ 386 char *name, *maj, *end, *cp; 387 int majnum; 388 struct drvinfo *drp; 389 390 fp = fopen("/etc/name_to_major", "r"); 391 if (fp == NULL) { 392 (void) fprintf(stderr, gettext("%s: cannot open " 393 "/etc/name_to_major\n"), progname); 394 exit(1); 395 } 396 397 while (fgets(line, sizeof (line), fp) != NULL) { 398 /* cut off comments starting with '#' */ 399 if ((cp = strchr(line, '#')) != NULL) 400 *cp = '\0'; 401 /* ignore comment or blank lines */ 402 if (is_blank(line)) 403 continue; 404 name = strtok(line, " \t"); /* must not be NULL */ 405 if ((maj = strtok(NULL, "\n")) == NULL) 406 continue; 407 majnum = strtol(maj, &end, 10); 408 if (end == maj) 409 continue; 410 /* 411 * Compare against our list and set the major 412 * number it it's a name we care about. 413 */ 414 for (drp = drvs; drp->name != NULL; drp++) { 415 if (strcmp(name, drp->name) == 0) { 416 drp->major = majnum; 417 break; 418 } 419 } 420 } 421 422 (void) fclose(fp); 423 } 424 425 /* 426 * Pick some reasonable number of file descriptors to let nftw() 427 * have open at a time. The number shouldn't be more than the 428 * currently available file descriptors but should be at least equal 429 * to the depth of the trees we're traversing for efficiency. Of 430 * course we don't know how deep the trees are before hand, so 431 * just use half the allowable descriptors which usually comes 432 * out to 32 which is usually more than enough. Using a depth 433 * smaller than the tree depth doesn't prevent traversal of the tree, 434 * it just makes it slower. sysconf() can't fail, but if it does 435 * use a depth of 1. 436 */ 437 static void 438 set_depth(void) 439 { 440 long num; 441 442 num = sysconf(_SC_OPEN_MAX); 443 if (num == -1) 444 depth = 1; 445 else 446 depth = num / 2; 447 } 448 449 /* 450 * Given a major number look it up in our list to see if it 451 * is associated with a device that needs a compatibility 452 * link. We cache the last lookup to avoid going through 453 * the list each time. 454 */ 455 static struct drvinfo * 456 interesting_major(int major) 457 { 458 struct drvinfo *drp; 459 static int last_major = -1; 460 static struct drvinfo *last_result = NULL; 461 462 if (major == last_major) 463 return (last_result); 464 last_major = major; 465 466 for (drp = drvs; drp->name != NULL; drp++) { 467 if (major == drp->major) { 468 last_result = drp; 469 return (drp); 470 } 471 } 472 473 last_result = NULL; 474 return (NULL); 475 } 476 477 /* 478 * Find the minor component of the device name which is what 479 * comes between the ':' and ',' in the last component of 480 * the pathname; make a copy and return a pointer to it. 481 */ 482 static char * 483 find_min_comp(char *name) 484 { 485 char *cp1, *cp2; 486 int len; 487 char *buf; 488 489 cp1 = strrchr(name, '/'); 490 if (cp1 == NULL) 491 cp1 = name; 492 else 493 cp1++; /* skip '/' */ 494 cp1 = strchr(cp1, ':'); 495 if (cp1 == NULL) 496 return (""); 497 498 cp1++; /* skip ':' */ 499 cp2 = cp1; 500 while (*cp2 != ',' && *cp2 != '\0') 501 cp2++; 502 len = cp2 - cp1; 503 if (len == 0) 504 return (""); 505 506 buf = xmalloc(len + 1); 507 (void) strncpy(buf, cp1, len); 508 buf[len] = '\0'; 509 return (buf); 510 } 511 512 /* 513 * To get an index into the hash table we compute a checksum 514 * of the string and mod HASHSIZE. The checksum came from 515 * awk, although we do a shift and subtract to implement 516 * multiplication by 31. We return the index as well as 517 * the whole checksum. The checksum is useful for efficient 518 * symbol lookup because the hash table will contain long strings 519 * that can be the same for the first 70 characters or more, so 520 * we compare checksums first before using strcmp(). 521 */ 522 static int 523 hash_sym(char *name, int *csp) 524 { 525 char c; 526 unsigned int csum = 0; 527 528 while ((c = *name++) != '\0') 529 csum = ((csum << 5) - csum) + c; 530 531 *csp = csum; 532 return (csum % HASHSIZE); 533 } 534 535 /* 536 * Insert a /devices entry symbol into the devices entry 537 * hash table. 538 */ 539 static void 540 insert_devices_sym(struct devices_ent *dep) 541 { 542 int hash; 543 struct devices_ent **pp; 544 545 hash = hash_sym(dep->devicename, &dep->csum); 546 pp = &de_hashtab[hash]; 547 dep->next = *pp; 548 *pp = dep; 549 } 550 551 /* 552 * Lookup a symbol in the devices entry hash table. Use 553 * the checksum to reduce the number of strcmp() calls. 554 */ 555 static struct devices_ent * 556 lookup_devices_sym(char *devicename) 557 { 558 int hash; 559 struct devices_ent *dep; 560 int csum; 561 562 hash = hash_sym(devicename, &csum); 563 dep = de_hashtab[hash]; 564 while (dep != NULL) { 565 if (csum == dep->csum) { 566 if (strcmp(devicename, dep->devicename) == 0) 567 return (dep); 568 } 569 dep = dep->next; 570 } 571 572 return (NULL); 573 } 574 575 /* 576 * This routine is called from nftw() for each /devices entry. 577 * If it isn't a device special file or doesn't have the major 578 * number of something we care about, don't do anything. 579 * Otherwise, allocate a structure for it and put it in the 580 * hash table. 581 */ 582 /* ARGSUSED2 */ 583 static int 584 devices_entry(const char *name, const struct stat *sp, 585 int flags, struct FTW *ftwp) 586 { 587 int type; 588 struct drvinfo *drp; 589 struct devices_ent *dep; 590 591 if (flags == FTW_NS) { /* couldn't stat the file */ 592 (void) fprintf(stderr, gettext("%s: cannot stat %s\n"), 593 progname, name); 594 return (0); 595 } 596 if (flags == FTW_DNR) { /* couldn't read a directory */ 597 (void) fprintf(stderr, gettext("%s: cannot read " 598 "directory %s\n"), progname, name); 599 return (0); 600 } 601 602 type = sp->st_mode & S_IFMT; 603 if (!(type == S_IFCHR || type == S_IFBLK)) 604 return (0); 605 606 drp = interesting_major(major(sp->st_rdev)); 607 if (drp == NULL) 608 return (0); 609 610 name += 2; /* skip "./" */ 611 dep = xmalloc(sizeof (struct devices_ent)); 612 dep->devicename = xstrdup(name); 613 dep->min_comp = find_min_comp(dep->devicename); 614 dep->minor = minor(sp->st_rdev); 615 dep->israw = (type == S_IFCHR); 616 dep->iscd = 0; 617 dep->issd = 0; 618 dep->linksto = NULL; 619 dep->drp = drp; 620 621 insert_devices_sym(dep); 622 num_devices_ents++; 623 624 return (0); 625 } 626 627 /* 628 * dcomp is the sort function called from qsort(). When comparing 629 * two device entries we sort by alphabetical order of the device's 630 * driver name, then minor number, then block vs. character, then 631 * the name of the device entry itself. 632 */ 633 static int 634 dcomp(const void *p1, const void *p2) 635 { 636 struct devices_ent *dep1 = *((struct devices_ent **)p1); 637 struct devices_ent *dep2 = *((struct devices_ent **)p2); 638 639 if (dep1->drp->index == dep2->drp->index) { 640 if (dep1->minor == dep2->minor) { 641 if (dep1->israw == dep2->israw) { 642 return (strcoll(dep1->devicename, 643 dep2->devicename)); 644 } else { 645 return (dep1->israw - dep2->israw); 646 } 647 } else { 648 return (dep1->minor - dep2->minor); 649 } 650 } else { 651 return (dep1->drp->index - dep2->drp->index); 652 } 653 } 654 655 /* 656 * Go to the /devices directory and recursively find all 657 * the device special files (with the handy library function 658 * nftw). nftw() will call devices_entry() which will put 659 * the entry in the hash table. After we find all the 660 * entries allocate a table and put pointers in it so 661 * we can sort the entries. 662 */ 663 static void 664 get_devices(void) 665 { 666 char *dir; 667 int i; 668 struct devices_ent *dep, **pht, **pdep; 669 670 dir = root_name("/devices"); 671 if (chdir(dir) == -1) { 672 xperror(dir); 673 exit(1); 674 } 675 676 /* 677 * Errors related to access permissions are handled 678 * by devices_entry() and devices_entry doesn't return 679 * non-zero so the only thing left is some other type 680 * of error. 681 */ 682 if (nftw(".", devices_entry, depth, FTW_PHYS) == -1) 683 xperror("nftw()"); 684 685 devices_list = xmalloc(sizeof (struct devices_ent *) * 686 num_devices_ents); 687 688 pdep = devices_list; 689 pht = de_hashtab; 690 for (i = 0; i < HASHSIZE; i++) { 691 dep = *pht; 692 while (dep != NULL) { 693 *pdep++ = dep; 694 dep = dep->next; 695 } 696 pht++; 697 } 698 699 /* 700 * After all the /devices entries are put in the hash 701 * table we sort the entries. We do this for two 702 * reasons: the rule functions may count on the order of 703 * devices it is called with (like the cdrom stuff in 704 * rule_sd) and if the rules create overlapping names the 705 * links will be made in an order based on sorted entries 706 * rather than be dependent on the order the entries 707 * happen to be in in a directory. 708 */ 709 qsort((void *) devices_list, num_devices_ents, 710 sizeof (struct devices_ent *), dcomp); 711 } 712 713 /* 714 * Like insert_devices_sym, but for link names and symlink 715 * structures. 716 */ 717 static void 718 insert_link_sym(struct symlink *slp) 719 { 720 int hash; 721 struct symlink **pp; 722 723 hash = hash_sym(slp->linkname, &slp->csum); 724 pp = &link_hashtab[hash]; 725 slp->hashnext = *pp; 726 *pp = slp; 727 } 728 729 /* 730 * Like lookup_devices_sym, but for link names and symlink 731 * structures. 732 */ 733 static struct symlink * 734 lookup_link_sym(char *linkname) 735 { 736 int hash; 737 struct symlink *slp; 738 int csum; 739 740 hash = hash_sym(linkname, &csum); 741 slp = link_hashtab[hash]; 742 while (slp != NULL) { 743 if (csum == slp->csum) { 744 if (strcmp(linkname, slp->linkname) == 0) 745 return (slp); 746 } 747 slp = slp->hashnext; 748 } 749 750 return (NULL); 751 } 752 753 /* 754 * Check this symlink to see if it points to an interesting 755 * /devices entry and hang it off the entry if it does. 756 */ 757 static void 758 check_link(struct symlink *slp) 759 { 760 int dirs; 761 char *cp; 762 int len; 763 char *devices = "../devices/"; 764 char *buf; 765 int i, off; 766 struct devices_ent *dep; 767 768 if (*slp->target != '.') 769 return; 770 771 /* 772 * Figure out how many directories deep the entry is 773 * so we can see if its link has the right number of 774 * ".."s to point to the /devices directory. 775 */ 776 dirs = 0; 777 cp = strchr(slp->linkname, '/'); 778 while (cp != NULL) { 779 dirs++; 780 cp = strchr(cp + 1, '/'); 781 } 782 len = strlen(devices); 783 buf = xmalloc(dirs * 3 + len + 1); 784 for (i = 0, off = 0; i < dirs; i++, off += 3) 785 (void) strcpy(buf + off, "../"); 786 (void) strcpy(buf + off, devices); 787 off += len; 788 789 /* 790 * The correct prefix of the path has been built up 791 * in "buf", compare it to the link and return 792 * if it doesn't match. 793 */ 794 if (strncmp(slp->target, buf, strlen(buf)) != 0) { 795 free(buf); 796 return; 797 } 798 free(buf); 799 800 /* 801 * Look up the /devices path (minus the prefix) and 802 * return if not found. 803 */ 804 dep = lookup_devices_sym(slp->target + off); 805 if (dep == NULL) 806 return; 807 808 /* hang it off the /devices entry */ 809 slp->deventnext = dep->linksto; 810 dep->linksto = slp; 811 812 } 813 814 /* 815 * This routine is called from nftw() for each /dev entry. 816 * We record all of the symlinks, not just those that point to 817 * interesting /devices entries, because when we have determined 818 * what a compatibility link should point to we want to know 819 * if it already points to the correct target and it is much 820 * cheaper to look it up in out list than to make a system call. 821 */ 822 /* ARGSUSED2 */ 823 static int 824 dev_entry(const char *name, const struct stat *sp, 825 int flags, struct FTW *ftwp) 826 { 827 int type; 828 char target[MAXPATHLEN + 1]; 829 int targetlen; 830 struct symlink *slp; 831 832 if (flags == FTW_NS) { /* couldn't stat the file */ 833 (void) fprintf(stderr, gettext("%s: cannot stat %s\n"), 834 progname, name); 835 return (0); 836 } 837 if (flags == FTW_DNR) { /* couldn't read a directory */ 838 (void) fprintf(stderr, gettext("%s: cannot read " 839 "directory %s\n"), progname, name); 840 return (0); 841 } 842 843 type = sp->st_mode & S_IFMT; 844 if (type != S_IFLNK) 845 return (0); 846 847 name += 2; /* skip "./" */ 848 targetlen = readlink(name, target, sizeof (target)); 849 if (targetlen == -1) 850 xperror(name); 851 852 target[targetlen] = '\0'; 853 854 slp = xmalloc(sizeof (struct symlink)); 855 slp->linkname = xstrdup(name); 856 slp->target = xstrdup(target); 857 slp->already = 0; 858 insert_link_sym(slp); 859 check_link(slp); 860 861 return (0); 862 } 863 864 /* 865 * Go to the /dev directory and recursively find all the 866 * symlinks. nftw() will call dev_entry() which will put 867 * the entry in the hash table. 868 */ 869 static void 870 get_dev_links(void) 871 { 872 char *devdir; 873 874 devdir = root_name("/dev"); 875 if (chdir(devdir) == -1) { 876 xperror(devdir); 877 exit(1); 878 } 879 880 /* 881 * Errors related to access permissions are handled 882 * by dev_entry() and dev_entry doesn't return non-zero 883 * so the only thing left is some other type of error. 884 */ 885 if (nftw(".", dev_entry, depth, FTW_PHYS) == -1) 886 xperror("nftw()"); 887 } 888 889 /* 890 * Spin through our sorted list of /devices entries and call 891 * the rule function for each. 892 */ 893 static void 894 call_device_rules(void) 895 { 896 struct devices_ent **pdep; 897 struct devices_ent *dep; 898 int i; 899 char *root_etc; 900 901 root_etc = root_name("/etc"); 902 link_handle = di_devlink_open(root_etc, 0); 903 904 pdep = devices_list; 905 for (i = 0; i < num_devices_ents; i++) { 906 dep = *pdep++; 907 dep->drp->rule_func(dep); 908 } 909 910 di_devlink_close(&link_handle, 0); 911 } 912 913 static void 914 update_db(char *compat_link, char *target, int link_type) 915 { 916 if (debug) { 917 (void) printf("adding %s link to database: %s -> %s\n", 918 link_type == DI_PRIMARY_LINK ? "primary" : "secondary", 919 compat_link, target); 920 } else { 921 (void) di_devlink_add_link(link_handle, compat_link, target, 922 link_type); 923 } 924 } 925 926 /* 927 * Create a symlink called compat_link that points to target. 928 * If it already exists correctly don't do anything. If it 929 * exists but is incorrect, delete the link and make it. If 930 * it doesn't exist just make it. 931 */ 932 static void 933 make_link( 934 char *compat_link, 935 char *target, 936 struct symlink *compat_slp, 937 int link_type) 938 { 939 if (compat_slp->target != NULL) { 940 if (strcmp(target, compat_slp->target) == 0) { 941 if (debug) 942 (void) printf("already %s -> %s\n", 943 compat_link, compat_slp->target); 944 update_db(compat_link, target, link_type); 945 return; 946 } else { 947 if (debug) 948 (void) printf("remove %s, link wrong (%s)\n", 949 compat_link, compat_slp->target); 950 else { 951 if (unlink(compat_link) == -1) 952 xperror(compat_link); 953 else 954 (void) di_devlink_rm_link(link_handle, 955 compat_link); 956 } 957 compat_slp->target = target; 958 } 959 } else 960 compat_slp->target = target; 961 962 if (debug) 963 (void) printf("link %s -> %s\n", compat_link, target); 964 else { 965 if (symlink(target, compat_link) == -1) 966 xperror(compat_link); 967 else 968 update_db(compat_link, target, link_type); 969 } 970 } 971 972 /* 973 * addlink is called from the rule functions when they want a 974 * compatibility link made. At this point we only know the 975 * link name, the /devices entry, and the prefix of a 5.x /dev 976 * name (that points to the /devices entry) that the rule would 977 * prefer the compatibility link point to. If a symlink already 978 * exists with the required prefix that points to the /devices 979 * entry, make the compatibility link point to that link. 980 * If a link with the required prefix doesn't exist, make the 981 * link point directly to the /devices entry. The idea is that 982 * someone looking at a compatibility link will be reminded of 983 * the "real" 5.x /dev name. For example, ls -l will show sd0a -> 984 * dsk/c0t3d0s0. 985 * 986 * If the symlink we're creating isn't already in the hash 987 * table we add it for possible future use by make_link. 988 * If multiple /devices entries exist with the same major 989 * and minor numbers this prevents problems with trying to 990 * make the link twice. Generally, though, there shouldn't 991 * be multiple /devices entries of the same type, major, and 992 * minor, except for tape devices. The tape rules pass 1 for 993 * the unique argument (all others pass 0) so we keep track 994 * and only create the first link for a particular compatibility 995 * name. 996 */ 997 static void 998 addlink(char *compat_link, char *prefix, struct devices_ent *dep, int unique) 999 { 1000 int len, link_type = 0; 1001 struct symlink *devent_slp; 1002 struct symlink *compat_slp; 1003 char *target = NULL; 1004 char linkbuf[MAXPATHLEN + 1]; 1005 1006 compat_slp = lookup_link_sym(compat_link); 1007 if (compat_slp == NULL) { 1008 compat_slp = xmalloc(sizeof (struct symlink)); 1009 compat_slp->linkname = xstrdup(compat_link); 1010 compat_slp->target = NULL; 1011 compat_slp->already = 0; 1012 insert_link_sym(compat_slp); 1013 } 1014 1015 if (unique) { 1016 if (compat_slp->already) 1017 return; 1018 else 1019 compat_slp->already = 1; 1020 } 1021 1022 /* 1023 * Look for a name with the correct prefix. 1024 */ 1025 len = strlen(prefix); 1026 devent_slp = dep->linksto; 1027 while (devent_slp != NULL) { 1028 if (strncmp(prefix, devent_slp->linkname, len) == 0) { 1029 target = devent_slp->linkname; 1030 link_type = DI_SECONDARY_LINK; 1031 break; 1032 } 1033 devent_slp = devent_slp->deventnext; 1034 } 1035 1036 /* 1037 * If we didn't find one with the prefix, point directly 1038 * to the /devices entry. 1039 */ 1040 if (target == NULL) { 1041 link_type = DI_PRIMARY_LINK; 1042 (void) sprintf(linkbuf, "../devices/%s", dep->devicename); 1043 target = xstrdup(linkbuf); 1044 } 1045 make_link(compat_link, target, compat_slp, link_type); 1046 } 1047 1048 /* 1049 * This is like addlink(), but it doesn't try to find a 5.x 1050 * link to point to. It is used by the rule functions to add 1051 * additional links to links already created with addlink(). 1052 */ 1053 static void 1054 addlink_nolookup(char *compat_link, char *target, int unique) 1055 { 1056 struct symlink *slp; 1057 char *oldtarg; 1058 1059 slp = lookup_link_sym(compat_link); 1060 if (slp == NULL) { 1061 slp = xmalloc(sizeof (struct symlink)); 1062 slp->linkname = xstrdup(compat_link); 1063 slp->target = NULL; 1064 slp->already = 0; 1065 insert_link_sym(slp); 1066 } 1067 oldtarg = slp->target; 1068 1069 if (unique) { 1070 if (slp->already) 1071 return; 1072 else 1073 slp->already = 1; 1074 } 1075 1076 make_link(compat_link, target, slp, DI_SECONDARY_LINK); 1077 1078 /* 1079 * If it didn't exist or pointed to the wrong 1080 * thing we need to duplicate the target and 1081 * note that the symlink now points to it. 1082 */ 1083 if (slp->target != oldtarg) 1084 slp->target = xstrdup(target); 1085 } 1086 1087 /* 1088 * The rest of this file is rule functions and support routines 1089 * for the rules. Each rule is passed a pointer to a devices_ent 1090 * struct which should be all it needs to determine what 1091 * compatibility link is needed. The rule functions use 1092 * addlink() and addlink_nolookup() to have the links 1093 * made. 1094 */ 1095 1096 /* 1097 * Rule for Archive tapes. "ar" isn't in name_to_major but 1098 * the awk-based version had a rule so it is here too. The 1099 * rule works the same as the awk rule, but who knows if it 1100 * is correct. 1101 */ 1102 static void 1103 rule_ar(struct devices_ent *dep) 1104 { 1105 char *min_comp = dep->min_comp; 1106 1107 if (*min_comp != '\0') { 1108 if (min_comp[strlen(min_comp) - 1] == 'n') 1109 (void) sprintf(namebuf, "%s%d", "nrar", 1110 (dep->minor - 16) / 4); 1111 else 1112 (void) sprintf(namebuf, "%s%d", "rar", dep->minor / 4); 1113 1114 addlink(namebuf, "rmt/", dep, 1); 1115 } 1116 } 1117 1118 /* 1119 * Rule for frame buffers. 1120 */ 1121 static void 1122 rule_fbs(struct devices_ent *dep) 1123 { 1124 addlink(dep->min_comp, "fbs/", dep, 0); 1125 } 1126 1127 /* 1128 * Rule for floppy drivers. 1129 */ 1130 static void 1131 rule_fd(struct devices_ent *dep) 1132 { 1133 int c_slice; 1134 int minor = dep->minor; 1135 char *link_pfx; 1136 char *targ_pfx; 1137 1138 c_slice = (strcmp(dep->min_comp, "c") == 0); 1139 1140 if (dep->israw) { 1141 link_pfx = "r"; 1142 targ_pfx = "rdiskette"; 1143 } else { 1144 link_pfx = ""; 1145 targ_pfx = "diskette"; 1146 } 1147 1148 (void) sprintf(namebuf, "%sfd%d%s", link_pfx, minor / 8, 1149 dep->min_comp); 1150 addlink(namebuf, targ_pfx, dep, 0); 1151 if (c_slice) { 1152 (void) sprintf(namebuf2, "%sfd%d", link_pfx, minor / 8); 1153 addlink_nolookup(namebuf2, namebuf, 0); 1154 } 1155 } 1156 1157 /* 1158 * Rule for IPI disks. This is hopelessly broken (see bug 1157501) 1159 * but someone may have come to depend on the broken names. 1160 */ 1161 static void 1162 rule_id(struct devices_ent *dep) 1163 { 1164 char *targ_pfx; 1165 char *link_pfx; 1166 1167 if (dep->israw) { 1168 link_pfx = "r"; 1169 targ_pfx = "rdsk/"; 1170 } else { 1171 link_pfx = ""; 1172 targ_pfx = "dsk/"; 1173 } 1174 1175 (void) sprintf(namebuf, "%sid%x%s", link_pfx, dep->minor, 1176 dep->min_comp); 1177 addlink(namebuf, targ_pfx, dep, 0); 1178 } 1179 1180 /* 1181 * Rule for obsolete mt devices. It works like the awk rule, 1182 * but unknown if correct. 1183 */ 1184 static void 1185 rule_mt(struct devices_ent *dep) 1186 { 1187 int minor = dep->minor; 1188 1189 if ((minor % 8) >= 4) { 1190 (void) sprintf(namebuf, "rmt%d", minor); 1191 addlink(namebuf, "rmt/", dep, 1); 1192 (void)