Home | History | Annotate | Download | only in ucblinks
      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)