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