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 2007 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 #include "libdevinfo.h"
29 #include "devinfo_devlink.h"
30 #include "device_info.h"
31 
32 #undef	DEBUG
33 #ifndef	DEBUG
34 #define	NDEBUG 1
35 #else
36 #undef	NDEBUG
37 #endif
38 
39 #include <assert.h>
40 
41 static mutex_t update_mutex = DEFAULTMUTEX; /* Protects update record lock */
42 
43 static const size_t elem_sizes[DB_TYPES] = {
44 	sizeof (struct db_node),
45 	sizeof (struct db_minor),
46 	sizeof (struct db_link),
47 	sizeof (char)
48 };
49 
50 /*
51  * List of directories/files skipped while physically walking /dev
52  * Paths are relative to "<root>/dev/"
53  */
54 static const char *skip_dirs[] = {"fd"};
55 static const char *skip_files[] = {
56 	"stdout",
57 	"stdin",
58 	"stderr"
59 };
60 
61 #define	N_SKIP_DIRS	(sizeof (skip_dirs) / sizeof (skip_dirs[0]))
62 #define	N_SKIP_FILES	(sizeof (skip_files) / sizeof (skip_files[0]))
63 
64 /*
65  *
66  * This file contains two sets of interfaces which operate on the reverse
67  * links database. One set (which includes di_devlink_open()/_close())
68  * allows link generators like devfsadm(1M) and ucblinks(1B) (writers) to
69  * populate the database with /devices -> /dev mappings. Another set
70  * of interfaces (which includes di_devlink_init()/_fini()) allows
71  * applications (readers) to lookup the database for /dev links corresponding
72  * to a given minor.
73  *
74  * Writers operate on a cached version of the database. The cache is created
75  * when di_devlink_open() is called. As links in /dev are created and removed,
76  * the cache is updated to keep it in synch with /dev. When the /dev updates
77  * are complete, the link generator calls di_devlink_close() which writes
78  * out the cache to the database.
79  *
80  * Applications which need to lookup the database, call di_devlink_init().
81  * di_devlink_init() checks the database file (if one exists). If the
82  * database is valid, it is mapped into the address space of the
83  * application. The database file consists of several segments. Each
84  * segment can be mapped in independently and is mapped on demand.
85  *
86  *		   Database Layout
87  *
88  *		---------------------
89  *		|	Magic #     |
90  *		| ----------------- |
91  *		|       Version	    |	HEADER
92  *		| ----------------- |
93  *		|        ...        |
94  *		---------------------
95  *		|		    |
96  *		|		    |	NODES
97  *		|	            |
98  *		|		    |
99  *		---------------------
100  *		|		    |
101  *		|		    |	MINORS
102  *		|	            |
103  *		|		    |
104  *		---------------------
105  *		|		    |
106  *		|		    |   LINKS
107  *		|	            |
108  *		|		    |
109  *		---------------------
110  *		|		    |
111  *		|		    |	STRINGS
112  *		|	            |
113  *		|		    |
114  *		---------------------
115  *
116  * Readers can lookup /dev links for a specific minor or
117  * lookup all /dev links. In the latter case, the node
118  * and minor segments are not mapped in and the reader
119  * walks through every link in the link segment.
120  *
121  */
122 di_devlink_handle_t
123 di_devlink_open(const char *root_dir, uint_t flags)
124 {
125 	int err;
126 	char path[PATH_MAX];
127 	struct di_devlink_handle *hdp;
128 	int retried = 0;
129 
130 retry:
131 	/*
132 	 * Allocate a read-write handle but open the DB in readonly
133 	 * mode. We do writes only to a temporary copy of the database.
134 	 */
135 	if ((hdp = handle_alloc(root_dir, OPEN_RDWR)) == NULL) {
136 		return (NULL);
137 	}
138 
139 	err = open_db(hdp, OPEN_RDONLY);
140 
141 	/*
142 	 * We don't want to unlink the db at this point - if we did we
143 	 * would be creating a window where consumers would take a slow
144 	 * code path (and those consumers might also trigger requests for
145 	 * db creation, which we are already in the process of doing).
146 	 * When we are done with our update, we use rename to install the
147 	 * latest version of the db file.
148 	 */
149 	get_db_path(hdp, DB_FILE, path, sizeof (path));
150 
151 	/*
152 	 * The flags argument is reserved for future use.
153 	 */
154 	if (flags != 0) {
155 		handle_free(&hdp); /* also closes the DB */
156 		errno = EINVAL;
157 		return (NULL);
158 	}
159 
160 	if (cache_alloc(hdp) != 0) {
161 		handle_free(&hdp);
162 		return (NULL);
163 	}
164 
165 	if (err) {
166 		/*
167 		 * Failed to open DB.
168 		 * The most likely cause is that DB file did not exist.
169 		 * Call di_devlink_close() to recreate the DB file and
170 		 * retry di_devlink_open().
171 		 */
172 		if (retried == 0) {
173 			(void) di_devlink_close(&hdp, 0);
174 			retried = 1;
175 			goto retry;
176 		}
177 
178 		/*
179 		 * DB cannot be opened, just return the
180 		 * handle. We will recreate the DB later.
181 		 */
182 		return (hdp);
183 	}
184 
185 	/* Read the database into the cache */
186 	CACHE(hdp)->update_count = DB_HDR(hdp)->update_count;
187 	(void) read_nodes(hdp, NULL, DB_HDR(hdp)->root_idx);
188 	(void) read_links(hdp, NULL, DB_HDR(hdp)->dngl_idx);
189 
190 	(void) close_db(hdp);
191 
192 	return (hdp);
193 }
194 
195 static void
196 get_db_path(
197 	struct di_devlink_handle *hdp,
198 	const char *fname,
199 	char *buf,
200 	size_t blen)
201 {
202 	char *dir = NULL;
203 
204 #ifdef	DEBUG
205 	if (dir = getenv(ALT_DB_DIR)) {
206 		(void) dprintf(DBG_INFO, "get_db_path: alternate db dir: %s\n",
207 		    dir);
208 	}
209 #endif
210 	if (dir == NULL) {
211 		dir = hdp->db_dir;
212 	}
213 
214 	(void) snprintf(buf, blen, "%s/%s", dir, fname);
215 }
216 
217 static int
218 open_db(struct di_devlink_handle *hdp, int flags)
219 {
220 	size_t sz;
221 	long page_sz;
222 	int fd, rv, flg;
223 	struct stat sbuf;
224 	uint32_t count[DB_TYPES] = {0};
225 	char path[PATH_MAX];
226 	void *cp;
227 
228 	assert(!DB_OPEN(hdp));
229 
230 #ifdef	DEBUG
231 	if (getenv(SKIP_DB)) {
232 		(void) dprintf(DBG_INFO, "open_db: skipping database\n");
233 		return (-1);
234 	}
235 #endif
236 	if ((page_sz = sysconf(_SC_PAGE_SIZE)) == -1) {
237 		return (-1);
238 	}
239 
240 	/*
241 	 * Use O_TRUNC flag for write access, so that the subsequent ftruncate()
242 	 * call will zero-fill the entire file
243 	 */
244 	if (IS_RDONLY(flags)) {
245 		flg = O_RDONLY;
246 		get_db_path(hdp, DB_FILE, path, sizeof (path));
247 	} else {
248 		flg = O_RDWR|O_CREAT|O_TRUNC;
249 		get_db_path(hdp, DB_TMP, path, sizeof (path));
250 	}
251 
252 	/*
253 	 * Avoid triggering /dev reconfigure for read when not present
254 	 */
255 	if (IS_RDONLY(flags) &&
256 	    (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) {
257 		return (-1);
258 	}
259 
260 	if ((fd = open(path, flg, DB_PERMS)) == -1) {
261 		return (-1);
262 	}
263 
264 	if (IS_RDONLY(flags)) {
265 		flg = PROT_READ;
266 		rv = fstat(fd, &sbuf);
267 		sz = sbuf.st_size;
268 	} else {
269 		flg = PROT_READ | PROT_WRITE;
270 		sz = size_db(hdp, page_sz, count);
271 		rv = ftruncate(fd, sz);
272 	}
273 
274 	if (rv == -1 || sz < HDR_LEN) {
275 		if (rv != -1)
276 			errno = EINVAL;
277 		(void) close(fd);
278 		return (-1);
279 	}
280 
281 	cp = mmap(0, HDR_LEN, flg, MAP_SHARED, fd, 0);
282 	if (cp == MAP_FAILED) {
283 		(void) close(fd);
284 		return (-1);
285 	}
286 	DB(hdp)->hdr = (struct db_hdr *)cp;
287 	DB(hdp)->db_fd = fd;
288 	DB(hdp)->flags = flags;
289 
290 	if (IS_RDONLY(flags)) {
291 		rv = invalid_db(hdp, sz, page_sz);
292 	} else {
293 		rv = init_hdr(hdp, page_sz, count);
294 	}
295 
296 	if (rv) {
297 		(void) dprintf(DBG_ERR, "open_db: invalid DB(%s)\n", path);
298 		(void) close_db(hdp);
299 		return (-1);
300 	} else {
301 		(void) dprintf(DBG_STEP, "open_db: DB(%s): opened\n", path);
302 		return (0);
303 	}
304 }
305 
306 /*
307  * A handle can be allocated for read-only or read-write access
308  */
309 static struct di_devlink_handle *
310 handle_alloc(const char *root_dir, uint_t flags)
311 {
312 	char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX];
313 	struct di_devlink_handle *hdp, proto = {0};
314 
315 	assert(flags == OPEN_RDWR || flags == OPEN_RDONLY);
316 
317 	dev_dir[0] = '\0';
318 	db_dir[0] = '\0';
319 
320 	/*
321 	 * NULL and the empty string are equivalent to "/"
322 	 */
323 	if (root_dir && root_dir[0] != '\0') {
324 
325 		if (root_dir[0] != '/') {
326 			errno = EINVAL;
327 			return (NULL);
328 		}
329 
330 #ifdef	DEBUG
331 		/*LINTED*/
332 		assert(sizeof (dev_dir) >= PATH_MAX);
333 #endif
334 		if ((realpath(root_dir, dev_dir) == NULL) ||
335 		    (realpath(root_dir, db_dir) == NULL)) {
336 			return (NULL);
337 		}
338 	}
339 
340 	if (strcmp(dev_dir, "/") == 0) {
341 		dev_dir[0] = 0;
342 		db_dir[0] = 0;
343 	} else {
344 		(void) strlcpy(db_dir, dev_dir, sizeof (db_dir));
345 	}
346 
347 	(void) strlcat(dev_dir, DEV, sizeof (dev_dir));
348 	(void) strlcat(db_dir, ETCDEV, sizeof (db_dir));
349 
350 	proto.dev_dir = dev_dir;
351 	proto.db_dir = db_dir;
352 	proto.flags = flags;
353 	proto.lock_fd = -1;
354 
355 	/*
356 	 * Lock database if a read-write handle is being allocated.
357 	 * Locks are needed to protect against multiple writers.
358 	 * Readers don't need locks.
359 	 */
360 	if (HDL_RDWR(&proto)) {
361 		if (enter_db_lock(&proto, root_dir) != 1) {
362 			return (NULL);
363 		}
364 	}
365 
366 	DB(&proto)->db_fd = -1;
367 
368 	hdp = calloc(1, sizeof (struct di_devlink_handle));
369 	if (hdp == NULL) {
370 		goto error;
371 	}
372 
373 	*hdp = proto;
374 
375 	/*
376 	 * The handle hdp now contains a pointer to local storage
377 	 * in the dev_dir field (obtained from the proto handle).
378 	 * In the following line, a dynamically allocated version
379 	 * is substituted.
380 	 */
381 
382 	if ((hdp->dev_dir = strdup(proto.dev_dir)) == NULL) {
383 		free(hdp);
384 		goto error;
385 	}
386 
387 	if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) {
388 		free(hdp->dev_dir);
389 		free(hdp);
390 		goto error;
391 	}
392 
393 	return (hdp);
394 
395 error:
396 	if (HDL_RDWR(&proto)) {
397 		/* Unlink DB file on error */
398 		get_db_path(&proto, DB_FILE, path, sizeof (path));
399 		(void) unlink(path);
400 		exit_db_lock(&proto);
401 	}
402 	return (NULL);
403 }
404 
405 
406 static int
407 cache_alloc(struct di_devlink_handle *hdp)
408 {
409 	size_t hash_sz = 0;
410 
411 	assert(HDL_RDWR(hdp));
412 
413 	if (DB_OPEN(hdp)) {
414 		hash_sz = DB_NUM(hdp, DB_LINK) / AVG_CHAIN_SIZE;
415 	}
416 	hash_sz = (hash_sz >= MIN_HASH_SIZE) ? hash_sz : MIN_HASH_SIZE;
417 
418 	CACHE(hdp)->hash = calloc(hash_sz, sizeof (cache_link_t *));
419 	if (CACHE(hdp)->hash == NULL) {
420 		return (-1);
421 	}
422 	CACHE(hdp)->hash_sz = hash_sz;
423 
424 	return (0);
425 }
426 
427 
428 static int
429 invalid_db(struct di_devlink_handle *hdp, size_t fsize, long page_sz)
430 {
431 	int i;
432 	char *cp;
433 	size_t sz;
434 
435 	if (DB_HDR(hdp)->magic != DB_MAGIC || DB_HDR(hdp)->vers != DB_VERSION) {
436 		return (1);
437 	}
438 
439 	if (DB_HDR(hdp)->page_sz == 0 || DB_HDR(hdp)->page_sz != page_sz) {
440 		return (1);
441 	}
442 
443 	sz = seg_size(hdp, DB_HEADER);
444 	for (i = 0; i < DB_TYPES; i++) {
445 		(void) dprintf(DBG_INFO, "N[%u] = %u\n", i, DB_NUM(hdp, i));
446 		/* There must be at least 1 element of each type */
447 		if (DB_NUM(hdp, i) < 1) {
448 			return (1);
449 		}
450 		sz += seg_size(hdp, i);
451 		assert(sz % page_sz == 0);
452 	}
453 
454 	if (sz != fsize) {
455 		return (1);
456 	}
457 
458 	if (!VALID_INDEX(hdp, DB_NODE, DB_HDR(hdp)->root_idx)) {
459 		return (1);
460 	}
461 
462 	if (!VALID_INDEX(hdp, DB_LINK, DB_HDR(hdp)->dngl_idx)) {
463 		return (1);
464 	}
465 
466 	if (DB_EMPTY(hdp)) {
467 		return (1);
468 	}
469 
470 	/*
471 	 * The last character in the string segment must be a NUL char.
472 	 */
473 	cp = get_string(hdp, DB_NUM(hdp, DB_STR) - 1);
474 	if (cp == NULL || *cp != '\0') {
475 		return (1);
476 	}
477 
478 	return (0);
479 }
480 
481 static int
482 read_nodes(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
483 {
484 	char *path;
485 	cache_node_t *cnp;
486 	struct db_node *dnp;
487 	const char *fcn = "read_nodes";
488 
489 	assert(HDL_RDWR(hdp));
490 
491 	/*
492 	 * parent node should be NULL only for the root node
493 	 */
494 	if ((pcnp == NULL) ^ (nidx == DB_HDR(hdp)->root_idx)) {
495 		(void) dprintf(DBG_ERR, "%s: invalid parent or index(%u)\n",
496 		    fcn, nidx);
497 		SET_DB_ERR(hdp);
498 		return (-1);
499 	}
500 
501 	for (; dnp = get_node(hdp, nidx); nidx = dnp->sib) {
502 
503 		path = get_string(hdp, dnp->path);
504 
505 		/*
506 		 * Insert at head of list to recreate original order
507 		 */
508 		cnp = node_insert(hdp, pcnp, path, INSERT_HEAD);
509 		if (cnp == NULL) {
510 			SET_DB_ERR(hdp);
511 			break;
512 		}
513 
514 		assert(strcmp(path, "/") ^ (nidx == DB_HDR(hdp)->root_idx));
515 		assert(strcmp(path, "/") != 0 || dnp->sib == DB_NIL);
516 
517 		if (read_minors(hdp, cnp, dnp->minor) != 0 ||
518 		    read_nodes(hdp, cnp, dnp->child) != 0) {
519 			break;
520 		}
521 
522 		(void) dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, nidx,
523 		    cnp->path);
524 	}
525 
526 	return (dnp ? -1 : 0);
527 }
528 
529 static int
530 read_minors(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
531 {
532 	cache_minor_t *cmnp;
533 	struct db_minor *dmp;
534 	char *name, *nodetype;
535 	const char *fcn = "read_minors";
536 
537 	assert(HDL_RDWR(hdp));
538 
539 	if (pcnp == NULL) {
540 		(void) dprintf(DBG_ERR, "%s: minor[%u]: orphan minor\n", fcn,
541 		    nidx);
542 		SET_DB_ERR(hdp);
543 		return (-1);
544 	}
545 
546 	for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) {
547 
548 		name = get_string(hdp, dmp->name);
549 		nodetype = get_string(hdp, dmp->nodetype);
550 
551 		cmnp = minor_insert(hdp, pcnp, name, nodetype, NULL);
552 		if (cmnp == NULL) {
553 			SET_DB_ERR(hdp);
554 			break;
555 		}
556 
557 		(void) dprintf(DBG_STEP, "%s: minor[%u]: %s\n", fcn, nidx,
558 		    cmnp->name);
559 
560 		if (read_links(hdp, cmnp, dmp->link) != 0) {
561 			break;
562 		}
563 	}
564 
565 	return (dmp ? -1 : 0);
566 }
567 
568 /*
569  * If the link is dangling the corresponding minor will be absent.
570  */
571 static int
572 read_links(struct di_devlink_handle *hdp, cache_minor_t *pcmp, uint32_t nidx)
573 {
574 	cache_link_t *clp;
575 	struct db_link *dlp;
576 	char *path, *content;
577 
578 	assert(HDL_RDWR(hdp));
579 
580 	if (nidx != DB_NIL &&
581 	    ((pcmp == NULL) ^ (nidx == DB_HDR(hdp)->dngl_idx))) {
582 		(void) dprintf(DBG_ERR, "read_links: invalid minor or"
583 		    " index(%u)\n", nidx);
584 		SET_DB_ERR(hdp);
585 		return (-1);
586 	}
587 
588 	for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) {
589 
590 		path = get_string(hdp, dlp->path);
591 		content = get_string(hdp, dlp->content);
592 
593 		clp = link_insert(hdp, pcmp, path, content, dlp->attr);
594 		if (clp == NULL) {
595 			SET_DB_ERR(hdp);
596 			break;
597 		}
598 
599 		(void) dprintf(DBG_STEP, "read_links: link[%u]: %s%s\n",
600 		    nidx, clp->path, pcmp == NULL ? "(DANGLING)" : "");
601 	}
602 
603 	return (dlp ? -1 : 0);
604 }
605 
606 int
607 di_devlink_close(di_devlink_handle_t *pp, int flag)
608 {
609 	int i, rv;
610 	char tmp[PATH_MAX];
611 	char file[PATH_MAX];
612 	uint32_t next[DB_TYPES] = {0};
613 	struct di_devlink_handle *hdp;
614 
615 	if (pp == NULL || *pp == NULL || !HDL_RDWR(*pp)) {
616 		errno = EINVAL;
617 		return (-1);
618 	}
619 
620 	hdp = *pp;
621 	*pp = NULL;
622 
623 	/*
624 	 * The caller encountered some error in their processing.
625 	 * so handle isn't valid. Discard it and return success.
626 	 */
627 	if (flag == DI_LINK_ERROR) {
628 		handle_free(&hdp);
629 		return (0);
630 	}
631 
632 	if (DB_ERR(hdp)) {
633 		handle_free(&hdp);
634 		errno = EINVAL;
635 		return (-1);
636 	}
637 
638 	/*
639 	 * Extract the DB path before the handle is freed.
640 	 */
641 	get_db_path(hdp, DB_FILE, file, sizeof (file));
642 	get_db_path(hdp, DB_TMP, tmp, sizeof (tmp));
643 
644 	/*
645 	 * update database with actual contents of /dev
646 	 */
647 	(void) dprintf(DBG_INFO, "di_devlink_close: update_count = %u\n",
648 	    CACHE(hdp)->update_count);
649 
650 	/*
651 	 * For performance reasons, synchronization of the database
652 	 * with /dev is turned off by default. However, applications
653 	 * with appropriate permissions can request a "sync" by
654 	 * calling di_devlink_update().
655 	 */
656 	if (CACHE(hdp)->update_count == 0) {
657 		CACHE(hdp)->update_count = 1;
658 		(void) dprintf(DBG_INFO,
659 		    "di_devlink_close: synchronizing DB\n");
660 		(void) synchronize_db(hdp);
661 	}
662 
663 	/*
664 	 * Resolve dangling links AFTER synchronizing DB with /dev as the
665 	 * synchronization process may create dangling links.
666 	 */
667 	resolve_dangling_links(hdp);
668 
669 	/*
670 	 * All changes to the cache are complete. Write out the cache
671 	 * to the database only if it is not empty.
672 	 */
673 	if (CACHE_EMPTY(hdp)) {
674 		(void) dprintf(DBG_INFO, "di_devlink_close: skipping write\n");
675 		(void) unlink(file);
676 		handle_free(&hdp);
677 		return (0);
678 	}
679 
680 	if (open_db(hdp, OPEN_RDWR) != 0) {
681 		handle_free(&hdp);
682 		return (-1);
683 	}
684 
685 	/*
686 	 * Keep track of array assignments. There is at least
687 	 * 1 element (the "NIL" element) per type.
688 	 */
689 	for (i = 0; i < DB_TYPES; i++) {
690 		next[i] = 1;
691 	}
692 
693 	(void) write_nodes(hdp, NULL, CACHE_ROOT(hdp), next);
694 	(void) write_links(hdp, NULL, CACHE(hdp)->dngl, next);
695 	DB_HDR(hdp)->update_count = CACHE(hdp)->update_count;
696 
697 	rv = close_db(hdp);
698 
699 	if (rv != 0 || DB_ERR(hdp) || rename(tmp, file) != 0) {
700 		(void) dprintf(DBG_ERR, "di_devlink_close: %s error: %s\n",
701 		    rv ? "close_db" : "DB or rename", strerror(errno));
702 		(void) unlink(tmp);
703 		(void) unlink(file);
704 		handle_free(&hdp);
705 		return (-1);
706 	}
707 
708 	handle_free(&hdp);
709 
710 	(void) dprintf(DBG_INFO, "di_devlink_close: wrote DB(%s)\n", file);
711 
712 	return (0);
713 }
714 
715 /*
716  * Inits the database header.
717  */
718 static int
719 init_hdr(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
720 {
721 	int i;
722 
723 	DB_HDR(hdp)->magic = DB_MAGIC;
724 	DB_HDR(hdp)->vers = DB_VERSION;
725 	DB_HDR(hdp)->root_idx = DB_NIL;
726 	DB_HDR(hdp)->dngl_idx = DB_NIL;
727 	DB_HDR(hdp)->page_sz = (uint32_t)page_sz;
728 
729 	for (i = 0; i < DB_TYPES; i++) {
730 		assert(count[i] >= 1);
731 		DB_NUM(hdp, i) = count[i];
732 	}
733 
734 	return (0);
735 }
736 
737 static int
738 write_nodes(
739 	struct di_devlink_handle *hdp,
740 	struct db_node *pdnp,
741 	cache_node_t *cnp,
742 	uint32_t *next)
743 {
744 	uint32_t idx;
745 	struct db_node *dnp;
746 	const char *fcn = "write_nodes";
747 
748 	assert(HDL_RDWR(hdp));
749 
750 	for (; cnp != NULL; cnp = cnp->sib) {
751 
752 		assert(cnp->path != NULL);
753 
754 		/* parent node should only be NULL for root node */
755 		if ((pdnp == NULL) ^ (cnp == CACHE_ROOT(hdp))) {
756 			(void) dprintf(DBG_ERR, "%s: invalid parent for: %s\n",
757 			    fcn, cnp->path);
758 			SET_DB_ERR(hdp);
759 			break;
760 		}
761 
762 		assert((strcmp(cnp->path, "/") != 0) ^
763 		    (cnp == CACHE_ROOT(hdp)));
764 
765 		idx = next[DB_NODE];
766 		if ((dnp = set_node(hdp, idx)) == NULL) {
767 			SET_DB_ERR(hdp);
768 			break;
769 		}
770 
771 		dnp->path = write_string(hdp, cnp->path, next);
772 		if (dnp->path == DB_NIL) {
773 			SET_DB_ERR(hdp);
774 			break;
775 		}
776 		/* commit write for this node */
777 		next[DB_NODE]++;
778 
779 		if (pdnp == NULL) {
780 			assert(DB_HDR(hdp)->root_idx == DB_NIL);
781 			DB_HDR(hdp)->root_idx = idx;
782 		} else {
783 			dnp->sib = pdnp->child;
784 			pdnp->child = idx;
785 		}
786 
787 		(void) dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, idx,
788 		    cnp->path);
789 
790 		if (write_minors(hdp, dnp, cnp->minor, next) != 0 ||
791 		    write_nodes(hdp, dnp, cnp->child, next) != 0) {
792 			break;
793 		}
794 	}
795 
796 	return (cnp ? -1 : 0);
797 }
798 
799 static int
800 write_minors(
801 	struct di_devlink_handle *hdp,
802 	struct db_node *pdnp,
803 	cache_minor_t *cmnp,
804 	uint32_t *next)
805 {
806 	uint32_t idx;
807 	struct db_minor *dmp;
808 	const char *fcn = "write_minors";
809 
810 	assert(HDL_RDWR(hdp));
811 
812 	if (pdnp == NULL) {
813 		(void) dprintf(DBG_ERR, "%s: no node for minor: %s\n", fcn,
814 		    cmnp ? cmnp->name : "<NULL>");
815 		SET_DB_ERR(hdp);
816 		return (-1);
817 	}
818 
819 	for (; cmnp != NULL; cmnp = cmnp->sib) {
820 
821 		assert(cmnp->name != NULL);
822 
823 		idx = next[DB_MINOR];
824 		if ((dmp = set_minor(hdp, idx)) == NULL) {
825 			SET_DB_ERR(hdp);
826 			break;
827 		}
828 
829 		dmp->name = write_string(hdp, cmnp->name, next);
830 		dmp->nodetype = write_string(hdp, cmnp->nodetype, next);
831 		if (dmp->name == DB_NIL || dmp->nodetype == DB_NIL) {
832 			dmp->name = dmp->nodetype = DB_NIL;
833 			SET_DB_ERR(hdp);
834 			break;
835 		}
836 
837 		/* Commit writes to this minor */
838 		next[DB_MINOR]++;
839 
840 		dmp->sib = pdnp->minor;
841 		pdnp->minor = idx;
842 
843 		(void) dprintf(DBG_STEP, "%s: minor[%u]: %s\n", fcn, idx,
844 		    cmnp->name);
845 
846 		if (write_links(hdp, dmp, cmnp->link, next) != 0) {
847 			break;
848 		}
849 	}
850 
851 	return (cmnp ? -1 : 0);
852 }
853 
854 static int
855 write_links(
856 	struct di_devlink_handle *hdp,
857 	struct db_minor *pdmp,
858 	cache_link_t *clp,
859 	uint32_t *next)
860 {
861 	uint32_t idx;
862 	struct db_link *dlp;
863 	const char *fcn = "write_links";
864 
865 	assert(HDL_RDWR(hdp));
866 
867 	/* A NULL minor if and only if the links are dangling */
868 	if (clp != NULL && ((pdmp == NULL) ^ (clp == CACHE(hdp)->dngl))) {
869 		(void) dprintf(DBG_ERR, "%s: invalid minor for link\n", fcn);
870 		SET_DB_ERR(hdp);
871 		return (-1);
872 	}
873 
874 	for (; clp != NULL; clp = clp->sib) {
875 
876 		assert(clp->path != NULL);
877 
878 		if ((pdmp == NULL) ^ (clp->minor == NULL)) {
879 			(void) dprintf(DBG_ERR, "%s: invalid minor for link"
880 			    "(%s)\n", fcn, clp->path);
881 			SET_DB_ERR(hdp);
882 			break;
883 		}
884 
885 		idx = next[DB_LINK];
886 		if ((dlp = set_link(hdp, idx)) == NULL) {
887 			SET_DB_ERR(hdp);
888 			break;
889 		}
890 
891 		dlp->path = write_string(hdp, clp->path, next);
892 		dlp->content = write_string(hdp, clp->content, next);
893 		if (dlp->path == DB_NIL || dlp->content == DB_NIL) {
894 			dlp->path = dlp->content = DB_NIL;
895 			SET_DB_ERR(hdp);
896 			break;
897 		}
898 
899 		dlp->attr = clp->attr;
900 
901 		/* Commit writes to this link */
902 		next[DB_LINK]++;
903 
904 		if (pdmp != NULL) {
905 			dlp->sib = pdmp->link;
906 			pdmp->link = idx;
907 		} else {
908 			dlp->sib = DB_HDR(hdp)->dngl_idx;
909 			DB_HDR(hdp)->dngl_idx = idx;
910 		}
911 
912 		(void) dprintf(DBG_STEP, "%s: link[%u]: %s%s\n", fcn, idx,
913 		    clp->path, pdmp == NULL ? "(DANGLING)" : "");
914 	}
915 
916 	return (clp ? -1 : 0);
917 }
918 
919 
920 static uint32_t
921 write_string(struct di_devlink_handle *hdp, const char *str, uint32_t *next)
922 {
923 	char *dstr;
924 	uint32_t idx;
925 
926 	assert(HDL_RDWR(hdp));
927 
928 	if (str == NULL) {
929 		(void) dprintf(DBG_ERR, "write_string: NULL argument\n");
930 		return (DB_NIL);
931 	}
932 
933 	idx = next[DB_STR];
934 	if (!VALID_STR(hdp, idx, str)) {
935 		(void) dprintf(DBG_ERR, "write_string: invalid index[%u],"
936 		    " string(%s)\n", idx, str);
937 		return (DB_NIL);
938 	}
939 
940 	if ((dstr = set_string(hdp, idx)) == NULL) {
941 		return (DB_NIL);
942 	}
943 
944 	(void) strcpy(dstr, str);
945 
946 	next[DB_STR] += strlen(dstr) + 1;
947 
948 	return (idx);
949 }
950 
951 static int
952 close_db(struct di_devlink_handle *hdp)
953 {
954 	int i, rv = 0;
955 	size_t sz;
956 
957 	if (!DB_OPEN(hdp)) {
958 #ifdef	DEBUG
959 		assert(DB(hdp)->db_fd == -1);
960 		assert(DB(hdp)->flags == 0);
961 		for (i = 0; i < DB_TYPES; i++) {
962 			assert(DB_SEG(hdp, i) == NULL);
963 			assert(DB_SEG_PROT(hdp, i) == 0);
964 		}
965 #endif
966 		return (0);
967 	}
968 
969 	/* Unmap header after unmapping all other mapped segments */
970 	for (i = 0; i < DB_TYPES; i++) {
971 		if (DB_SEG(hdp, i)) {
972 			sz = seg_size(hdp, i);
973 			if (DB_RDWR(hdp))
974 				rv += msync(DB_SEG(hdp, i), sz, MS_SYNC);
975 			(void) munmap(DB_SEG(hdp, i), sz);
976 			DB_SEG(hdp, i) = NULL;
977 			DB_SEG_PROT(hdp, i) = 0;
978 		}
979 	}
980 
981 	if (DB_RDWR(hdp))
982 		rv += msync((caddr_t)DB_HDR(hdp), HDR_LEN, MS_SYNC);
983 	(void) munmap((caddr_t)DB_HDR(hdp), HDR_LEN);
984 	DB(hdp)->hdr = NULL;
985 
986 	(void) close(DB(hdp)->db_fd);
987 	DB(hdp)->db_fd = -1;
988 	DB(hdp)->flags = 0;
989 
990 	return (rv ? -1 : 0);
991 }
992 
993 
994 static void
995 cache_free(struct di_devlink_handle *hdp)
996 {
997 	cache_link_t *clp;
998 
999 	subtree_free(hdp, &(CACHE_ROOT(hdp)));
1000 	assert(CACHE_LAST(hdp) == NULL);
1001 
1002 	/*
1003 	 * Don't bother removing links from hash table chains,
1004 	 * as we are freeing the hash table itself.
1005 	 */
1006 	while (CACHE(hdp)->dngl != NULL) {
1007 		clp = CACHE(hdp)->dngl;
1008 		CACHE(hdp)->dngl = clp->sib;
1009 		assert(clp->minor == NULL);
1010 		link_free(&clp);
1011 	}
1012 
1013 	assert((CACHE(hdp)->hash == NULL) ^ (CACHE(hdp)->hash_sz != 0));
1014 
1015 	free(CACHE(hdp)->hash);
1016 	CACHE(hdp)->hash = NULL;
1017 	CACHE(hdp)->hash_sz = 0;
1018 }
1019 
1020 static void
1021 handle_free(struct di_devlink_handle **pp)
1022 {
1023 	struct di_devlink_handle *hdp = *pp;
1024 
1025 	*pp = NULL;
1026 
1027 	if (hdp == NULL)
1028 		return;
1029 
1030 	(void) close_db(hdp);
1031 	cache_free(hdp);
1032 
1033 	if (HDL_RDWR(hdp))
1034 		exit_db_lock(hdp);
1035 	assert(hdp->lock_fd == -1);
1036 
1037 	free(hdp->dev_dir);
1038 	free(hdp->db_dir);
1039 	free(hdp);
1040 }
1041 
1042 /*
1043  * Frees the tree rooted at a node. Siblings of the subtree root
1044  * have to be handled by the caller.
1045  */
1046 static void
1047 subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp)
1048 {
1049 	cache_node_t *np;
1050 	cache_link_t *clp;
1051 	cache_minor_t *cmnp;
1052 
1053 	if (pp == NULL || *pp == NULL)
1054 		return;
1055 
1056 	while ((*pp)->child != NULL) {
1057 		np = (*pp)->child;
1058 		(*pp)->child = np->sib;
1059 		subtree_free(hdp, &np);
1060 	}
1061 
1062 	while ((*pp)->minor != NULL) {
1063 		cmnp = (*pp)->minor;
1064 		(*pp)->minor = cmnp->sib;
1065 
1066 		while (cmnp->link != NULL) {
1067 			clp = cmnp->link;
1068 			cmnp->link = clp->sib;
1069 			rm_link_from_hash(hdp, clp);
1070 			link_free(&clp);
1071 		}
1072 		minor_free(hdp, &cmnp);
1073 	}
1074 
1075 	node_free(pp);
1076 }
1077 
1078 static void
1079 rm_link_from_hash(struct di_devlink_handle *hdp, cache_link_t *clp)
1080 {
1081 	int hval;
1082 	cache_link_t **pp;
1083 
1084 	if (clp == NULL)
1085 		return;
1086 
1087 	if (clp->path == NULL)
1088 		return;
1089 
1090 	hval = hashfn(hdp, clp->path);
1091 	pp = &(CACHE_HASH(hdp, hval));
1092 	for (; *pp != NULL; pp = &(*pp)->hash) {
1093 		if (*pp == clp) {
1094 			*pp = clp->hash;
1095 			clp->hash = NULL;
1096 			return;
1097 		}
1098 	}
1099 
1100 	dprintf(DBG_ERR, "rm_link_from_hash: link(%s) not found\n", clp->path);
1101 }
1102 
1103 static cache_link_t *
1104 link_hash(di_devlink_handle_t hdp, const char *link, uint_t flags)
1105 {
1106 	int hval;
1107 	cache_link_t **pp, *clp;
1108 
1109 	if (link == NULL)
1110 		return (NULL);
1111 
1112 	hval = hashfn(hdp, link);
1113 	pp = &(CACHE_HASH(hdp, hval));
1114 	for (; (clp = *pp) != NULL; pp = &clp->hash) {
1115 		if (strcmp(clp->path, link) == 0) {
1116 			break;
1117 		}
1118 	}
1119 
1120 	if (clp == NULL)
1121 		return (NULL);
1122 
1123 	if ((flags & UNLINK_FROM_HASH) == UNLINK_FROM_HASH) {
1124 		*pp = clp->hash;
1125 		clp->hash = NULL;
1126 	}
1127 
1128 	return (clp);
1129 }
1130 
1131 static cache_minor_t *
1132 link2minor(struct di_devlink_handle *hdp, cache_link_t *clp)
1133 {
1134 	cache_link_t *plp;
1135 	const char *minor_path;
1136 	char *cp, buf[PATH_MAX], link[PATH_MAX];
1137 	char abspath[PATH_MAX];
1138 	struct stat st;
1139 
1140 	if (TYPE_PRI(attr2type(clp->attr))) {
1141 		/*
1142 		 * For primary link, content should point to a /devices node.
1143 		 */
1144 		if (!is_minor_node(clp->content, &minor_path)) {
1145 			return (NULL);
1146 		}
1147 
1148 		return (lookup_minor(hdp, minor_path, NULL,
1149 		    TYPE_CACHE|CREATE_FLAG));
1150 
1151 	}
1152 
1153 	/*
1154 	 * If secondary, the primary link is derived from the secondary
1155 	 * link contents. Secondary link contents can have two formats:
1156 	 *	audio -> /dev/sound/0
1157 	 *	fb0 -> fbs/afb0
1158 	 */
1159 
1160 	buf[0] = '\0';
1161 	if (strncmp(clp->content, DEV"/", strlen(DEV"/")) == 0) {
1162 		cp = &clp->content[strlen(DEV"/")];
1163 	} else if (clp->content[0] != '/') {
1164 		if ((cp = strrchr(clp->path, '/')) != NULL) {
1165 			char savechar = *(cp + 1);
1166 			*(cp + 1) = '\0';
1167 			(void) snprintf(buf, sizeof (buf), "%s", clp->path);
1168 			*(cp + 1) = savechar;
1169 		}
1170 		(void) strlcat(buf, clp->content, sizeof (buf));
1171 		cp = buf;
1172 	} else {
1173 		goto follow_link;
1174 	}
1175 
1176 	/*
1177 	 * Lookup the primary link if possible and find its minor.
1178 	 */
1179 	if ((plp = link_hash(hdp, cp, 0)) != NULL && plp->minor != NULL) {
1180 		return (plp->minor);
1181 	}
1182 
1183 	/* realpath() used only as a last resort because it is expensive */
1184 follow_link:
1185 	(void) snprintf(link, sizeof (link), "%s/%s", hdp->dev_dir, clp->path);
1186 
1187 #ifdef	DEBUG
1188 	/*LINTED*/
1189 	assert(sizeof (buf) >= PATH_MAX);
1190 #endif
1191 
1192 	/*
1193 	 * A realpath attempt to lookup a dangling link can invoke implicit
1194 	 * reconfig so verify there's an actual device behind the link first.
1195 	 */
1196 	if (lstat(link, &st) == -1)
1197 		return (NULL);
1198 	if (S_ISLNK(st.st_mode)) {
1199 		if (s_readlink(link, buf, sizeof (buf)) < 0)
1200 			return (NULL);
1201 		if (buf[0] != '/') {
1202 			char *p;
1203 			size_t n = sizeof (abspath);
1204 			if (strlcpy(abspath, link, n) >= n)
1205 				return (NULL);
1206 			p = strrchr(abspath, '/') + 1;
1207 			*p = 0;
1208 			n = sizeof (abspath) - strlen(p);
1209 			if (strlcpy(p, buf, n) >= n)
1210 				return (NULL);
1211 		} else {
1212 			if (strlcpy(abspath, buf, sizeof (abspath)) >=
1213 			    sizeof (abspath))
1214 				return (NULL);
1215 		}
1216 		if (!device_exists(abspath))
1217 			return (NULL);
1218 	}
1219 
1220 	if (s_realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) {
1221 		return (NULL);
1222 	}
1223 	return (lookup_minor(hdp, minor_path, NULL, TYPE_CACHE|CREATE_FLAG));
1224 }
1225 
1226 
1227 static void
1228 resolve_dangling_links(struct di_devlink_handle *hdp)
1229 {
1230 	cache_minor_t *cmnp;
1231 	cache_link_t *clp, **pp;
1232 
1233 	for (pp = &(CACHE(hdp)->dngl); *pp != NULL; ) {
1234 		clp = *pp;
1235 		if ((cmnp = link2minor(hdp, clp)) != NULL) {
1236 			*pp = clp->sib;
1237 			clp->sib = cmnp->link;
1238 			cmnp->link = clp;
1239 			assert(clp->minor == NULL);
1240 			clp->minor = cmnp;
1241 		} else {
1242 			dprintf(DBG_INFO, "resolve_dangling_links: link(%s):"
1243 			    " unresolved\n", clp->path);
1244 			pp = &clp->sib;
1245 		}
1246 	}
1247 }
1248 
1249 
1250 /*
1251  * The elements are assumed to be detached from the cache tree.
1252  */
1253 static void
1254 node_free(cache_node_t **pp)
1255 {
1256 	cache_node_t *cnp = *pp;
1257 
1258 	*pp = NULL;
1259 
1260 	if (cnp == NULL)
1261 		return;
1262 
1263 	free(cnp->path);
1264 	free(cnp);
1265 }
1266 
1267 static void
1268 minor_free(struct di_devlink_handle *hdp, cache_minor_t **pp)
1269 {
1270 	cache_minor_t *cmnp = *pp;
1271 
1272 	*pp = NULL;
1273 
1274 	if (cmnp == NULL)
1275 		return;
1276 
1277 	if (CACHE_LAST(hdp) == cmnp) {
1278 		dprintf(DBG_STEP, "minor_free: last_minor(%s)\n", cmnp->name);
1279 		CACHE_LAST(hdp) = NULL;
1280 	}
1281 
1282 	free(cmnp->name);
1283 	free(cmnp->nodetype);
1284 	free(cmnp);
1285 }
1286 
1287 static void
1288 link_free(cache_link_t **pp)
1289 {
1290 	cache_link_t *clp = *pp;
1291 
1292 	*pp = NULL;
1293 
1294 	if (clp == NULL)
1295 		return;
1296 
1297 	free(clp->path);
1298 	free(clp->content);
1299 	free(clp);
1300 }
1301 
1302 /*
1303  * Returns the ':' preceding the minor name
1304  */
1305 static char *
1306 minor_colon(const char *path)
1307 {
1308 	char *cp;
1309 
1310 	if ((cp = strrchr(path, '/')) == NULL) {
1311 		return (NULL);
1312 	}
1313 
1314 	return (strchr(cp, ':'));
1315 }
1316 
1317 static void *
1318 lookup_minor(
1319 	struct di_devlink_handle *hdp,
1320 	const char *minor_path,
1321 	const char *nodetype,
1322 	const int flags)
1323 {
1324 	void *vp;
1325 	char *colon;
1326 	char pdup[PATH_MAX];
1327 	const char *fcn = "lookup_minor";
1328 
1329 	if (minor_path == NULL) {
1330 		errno = EINVAL;
1331 		return (NULL);
1332 	}
1333 
1334 	(void) snprintf(pdup, sizeof (pdup), "%s", minor_path);
1335 
1336 	if ((colon = minor_colon(pdup)) == NULL) {
1337 		(void) dprintf(DBG_ERR, "%s: invalid minor path(%s)\n", fcn,
1338 		    minor_path);
1339 		errno = EINVAL;
1340 		return (NULL);
1341 	}
1342 	*colon = '\0';
1343 
1344 	if ((vp = get_last_minor(hdp, pdup, colon + 1, flags)) != NULL) {
1345 		return (vp);
1346 	}
1347 
1348 	if ((vp = lookup_node(hdp, pdup, flags)) == NULL) {
1349 		(void) dprintf(DBG_ERR, "%s: node(%s) not found\n", fcn, pdup);
1350 		return (NULL);
1351 	}
1352 	*colon = ':';
1353 
1354 	if (LOOKUP_CACHE(flags)) {
1355 		cache_minor_t **pp;
1356 
1357 		pp = &((cache_node_t *)vp)->minor;
1358 		for (; *pp != NULL; pp = &(*pp)->sib) {
1359 			if (strcmp((*pp)->name, colon + 1) == 0)
1360 				break;
1361 		}
1362 
1363 		if (*pp == NULL && CREATE_ELEM(flags)) {
1364 			*pp = minor_insert(hdp, vp, colon + 1, nodetype, pp);
1365 		}
1366 		set_last_minor(hdp, *pp, flags);
1367 
1368 		return (*pp);
1369 	} else {
1370 		char *cp;
1371 		uint32_t nidx;
1372 		struct db_minor *dmp;
1373 
1374 		nidx = (((struct db_node *)vp)->minor);
1375 		for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) {
1376 			cp = get_string(hdp, dmp->name);
1377 			if (cp && strcmp(cp, colon + 1) == 0)
1378 				break;
1379 		}
1380 		return (dmp);
1381 	}
1382 }
1383 
1384 static void *
1385 lookup_node(struct di_devlink_handle *hdp, char *path, const int flags)
1386 {
1387 	struct tnode tnd = {NULL};
1388 
1389 	if (tnd.node = get_last_node(hdp, path, flags))
1390 		return (tnd.node);
1391 
1392 	tnd.handle = hdp;
1393 	tnd.flags = flags;
1394 
1395 	if (walk_tree(path, &tnd, visit_node) != 0)
1396 		return (NULL);
1397 
1398 	return (tnd.node);
1399 }
1400 
1401 /*
1402  * last_minor is used for nodes of TYPE_CACHE only.
1403  */
1404 static void *
1405 get_last_node(struct di_devlink_handle *hdp, const char *path, int flags)
1406 {
1407 	cache_node_t *cnp;
1408 
1409 #ifdef	DEBUG
1410 	if (getenv(SKIP_LAST_CACHE)) {
1411 		(void) dprintf(DBG_INFO, "get_last_node: SKIPPING \"last\" "
1412 		    "node cache\n");
1413 		return (NULL);
1414 	}
1415 #endif
1416 
1417 	if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL ||
1418 	    CACHE_LAST(hdp)->node == NULL) {
1419 		return (NULL);
1420 	}
1421 
1422 	cnp = CACHE_LAST(hdp)->node;
1423 	if (strcmp(cnp->path, path) == 0) {
1424 		return (cnp);
1425 	}
1426 
1427 	cnp = cnp->sib;
1428 	if (cnp && strcmp(cnp->path, path) == 0) {
1429 		return (cnp);
1430 	}
1431 
1432 	return (NULL);
1433 }
1434 
1435 static void *
1436 get_last_minor(
1437 	struct di_devlink_handle *hdp,
1438 	const char *devfs_path,
1439 	const char *minor_name,
1440 	int flags)
1441 {
1442 	cache_minor_t *cmnp;
1443 
1444 #ifdef	DEBUG
1445 	if (getenv(SKIP_LAST_CACHE)) {
1446 		(void) dprintf(DBG_INFO, "get_last_minor: SKIPPING \"last\" "
1447 		    "minor cache\n");
1448 		return (NULL);
1449 	}
1450 #endif
1451 
1452 	if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL) {
1453 		return (NULL);
1454 	}
1455 
1456 	cmnp = CACHE_LAST(hdp);
1457 	if (strcmp(cmnp->name, minor_name) == 0 && cmnp->node &&
1458 	    strcmp(cmnp->node->path, devfs_path) == 0) {
1459 		return (cmnp);
1460 	}
1461 
1462 	cmnp = cmnp->sib;
1463 	if (cmnp && strcmp(cmnp->name, minor_name) == 0 && cmnp->node &&
1464 	    strcmp(cmnp->node->path, devfs_path) == 0) {
1465 		set_last_minor(hdp, cmnp, TYPE_CACHE);
1466 		return (cmnp);
1467 	}
1468 
1469 	return (NULL);
1470 }
1471 
1472 static void
1473 set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp, int flags)
1474 {
1475 #ifdef	DEBUG
1476 	if (getenv(SKIP_LAST_CACHE)) {
1477 		(void) dprintf(DBG_INFO, "set_last_minor: SKIPPING \"last\" "
1478 		    "minor cache\n");
1479 		return;
1480 	}
1481 #endif
1482 
1483 	if (LOOKUP_CACHE(flags) && cmnp) {
1484 		CACHE_LAST(hdp) = cmnp;
1485 	}
1486 }
1487 
1488 
1489 /*
1490  * Returns 0 if normal return or -1 otherwise.
1491  */
1492 static int
1493 walk_tree(
1494 	char *cur,
1495 	void *arg,
1496 	int (*node_callback)(const char *path, void *arg))
1497 {
1498 	char *slash, buf[PATH_MAX];
1499 
1500 	if (cur == NULL || cur[0] != '/' || strlen(cur) > sizeof (buf) - 1) {
1501 		errno = EINVAL;
1502 		return (-1);
1503 	}
1504 
1505 	(void) strcpy(buf, "/");
1506 
1507 	for (;;) {
1508 
1509 		if (node_callback(buf, arg) != DI_WALK_CONTINUE)
1510 			break;
1511 
1512 		while (*cur == '/')
1513 			cur++;
1514 
1515 		if (*cur == '\0')
1516 			break;
1517 
1518 		/*
1519 		 * There is a next component(s). Append a "/" separator for all
1520 		 * but the first (root) component.
1521 		 */
1522 		if (buf[1] != '\0') {
1523 			(void) strlcat(buf, "/", sizeof (buf));
1524 		}
1525 
1526 		if (slash = strchr(cur, '/')) {
1527 			*slash = '\0';
1528 			(void) strlcat(buf, cur, sizeof (buf));
1529 			*slash = '/';
1530 			cur = slash;
1531 		} else {
1532 			(void) strlcat(buf, cur, sizeof (buf));
1533 			cur += strlen(cur);
1534 		}
1535 
1536 	}
1537 
1538 	return (0);
1539 }
1540 
1541 
1542 static int
1543 visit_node(const char *path, void *arg)
1544 {
1545 	struct tnode *tnp = arg;
1546 
1547 	if (LOOKUP_CACHE(tnp->flags)) {
1548 
1549 		cache_node_t *cnp = tnp->node;
1550 
1551 		cnp = (cnp) ? cnp->child : CACHE_ROOT(tnp->handle);
1552 
1553 		for (; cnp != NULL; cnp = cnp->sib) {
1554 			if (strcmp(cnp->path, path) == 0)
1555 				break;
1556 		}
1557 		if (cnp == NULL && CREATE_ELEM(tnp->flags)) {
1558 			cnp = node_insert(tnp->handle, tnp->node, path,
1559 			    INSERT_TAIL);
1560 		}
1561 		tnp->node = cnp;
1562 	} else {
1563 		char *cp;
1564 		struct db_node *dnp = tnp->node;
1565 
1566 		dnp = (dnp) ? get_node(tnp->handle, dnp->child)
1567 		    : get_node(tnp->handle, DB_HDR(tnp->handle)->root_idx);
1568 
1569 		for (; dnp != NULL; dnp = get_node(tnp->handle, dnp->sib)) {
1570 			cp = get_string(tnp->handle, dnp->path);
1571 			if (cp && strcmp(cp, path) == 0) {
1572 				break;
1573 			}
1574 		}
1575 		tnp->node = dnp;
1576 	}
1577 
1578 	/*
1579 	 * Terminate walk if node is not found for a path component.
1580 	 */
1581 	return (tnp->node ? DI_WALK_CONTINUE : DI_WALK_TERMINATE);
1582 }
1583 
1584 static void
1585 minor_delete(di_devlink_handle_t hdp, cache_minor_t *cmnp)
1586 {
1587 	cache_link_t **lpp;
1588 	cache_minor_t **mpp;
1589 	const char *fcn = "minor_delete";
1590 
1591 	(void) dprintf(DBG_STEP, "%s: removing minor: %s\n", fcn, cmnp->name);
1592 
1593 	/* detach minor from node */
1594 	if (cmnp->node != NULL) {
1595 		mpp = &cmnp->node->minor;
1596 		for (; *mpp != NULL; mpp = &(*mpp)->sib) {
1597 			if (*mpp == cmnp)
1598 				break;
1599 		}
1600 
1601 		if (*mpp == NULL) {
1602 			(void) dprintf(DBG_ERR, "%s: dangling minor: %s\n",
1603 			    fcn, cmnp->name);
1604 		} else {
1605 			*mpp = cmnp->sib;
1606 		}
1607 	} else {
1608 		(void) dprintf(DBG_ERR, "%s: orphan minor(%s)\n", fcn,
1609 		    cmnp->name);
1610 	}
1611 
1612 	delete_unused_nodes(hdp, cmnp->node);
1613 
1614 	cmnp->node = NULL;
1615 	cmnp->sib = NULL;
1616 
1617 	/* Move all remaining links to dangling list */
1618 	for (lpp = &cmnp->link; *lpp != NULL; lpp = &(*lpp)->sib) {
1619 		(*lpp)->minor = NULL;
1620 	}
1621 	*lpp = CACHE(hdp)->dngl;
1622 	CACHE(hdp)->dngl = cmnp->link;
1623 	cmnp->link = NULL;
1624 
1625 	minor_free(hdp, &cmnp);
1626 }
1627 
1628 static void
1629 delete_unused_nodes(di_devlink_handle_t hdp, cache_node_t *cnp)
1630 {
1631 	cache_node_t **npp;
1632 	const char *fcn = "delete_unused_nodes";
1633 
1634 	if (cnp == NULL)
1635 		return;
1636 
1637 	if (cnp->minor != NULL || cnp->child != NULL)
1638 		return;
1639 
1640 	(void) dprintf(DBG_INFO, "%s: removing unused node: %s\n", fcn,
1641 	    cnp->path);
1642 
1643 	/* Unlink node from tree */
1644 	if (cnp->parent != NULL) {
1645 		npp = &cnp->parent->child;
1646 		for (; *npp != NULL; npp = &(*npp)->sib) {
1647 			if (*npp == cnp)
1648 				break;
1649 		}
1650 
1651 		if (*npp == NULL) {
1652 			(void) dprintf(DBG_ERR, "%s: dangling node: %s\n", fcn,
1653 			    cnp->path);
1654 		} else {
1655 			*npp = cnp->sib;
1656 		}
1657 	} else if (cnp == CACHE_ROOT(hdp)) {
1658 		CACHE_ROOT(hdp) = NULL;
1659 	} else {
1660 		(void) dprintf(DBG_ERR, "%s: orphan node (%s)\n", fcn,
1661 		    cnp->path);
1662 	}
1663 
1664 	delete_unused_nodes(hdp, cnp->parent);
1665 
1666 	cnp->parent = cnp->sib = NULL;
1667 
1668 	node_free(&cnp);
1669 }
1670 
1671 static int
1672 rm_link(di_devlink_handle_t hdp, const char *link)
1673 {
1674 	cache_link_t *clp;
1675 	const char *fcn = "rm_link";
1676 
1677 	if (hdp == NULL || DB_ERR(hdp) || link == NULL || link[0] == '/' ||
1678 	    (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) {
1679 		dprintf(DBG_ERR, "%s: %s: invalid args\n",
1680 		    fcn, link ? link : "<NULL>");
1681 		errno = EINVAL;
1682 		return (-1);
1683 	}
1684 
1685 	dprintf(DBG_STEP, "%s: link(%s)\n", fcn, link);
1686 
1687 	if ((clp = link_hash(hdp, link, UNLINK_FROM_HASH)) == NULL) {
1688 		return (0);
1689 	}
1690 
1691 	link_delete(hdp, clp);
1692 
1693 	return (0);
1694 }
1695 
1696 int
1697 di_devlink_rm_link(di_devlink_handle_t hdp, const char *link)
1698 {
1699 	if (hdp == NULL || !HDL_RDWR(hdp)) {
1700 		errno = EINVAL;
1701 		return (-1);
1702 	}
1703 
1704 	return (rm_link(hdp, link));
1705 }
1706 
1707 static void
1708 link_delete(di_devlink_handle_t hdp, cache_link_t *clp)
1709 {
1710 	cache_link_t **pp;
1711 	const char *fcn = "link_delete";
1712 
1713 	(void) dprintf(DBG_STEP, "%s: removing link: %s\n", fcn, clp->path);
1714 
1715 	if (clp->minor == NULL)
1716 		pp = &(CACHE(hdp)->dngl);
1717 	else
1718 		pp = &clp->minor->link;
1719 
1720 	for (; *pp != NULL; pp = &(*pp)->sib) {
1721 		if (*pp == clp)
1722 			break;
1723 	}
1724 
1725 	if (*pp == NULL) {
1726 		(void) dprintf(DBG_ERR, "%s: link(%s) not on list\n",
1727 		    fcn, clp->path);
1728 	} else {
1729 		*pp = clp->sib;
1730 	}
1731 
1732 	delete_unused_minor(hdp, clp->minor);
1733 
1734 	clp->minor = NULL;
1735 
1736 	link_free(&clp);
1737 }
1738 
1739 static void
1740 delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp)
1741 {
1742 	if (cmnp == NULL)
1743 		return;
1744 
1745 	if (cmnp->link != NULL)
1746 		return;
1747 
1748 	dprintf(DBG_STEP, "delete_unused_minor: removing minor(%s)\n",
1749 	    cmnp->name);
1750 
1751 	minor_delete(hdp, cmnp);
1752 }
1753 
1754 int
1755 di_devlink_add_link(
1756 	di_devlink_handle_t hdp,
1757 	const char *link,
1758 	const char *content,
1759 	int flags)
1760 {
1761 	return (add_link(hdp, link, content, flags) != NULL ? 0 : -1);
1762 }
1763 
1764 static cache_link_t *
1765 add_link(
1766 	struct di_devlink_handle *hdp,
1767 	const char *link,
1768 	const char *content,
1769 	int flags)
1770 {
1771 	uint32_t attr;
1772 	cache_link_t *clp;
1773 	cache_minor_t *cmnp;
1774 	const char *fcn = "add_link";
1775 
1776 	if (hdp == NULL || DB_ERR(hdp) || link == NULL ||
1777 	    link[0] == '/' || content == NULL || !link_flag(flags) ||
1778 	    (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) {
1779 		dprintf(DBG_ERR, "%s: %s: invalid args\n",
1780 		    fcn, link ? link : "<NULL>");
1781 		errno = EINVAL;
1782 		return (NULL);
1783 	}
1784 
1785 	if ((clp = link_hash(hdp, link, 0)) != NULL) {
1786 		if (link_cmp(clp, content, LINK_TYPE(flags)) != 0) {
1787 			(void) rm_link(hdp, link);
1788 		} else {
1789 			return (clp);
1790 		}
1791 	}
1792 
1793 	if (TYPE_PRI(flags)) {
1794 		const char *minor_path = NULL;
1795 
1796 		if (!is_minor_node(content, &minor_path)) {
1797 			(void) dprintf(DBG_ERR, "%s: invalid content(%s)"
1798 			    " for primary link\n", fcn, content);
1799 			errno = EINVAL;
1800 			return (NULL);
1801 		}
1802 		if ((cmnp = lookup_minor(hdp, minor_path, NULL,
1803 		    TYPE_CACHE|CREATE_FLAG)) == NULL) {
1804 			return (NULL);
1805 		}
1806 		attr = A_PRIMARY;
1807 	} else {
1808 		/*
1809 		 * Defer resolving a secondary link to a minor until the
1810 		 * database is closed. This ensures that the primary link
1811 		 * (required for a successful resolve) has also been created.
1812 		 */
1813 		cmnp = NULL;
1814 		attr = A_SECONDARY;
1815 	}
1816 
1817 	return (link_insert(hdp, cmnp, link, content, attr));
1818 }
1819 
1820 /*
1821  * Returns 0 on match or 1 otherwise.
1822  */
1823 static int
1824 link_cmp(cache_link_t *clp, const char *content, int type)
1825 {
1826 	if (strcmp(clp->content, content) != 0)
1827 		return (1);
1828 
1829 	if (attr2type(clp->attr) != type)
1830 		return (1);
1831 
1832 	return (0);
1833 }
1834 
1835 int
1836 di_devlink_update(di_devlink_handle_t hdp)
1837 {
1838 	if (hdp == NULL || !HDL_RDWR(hdp) || DB_ERR(hdp)) {
1839 		errno = EINVAL;
1840 		return (-1);
1841 	}
1842 
1843 	/*
1844 	 * Reset the counter to schedule a synchronization with /dev on the next
1845 	 * di_devlink_close().
1846 	 */
1847 	CACHE(hdp)->update_count = 0;
1848 
1849 	return (0);
1850 }
1851 
1852 static int
1853 synchronize_db(di_devlink_handle_t hdp)
1854 {
1855 	int hval;
1856 	cache_link_t *clp;
1857 	char pdup[PATH_MAX];
1858 	recurse_t rec = {NULL};
1859 	const char *fcn = "synchronize_db";
1860 
1861 	rec.data = NULL;
1862 	rec.fcn = cache_dev_link;
1863 
1864 	/*
1865 	 * Walk through $ROOT/dev, reading every link and marking the
1866 	 * corresponding cached version as valid(adding new links as needed).
1867 	 * Then walk through the cache and remove all unmarked links.
1868 	 */
1869 	if (recurse_dev(hdp, &rec) != 0) {
1870 		return (-1);
1871 	}
1872 
1873 	for (hval = 0; hval < CACHE(hdp)->hash_sz; hval++) {
1874 		for (clp = CACHE_HASH(hdp, hval); clp != NULL; ) {
1875 			if (GET_VALID_ATTR(clp->attr)) {
1876 				CLR_VALID_ATTR(clp->attr);
1877 				clp = clp->hash;
1878 				continue;
1879 			}
1880 
1881 			/*
1882 			 * The link is stale, so remove it. Since the link
1883 			 * will be destroyed, use a copy of the link path to
1884 			 * invoke the remove function.
1885 			 */
1886 			(void) snprintf(pdup, sizeof (pdup), "%s", clp->path);
1887 			clp = clp->hash;
1888 			(void) dprintf(DBG_STEP, "%s: removing invalid link:"
1889 			    " %s\n", fcn, pdup);
1890 			(void) di_devlink_rm_link(hdp, pdup);
1891 		}
1892 	}
1893 
1894 	(void) dprintf(DBG_STEP, "%s: update completed\n", fcn);
1895 
1896 	return (0);
1897 }
1898 
1899 static di_devlink_handle_t
1900 di_devlink_init_impl(const char *root, const char *name, uint_t flags)
1901 {
1902 	int	err = 0;
1903 
1904 	if ((flags != 0 && flags != DI_MAKE_LINK) ||
1905 	    (flags == 0 && name != NULL)) {
1906 		errno = EINVAL;
1907 		return (NULL);
1908 	}
1909 
1910 	if ((flags == DI_MAKE_LINK) &&
1911 	    (err = devlink_create(root, name, DCA_DEVLINK_CACHE))) {
1912 		errno = err;
1913 		return (NULL);
1914 	}
1915 
1916 	(void) dprintf(DBG_INFO, "devlink_init_impl: success\n");
1917 
1918 	return (devlink_snapshot(root));
1919 }
1920 
1921 di_devlink_handle_t
1922 di_devlink_init(const char *name, uint_t flags)
1923 {
1924 	return (di_devlink_init_impl("/", name, flags));
1925 }
1926 
1927 di_devlink_handle_t
1928 di_devlink_init_root(const char *root, const char *name, uint_t flags)
1929 {
1930 	return (di_devlink_init_impl(root, name, flags));
1931 }
1932 
1933 static di_devlink_handle_t
1934 devlink_snapshot(const char *root_dir)
1935 {
1936 	struct di_devlink_handle *hdp;
1937 	int		err;
1938 	static int	retried = 0;
1939 
1940 	if ((hdp = handle_alloc(root_dir, OPEN_RDONLY)) == NULL) {
1941 		return (NULL);
1942 	}
1943 
1944 	/*
1945 	 * We don't need to lock.  If a consumer wants the very latest db
1946 	 * then he must perform a di_devlink_init with the DI_MAKE_LINK
1947 	 * flag to force a sync with devfsadm first.  Otherwise, the
1948 	 * current database file is opened and mmaped on demand: the rename
1949 	 * associated with a db update does not change the contents
1950 	 * of files already opened.
1951 	 */
1952 again:	err = open_db(hdp, OPEN_RDONLY);
1953 
1954 	/*
1955 	 * If we failed to open DB the most likely cause is that DB file did
1956 	 * not exist. If we have not done a retry, signal devfsadmd to
1957 	 * recreate the DB file and retry. If we fail to open the DB after
1958 	 * retry, we will walk /dev in di_devlink_walk.
1959 	 */
1960 	if (err && (retried == 0)) {
1961 		retried++;
1962 		(void) devlink_create(root_dir, NULL, DCA_DEVLINK_SYNC);
1963 		goto again;
1964 	}
1965 	return (hdp);
1966 }
1967 
1968 int
1969 di_devlink_fini(di_devlink_handle_t *pp)
1970 {
1971 	if (pp == NULL || *pp == NULL || !HDL_RDONLY(*pp)) {
1972 		errno = EINVAL;
1973 		return (-1);
1974 	}
1975 
1976 	/* Freeing the handle also closes the DB */
1977 	handle_free(pp);
1978 
1979 	return (0);
1980 }
1981 
1982 int
1983 di_devlink_walk(
1984 	di_devlink_handle_t hdp,
1985 	const char *re,
1986 	const char *minor_path,
1987 	uint_t flags,
1988 	void *arg,
1989 	int (*devlink_callback)(di_devlink_t, void *))
1990 {
1991 	int rv;
1992 	regex_t reg;
1993 	link_desc_t linkd = {NULL};
1994 
1995 	if (hdp == NULL || !HDL_RDONLY(hdp)) {
1996 		errno = EINVAL;
1997 		return (-1);
1998 	}
1999 
2000 	linkd.minor_path = minor_path;
2001 	linkd.flags = flags;
2002 	linkd.arg = arg;
2003 	linkd.fcn = devlink_callback;
2004 
2005 	if (re) {
2006 		if (regcomp(&reg, re, REG_EXTENDED) != 0)
2007 			return (-1);
2008 		linkd.regp = &reg;
2009 	}
2010 
2011 	if (check_args(&linkd)) {
2012 		errno = EINVAL;
2013 		rv = -1;
2014 		goto out;
2015 	}
2016 
2017 	if (DB_OPEN(hdp)) {
2018 		rv = walk_db(hdp, &linkd);
2019 	} else {
2020 		rv = walk_dev(hdp, &linkd);
2021 	}
2022 
2023 out:
2024 	if (re) {
2025 		regfree(&reg);
2026 	}
2027 
2028 	return (rv ? -1 : 0);
2029 }
2030 
2031 static int
2032 link_flag(uint_t flags)
2033 {
2034 	if (flags != 0 && flags != DI_PRIMARY_LINK &&
2035 	    flags != DI_SECONDARY_LINK) {
2036 		return (0);
2037 	}
2038 
2039 	return (1);
2040 }
2041 
2042 /*
2043  * Currently allowed flags are:
2044  *	DI_PRIMARY_LINK
2045  *	DI_SECONDARY_LINK
2046  */
2047 static int
2048 check_args(link_desc_t *linkp)
2049 {
2050 	if (linkp->fcn == NULL)
2051 		return (-1);
2052 
2053 	if (!link_flag(linkp->flags)) {
2054 		return (-1);
2055 	}
2056 
2057 	/*
2058 	 * Minor path can be NULL. In that case, all links will be
2059 	 * selected.
2060 	 */
2061 	if (linkp->minor_path) {
2062 		if (linkp->minor_path[0] != '/' ||
2063 		    minor_colon(linkp->minor_path) == NULL) {
2064 			return (-1);
2065 		}
2066 	}
2067 
2068 	return (0);
2069 }
2070 
2071 
2072 /*
2073  * Walk all links in database if no minor path is specified.
2074  */
2075 static int
2076 walk_db(struct di_devlink_handle *hdp, link_desc_t *linkp)
2077 {
2078 	assert(DB_OPEN(hdp));
2079 
2080 	if (linkp->minor_path == NULL) {
2081 		return (walk_all_links(hdp, linkp));
2082 	} else {
2083 		return (walk_matching_links(hdp, linkp));
2084 	}
2085 }
2086 
2087 static int
2088 cache_dev(struct di_devlink_handle *hdp)
2089 {
2090 	size_t sz;
2091 	recurse_t rec = {NULL};
2092 
2093 	assert(hdp);
2094 	assert(HDL_RDONLY(hdp));
2095 
2096 	if (hdp == NULL || !HDL_RDONLY(hdp)) {
2097 		dprintf(DBG_ERR, "cache_dev: invalid arg\n");
2098 		return (-1);
2099 	}
2100 
2101 	sz = MIN_HASH_SIZE;
2102 
2103 	CACHE(hdp)->hash = calloc(sz, sizeof (cache_link_t *));
2104 	if (CACHE(hdp)->hash == NULL) {
2105 		return (-1);
2106 	}
2107 	CACHE(hdp)->hash_sz = sz;
2108 
2109 	rec.data = NULL;
2110 	rec.fcn = cache_dev_link;
2111 
2112 	return (recurse_dev(hdp, &rec));
2113 }
2114 
2115 static int
2116 walk_dev(struct di_devlink_handle *hdp, link_desc_t *linkp)
2117 {
2118 	assert(hdp && linkp);
2119 	assert(!DB_OPEN(hdp));
2120 	assert(HDL_RDONLY(hdp));
2121 
2122 	if (hdp == NULL || !HDL_RDONLY(hdp) || DB_OPEN(hdp)) {
2123 		dprintf(DBG_ERR, "walk_dev: invalid args\n");
2124 		return (-1);
2125 	}
2126 
2127 	if (CACHE_EMPTY(hdp) && cache_dev(hdp) != 0) {
2128 		dprintf(DBG_ERR, "walk_dev: /dev caching failed\n");
2129 		return (-1);
2130 	}
2131 
2132 	if (linkp->minor_path)
2133 		walk_cache_minor(hdp, linkp->minor_path, linkp);
2134 	else
2135 		walk_all_cache(hdp, linkp);
2136 
2137 	return (linkp->retval);
2138 }
2139 
2140 /* ARGSUSED */
2141 static int
2142 cache_dev_link(struct di_devlink_handle *hdp, void *data, const char *link)
2143 {
2144 	int flags;
2145 	cache_link_t *clp;
2146 	char content[PATH_MAX];
2147 
2148 	assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp));
2149 
2150 	if (s_readlink(link, content, sizeof (content)) < 0) {
2151 		return (DI_WALK_CONTINUE);
2152 	}
2153 
2154 	if (is_minor_node(content, NULL)) {
2155 		flags = DI_PRIMARY_LINK;
2156 	} else {
2157 		flags = DI_SECONDARY_LINK;
2158 	}
2159 
2160 	assert(strncmp(link, hdp->dev_dir, strlen(hdp->dev_dir)) == 0);
2161 
2162 	/*
2163 	 * Store only the part after <root-dir>/dev/
2164 	 */
2165 	link += strlen(hdp->dev_dir) + 1;
2166 
2167 	if ((clp = add_link(hdp, link, content, flags)) != NULL) {
2168 		SET_VALID_ATTR(clp->attr);
2169 	}
2170 
2171 	return (DI_WALK_CONTINUE);
2172 }
2173 
2174 
2175 static int
2176 walk_all_links(struct di_devlink_handle *hdp, link_desc_t *linkp)
2177 {
2178 	struct db_link *dlp;
2179 	uint32_t nidx, eidx;
2180 
2181 	assert(DB_NUM(hdp, DB_LINK) >= 1);
2182 
2183 	eidx = DB_NUM(hdp, DB_LINK);
2184 
2185 	/* Skip the "NIL" (index == 0) link. */
2186 	for (nidx = 1; nidx < eidx; nidx++) {
2187 		/*
2188 		 * Declare this local to the block with zero
2189 		 * initializer so that it gets rezeroed
2190 		 * for each iteration.
2191 		 */
2192 		struct di_devlink vlink = {NULL};
2193 
2194 		if ((dlp = get_link(hdp, nidx)) == NULL)
2195 			continue;
2196 
2197 		vlink.rel_path = get_string(hdp, dlp->path);
2198 		vlink.content = get_string(hdp, dlp->content);
2199 		vlink.type = attr2type(dlp->attr);
2200 
2201 		if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE) {
2202 			break;
2203 		}
2204 	}
2205 
2206 	return (linkp->retval);
2207 }
2208 
2209 static int
2210 walk_matching_links(struct di_devlink_handle *hdp, link_desc_t *linkp)
2211 {
2212 	uint32_t nidx;
2213 	struct db_link *dlp;
2214 	struct db_minor *dmp;
2215 
2216 	assert(linkp->minor_path != NULL);
2217 
2218 	dmp = lookup_minor(hdp, linkp->minor_path, NULL, TYPE_DB);
2219 
2220 	/*
2221 	 * If a minor matching the path exists, walk that minor's devlinks list.
2222 	 * Then walk the dangling devlinks list. Non-matching devlinks will be
2223 	 * filtered out in visit_link.
2224 	 */
2225 	for (;;) {
2226 		nidx = dmp ? dmp->link : DB_HDR(hdp)->dngl_idx;
2227 		for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) {
2228 			struct di_devlink vlink = {NULL};
2229 
2230 			vlink.rel_path = get_string(hdp, dlp->path);
2231 			vlink.content = get_string(hdp, dlp->content);
2232 			vlink.type = attr2type(dlp->attr);
2233 
2234 			if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE)
2235 				goto out;
2236 		}
2237 		if (dmp == NULL) {
2238 			break;
2239 		} else {
2240 			dmp = NULL;
2241 		}
2242 	}
2243 
2244 out:
2245 	return (linkp->retval);
2246 }
2247 
2248 static int
2249 visit_link(
2250 	struct di_devlink_handle *hdp,
2251 	link_desc_t *linkp,
2252 	struct di_devlink *vlp)
2253 {
2254 	struct stat sbuf;
2255 	const char *minor_path = NULL;
2256 	char abs_path[PATH_MAX], cont[PATH_MAX];
2257 
2258 	/*
2259 	 * It is legal for the link's content and type to be unknown.
2260 	 * but one of absolute or relative path must be set.
2261 	 */
2262 	if (vlp->rel_path == NULL && vlp->abs_path == NULL) {
2263 		(void) dprintf(DBG_ERR, "visit_link: invalid arguments\n");
2264 		return (DI_WALK_CONTINUE);
2265 	}
2266 
2267 	if (vlp->rel_path == NULL) {
2268 		vlp->rel_path = (char *)rel_path(hdp, vlp->abs_path);
2269 		if (vlp->rel_path == NULL || vlp->rel_path[0] == '\0')
2270 			return (DI_WALK_CONTINUE);
2271 	}
2272 
2273 	if (linkp->regp) {
2274 		if (regexec(linkp->regp, vlp->rel_path, 0, NULL, 0) != 0)
2275 			return (DI_WALK_CONTINUE);
2276 	}
2277 
2278 	if (vlp->abs_path == NULL) {
2279 		assert(vlp->rel_path[0] != '/');
2280 		(void) snprintf(abs_path, sizeof (abs_path), "%s/%s",
2281 		    hdp->dev_dir, vlp->rel_path);
2282 		vlp->abs_path = abs_path;
2283 	}
2284 
2285 	if (vlp->content == NULL) {
2286 		if (s_readlink(vlp->abs_path, cont, sizeof (cont)) < 0) {
2287 			return (DI_WALK_CONTINUE);
2288 		}
2289 		vlp->content = cont;
2290 	}
2291 
2292 
2293 	if (vlp->type == 0) {
2294 		if (is_minor_node(vlp->content, &minor_path)) {
2295 			vlp->type = DI_PRIMARY_LINK;
2296 		} else {
2297 			vlp->type = DI_SECONDARY_LINK;
2298 		}
2299 	}
2300 
2301 	/*
2302 	 * Filter based on minor path
2303 	 */
2304 	if (linkp->minor_path) {
2305 		char tmp[PATH_MAX];
2306 
2307 		/*
2308 		 * derive minor path
2309 		 */
2310 		if (vlp->type == DI_SECONDARY_LINK) {
2311 
2312 #ifdef	DEBUG
2313 			/*LINTED*/
2314 			assert(sizeof (tmp) >= PATH_MAX);
2315 #endif
2316 			if (s_realpath(vlp->abs_path, tmp) == NULL)
2317 				return (DI_WALK_CONTINUE);
2318 
2319 			if (!is_minor_node(tmp, &minor_path))
2320 				return (DI_WALK_CONTINUE);
2321 
2322 		} else if (minor_path == NULL) {
2323 			if (!is_minor_node(vlp->content, &minor_path))
2324 				return (DI_WALK_CONTINUE);
2325 		}
2326 
2327 		assert(minor_path != NULL);
2328 
2329 		if (strcmp(linkp->minor_path, minor_path) != 0)
2330 			return (DI_WALK_CONTINUE);
2331 	}
2332 
2333 	/*
2334 	 * Filter based on link type
2335 	 */
2336 	if (!TYPE_NONE(linkp->flags) && LINK_TYPE(linkp->flags) != vlp->type) {
2337 		return (DI_WALK_CONTINUE);
2338 	}
2339 
2340 	if (lstat(vlp->abs_path, &sbuf) < 0) {
2341 		dprintf(DBG_ERR, "visit_link: %s: lstat failed: %s\n",
2342 		    vlp->abs_path, strerror(errno));
2343 		return (DI_WALK_CONTINUE);
2344 	}
2345 
2346 	return (linkp->fcn(vlp, linkp->arg));
2347 }
2348 
2349 static int
2350 devlink_valid(di_devlink_t devlink)
2351 {
2352 	if (devlink == NULL || devlink->rel_path == NULL ||
2353 	    devlink->abs_path == NULL || devlink->content == NULL ||
2354 	    TYPE_NONE(devlink->type)) {
2355 		return (0);
2356 	}
2357 
2358 	return (1);
2359 }
2360 
2361 const char *
2362 di_devlink_path(di_devlink_t devlink)
2363 {
2364 	if (!devlink_valid(devlink)) {
2365 		errno = EINVAL;
2366 		return (NULL);
2367 	}
2368 
2369 	return (devlink->abs_path);
2370 }
2371 
2372 const char *
2373 di_devlink_content(di_devlink_t devlink)
2374 {
2375 	if (!devlink_valid(devlink)) {
2376 		errno = EINVAL;
2377 		return (NULL);
2378 	}
2379 
2380 	return (devlink->content);
2381 }
2382 
2383 int
2384 di_devlink_type(di_devlink_t devlink)
2385 {
2386 	if (!devlink_valid(devlink)) {
2387 		errno = EINVAL;
2388 		return (-1);
2389 	}
2390 
2391 	return (devlink->type);
2392 }
2393 
2394 di_devlink_t
2395 di_devlink_dup(di_devlink_t devlink)
2396 {
2397 	struct di_devlink *duplink;
2398 
2399 	if (!devlink_valid(devlink)) {
2400 		errno = EINVAL;
2401 		return (NULL);
2402 	}
2403 
2404 	if ((duplink = calloc(1, sizeof (struct di_devlink))) == NULL) {
2405 		return (NULL);
2406 	}
2407 
2408 	duplink->rel_path = strdup(devlink->rel_path);
2409 	duplink->abs_path = strdup(devlink->abs_path);
2410 	duplink->content  = strdup(devlink->content);
2411 	duplink->type	  = devlink->type;
2412 
2413 	if (!devlink_valid(duplink)) {
2414 		(void) di_devlink_free(duplink);
2415 		errno = ENOMEM;
2416 		return (NULL);
2417 	}
2418 
2419 	return (duplink);
2420 }
2421 
2422 int
2423 di_devlink_free(di_devlink_t devlink)
2424 {
2425 	if (devlink == NULL) {
2426 		errno = EINVAL;
2427 		return (-1);
2428 	}
2429 
2430 	free(devlink->rel_path);
2431 	free(devlink->abs_path);
2432 	free(devlink->content);
2433 	free(devlink);
2434 
2435 	return (0);
2436 }
2437 
2438 /*
2439  * Obtain path relative to dev_dir
2440  */
2441 static const char *
2442 rel_path(struct di_devlink_handle *hdp, const char *path)
2443 {
2444 	const size_t len = strlen(hdp->dev_dir);
2445 
2446 	if (strncmp(path, hdp->dev_dir, len) != 0)
2447 		return (NULL);
2448 
2449 	if (path[len] == '\0')
2450 		return (&path[len]);
2451 
2452 	if (path[len] != '/')
2453 		return (NULL);
2454 
2455 	return (&path[len+1]);
2456 }
2457 
2458 static int
2459 recurse_dev(struct di_devlink_handle *hdp, recurse_t *rp)
2460 {
2461 	int ret = 0;
2462 
2463 	(void) do_recurse(hdp->dev_dir, hdp, rp, &ret);
2464 
2465 	return (ret);
2466 }
2467 
2468 static int
2469 do_recurse(
2470 	const char *dir,
2471 	struct di_devlink_handle *hdp,
2472 	recurse_t *rp,
2473 	int *retp)
2474 {
2475 	size_t len;
2476 	const char *rel;
2477 	struct stat sbuf;
2478 	char cur[PATH_MAX], *cp;
2479 	int i, rv = DI_WALK_CONTINUE;
2480 	finddevhdl_t handle;
2481 	char *d_name;
2482 
2483 
2484 	if ((rel = rel_path(hdp, dir)) == NULL)
2485 		return (DI_WALK_CONTINUE);
2486 
2487 	/*
2488 	 * Skip directories we are not interested in.
2489 	 */
2490 	for (i = 0; i < N_SKIP_DIRS; i++) {
2491 		if (strcmp(rel, skip_dirs[i]) == 0) {
2492 			(void) dprintf(DBG_STEP, "do_recurse: skipping %s\n",
2493 			    dir);
2494 			return (DI_WALK_CONTINUE);
2495 		}
2496 	}
2497 
2498 	(void) dprintf(DBG_STEP, "do_recurse: dir = %s\n", dir);
2499 
2500 	if (finddev_readdir(dir, &handle) != 0)
2501 		return (DI_WALK_CONTINUE);
2502 
2503 	(void) snprintf(cur, sizeof (cur), "%s/", dir);
2504 	len = strlen(cur);
2505 	cp = cur + len;
2506 	len = sizeof (cur) - len;
2507 
2508 	for (;;) {
2509 		if ((d_name = (char *)finddev_next(handle)) == NULL)
2510 			break;
2511 
2512 		if (strlcpy(cp, d_name, len) >= len)
2513 			break;
2514 
2515 		/*
2516 		 * Skip files we are not interested in.
2517 		 */
2518 		for (i = 0; i < N_SKIP_FILES; i++) {
2519 
2520 			rel = rel_path(hdp, cur);
2521 			if (rel == NULL || strcmp(rel, skip_files[i]) == 0) {
2522 				(void) dprintf(DBG_STEP,
2523 				    "do_recurse: skipping %s\n", cur);
2524 				goto next_entry;
2525 			}
2526 		}
2527 
2528 		if (lstat(cur, &sbuf) == 0) {
2529 			if (S_ISDIR(sbuf.st_mode)) {
2530 				rv = do_recurse(cur, hdp, rp, retp);
2531 			} else if (S_ISLNK(sbuf.st_mode)) {
2532 				rv = rp->fcn(hdp, rp->data, cur);
2533 			} else {
2534 				(void) dprintf(DBG_STEP,
2535 				    "do_recurse: Skipping entry: %s\n", cur);
2536 			}
2537 		} else {
2538 			(void) dprintf(DBG_ERR, "do_recurse: cur(%s): lstat"
2539 			    " failed: %s\n", cur, strerror(errno));
2540 		}
2541 
2542 next_entry:
2543 		*cp = '\0';
2544 
2545 		if (rv != DI_WALK_CONTINUE)
2546 			break;
2547 	}
2548 
2549 	finddev_close(handle);
2550 
2551 	return (rv);
2552 }
2553 
2554 
2555 static int
2556 check_attr(uint32_t attr)
2557 {
2558 	switch (attr & A_LINK_TYPES) {
2559 		case A_PRIMARY:
2560 		case A_SECONDARY:
2561 			return (1);
2562 		default:
2563 			dprintf(DBG_ERR, "check_attr: incorrect attr(%u)\n",
2564 			    attr);
2565 			return (0);
2566 	}
2567 }
2568 
2569 static int
2570 attr2type(uint32_t attr)
2571 {
2572 	switch (attr & A_LINK_TYPES) {
2573 		case A_PRIMARY:
2574 			return (DI_PRIMARY_LINK);
2575 		case A_SECONDARY:
2576 			return (DI_SECONDARY_LINK);
2577 		default:
2578 			dprintf(DBG_ERR, "attr2type: incorrect attr(%u)\n",
2579 			    attr);
2580 			return (0);
2581 	}
2582 }
2583 
2584 /* Allocate new node and link it in */
2585 static cache_node_t *
2586 node_insert(
2587 	struct di_devlink_handle *hdp,
2588 	cache_node_t *pcnp,
2589 	const char *path,
2590 	int insert)
2591 {
2592 	cache_node_t *cnp;
2593 
2594 	if (path == NULL) {
2595 		errno = EINVAL;
2596 		SET_DB_ERR(hdp);
2597 		return (NULL);
2598 	}
2599 
2600 	if ((cnp = calloc(1, sizeof (cache_node_t))) == NULL) {
2601 		SET_DB_ERR(hdp);
2602 		return (NULL);
2603 	}
2604 
2605 	if ((cnp->path = strdup(path)) == NULL) {
2606 		SET_DB_ERR(hdp);
2607 		free(cnp);
2608 		return (NULL);
2609 	}
2610 
2611 	cnp->parent = pcnp;
2612 
2613 	if (pcnp == NULL) {
2614 		assert(strcmp(path, "/") == 0);
2615 		assert(CACHE(hdp)->root == NULL);
2616 		CACHE(hdp)->root = cnp;
2617 	} else if (insert == INSERT_HEAD) {
2618 		cnp->sib = pcnp->child;
2619 		pcnp->child = cnp;
2620 	} else if (CACHE_LAST(hdp) && CACHE_LAST(hdp)->node &&
2621 	    CACHE_LAST(hdp)->node->parent == pcnp &&
2622 	    CACHE_LAST(hdp)->node->sib == NULL) {
2623 
2624 		CACHE_LAST(hdp)->node->sib = cnp;
2625 
2626 	} else {
2627 		cache_node_t **pp;
2628 
2629 		for (pp = &pcnp->child; *pp != NULL; pp = &(*pp)->sib)
2630 			;
2631 		*pp = cnp;
2632 	}
2633 
2634 	return (cnp);
2635 }
2636 
2637 /*
2638  * Allocate a new minor and link it in either at the tail or head
2639  * of the minor list depending on the value of "prev".
2640  */
2641 static cache_minor_t *
2642 minor_insert(
2643 	struct di_devlink_handle *hdp,
2644 	cache_node_t *pcnp,
2645 	const char *name,
2646 	const char *nodetype,
2647 	cache_minor_t **prev)
2648 {
2649 	cache_minor_t *cmnp;
2650 
2651 	if (pcnp == NULL || name == NULL) {
2652 		errno = EINVAL;
2653 		SET_DB_ERR(hdp);
2654 		return (NULL);
2655 	}
2656 
2657 	/*
2658 	 * Some pseudo drivers don't specify nodetype. Assume pseudo if
2659 	 * nodetype is not specified.
2660 	 */
2661 	if (nodetype == NULL)
2662 		nodetype = DDI_PSEUDO;
2663 
2664 	if ((cmnp = calloc(1, sizeof (cache_minor_t))) == NULL) {
2665 		SET_DB_ERR(hdp);
2666 		return (NULL);
2667 	}
2668 
2669 	cmnp->name = strdup(name);
2670 	cmnp->nodetype = strdup(nodetype);
2671 	if (cmnp->name == NULL || cmnp->nodetype == NULL) {
2672 		SET_DB_ERR(hdp);
2673 		free(cmnp->name);
2674 		free(cmnp->nodetype);
2675 		free(cmnp);
2676 		return (NULL);
2677 	}
2678 
2679 	cmnp->node = pcnp;
2680 
2681 	/* Add to node's minor list */
2682 	if (prev == NULL) {
2683 		cmnp->sib = pcnp->minor;
2684 		pcnp->minor = cmnp;
2685 	} else {
2686 		assert(*prev == NULL);
2687 		*prev = cmnp;
2688 	}
2689 
2690 	return (cmnp);
2691 }
2692 
2693 static cache_link_t *
2694 link_insert(
2695 	struct di_devlink_handle *hdp,
2696 	cache_minor_t *cmnp,
2697 	const char *path,
2698 	const char *content,
2699 	uint32_t attr)
2700 {
2701 	cache_link_t *clp;
2702 
2703 	if (path == NULL || content == NULL || !check_attr(attr)) {
2704 		errno = EINVAL;
2705 		SET_DB_ERR(hdp);
2706 		return (NULL);
2707 	}
2708 
2709 	if ((clp = calloc(1, sizeof (cache_link_t))) == NULL) {
2710 		SET_DB_ERR(hdp);
2711 		return (NULL);
2712 	}
2713 
2714 	clp->path = strdup(path);
2715 	clp->content = strdup(content);
2716 	if (clp->path == NULL || clp->content == NULL) {
2717 		SET_DB_ERR(hdp);
2718 		link_free(&clp);
2719 		return (NULL);
2720 	}
2721 
2722 	clp->attr = attr;
2723 	hash_insert(hdp, clp);
2724 	clp->minor = cmnp;
2725 
2726 	/* Add to minor's link list */
2727 	if (cmnp != NULL) {
2728 		clp->sib = cmnp->link;
2729 		cmnp->link = clp;
2730 	} else {
2731 		clp->sib = CACHE(hdp)->dngl;
2732 		CACHE(hdp)->dngl = clp;
2733 	}
2734 
2735 	return (clp);
2736 }
2737 
2738 static void
2739 hash_insert(struct di_devlink_handle *hdp, cache_link_t *clp)
2740 {
2741 	uint_t hval;
2742 
2743 	hval = hashfn(hdp, clp->path);
2744 	clp->hash = CACHE_HASH(hdp, hval);
2745 	CACHE_HASH(hdp, hval) = clp;
2746 }
2747 
2748 
2749 static struct db_node *
2750 get_node(struct di_devlink_handle *hdp, uint32_t idx)
2751 {
2752 	return (map_seg(hdp, idx, PROT_READ, DB_NODE));
2753 }
2754 
2755 static struct db_node *
2756 set_node(struct di_devlink_handle *hdp, uint32_t idx)
2757 {
2758 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_NODE));
2759 }
2760 
2761 static struct db_minor *
2762 get_minor(struct di_devlink_handle *hdp, uint32_t idx)
2763 {
2764 	return (map_seg(hdp, idx, PROT_READ, DB_MINOR));
2765 }
2766 
2767 static struct db_minor *
2768 set_minor(struct di_devlink_handle *hdp, uint32_t idx)
2769 {
2770 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_MINOR));
2771 }
2772 
2773 static struct db_link *
2774 get_link(struct di_devlink_handle *hdp, uint32_t idx)
2775 {
2776 	return (map_seg(hdp, idx, PROT_READ, DB_LINK));
2777 }
2778 
2779 static struct db_link *
2780 set_link(struct di_devlink_handle *hdp, uint32_t idx)
2781 {
2782 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_LINK));
2783 }
2784 
2785 static char *
2786 get_string(struct di_devlink_handle *hdp, uint32_t idx)
2787 {
2788 	return (map_seg(hdp, idx, PROT_READ, DB_STR));
2789 }
2790 
2791 static char *
2792 set_string(struct di_devlink_handle *hdp, uint32_t idx)
2793 {
2794 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_STR));
2795 }
2796 
2797 
2798 /*
2799  * Returns the element corresponding to idx. If the portion of file involved
2800  * is not yet mapped, does an mmap() as well. Existing mappings are not changed.
2801  */
2802 static void *
2803 map_seg(
2804 	struct di_devlink_handle *hdp,
2805 	uint32_t idx,
2806 	int prot,
2807 	db_seg_t seg)
2808 {
2809 	int s;
2810 	off_t off;
2811 	size_t slen;
2812 	caddr_t addr;
2813 
2814 	if (idx == DB_NIL) {
2815 		return (NULL);
2816 	}
2817 
2818 	if (!VALID_INDEX(hdp, seg, idx)) {
2819 		(void) dprintf(DBG_ERR, "map_seg: seg(%d): invalid idx(%u)\n",
2820 		    seg, idx);
2821 		return (NULL);
2822 	}
2823 
2824 	/*
2825 	 * If the seg is already mapped in, use it if the access type is
2826 	 * valid.
2827 	 */
2828 	if (DB_SEG(hdp, seg) != NULL) {
2829 		if (DB_SEG_PROT(hdp, seg) != prot) {
2830 			(void) dprintf(DBG_ERR, "map_seg: illegal access: "
2831 			    "seg[%d]: idx=%u, seg_prot=%d, access=%d\n",
2832 			    seg, idx, DB_SEG_PROT(hdp, seg), prot);
2833 			return (NULL);
2834 		}
2835 		return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]);
2836 	}
2837 
2838 	/*
2839 	 * Segment is not mapped. Mmap() the segment.
2840 	 */
2841 	off = seg_size(hdp, DB_HEADER);
2842 	for (s = 0; s < seg; s++) {
2843 		off += seg_size(hdp, s);
2844 	}
2845 	slen = seg_size(hdp, seg);
2846 
2847 	addr = mmap(0, slen, prot, MAP_SHARED, DB(hdp)->db_fd, off);
2848 	if (addr == MAP_FAILED) {
2849 		(void) dprintf(DBG_ERR, "map_seg: seg[%d]: mmap failed: %s\n",
2850 		    seg, strerror(errno));
2851 		(void) dprintf(DBG_ERR, "map_seg: args: len=%lu, prot=%d,"
2852 		    " fd=%d, off=%ld\n", (ulong_t)slen, prot, DB(hdp)->db_fd,
2853 		    off);
2854 		return (NULL);
2855 	}
2856 
2857 	DB_SEG(hdp, seg) = addr;
2858 	DB_SEG_PROT(hdp, seg) = prot;
2859 
2860 	(void) dprintf(DBG_STEP, "map_seg: seg[%d]: len=%lu, prot=%d, fd=%d, "
2861 	    "off=%ld, seg_base=%p\n", seg, (ulong_t)slen, prot, DB(hdp)->db_fd,
2862 	    off, (void *)addr);
2863 
2864 	return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]);
2865 }
2866 
2867 /*
2868  * Computes the size of a segment rounded up to the nearest page boundary.
2869  */
2870 static size_t
2871 seg_size(struct di_devlink_handle *hdp, int seg)
2872 {
2873 	size_t sz;
2874 
2875 	assert(DB_HDR(hdp)->page_sz);
2876 
2877 	if (seg == DB_HEADER) {
2878 		sz = HDR_LEN;
2879 	} else {
2880 		assert(DB_NUM(hdp, seg) >= 1);
2881 		sz = DB_NUM(hdp, seg) * elem_sizes[seg];
2882 	}
2883 
2884 	sz = (sz / DB_HDR(hdp)->page_sz) + 1;
2885 
2886 	sz *= DB_HDR(hdp)->page_sz;
2887 
2888 	return (sz);
2889 }
2890 
2891 static size_t
2892 size_db(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
2893 {
2894 	int i;
2895 	size_t sz;
2896 	cache_link_t *clp;
2897 
2898 	assert(page_sz > 0);
2899 
2900 	/* Take "NIL" element into account */
2901 	for (i = 0; i < DB_TYPES; i++) {
2902 		count[i] = 1;
2903 	}
2904 
2905 	count_node(CACHE(hdp)->root, count);
2906 
2907 	for (clp = CACHE(hdp)->dngl; clp != NULL; clp = clp->sib) {
2908 		count_link(clp, count);
2909 	}
2910 
2911 	sz = ((HDR_LEN / page_sz) + 1) * page_sz;
2912 	for (i = 0; i < DB_TYPES; i++) {
2913 		assert(count[i] >= 1);
2914 		sz += (((count[i] * elem_sizes[i]) / page_sz) + 1) * page_sz;
2915 		(void) dprintf(DBG_INFO, "N[%u]=%u\n", i, count[i]);
2916 	}
2917 	(void) dprintf(DBG_INFO, "DB size=%lu\n", (ulong_t)sz);
2918 
2919 	return (sz);
2920 }
2921 
2922 
2923 static void
2924 count_node(cache_node_t *cnp, uint32_t *count)
2925 {
2926 	cache_minor_t *cmnp;
2927 
2928 	if (cnp == NULL)
2929 		return;
2930 
2931 	count[DB_NODE]++;
2932 	count_string(cnp->path, count);
2933 
2934 	for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) {
2935 		count_minor(cmnp, count);
2936 	}
2937 
2938 	for (cnp = cnp->child; cnp != NULL; cnp = cnp->sib) {
2939 		count_node(cnp, count);
2940 	}
2941 
2942 }
2943 
2944 static void
2945 count_minor(cache_minor_t *cmnp, uint32_t *count)
2946 {
2947 	cache_link_t *clp;
2948 
2949 	if (cmnp == NULL)
2950 		return;
2951 
2952 	count[DB_MINOR]++;
2953 	count_string(cmnp->name, count);
2954 	count_string(cmnp->nodetype, count);
2955 
2956 	for (clp = cmnp->link; clp != NULL; clp = clp->sib) {
2957 		count_link(clp, count);
2958 	}
2959 }
2960 
2961 static void
2962 count_link(cache_link_t *clp, uint32_t *count)
2963 {
2964 	if (clp == NULL)
2965 		return;
2966 
2967 	count[DB_LINK]++;
2968 	count_string(clp->path, count);
2969 	count_string(clp->content, count);
2970 }
2971 
2972 
2973 static void
2974 count_string(const char *str, uint32_t *count)
2975 {
2976 	if (str == NULL) {
2977 		(void) dprintf(DBG_ERR, "count_string: NULL argument\n");
2978 		return;
2979 	}
2980 
2981 	count[DB_STR] += strlen(str) + 1;
2982 }
2983 
2984 static uint_t
2985 hashfn(struct di_devlink_handle *hdp, const char *str)
2986 {
2987 	const char *cp;
2988 	ulong_t hval = 0;
2989 
2990 	if (str == NULL) {
2991 		return (0);
2992 	}
2993 
2994 	assert(CACHE(hdp)->hash_sz >= MIN_HASH_SIZE);
2995 
2996 	for (cp = str; *cp != '\0'; cp++) {
2997 		hval += *cp;
2998 	}
2999 
3000 	return (hval % CACHE(hdp)->hash_sz);
3001 }
3002 
3003 /*
3004  * enter_db_lock()
3005  *
3006  * If the handle is IS_RDWR then we lock as writer to "update" database,
3007  * if IS_RDONLY then we lock as reader to "snapshot" database. The
3008  * implementation uses advisory file locking.
3009  *
3010  * This function returns:
3011  *   == 1	success and grabbed the lock file, we can open the DB.
3012  *   == 0	success but did not lock the lock file,	reader must walk
3013  *		the /dev directory.
3014  *   == -1	failure.
3015  */
3016 static int
3017 enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir)
3018 {
3019 	int		fd;
3020 	struct flock	lock;
3021 	char		lockfile[PATH_MAX];
3022 	int		rv;
3023 	int		writer = HDL_RDWR(hdp);
3024 	static int	did_sync = 0;
3025 	int		eintrs;
3026 
3027 	assert(hdp->lock_fd < 0);
3028 
3029 	get_db_path(hdp, DB_LOCK, lockfile, sizeof (lockfile));
3030 
3031 	dprintf(DBG_LCK, "enter_db_lock: %s BEGIN\n",
3032 	    writer ? "update" : "snapshot");
3033 
3034 	/* Record locks are per-process. Protect against multiple threads. */
3035 	(void) mutex_lock(&update_mutex);
3036 
3037 again:	if ((fd = open(lockfile,
3038 	    (writer ? (O_RDWR|O_CREAT) : O_RDONLY), DB_LOCK_PERMS)) < 0) {
3039 		/*
3040 		 * Typically the lock file and the database go hand in hand.
3041 		 * If we find that the lock file does not exist (for some
3042 		 * unknown reason) and we are the reader then we return
3043 		 * success (after triggering devfsadm to create the file and
3044 		 * a retry) so that we can still provide service via slow
3045 		 * /dev walk.  If we get a failure as a writer we want the
3046 		 * error to manifests itself.
3047 		 */
3048 		if ((errno == ENOENT) && !writer) {
3049 			/* If reader, signal once to get files created */
3050 			if (did_sync == 0) {
3051 				did_sync = 1;
3052 				dprintf(DBG_LCK, "enter_db_lock: %s OSYNC\n",
3053 				    writer ? "update" : "snapshot");
3054 
3055 				/* signal to get files created */
3056 				(void) devlink_create(root_dir, NULL,
3057 				    DCA_DEVLINK_SYNC);
3058 				goto again;
3059 			}
3060 			dprintf(DBG_LCK, "enter_db_lock: %s OPENFAILD %s: "
3061 			    "WALK\n", writer ? "update" : "snapshot",
3062 			    strerror(errno));
3063 			(void) mutex_unlock(&update_mutex);
3064 			return (0);		/* success, but not locked */
3065 		} else {
3066 			dprintf(DBG_LCK, "enter_db_lock: %s OPENFAILD %s\n",
3067 			    writer ? "update" : "snapshot", strerror(errno));
3068 			(void) mutex_unlock(&update_mutex);
3069 			return (-1);		/* failed */
3070 		}
3071 	}
3072 
3073 	lock.l_type = writer ? F_WRLCK : F_RDLCK;
3074 	lock.l_whence = SEEK_SET;
3075 	lock.l_start = 0;
3076 	lock.l_len = 0;
3077 
3078 	/* Enter the lock. */
3079 	for (eintrs = 0; eintrs < MAX_LOCK_RETRY; eintrs++) {
3080 		rv = fcntl(fd, F_SETLKW, &lock);
3081 		if ((rv != -1) || (errno != EINTR))
3082 			break;
3083 	}
3084 
3085 	if (rv != -1) {
3086 		hdp->lock_fd = fd;
3087 		dprintf(DBG_LCK, "enter_db_lock: %s LOCKED\n",
3088 		    writer ? "update" : "snapshot");
3089 		return (1);		/* success, locked */
3090 	}
3091 
3092 	(void) close(fd);
3093 	dprintf(DBG_ERR, "enter_db_lock: %s FAILED: %s: WALK\n",
3094 	    writer ? "update" : "snapshot", strerror(errno));
3095 	(void) mutex_unlock(&update_mutex);
3096 	return (-1);
3097 }
3098 
3099 /*
3100  * Close and re-open lock file every time so that it is recreated if deleted.
3101  */
3102 static void
3103 exit_db_lock(struct di_devlink_handle *hdp)
3104 {
3105 	struct flock	unlock;
3106 	int		writer = HDL_RDWR(hdp);
3107 
3108 	if (hdp->lock_fd < 0) {
3109 		return;
3110 	}
3111 
3112 	unlock.l_type = F_UNLCK;
3113 	unlock.l_whence = SEEK_SET;
3114 	unlock.l_start = 0;
3115 	unlock.l_len = 0;
3116 
3117 	dprintf(DBG_LCK, "exit_db_lock : %s UNLOCKED\n",
3118 	    writer ? "update" : "snapshot");
3119 	if (fcntl(hdp->lock_fd, F_SETLK, &unlock) == -1) {
3120 		dprintf(DBG_ERR, "exit_db_lock : %s failed: %s\n",
3121 		    writer ? "update" : "snapshot", strerror(errno));
3122 	}
3123 
3124 	(void) close(hdp->lock_fd);
3125 
3126 	hdp->lock_fd = -1;
3127 
3128 	(void) mutex_unlock(&update_mutex);
3129 }
3130 
3131 /*
3132  * returns 1 if contents is a minor node in /devices.
3133  * If mn_root is not NULL, mn_root is set to:
3134  *	if contents is a /dev node, mn_root = contents
3135  *			OR
3136  *	if contents is a /devices node, mn_root set to the '/'
3137  *	following /devices.
3138  */
3139 int
3140 is_minor_node(const char *contents, const char **mn_root)
3141 {
3142 	char *ptr, *prefix;
3143 
3144 	prefix = "../devices/";
3145 
3146 	if ((ptr = strstr(contents, prefix)) != NULL) {
3147 
3148 		/* mn_root should point to the / following /devices */
3149 		if (mn_root != NULL) {
3150 			*mn_root = ptr += strlen(prefix) - 1;
3151 		}
3152 		return (1);
3153 	}
3154 
3155 	prefix = "/devices/";
3156 
3157 	if (strncmp(contents, prefix, strlen(prefix)) == 0) {
3158 
3159 		/* mn_root should point to the / following /devices/ */
3160 		if (mn_root != NULL) {
3161 			*mn_root = contents + strlen(prefix) - 1;
3162 		}
3163 		return (1);
3164 	}
3165 
3166 	if (mn_root != NULL) {
3167 		*mn_root = contents;
3168 	}
3169 	return (0);
3170 }
3171 
3172 static int
3173 s_readlink(const char *link, char *buf, size_t blen)
3174 {
3175 	int rv;
3176 
3177 	if ((rv = readlink(link, buf, blen)) == -1)
3178 		goto bad;
3179 
3180 	if (rv >= blen && buf[blen - 1] != '\0') {
3181 		errno = ENAMETOOLONG;
3182 		goto bad;
3183 	} else if (rv < blen) {
3184 		buf[rv] = '\0';
3185 	}
3186 
3187 	return (0);
3188 bad:
3189 	dprintf(DBG_ERR, "s_readlink: %s: failed: %s\n",
3190 	    link, strerror(errno));
3191 	return (-1);
3192 }
3193 
3194 /*
3195  * Synchronous link creation interface routines
3196  * The scope of the operation is determined by the "name" arg.
3197  * "name" can be NULL, a driver name or a devfs pathname (without /devices)
3198  *
3199  *	"name"				creates
3200  *	======				=======
3201  *
3202  *	NULL		=>		All devlinks in system
3203  *	<driver>	=>		devlinks for named driver
3204  *	/pci@1		=>		devlinks for subtree rooted at pci@1
3205  *	/pseudo/foo@0:X	=>		devlinks for minor X
3206  *
3207  * devlink_create() returns 0 on success or an errno value on failure
3208  */
3209 
3210 #define	MAX_DAEMON_ATTEMPTS 2
3211 
3212 static int
3213 devlink_create(const char *root, const char *name, int dca_devlink_flag)
3214 {
3215 	int i;
3216 	struct dca_off dca;
3217 
3218 	assert(root);
3219 
3220 	/*
3221 	 * Convert name into arg for door_call
3222 	 */
3223 	if (dca_init(name, &dca, dca_devlink_flag) != 0)
3224 		return (EINVAL);
3225 
3226 	/*
3227 	 * Attempt to use the daemon first
3228 	 */
3229 	i = 0;
3230 	do {
3231 		daemon_call(root, &dca);
3232 
3233 		dprintf(DBG_INFO, "daemon_call() retval=%d\n", dca.dca_error);
3234 
3235 		/*
3236 		 * Retry only if door server isn't running
3237 		 */
3238 		if (dca.dca_error != ENOENT && dca.dca_error != EBADF) {
3239 			return (dca.dca_error);
3240 		}
3241 
3242 		dca.dca_error = 0;
3243 
3244 		/*
3245 		 * To improve performance defer this check until the first
3246 		 * failure. Safe to defer as door server checks perms.
3247 		 */
3248 		if (geteuid() != 0)
3249 			return (EPERM);
3250 	/*
3251 	 * Daemon may not be running. Try to start it.
3252 	 */
3253 	} while ((++i < MAX_DAEMON_ATTEMPTS) && start_daemon(root) == 0);
3254 
3255 	dprintf(DBG_INFO, "devlink_create: can't start daemon\n");
3256 
3257 	assert(dca.dca_error == 0);
3258 
3259 	/*
3260 	 * If the daemon cannot be started execute the devfsadm command.
3261 	 */
3262 	exec_cmd(root, &dca);
3263 
3264 	return (dca.dca_error);
3265 }
3266 
3267 /*
3268  * The "name" member of "struct dca" contains data in the following order
3269  *	root'\0'minor'\0'driver'\0'
3270  * The root component is always present at offset 0 in the "name" field.
3271  * The driver and minor are optional. If present they have a non-zero
3272  * offset in the "name" member.
3273  */
3274 static int
3275 dca_init(const char *name, struct dca_off *dcp, int dca_flags)
3276 {
3277 	char *cp;
3278 
3279 	dcp->dca_root = 0;
3280 	dcp->dca_minor = 0;
3281 	dcp->dca_driver = 0;
3282 	dcp->dca_error = 0;
3283 	dcp->dca_flags = dca_flags;
3284 	dcp->dca_name[0] = '\0';
3285 
3286 	name = name ? name : "/";
3287 
3288 	/*
3289 	 *  Check if name is a driver name
3290 	 */
3291 	if (*name != '/') {
3292 		(void) snprintf(dcp->dca_name, sizeof (dcp->dca_name),
3293 		    "/ %s", name);
3294 		dcp->dca_root = 0;
3295 		*(dcp->dca_name + 1) = '\0';
3296 		dcp->dca_driver = 2;
3297 		return (0);
3298 	}
3299 
3300 	(void) snprintf(dcp->dca_name, sizeof (dcp->dca_name), "%s", name);
3301 
3302 	/*
3303 	 * "/devices" not allowed in devfs pathname
3304 	 */
3305 	if (is_minor_node(name, NULL))
3306 		return (-1);
3307 
3308 	dcp->dca_root = 0;
3309 	if (cp = strrchr(dcp->dca_name, ':')) {
3310 		*cp++ = '\0';
3311 		dcp->dca_minor = cp - dcp->dca_name;
3312 	}
3313 
3314 	return (0);
3315 }
3316 
3317 
3318 #define	DAEMON_STARTUP_TIME	1 /* 1 second. This may need to be adjusted */
3319 #define	DEVNAME_CHECK_FILE	"/etc/devname_check_RDONLY"
3320 
3321 static void
3322 daemon_call(const char *root, struct dca_off *dcp)
3323 {
3324 	door_arg_t	arg;
3325 	int		fd, door_error;
3326 	sigset_t	oset, nset;
3327 	char		synch_door[PATH_MAX];
3328 	struct stat	sb;
3329 	char		*prefix;
3330 	int		rofd;
3331 
3332 	/*
3333 	 * If /etc/dev missing and readonly root, assume we are in install.
3334 	 * Don't use statvfs() since it doesn't always report the truth.
3335 	 */
3336 	rofd = -1;
3337 	if (stat("/etc/dev", &sb) == -1 &&
3338 	    (rofd = open(DEVNAME_CHECK_FILE,
3339 	    O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1 && errno == EROFS)
3340 		prefix = "/tmp";
3341 	else {
3342 		if (rofd != -1) {
3343 			(void) close(rofd);
3344 			(void) unlink(DEVNAME_CHECK_FILE);
3345 		}
3346 		prefix = (char *)root;
3347 	}
3348 
3349 	(void) snprintf(synch_door, sizeof (synch_door),
3350 	    "%s/etc/dev/%s", prefix, DEVFSADM_SYNCH_DOOR);
3351 
3352 	if ((fd = open(synch_door, O_RDONLY)) == -1) {
3353 		dcp->dca_error = errno;
3354 		dprintf(DBG_ERR, "open of %s failed: %s\n",
3355 		    synch_door, strerror(errno));
3356 		return;
3357 	}
3358 
3359 	arg.data_ptr = (char *)dcp;
3360 	arg.data_size = sizeof (*dcp);
3361 	arg.desc_ptr = NULL;
3362 	arg.desc_num = 0;
3363 	arg.rbuf = (char *)dcp;
3364 	arg.rsize = sizeof (*dcp);
3365 
3366 	/*
3367 	 * Block signals to this thread until door call
3368 	 * completes.
3369 	 */
3370 	(void) sigfillset(&nset);
3371 	(void) sigemptyset(&oset);
3372 	(void) sigprocmask(SIG_SETMASK, &nset, &oset);
3373 	if (door_call(fd, &arg)) {
3374 		door_error = 1;
3375 		dcp->dca_error = errno;
3376 	}
3377 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
3378 
3379 	(void) close(fd);
3380 
3381 	if (door_error)
3382 		return;
3383 
3384 	assert(arg.data_ptr);
3385 
3386 	/*LINTED*/
3387 	dcp->dca_error = ((struct dca_off *)arg.data_ptr)->dca_error;
3388 
3389 	/*
3390 	 * The doors interface may return data in a different buffer
3391 	 * If that happens, deallocate buffer via munmap()
3392 	 */
3393 	if (arg.rbuf != (char *)dcp)
3394 		(void) munmap(arg.rbuf, arg.rsize);
3395 }
3396 
3397 #define	DEVFSADM_PATH	"/usr/sbin/devfsadm"
3398 #define	DEVFSADM	"devfsadm"
3399 
3400 #define	DEVFSADMD_PATH	"/usr/lib/devfsadm/devfsadmd"
3401 #define	DEVFSADM_DAEMON	"devfsadmd"
3402 
3403 static int
3404 start_daemon(const char *root)
3405 {
3406 	int rv, i = 0;
3407 	char *argv[20];
3408 
3409 	argv[i++] = DEVFSADM_DAEMON;
3410 	if (strcmp(root, "/")) {
3411 		argv[i++] = "-r";
3412 		argv[i++] = (char *)root;
3413 	}
3414 	argv[i++] = NULL;
3415 
3416 	rv = do_exec(DEVFSADMD_PATH, argv);
3417 
3418 	(void) sleep(DAEMON_STARTUP_TIME);
3419 
3420 	return (rv);
3421 }
3422 
3423 static void
3424 exec_cmd(const char *root, struct dca_off *dcp)
3425 {
3426 	int i;
3427 	char *argv[20];
3428 
3429 	i = 0;
3430 	argv[i++] = DEVFSADM;
3431 
3432 	/*
3433 	 * Load drivers only if -i is specified
3434 	 */
3435 	if (dcp->dca_driver) {
3436 		argv[i++] = "-i";
3437 		argv[i++] = &dcp->dca_name[dcp->dca_driver];
3438 	} else {
3439 		argv[i++] = "-n";
3440 	}
3441 
3442 	if (root != NULL && strcmp(root, "/") != 0) {
3443 		argv[i++] = "-r";
3444 		argv[i++] = (char *)root;
3445 	}
3446 
3447 	argv[i] = NULL;
3448 
3449 	if (do_exec(DEVFSADM_PATH, argv))
3450 		dcp->dca_error = errno;
3451 }
3452 
3453 static int
3454 do_exec(const char *path, char *const argv[])
3455 {
3456 	int i;
3457 	pid_t cpid;
3458 
3459 #ifdef	DEBUG
3460 	dprintf(DBG_INFO, "Executing %s\n\tArgument list:", path);
3461 	for (i = 0; argv[i] != NULL; i++) {
3462 		dprintf(DBG_INFO, " %s", argv[i]);
3463 	}
3464 	dprintf(DBG_INFO, "\n");
3465 #endif
3466 
3467 	if ((cpid = fork1()) == -1) {
3468 		dprintf(DBG_ERR, "fork1 failed: %s\n", strerror(errno));
3469 		return (-1);
3470 	}
3471 
3472 	if (cpid == 0) { /* child process */
3473 		int fd;
3474 
3475 		if ((fd = open("/dev/null", O_RDWR)) >= 0) {
3476 			(void) dup2(fd, fileno(stdout));
3477 			(void) dup2(fd, fileno(stderr));
3478 			(void) close(fd);
3479 
3480 			(void) execv(path, argv);
3481 		} else {
3482 			dprintf(DBG_ERR, "open of /dev/null failed: %s\n",
3483 			    strerror(errno));
3484 		}
3485 
3486 		_exit(-1);
3487 	}
3488 
3489 	/* Parent process */
3490 	if (waitpid(cpid, &i, 0) == cpid) {
3491 		if (WIFEXITED(i)) {
3492 			if (WEXITSTATUS(i) == 0) {
3493 				dprintf(DBG_STEP,
3494 				    "do_exec: child exited normally\n");
3495 				return (0);
3496 			} else
3497 				errno = EINVAL;
3498 		} else {
3499 			/*
3500 			 * The child was interrupted by a signal
3501 			 */
3502 			errno = EINTR;
3503 		}
3504 		dprintf(DBG_ERR, "child terminated abnormally: %s\n",
3505 		    strerror(errno));
3506 	} else {
3507 		dprintf(DBG_ERR, "waitpid failed: %s\n", strerror(errno));
3508 	}
3509 
3510 	return (-1);
3511 }
3512 
3513 static int
3514 walk_cache_links(di_devlink_handle_t hdp, cache_link_t *clp, link_desc_t *linkp)
3515 {
3516 	int i;
3517 
3518 	assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp));
3519 
3520 	dprintf(DBG_INFO, "walk_cache_links: initial link: %s\n",
3521 	    clp ? clp->path : "<NULL>");
3522 
3523 	/*
3524 	 * First search the links under the specified minor. On the
3525 	 * 2nd pass, search the dangling list - secondary links may
3526 	 * exist on this list since they are not resolved during the
3527 	 * /dev walk.
3528 	 */
3529 	for (i = 0; i < 2; i++) {
3530 		for (; clp != NULL; clp = clp->sib) {
3531 			struct di_devlink vlink = {NULL};
3532 
3533 			assert(clp->path[0] != '/');
3534 
3535 			vlink.rel_path = clp->path;
3536 			vlink.content = clp->content;
3537 			vlink.type = attr2type(clp->attr);
3538 
3539 			if (visit_link(hdp, linkp, &vlink)
3540 			    != DI_WALK_CONTINUE) {
3541 				dprintf(DBG_INFO, "walk_cache_links: "
3542 				    "terminating at link: %s\n",