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