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);
1036 }
1037 
1038 /*
1039  * Frees the tree rooted at a node. Siblings of the subtree root
1040  * have to be handled by the caller.
1041  */
1042 static void
1043 subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp)
1044 {
1045 	cache_node_t *np;
1046 	cache_link_t *clp;
1047 	cache_minor_t *cmnp;
1048 
1049 	if (pp == NULL || *pp == NULL)
1050 		return;
1051 
1052 	while ((*pp)->child != NULL) {
1053 		np = (*pp)->child;
1054 		(*pp)->child = np->sib;
1055 		subtree_free(hdp, &np);
1056 	}
1057 
1058 	while ((*pp)->minor != NULL) {
1059 		cmnp = (*pp)->minor;
1060 		(*pp)->minor = cmnp->sib;
1061 
1062 		while (cmnp->link != NULL) {
1063 			clp = cmnp->link;
1064 			cmnp->link = clp->sib;
1065 			rm_link_from_hash(hdp, clp);
1066 			link_free(&clp);
1067 		}
1068 		minor_free(hdp, &cmnp);
1069 	}
1070 
1071 	node_free(pp);
1072 }
1073 
1074 static void
1075 rm_link_from_hash(struct di_devlink_handle *hdp, cache_link_t *clp)
1076 {
1077 	int hval;
1078 	cache_link_t **pp;
1079 
1080 	if (clp == NULL)
1081 		return;
1082 
1083 	if (clp->path == NULL)
1084 		return;
1085 
1086 	hval = hashfn(hdp, clp->path);
1087 	pp = &(CACHE_HASH(hdp, hval));
1088 	for (; *pp != NULL; pp = &(*pp)->hash) {
1089 		if (*pp == clp) {
1090 			*pp = clp->hash;
1091 			clp->hash = NULL;
1092 			return;
1093 		}
1094 	}
1095 
1096 	dprintf(DBG_ERR, "rm_link_from_hash: link(%s) not found\n", clp->path);
1097 }
1098 
1099 static cache_link_t *
1100 link_hash(di_devlink_handle_t hdp, const char *link, uint_t flags)
1101 {
1102 	int hval;
1103 	cache_link_t **pp, *clp;
1104 
1105 	if (link == NULL)
1106 		return (NULL);
1107 
1108 	hval = hashfn(hdp, link);
1109 	pp = &(CACHE_HASH(hdp, hval));
1110 	for (; (clp = *pp) != NULL; pp = &clp->hash) {
1111 		if (strcmp(clp->path, link) == 0) {
1112 			break;
1113 		}
1114 	}
1115 
1116 	if (clp == NULL)
1117 		return (NULL);
1118 
1119 	if ((flags & UNLINK_FROM_HASH) == UNLINK_FROM_HASH) {
1120 		*pp = clp->hash;
1121 		clp->hash = NULL;
1122 	}
1123 
1124 	return (clp);
1125 }
1126 
1127 static cache_minor_t *
1128 link2minor(struct di_devlink_handle *hdp, cache_link_t *clp)
1129 {
1130 	cache_link_t *plp;
1131 	const char *minor_path;
1132 	char *cp, buf[PATH_MAX], link[PATH_MAX];
1133 	char abspath[PATH_MAX];
1134 	struct stat st;
1135 
1136 	if (TYPE_PRI(attr2type(clp->attr))) {
1137 		/*
1138 		 * For primary link, content should point to a /devices node.
1139 		 */
1140 		if (!is_minor_node(clp->content, &minor_path)) {
1141 			return (NULL);
1142 		}
1143 
1144 		return (lookup_minor(hdp, minor_path, NULL,
1145 		    TYPE_CACHE|CREATE_FLAG));
1146 
1147 	}
1148 
1149 	/*
1150 	 * If secondary, the primary link is derived from the secondary
1151 	 * link contents. Secondary link contents can have two formats:
1152 	 *	audio -> /dev/sound/0
1153 	 *	fb0 -> fbs/afb0
1154 	 */
1155 
1156 	buf[0] = '\0';
1157 	if (strncmp(clp->content, DEV"/", strlen(DEV"/")) == 0) {
1158 		cp = &clp->content[strlen(DEV"/")];
1159 	} else if (clp->content[0] != '/') {
1160 		if ((cp = strrchr(clp->path, '/')) != NULL) {
1161 			char savechar = *(cp + 1);
1162 			*(cp + 1) = '\0';
1163 			(void) snprintf(buf, sizeof (buf), "%s", clp->path);
1164 			*(cp + 1) = savechar;
1165 		}
1166 		(void) strlcat(buf, clp->content, sizeof (buf));
1167 		cp = buf;
1168 	} else {
1169 		goto follow_link;
1170 	}
1171 
1172 	/*
1173 	 * Lookup the primary link if possible and find its minor.
1174 	 */
1175 	if ((plp = link_hash(hdp, cp, 0)) != NULL && plp->minor != NULL) {
1176 		return (plp->minor);
1177 	}
1178 
1179 	/* realpath() used only as a last resort because it is expensive */
1180 follow_link:
1181 	(void) snprintf(link, sizeof (link), "%s/%s", hdp->dev_dir, clp->path);
1182 
1183 #ifdef	DEBUG
1184 	/*LINTED*/
1185 	assert(sizeof (buf) >= PATH_MAX);
1186 #endif
1187 
1188 	/*
1189 	 * A realpath attempt to lookup a dangling link can invoke implicit
1190 	 * reconfig so verify there's an actual device behind the link first.
1191 	 */
1192 	if (lstat(link, &st) == -1)
1193 		return (NULL);
1194 	if (S_ISLNK(st.st_mode)) {
1195 		if (s_readlink(link, buf, sizeof (buf)) < 0)
1196 			return (NULL);
1197 		if (buf[0] != '/') {
1198 			char *p;
1199 			size_t n = sizeof (abspath);
1200 			if (strlcpy(abspath, link, n) >= n)
1201 				return (NULL);
1202 			p = strrchr(abspath, '/') + 1;
1203 			*p = 0;
1204 			n = sizeof (abspath) - strlen(p);
1205 			if (strlcpy(p, buf, n) >= n)
1206 				return (NULL);
1207 		} else {
1208 			if (strlcpy(abspath, buf, sizeof (abspath)) >=
1209 			    sizeof (abspath))
1210 				return (NULL);
1211 		}
1212 		if (!device_exists(abspath))
1213 			return (NULL);
1214 	}
1215 
1216 	if (realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) {
1217 		return (NULL);
1218 	}
1219 	return (lookup_minor(hdp, minor_path, NULL, TYPE_CACHE|CREATE_FLAG));
1220 }
1221 
1222 
1223 static void
1224 resolve_dangling_links(struct di_devlink_handle *hdp)
1225 {
1226 	cache_minor_t *cmnp;
1227 	cache_link_t *clp, **pp;
1228 
1229 	for (pp = &(CACHE(hdp)->dngl); *pp != NULL; ) {
1230 		clp = *pp;
1231 		if ((cmnp = link2minor(hdp, clp)) != NULL) {
1232 			*pp = clp->sib;
1233 			clp->sib = cmnp->link;
1234 			cmnp->link = clp;
1235 			assert(clp->minor == NULL);
1236 			clp->minor = cmnp;
1237 		} else {
1238 			dprintf(DBG_INFO, "resolve_dangling_links: link(%s):"
1239 			    " unresolved\n", clp->path);
1240 			pp = &clp->sib;
1241 		}
1242 	}
1243 }
1244 
1245 
1246 /*
1247  * The elements are assumed to be detached from the cache tree.
1248  */
1249 static void
1250 node_free(cache_node_t **pp)
1251 {
1252 	cache_node_t *cnp = *pp;
1253 
1254 	*pp = NULL;
1255 
1256 	if (cnp == NULL)
1257 		return;
1258 
1259 	free(cnp->path);
1260 	free(cnp);
1261 }
1262 
1263 static void
1264 minor_free(struct di_devlink_handle *hdp, cache_minor_t **pp)
1265 {
1266 	cache_minor_t *cmnp = *pp;
1267 
1268 	*pp = NULL;
1269 
1270 	if (cmnp == NULL)
1271 		return;
1272 
1273 	if (CACHE_LAST(hdp) == cmnp) {
1274 		dprintf(DBG_STEP, "minor_free: last_minor(%s)\n", cmnp->name);
1275 		CACHE_LAST(hdp) = NULL;
1276 	}
1277 
1278 	free(cmnp->name);
1279 	free(cmnp->nodetype);
1280 	free(cmnp);
1281 }
1282 
1283 static void
1284 link_free(cache_link_t **pp)
1285 {
1286 	cache_link_t *clp = *pp;
1287 
1288 	*pp = NULL;
1289 
1290 	if (clp == NULL)
1291 		return;
1292 
1293 	free(clp->path);
1294 	free(clp->content);
1295 	free(clp);
1296 }
1297 
1298 /*
1299  * Returns the ':' preceding the minor name
1300  */
1301 static char *
1302 minor_colon(const char *path)
1303 {
1304 	char *cp;
1305 
1306 	if ((cp = strrchr(path, '/')) == NULL) {
1307 		return (NULL);
1308 	}
1309 
1310 	return (strchr(cp, ':'));
1311 }
1312 
1313 static void *
1314 lookup_minor(
1315 	struct di_devlink_handle *hdp,
1316 	const char *minor_path,
1317 	const char *nodetype,
1318 	const int flags)
1319 {
1320 	void *vp;
1321 	char *colon;
1322 	char pdup[PATH_MAX];
1323 	const char *fcn = "lookup_minor";
1324 
1325 	if (minor_path == NULL) {
1326 		errno = EINVAL;
1327 		return (NULL);
1328 	}
1329 
1330 	(void) snprintf(pdup, sizeof (pdup), "%s", minor_path);
1331 
1332 	if ((colon = minor_colon(pdup)) == NULL) {
1333 		(void) dprintf(DBG_ERR, "%s: invalid minor path(%s)\n", fcn,
1334 		    minor_path);
1335 		errno = EINVAL;
1336 		return (NULL);
1337 	}
1338 	*colon = '\0';
1339 
1340 	if ((vp = get_last_minor(hdp, pdup, colon + 1, flags)) != NULL) {
1341 		return (vp);
1342 	}
1343 
1344 	if ((vp = lookup_node(hdp, pdup, flags)) == NULL) {
1345 		(void) dprintf(DBG_ERR, "%s: node(%s) not found\n", fcn, pdup);
1346 		return (NULL);
1347 	}
1348 	*colon = ':';
1349 
1350 	if (LOOKUP_CACHE(flags)) {
1351 		cache_minor_t **pp;
1352 
1353 		pp = &((cache_node_t *)vp)->minor;
1354 		for (; *pp != NULL; pp = &(*pp)->sib) {
1355 			if (strcmp((*pp)->name, colon + 1) == 0)
1356 				break;
1357 		}
1358 
1359 		if (*pp == NULL && CREATE_ELEM(flags)) {
1360 			*pp = minor_insert(hdp, vp, colon + 1, nodetype, pp);
1361 		}
1362 		set_last_minor(hdp, *pp, flags);
1363 
1364 		return (*pp);
1365 	} else {
1366 		char *cp;
1367 		uint32_t nidx;
1368 		struct db_minor *dmp;
1369 
1370 		nidx = (((struct db_node *)vp)->minor);
1371 		for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) {
1372 			cp = get_string(hdp, dmp->name);
1373 			if (cp && strcmp(cp, colon + 1) == 0)
1374 				break;
1375 		}
1376 		return (dmp);
1377 	}
1378 }
1379 
1380 static void *
1381 lookup_node(struct di_devlink_handle *hdp, char *path, const int flags)
1382 {
1383 	struct tnode tnd = {NULL};
1384 
1385 	if (tnd.node = get_last_node(hdp, path, flags))
1386 		return (tnd.node);
1387 
1388 	tnd.handle = hdp;
1389 	tnd.flags = flags;
1390 
1391 	if (walk_tree(path, &tnd, visit_node) != 0)
1392 		return (NULL);
1393 
1394 	return (tnd.node);
1395 }
1396 
1397 /*
1398  * last_minor is used for nodes of TYPE_CACHE only.
1399  */
1400 static void *
1401 get_last_node(struct di_devlink_handle *hdp, const char *path, int flags)
1402 {
1403 	cache_node_t *cnp;
1404 
1405 #ifdef	DEBUG
1406 	if (getenv(SKIP_LAST_CACHE)) {
1407 		(void) dprintf(DBG_INFO, "get_last_node: SKIPPING \"last\" "
1408 		    "node cache\n");
1409 		return (NULL);
1410 	}
1411 #endif
1412 
1413 	if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL ||
1414 	    CACHE_LAST(hdp)->node == NULL) {
1415 		return (NULL);
1416 	}
1417 
1418 	cnp = CACHE_LAST(hdp)->node;
1419 	if (strcmp(cnp->path, path) == 0) {
1420 		return (cnp);
1421 	}
1422 
1423 	cnp = cnp->sib;
1424 	if (cnp && strcmp(cnp->path, path) == 0) {
1425 		return (cnp);
1426 	}
1427 
1428 	return (NULL);
1429 }
1430 
1431 static void *
1432 get_last_minor(
1433 	struct di_devlink_handle *hdp,
1434 	const char *devfs_path,
1435 	const char *minor_name,
1436 	int flags)
1437 {
1438 	cache_minor_t *cmnp;
1439 
1440 #ifdef	DEBUG
1441 	if (getenv(SKIP_LAST_CACHE)) {
1442 		(void) dprintf(DBG_INFO, "get_last_minor: SKIPPING \"last\" "
1443 		    "minor cache\n");
1444 		return (NULL);
1445 	}
1446 #endif
1447 
1448 	if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL) {
1449 		return (NULL);
1450 	}
1451 
1452 	cmnp = CACHE_LAST(hdp);
1453 	if (strcmp(cmnp->name, minor_name) == 0 && cmnp->node &&
1454 	    strcmp(cmnp->node->path, devfs_path) == 0) {
1455 		return (cmnp);
1456 	}
1457 
1458 	cmnp = cmnp->sib;
1459 	if (cmnp && strcmp(cmnp->name, minor_name) == 0 && cmnp->node &&
1460 	    strcmp(cmnp->node->path, devfs_path) == 0) {
1461 		set_last_minor(hdp, cmnp, TYPE_CACHE);
1462 		return (cmnp);
1463 	}
1464 
1465 	return (NULL);
1466 }
1467 
1468 static void
1469 set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp, int flags)
1470 {
1471 #ifdef	DEBUG
1472 	if (getenv(SKIP_LAST_CACHE)) {
1473 		(void) dprintf(DBG_INFO, "set_last_minor: SKIPPING \"last\" "
1474 		    "minor cache\n");
1475 		return;
1476 	}
1477 #endif
1478 
1479 	if (LOOKUP_CACHE(flags) && cmnp) {
1480 		CACHE_LAST(hdp) = cmnp;
1481 	}
1482 }
1483 
1484 
1485 /*
1486  * Returns 0 if normal return or -1 otherwise.
1487  */
1488 static int
1489 walk_tree(
1490 	char *cur,
1491 	void *arg,
1492 	int (*node_callback)(const char *path, void *arg))
1493 {
1494 	char *slash, buf[PATH_MAX];
1495 
1496 	if (cur == NULL || cur[0] != '/' || strlen(cur) > sizeof (buf) - 1) {
1497 		errno = EINVAL;
1498 		return (-1);
1499 	}
1500 
1501 	(void) strcpy(buf, "/");
1502 
1503 	for (;;) {
1504 
1505 		if (node_callback(buf, arg) != DI_WALK_CONTINUE)
1506 			break;
1507 
1508 		while (*cur == '/')
1509 			cur++;
1510 
1511 		if (*cur == '\0')
1512 			break;
1513 
1514 		/*
1515 		 * There is a next component(s). Append a "/" separator for all
1516 		 * but the first (root) component.
1517 		 */
1518 		if (buf[1] != '\0') {
1519 			(void) strlcat(buf, "/", sizeof (buf));
1520 		}
1521 
1522 		if (slash = strchr(cur, '/')) {
1523 			*slash = '\0';
1524 			(void) strlcat(buf, cur, sizeof (buf));
1525 			*slash = '/';
1526 			cur = slash;
1527 		} else {
1528 			(void) strlcat(buf, cur, sizeof (buf));
1529 			cur += strlen(cur);
1530 		}
1531 
1532 	}
1533 
1534 	return (0);
1535 }
1536 
1537 
1538 static int
1539 visit_node(const char *path, void *arg)
1540 {
1541 	struct tnode *tnp = arg;
1542 
1543 	if (LOOKUP_CACHE(tnp->flags)) {
1544 
1545 		cache_node_t *cnp = tnp->node;
1546 
1547 		cnp = (cnp) ? cnp->child : CACHE_ROOT(tnp->handle);
1548 
1549 		for (; cnp != NULL; cnp = cnp->sib) {
1550 			if (strcmp(cnp->path, path) == 0)
1551 				break;
1552 		}
1553 		if (cnp == NULL && CREATE_ELEM(tnp->flags)) {
1554 			cnp = node_insert(tnp->handle, tnp->node, path,
1555 			    INSERT_TAIL);
1556 		}
1557 		tnp->node = cnp;
1558 	} else {
1559 		char *cp;
1560 		struct db_node *dnp = tnp->node;
1561 
1562 		dnp = (dnp) ? get_node(tnp->handle, dnp->child)
1563 		    : get_node(tnp->handle, DB_HDR(tnp->handle)->root_idx);
1564 
1565 		for (; dnp != NULL; dnp = get_node(tnp->handle, dnp->sib)) {
1566 			cp = get_string(tnp->handle, dnp->path);
1567 			if (cp && strcmp(cp, path) == 0) {
1568 				break;
1569 			}
1570 		}
1571 		tnp->node = dnp;
1572 	}
1573 
1574 	/*
1575 	 * Terminate walk if node is not found for a path component.
1576 	 */
1577 	return (tnp->node ? DI_WALK_CONTINUE : DI_WALK_TERMINATE);
1578 }
1579 
1580 static void
1581 minor_delete(di_devlink_handle_t hdp, cache_minor_t *cmnp)
1582 {
1583 	cache_link_t **lpp;
1584 	cache_minor_t **mpp;
1585 	const char *fcn = "minor_delete";
1586 
1587 	(void) dprintf(DBG_STEP, "%s: removing minor: %s\n", fcn, cmnp->name);
1588 
1589 	/* detach minor from node */
1590 	if (cmnp->node != NULL) {
1591 		mpp = &cmnp->node->minor;
1592 		for (; *mpp != NULL; mpp = &(*mpp)->sib) {
1593 			if (*mpp == cmnp)
1594 				break;
1595 		}
1596 
1597 		if (*mpp == NULL) {
1598 			(void) dprintf(DBG_ERR, "%s: dangling minor: %s\n",
1599 			    fcn, cmnp->name);
1600 		} else {
1601 			*mpp = cmnp->sib;
1602 		}
1603 	} else {
1604 		(void) dprintf(DBG_ERR, "%s: orphan minor(%s)\n", fcn,
1605 		    cmnp->name);
1606 	}
1607 
1608 	delete_unused_nodes(hdp, cmnp->node);
1609 
1610 	cmnp->node = NULL;
1611 	cmnp->sib = NULL;
1612 
1613 	/* Move all remaining links to dangling list */
1614 	for (lpp = &cmnp->link; *lpp != NULL; lpp = &(*lpp)->sib) {
1615 		(*lpp)->minor = NULL;
1616 	}
1617 	*lpp = CACHE(hdp)->dngl;
1618 	CACHE(hdp)->dngl = cmnp->link;
1619 	cmnp->link = NULL;
1620 
1621 	minor_free(hdp, &cmnp);
1622 }
1623 
1624 static void
1625 delete_unused_nodes(di_devlink_handle_t hdp, cache_node_t *cnp)
1626 {
1627 	cache_node_t **npp;
1628 	const char *fcn = "delete_unused_nodes";
1629 
1630 	if (cnp == NULL)
1631 		return;
1632 
1633 	if (cnp->minor != NULL || cnp->child != NULL)
1634 		return;
1635 
1636 	(void) dprintf(DBG_INFO, "%s: removing unused node: %s\n", fcn,
1637 	    cnp->path);
1638 
1639 	/* Unlink node from tree */
1640 	if (cnp->parent != NULL) {
1641 		npp = &cnp->parent->child;
1642 		for (; *npp != NULL; npp = &(*npp)->sib) {
1643 			if (*npp == cnp)
1644 				break;
1645 		}
1646 
1647 		if (*npp == NULL) {
1648 			(void) dprintf(DBG_ERR, "%s: dangling node: %s\n", fcn,
1649 			    cnp->path);
1650 		} else {
1651 			*npp = cnp->sib;
1652 		}
1653 	} else if (cnp == CACHE_ROOT(hdp)) {
1654 		CACHE_ROOT(hdp) = NULL;
1655 	} else {
1656 		(void) dprintf(DBG_ERR, "%s: orphan node (%s)\n", fcn,
1657 		    cnp->path);
1658 	}
1659 
1660 	delete_unused_nodes(hdp, cnp->parent);
1661 
1662 	cnp->parent = cnp->sib = NULL;
1663 
1664 	node_free(&cnp);
1665 }
1666 
1667 static int
1668 rm_link(di_devlink_handle_t hdp, const char *link)
1669 {
1670 	cache_link_t *clp;
1671 	const char *fcn = "rm_link";
1672 
1673 	if (hdp == NULL || DB_ERR(hdp) || link == NULL || link[0] == '/' ||
1674 	    (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) {
1675 		dprintf(DBG_ERR, "%s: %s: invalid args\n",
1676 		    fcn, link ? link : "<NULL>");
1677 		errno = EINVAL;
1678 		return (-1);
1679 	}
1680 
1681 	dprintf(DBG_STEP, "%s: link(%s)\n", fcn, link);
1682 
1683 	if ((clp = link_hash(hdp, link, UNLINK_FROM_HASH)) == NULL) {
1684 		return (0);
1685 	}
1686 
1687 	link_delete(hdp, clp);
1688 
1689 	return (0);
1690 }
1691 
1692 int
1693 di_devlink_rm_link(di_devlink_handle_t hdp, const char *link)
1694 {
1695 	if (hdp == NULL || !HDL_RDWR(hdp)) {
1696 		errno = EINVAL;
1697 		return (-1);
1698 	}
1699 
1700 	return (rm_link(hdp, link));
1701 }
1702 
1703 static void
1704 link_delete(di_devlink_handle_t hdp, cache_link_t *clp)
1705 {
1706 	cache_link_t **pp;
1707 	const char *fcn = "link_delete";
1708 
1709 	(void) dprintf(DBG_STEP, "%s: removing link: %s\n", fcn, clp->path);
1710 
1711 	if (clp->minor == NULL)
1712 		pp = &(CACHE(hdp)->dngl);
1713 	else
1714 		pp = &clp->minor->link;
1715 
1716 	for (; *pp != NULL; pp = &(*pp)->sib) {
1717 		if (*pp == clp)
1718 			break;
1719 	}
1720 
1721 	if (*pp == NULL) {
1722 		(void) dprintf(DBG_ERR, "%s: link(%s) not on list\n",
1723 		    fcn, clp->path);
1724 	} else {
1725 		*pp = clp->sib;
1726 	}
1727 
1728 	delete_unused_minor(hdp, clp->minor);
1729 
1730 	clp->minor = NULL;
1731 
1732 	link_free(&clp);
1733 }
1734 
1735 static void
1736 delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp)
1737 {
1738 	if (cmnp == NULL)
1739 		return;
1740 
1741 	if (cmnp->link != NULL)
1742 		return;
1743 
1744 	dprintf(DBG_STEP, "delete_unused_minor: removing minor(%s)\n",
1745 	    cmnp->name);
1746 
1747 	minor_delete(hdp, cmnp);
1748 }
1749 
1750 int
1751 di_devlink_add_link(
1752 	di_devlink_handle_t hdp,
1753 	const char *link,
1754 	const char *content,
1755 	int flags)
1756 {
1757 	return (add_link(hdp, link, content, flags) != NULL ? 0 : -1);
1758 }
1759 
1760 static cache_link_t *
1761 add_link(
1762 	struct di_devlink_handle *hdp,
1763 	const char *link,
1764 	const char *content,
1765 	int flags)
1766 {
1767 	uint32_t attr;
1768 	cache_link_t *clp;
1769 	cache_minor_t *cmnp;
1770 	const char *fcn = "add_link";
1771 
1772 	if (hdp == NULL || DB_ERR(hdp) || link == NULL ||
1773 	    link[0] == '/' || content == NULL || !link_flag(flags) ||
1774 	    (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) {
1775 		dprintf(DBG_ERR, "%s: %s: invalid args\n",
1776 		    fcn, link ? link : "<NULL>");
1777 		errno = EINVAL;
1778 		return (NULL);
1779 	}
1780 
1781 	if ((clp = link_hash(hdp, link, 0)) != NULL) {
1782 		if (link_cmp(clp, content, LINK_TYPE(flags)) != 0) {
1783 			(void) rm_link(hdp, link);
1784 		} else {
1785 			return (clp);
1786 		}
1787 	}
1788 
1789 	if (TYPE_PRI(flags)) {
1790 		const char *minor_path = NULL;
1791 
1792 		if (!is_minor_node(content, &minor_path)) {
1793 			(void) dprintf(DBG_ERR, "%s: invalid content(%s)"
1794 			    " for primary link\n", fcn, content);
1795 			errno = EINVAL;
1796 			return (NULL);
1797 		}
1798 		if ((cmnp = lookup_minor(hdp, minor_path, NULL,
1799 		    TYPE_CACHE|CREATE_FLAG)) == NULL) {
1800 			return (NULL);
1801 		}
1802 		attr = A_PRIMARY;
1803 	} else {
1804 		/*
1805 		 * Defer resolving a secondary link to a minor until the
1806 		 * database is closed. This ensures that the primary link
1807 		 * (required for a successful resolve) has also been created.
1808 		 */
1809 		cmnp = NULL;
1810 		attr = A_SECONDARY;
1811 	}
1812 
1813 	return (link_insert(hdp, cmnp, link, content, attr));
1814 }
1815 
1816 /*
1817  * Returns 0 on match or 1 otherwise.
1818  */
1819 static int
1820 link_cmp(cache_link_t *clp, const char *content, int type)
1821 {
1822 	if (strcmp(clp->content, content) != 0)
1823 		return (1);
1824 
1825 	if (attr2type(clp->attr) != type)
1826 		return (1);
1827 
1828 	return (0);
1829 }
1830 
1831 int
1832 di_devlink_update(di_devlink_handle_t hdp)
1833 {
1834 	if (hdp == NULL || !HDL_RDWR(hdp) || DB_ERR(hdp)) {
1835 		errno = EINVAL;
1836 		return (-1);
1837 	}
1838 
1839 	/*
1840 	 * Reset the counter to schedule a synchronization with /dev on the next
1841 	 * di_devlink_close().
1842 	 */
1843 	CACHE(hdp)->update_count = 0;
1844 
1845 	return (0);
1846 }
1847 
1848 static int
1849 synchronize_db(di_devlink_handle_t hdp)
1850 {
1851 	int hval;
1852 	cache_link_t *clp;
1853 	char pdup[PATH_MAX];
1854 	recurse_t rec = {NULL};
1855 	const char *fcn = "synchronize_db";
1856 
1857 	rec.data = NULL;
1858 	rec.fcn = cache_dev_link;
1859 
1860 	/*
1861 	 * Walk through $ROOT/dev, reading every link and marking the
1862 	 * corresponding cached version as valid(adding new links as needed).
1863 	 * Then walk through the cache and remove all unmarked links.
1864 	 */
1865 	if (recurse_dev(hdp, &rec) != 0) {
1866 		return (-1);
1867 	}
1868 
1869 	for (hval = 0; hval < CACHE(hdp)->hash_sz; hval++) {
1870 		for (clp = CACHE_HASH(hdp, hval); clp != NULL; ) {
1871 			if (GET_VALID_ATTR(clp->attr)) {
1872 				CLR_VALID_ATTR(clp->attr);
1873 				clp = clp->hash;
1874 				continue;
1875 			}
1876 
1877 			/*
1878 			 * The link is stale, so remove it. Since the link
1879 			 * will be destroyed, use a copy of the link path to
1880 			 * invoke the remove function.
1881 			 */
1882 			(void) snprintf(pdup, sizeof (pdup), "%s", clp->path);
1883 			clp = clp->hash;
1884 			(void) dprintf(DBG_STEP, "%s: removing invalid link:"
1885 			    " %s\n", fcn, pdup);
1886 			(void) di_devlink_rm_link(hdp, pdup);
1887 		}
1888 	}
1889 
1890 	(void) dprintf(DBG_STEP, "%s: update completed\n", fcn);
1891 
1892 	return (0);
1893 }
1894 
1895 static di_devlink_handle_t
1896 di_devlink_init_impl(const char *root, const char *name, uint_t flags)
1897 {
1898 	int	err = 0;
1899 
1900 	if ((flags != 0 && flags != DI_MAKE_LINK) ||
1901 	    (flags == 0 && name != NULL)) {
1902 		errno = EINVAL;
1903 		return (NULL);
1904 	}
1905 
1906 	if (flags == DI_MAKE_LINK && (err = devlink_create(root, name))) {
1907 		errno = err;
1908 		return (NULL);
1909 	}
1910 
1911 	(void) dprintf(DBG_INFO, "devlink_init_impl: success\n");
1912 
1913 	return (devlink_snapshot(root));
1914 }
1915 
1916 di_devlink_handle_t
1917 di_devlink_init(const char *name, uint_t flags)
1918 {
1919 	return (di_devlink_init_impl("/", name, flags));
1920 }
1921 
1922 di_devlink_handle_t
1923 di_devlink_init_root(const char *root, const char *name, uint_t flags)
1924 {
1925 	return (di_devlink_init_impl(root, name, flags));
1926 }
1927 
1928 static di_devlink_handle_t
1929 devlink_snapshot(const char *root_dir)
1930 {
1931 	struct di_devlink_handle *hdp;
1932 
1933 	if ((hdp = handle_alloc(root_dir, OPEN_RDONLY)) == NULL) {
1934 		return (NULL);
1935 	}
1936 
1937 	/*
1938 	 * If we cannot open the DB below, we will walk /dev
1939 	 * in di_devlink_walk.
1940 	 */
1941 	(void) open_db(hdp, OPEN_RDONLY);
1942 
1943 	return (hdp);
1944 }
1945 
1946 int
1947 di_devlink_fini(di_devlink_handle_t *pp)
1948 {
1949 	if (pp == NULL || *pp == NULL || !HDL_RDONLY(*pp)) {
1950 		errno = EINVAL;
1951 		return (-1);
1952 	}
1953 
1954 	/* Freeing the handle also closes the DB */
1955 	handle_free(pp);
1956 
1957 	return (0);
1958 }
1959 
1960 int
1961 di_devlink_walk(
1962 	di_devlink_handle_t hdp,
1963 	const char *re,
1964 	const char *minor_path,
1965 	uint_t flags,
1966 	void *arg,
1967 	int (*devlink_callback)(di_devlink_t, void *))
1968 {
1969 	int rv;
1970 	regex_t reg;
1971 	link_desc_t linkd = {NULL};
1972 
1973 	if (hdp == NULL || !HDL_RDONLY(hdp)) {
1974 		errno = EINVAL;
1975 		return (-1);
1976 	}
1977 
1978 	linkd.minor_path = minor_path;
1979 	linkd.flags = flags;
1980 	linkd.arg = arg;
1981 	linkd.fcn = devlink_callback;
1982 
1983 	if (re) {
1984 		if (regcomp(&reg, re, REG_EXTENDED) != 0)
1985 			return (-1);
1986 		linkd.regp = &reg;
1987 	}
1988 
1989 	if (check_args(&linkd)) {
1990 		errno = EINVAL;
1991 		rv = -1;
1992 		goto out;
1993 	}
1994 
1995 	if (DB_OPEN(hdp)) {
1996 		rv = walk_db(hdp, &linkd);
1997 	} else {
1998 		rv = walk_dev(hdp, &linkd);
1999 	}
2000 
2001 out:
2002 	if (re) {
2003 		regfree(&reg);
2004 	}
2005 
2006 	return (rv ? -1 : 0);
2007 }
2008 
2009 static int
2010 link_flag(uint_t flags)
2011 {
2012 	if (flags != 0 && flags != DI_PRIMARY_LINK &&
2013 	    flags != DI_SECONDARY_LINK) {
2014 		return (0);
2015 	}
2016 
2017 	return (1);
2018 }
2019 
2020 /*
2021  * Currently allowed flags are:
2022  *	DI_PRIMARY_LINK
2023  *	DI_SECONDARY_LINK
2024  */
2025 static int
2026 check_args(link_desc_t *linkp)
2027 {
2028 	if (linkp->fcn == NULL)
2029 		return (-1);
2030 
2031 	if (!link_flag(linkp->flags)) {
2032 		return (-1);
2033 	}
2034 
2035 	/*
2036 	 * Minor path can be NULL. In that case, all links will be
2037 	 * selected.
2038 	 */
2039 	if (linkp->minor_path) {
2040 		if (linkp->minor_path[0] != '/' ||
2041 		    minor_colon(linkp->minor_path) == NULL) {
2042 			return (-1);
2043 		}
2044 	}
2045 
2046 	return (0);
2047 }
2048 
2049 
2050 /*
2051  * Walk all links in database if no minor path is specified.
2052  */
2053 static int
2054 walk_db(struct di_devlink_handle *hdp, link_desc_t *linkp)
2055 {
2056 	assert(DB_OPEN(hdp));
2057 
2058 	if (linkp->minor_path == NULL) {
2059 		return (walk_all_links(hdp, linkp));
2060 	} else {
2061 		return (walk_matching_links(hdp, linkp));
2062 	}
2063 }
2064 
2065 static int
2066 cache_dev(struct di_devlink_handle *hdp)
2067 {
2068 	size_t sz;
2069 	recurse_t rec = {NULL};
2070 
2071 	assert(hdp);
2072 	assert(HDL_RDONLY(hdp));
2073 
2074 	if (hdp == NULL || !HDL_RDONLY(hdp)) {
2075 		dprintf(DBG_ERR, "cache_dev: invalid arg\n");
2076 		return (-1);
2077 	}
2078 
2079 	sz = MIN_HASH_SIZE;
2080 
2081 	CACHE(hdp)->hash = calloc(sz, sizeof (cache_link_t *));
2082 	if (CACHE(hdp)->hash == NULL) {
2083 		return (-1);
2084 	}
2085 	CACHE(hdp)->hash_sz = sz;
2086 
2087 	rec.data = NULL;
2088 	rec.fcn = cache_dev_link;
2089 
2090 	return (recurse_dev(hdp, &rec));
2091 }
2092 
2093 static int
2094 walk_dev(struct di_devlink_handle *hdp, link_desc_t *linkp)
2095 {
2096 	assert(hdp && linkp);
2097 	assert(!DB_OPEN(hdp));
2098 	assert(HDL_RDONLY(hdp));
2099 
2100 	if (hdp == NULL || !HDL_RDONLY(hdp) || DB_OPEN(hdp)) {
2101 		dprintf(DBG_ERR, "walk_dev: invalid args\n");
2102 		return (-1);
2103 	}
2104 
2105 	if (CACHE_EMPTY(hdp) && cache_dev(hdp) != 0) {
2106 		dprintf(DBG_ERR, "walk_dev: /dev caching failed\n");
2107 		return (-1);
2108 	}
2109 
2110 	if (linkp->minor_path)
2111 		walk_cache_minor(hdp, linkp->minor_path, linkp);
2112 	else
2113 		walk_all_cache(hdp, linkp);
2114 
2115 	return (linkp->retval);
2116 }
2117 
2118 /* ARGSUSED */
2119 static int
2120 cache_dev_link(struct di_devlink_handle *hdp, void *data, const char *link)
2121 {
2122 	int flags;
2123 	cache_link_t *clp;
2124 	char content[PATH_MAX];
2125 
2126 	assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp));
2127 
2128 	if (s_readlink(link, content, sizeof (content)) < 0) {
2129 		return (DI_WALK_CONTINUE);
2130 	}
2131 
2132 	if (is_minor_node(content, NULL)) {
2133 		flags = DI_PRIMARY_LINK;
2134 	} else {
2135 		flags = DI_SECONDARY_LINK;
2136 	}
2137 
2138 	assert(strncmp(link, hdp->dev_dir, strlen(hdp->dev_dir)) == 0);
2139 
2140 	/*
2141 	 * Store only the part after <root-dir>/dev/
2142 	 */
2143 	link += strlen(hdp->dev_dir) + 1;
2144 
2145 	if ((clp = add_link(hdp, link, content, flags)) != NULL) {
2146 		SET_VALID_ATTR(clp->attr);
2147 	}
2148 
2149 	return (DI_WALK_CONTINUE);
2150 }
2151 
2152 
2153 static int
2154 walk_all_links(struct di_devlink_handle *hdp, link_desc_t *linkp)
2155 {
2156 	struct db_link *dlp;
2157 	uint32_t nidx, eidx;
2158 
2159 	assert(DB_NUM(hdp, DB_LINK) >= 1);
2160 
2161 	eidx = DB_NUM(hdp, DB_LINK);
2162 
2163 	/* Skip the "NIL" (index == 0) link. */
2164 	for (nidx = 1; nidx < eidx; nidx++) {
2165 		/*
2166 		 * Declare this local to the block with zero
2167 		 * initializer so that it gets rezeroed
2168 		 * for each iteration.
2169 		 */
2170 		struct di_devlink vlink = {NULL};
2171 
2172 		if ((dlp = get_link(hdp, nidx)) == NULL)
2173 			continue;
2174 
2175 		vlink.rel_path = get_string(hdp, dlp->path);
2176 		vlink.content = get_string(hdp, dlp->content);
2177 		vlink.type = attr2type(dlp->attr);
2178 
2179 		if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE) {
2180 			break;
2181 		}
2182 	}
2183 
2184 	return (linkp->retval);
2185 }
2186 
2187 static int
2188 walk_matching_links(struct di_devlink_handle *hdp, link_desc_t *linkp)
2189 {
2190 	uint32_t nidx;
2191 	struct db_link *dlp;
2192 	struct db_minor *dmp;
2193 
2194 	assert(linkp->minor_path != NULL);
2195 
2196 	dmp = lookup_minor(hdp, linkp->minor_path, NULL, TYPE_DB);
2197 
2198 	/*
2199 	 * If a minor matching the path exists, walk that minor's devlinks list.
2200 	 * Then walk the dangling devlinks list. Non-matching devlinks will be
2201 	 * filtered out in visit_link.
2202 	 */
2203 	for (;;) {
2204 		nidx = dmp ? dmp->link : DB_HDR(hdp)->dngl_idx;
2205 		for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) {
2206 			struct di_devlink vlink = {NULL};
2207 
2208 			vlink.rel_path = get_string(hdp, dlp->path);
2209 			vlink.content = get_string(hdp, dlp->content);
2210 			vlink.type = attr2type(dlp->attr);
2211 
2212 			if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE)
2213 				goto out;
2214 		}
2215 		if (dmp == NULL) {
2216 			break;
2217 		} else {
2218 			dmp = NULL;
2219 		}
2220 	}
2221 
2222 out:
2223 	return (linkp->retval);
2224 }
2225 
2226 static int
2227 visit_link(
2228 	struct di_devlink_handle *hdp,
2229 	link_desc_t *linkp,
2230 	struct di_devlink *vlp)
2231 {
2232 	struct stat sbuf;
2233 	const char *minor_path = NULL;
2234 	char abs_path[PATH_MAX], cont[PATH_MAX];
2235 
2236 	/*
2237 	 * It is legal for the link's content and type to be unknown.
2238 	 * but one of absolute or relative path must be set.
2239 	 */
2240 	if (vlp->rel_path == NULL && vlp->abs_path == NULL) {
2241 		(void) dprintf(DBG_ERR, "visit_link: invalid arguments\n");
2242 		return (DI_WALK_CONTINUE);
2243 	}
2244 
2245 	if (vlp->rel_path == NULL) {
2246 		vlp->rel_path = (char *)rel_path(hdp, vlp->abs_path);
2247 		if (vlp->rel_path == NULL || vlp->rel_path[0] == '\0')
2248 			return (DI_WALK_CONTINUE);
2249 	}
2250 
2251 	if (linkp->regp) {
2252 		if (regexec(linkp->regp, vlp->rel_path, 0, NULL, 0) != 0)
2253 			return (DI_WALK_CONTINUE);
2254 	}
2255 
2256 	if (vlp->abs_path == NULL) {
2257 		assert(vlp->rel_path[0] != '/');
2258 		(void) snprintf(abs_path, sizeof (abs_path), "%s/%s",
2259 		    hdp->dev_dir, vlp->rel_path);
2260 		vlp->abs_path = abs_path;
2261 	}
2262 
2263 	if (vlp->content == NULL) {
2264 		if (s_readlink(vlp->abs_path, cont, sizeof (cont)) < 0) {
2265 			return (DI_WALK_CONTINUE);
2266 		}
2267 		vlp->content = cont;
2268 	}
2269 
2270 
2271 	if (vlp->type == 0) {
2272 		if (is_minor_node(vlp->content, &minor_path)) {
2273 			vlp->type = DI_PRIMARY_LINK;
2274 		} else {
2275 			vlp->type = DI_SECONDARY_LINK;
2276 		}
2277 	}
2278 
2279 	/*
2280 	 * Filter based on minor path
2281 	 */
2282 	if (linkp->minor_path) {
2283 		char tmp[PATH_MAX];
2284 
2285 		/*
2286 		 * derive minor path
2287 		 */
2288 		if (vlp->type == DI_SECONDARY_LINK) {
2289 
2290 #ifdef	DEBUG
2291 			/*LINTED*/
2292 			assert(sizeof (tmp) >= PATH_MAX);
2293 #endif
2294 			if (realpath(vlp->abs_path, tmp) == NULL)
2295 				return (DI_WALK_CONTINUE);
2296 
2297 			if (!is_minor_node(tmp, &minor_path))
2298 				return (DI_WALK_CONTINUE);
2299 
2300 		} else if (minor_path == NULL) {
2301 			if (!is_minor_node(vlp->content, &minor_path))
2302 				return (DI_WALK_CONTINUE);
2303 		}
2304 
2305 		assert(minor_path != NULL);
2306 
2307 		if (strcmp(linkp->minor_path, minor_path) != 0)
2308 			return (DI_WALK_CONTINUE);
2309 	}
2310 
2311 	/*
2312 	 * Filter based on link type
2313 	 */
2314 	if (!TYPE_NONE(linkp->flags) && LINK_TYPE(linkp->flags) != vlp->type) {
2315 		return (DI_WALK_CONTINUE);
2316 	}
2317 
2318 	if (lstat(vlp->abs_path, &sbuf) < 0) {
2319 		dprintf(DBG_ERR, "visit_link: %s: lstat failed: %s\n",
2320 		    vlp->abs_path, strerror(errno));
2321 		return (DI_WALK_CONTINUE);
2322 	}
2323 
2324 	return (linkp->fcn(vlp, linkp->arg));
2325 }
2326 
2327 static int
2328 devlink_valid(di_devlink_t devlink)
2329 {
2330 	if (devlink == NULL || devlink->rel_path == NULL ||
2331 	    devlink->abs_path == NULL || devlink->content == NULL ||
2332 	    TYPE_NONE(devlink->type)) {
2333 		return (0);
2334 	}
2335 
2336 	return (1);
2337 }
2338 
2339 const char *
2340 di_devlink_path(di_devlink_t devlink)
2341 {
2342 	if (!devlink_valid(devlink)) {
2343 		errno = EINVAL;
2344 		return (NULL);
2345 	}
2346 
2347 	return (devlink->abs_path);
2348 }
2349 
2350 const char *
2351 di_devlink_content(di_devlink_t devlink)
2352 {
2353 	if (!devlink_valid(devlink)) {
2354 		errno = EINVAL;
2355 		return (NULL);
2356 	}
2357 
2358 	return (devlink->content);
2359 }
2360 
2361 int
2362 di_devlink_type(di_devlink_t devlink)
2363 {
2364 	if (!devlink_valid(devlink)) {
2365 		errno = EINVAL;
2366 		return (-1);
2367 	}
2368 
2369 	return (devlink->type);
2370 }
2371 
2372 di_devlink_t
2373 di_devlink_dup(di_devlink_t devlink)
2374 {
2375 	struct di_devlink *duplink;
2376 
2377 	if (!devlink_valid(devlink)) {
2378 		errno = EINVAL;
2379 		return (NULL);
2380 	}
2381 
2382 	if ((duplink = calloc(1, sizeof (struct di_devlink))) == NULL) {
2383 		return (NULL);
2384 	}
2385 
2386 	duplink->rel_path = strdup(devlink->rel_path);
2387 	duplink->abs_path = strdup(devlink->abs_path);
2388 	duplink->content  = strdup(devlink->content);
2389 	duplink->type	  = devlink->type;
2390 
2391 	if (!devlink_valid(duplink)) {
2392 		(void) di_devlink_free(duplink);
2393 		errno = ENOMEM;
2394 		return (NULL);
2395 	}
2396 
2397 	return (duplink);
2398 }
2399 
2400 int
2401 di_devlink_free(di_devlink_t devlink)
2402 {
2403 	if (devlink == NULL) {
2404 		errno = EINVAL;
2405 		return (-1);
2406 	}
2407 
2408 	free(devlink->rel_path);
2409 	free(devlink->abs_path);
2410 	free(devlink->content);
2411 	free(devlink);
2412 
2413 	return (0);
2414 }
2415 
2416 /*
2417  * Obtain path relative to dev_dir
2418  */
2419 static const char *
2420 rel_path(struct di_devlink_handle *hdp, const char *path)
2421 {
2422 	const size_t len = strlen(hdp->dev_dir);
2423 
2424 	if (strncmp(path, hdp->dev_dir, len) != 0)
2425 		return (NULL);
2426 
2427 	if (path[len] == '\0')
2428 		return (&path[len]);
2429 
2430 	if (path[len] != '/')
2431 		return (NULL);
2432 
2433 	return (&path[len+1]);
2434 }
2435 
2436 static int
2437 recurse_dev(struct di_devlink_handle *hdp, recurse_t *rp)
2438 {
2439 	int ret = 0;
2440 
2441 	(void) do_recurse(hdp->dev_dir, hdp, rp, &ret);
2442 
2443 	return (ret);
2444 }
2445 
2446 static int
2447 do_recurse(
2448 	const char *dir,
2449 	struct di_devlink_handle *hdp,
2450 	recurse_t *rp,
2451 	int *retp)
2452 {
2453 	size_t len;
2454 	const char *rel;
2455 	struct stat sbuf;
2456 	char cur[PATH_MAX], *cp;
2457 	int i, rv = DI_WALK_CONTINUE;
2458 	finddevhdl_t handle;
2459 	char *d_name;
2460 
2461 
2462 	if ((rel = rel_path(hdp, dir)) == NULL)
2463 		return (DI_WALK_CONTINUE);
2464 
2465 	/*
2466 	 * Skip directories we are not interested in.
2467 	 */
2468 	for (i = 0; i < N_SKIP_DIRS; i++) {
2469 		if (strcmp(rel, skip_dirs[i]) == 0) {
2470 			(void) dprintf(DBG_STEP, "do_recurse: skipping %s\n",
2471 			    dir);
2472 			return (DI_WALK_CONTINUE);
2473 		}
2474 	}
2475 
2476 	(void) dprintf(DBG_STEP, "do_recurse: dir = %s\n", dir);
2477 
2478 	if (finddev_readdir(dir, &handle) != 0)
2479 		return (DI_WALK_CONTINUE);
2480 
2481 	(void) snprintf(cur, sizeof (cur), "%s/", dir);
2482 	len = strlen(cur);
2483 	cp = cur + len;
2484 	len = sizeof (cur) - len;
2485 
2486 	for (;;) {
2487 		if ((d_name = (char *)finddev_next(handle)) == NULL)
2488 			break;
2489 
2490 		if (strlcpy(cp, d_name, len) >= len)
2491 			break;
2492 
2493 		/*
2494 		 * Skip files we are not interested in.
2495 		 */
2496 		for (i = 0; i < N_SKIP_FILES; i++) {
2497 
2498 			rel = rel_path(hdp, cur);
2499 			if (rel == NULL || strcmp(rel, skip_files[i]) == 0) {
2500 				(void) dprintf(DBG_STEP,
2501 				    "do_recurse: skipping %s\n", cur);
2502 				goto next_entry;
2503 			}
2504 		}
2505 
2506 		if (lstat(cur, &sbuf) == 0) {
2507 			if (S_ISDIR(sbuf.st_mode)) {
2508 				rv = do_recurse(cur, hdp, rp, retp);
2509 			} else if (S_ISLNK(sbuf.st_mode)) {
2510 				rv = rp->fcn(hdp, rp->data, cur);
2511 			} else {
2512 				(void) dprintf(DBG_STEP,
2513 				    "do_recurse: Skipping entry: %s\n", cur);
2514 			}
2515 		} else {
2516 			(void) dprintf(DBG_ERR, "do_recurse: cur(%s): lstat"
2517 			    " failed: %s\n", cur, strerror(errno));
2518 		}
2519 
2520 next_entry:
2521 		*cp = '\0';
2522 
2523 		if (rv != DI_WALK_CONTINUE)
2524 			break;
2525 	}
2526 
2527 	finddev_close(handle);
2528 
2529 	return (rv);
2530 }
2531 
2532 
2533 static int
2534 check_attr(uint32_t attr)
2535 {
2536 	switch (attr & A_LINK_TYPES) {
2537 		case A_PRIMARY:
2538 		case A_SECONDARY:
2539 			return (1);
2540 		default:
2541 			dprintf(DBG_ERR, "check_attr: incorrect attr(%u)\n",
2542 			    attr);
2543 			return (0);
2544 	}
2545 }
2546 
2547 static int
2548 attr2type(uint32_t attr)
2549 {
2550 	switch (attr & A_LINK_TYPES) {
2551 		case A_PRIMARY:
2552 			return (DI_PRIMARY_LINK);
2553 		case A_SECONDARY:
2554 			return (DI_SECONDARY_LINK);
2555 		default:
2556 			dprintf(DBG_ERR, "attr2type: incorrect attr(%u)\n",
2557 			    attr);
2558 			return (0);
2559 	}
2560 }
2561 
2562 /* Allocate new node and link it in */
2563 static cache_node_t *
2564 node_insert(
2565 	struct di_devlink_handle *hdp,
2566 	cache_node_t *pcnp,
2567 	const char *path,
2568 	int insert)
2569 {
2570 	cache_node_t *cnp;
2571 
2572 	if (path == NULL) {
2573 		errno = EINVAL;
2574 		SET_DB_ERR(hdp);
2575 		return (NULL);
2576 	}
2577 
2578 	if ((cnp = calloc(1, sizeof (cache_node_t))) == NULL) {
2579 		SET_DB_ERR(hdp);
2580 		return (NULL);
2581 	}
2582 
2583 	if ((cnp->path = strdup(path)) == NULL) {
2584 		SET_DB_ERR(hdp);
2585 		free(cnp);
2586 		return (NULL);
2587 	}
2588 
2589 	cnp->parent = pcnp;
2590 
2591 	if (pcnp == NULL) {
2592 		assert(strcmp(path, "/") == 0);
2593 		assert(CACHE(hdp)->root == NULL);
2594 		CACHE(hdp)->root = cnp;
2595 	} else if (insert == INSERT_HEAD) {
2596 		cnp->sib = pcnp->child;
2597 		pcnp->child = cnp;
2598 	} else if (CACHE_LAST(hdp) && CACHE_LAST(hdp)->node &&
2599 	    CACHE_LAST(hdp)->node->parent == pcnp &&
2600 	    CACHE_LAST(hdp)->node->sib == NULL) {
2601 
2602 		CACHE_LAST(hdp)->node->sib = cnp;
2603 
2604 	} else {
2605 		cache_node_t **pp;
2606 
2607 		for (pp = &pcnp->child; *pp != NULL; pp = &(*pp)->sib)
2608 			;
2609 		*pp = cnp;
2610 	}
2611 
2612 	return (cnp);
2613 }
2614 
2615 /*
2616  * Allocate a new minor and link it in either at the tail or head
2617  * of the minor list depending on the value of "prev".
2618  */
2619 static cache_minor_t *
2620 minor_insert(
2621 	struct di_devlink_handle *hdp,
2622 	cache_node_t *pcnp,
2623 	const char *name,
2624 	const char *nodetype,
2625 	cache_minor_t **prev)
2626 {
2627 	cache_minor_t *cmnp;
2628 
2629 	if (pcnp == NULL || name == NULL) {
2630 		errno = EINVAL;
2631 		SET_DB_ERR(hdp);
2632 		return (NULL);
2633 	}
2634 
2635 	/*
2636 	 * Some pseudo drivers don't specify nodetype. Assume pseudo if
2637 	 * nodetype is not specified.
2638 	 */
2639 	if (nodetype == NULL)
2640 		nodetype = DDI_PSEUDO;
2641 
2642 	if ((cmnp = calloc(1, sizeof (cache_minor_t))) == NULL) {
2643 		SET_DB_ERR(hdp);
2644 		return (NULL);
2645 	}
2646 
2647 	cmnp->name = strdup(name);
2648 	cmnp->nodetype = strdup(nodetype);
2649 	if (cmnp->name == NULL || cmnp->nodetype == NULL) {
2650 		SET_DB_ERR(hdp);
2651 		free(cmnp->name);
2652 		free(cmnp->nodetype);
2653 		free(cmnp);
2654 		return (NULL);
2655 	}
2656 
2657 	cmnp->node = pcnp;
2658 
2659 	/* Add to node's minor list */
2660 	if (prev == NULL) {
2661 		cmnp->sib = pcnp->minor;
2662 		pcnp->minor = cmnp;
2663 	} else {
2664 		assert(*prev == NULL);
2665 		*prev = cmnp;
2666 	}
2667 
2668 	return (cmnp);
2669 }
2670 
2671 static cache_link_t *
2672 link_insert(
2673 	struct di_devlink_handle *hdp,
2674 	cache_minor_t *cmnp,
2675 	const char *path,
2676 	const char *content,
2677 	uint32_t attr)
2678 {
2679 	cache_link_t *clp;
2680 
2681 	if (path == NULL || content == NULL || !check_attr(attr)) {
2682 		errno = EINVAL;
2683 		SET_DB_ERR(hdp);
2684 		return (NULL);
2685 	}
2686 
2687 	if ((clp = calloc(1, sizeof (cache_link_t))) == NULL) {
2688 		SET_DB_ERR(hdp);
2689 		return (NULL);
2690 	}
2691 
2692 	clp->path = strdup(path);
2693 	clp->content = strdup(content);
2694 	if (clp->path == NULL || clp->content == NULL) {
2695 		SET_DB_ERR(hdp);
2696 		link_free(&clp);
2697 		return (NULL);
2698 	}
2699 
2700 	clp->attr = attr;
2701 	hash_insert(hdp, clp);
2702 	clp->minor = cmnp;
2703 
2704 	/* Add to minor's link list */
2705 	if (cmnp != NULL) {
2706 		clp->sib = cmnp->link;
2707 		cmnp->link = clp;
2708 	} else {
2709 		clp->sib = CACHE(hdp)->dngl;
2710 		CACHE(hdp)->dngl = clp;
2711 	}
2712 
2713 	return (clp);
2714 }
2715 
2716 static void
2717 hash_insert(struct di_devlink_handle *hdp, cache_link_t *clp)
2718 {
2719 	uint_t hval;
2720 
2721 	hval = hashfn(hdp, clp->path);
2722 	clp->hash = CACHE_HASH(hdp, hval);
2723 	CACHE_HASH(hdp, hval) = clp;
2724 }
2725 
2726 
2727 static struct db_node *
2728 get_node(struct di_devlink_handle *hdp, uint32_t idx)
2729 {
2730 	return (map_seg(hdp, idx, PROT_READ, DB_NODE));
2731 }
2732 
2733 static struct db_node *
2734 set_node(struct di_devlink_handle *hdp, uint32_t idx)
2735 {
2736 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_NODE));
2737 }
2738 
2739 static struct db_minor *
2740 get_minor(struct di_devlink_handle *hdp, uint32_t idx)
2741 {
2742 	return (map_seg(hdp, idx, PROT_READ, DB_MINOR));
2743 }
2744 
2745 static struct db_minor *
2746 set_minor(struct di_devlink_handle *hdp, uint32_t idx)
2747 {
2748 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_MINOR));
2749 }
2750 
2751 static struct db_link *
2752 get_link(struct di_devlink_handle *hdp, uint32_t idx)
2753 {
2754 	return (map_seg(hdp, idx, PROT_READ, DB_LINK));
2755 }
2756 
2757 static struct db_link *
2758 set_link(struct di_devlink_handle *hdp, uint32_t idx)
2759 {
2760 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_LINK));
2761 }
2762 
2763 static char *
2764 get_string(struct di_devlink_handle *hdp, uint32_t idx)
2765 {
2766 	return (map_seg(hdp, idx, PROT_READ, DB_STR));
2767 }
2768 
2769 static char *
2770 set_string(struct di_devlink_handle *hdp, uint32_t idx)
2771 {
2772 	return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_STR));
2773 }
2774 
2775 
2776 /*
2777  * Returns the element corresponding to idx. If the portion of file involved
2778  * is not yet mapped, does an mmap() as well. Existing mappings are not changed.
2779  */
2780 static void *
2781 map_seg(
2782 	struct di_devlink_handle *hdp,
2783 	uint32_t idx,
2784 	int prot,
2785 	db_seg_t seg)
2786 {
2787 	int s;
2788 	off_t off;
2789 	size_t slen;
2790 	caddr_t addr;
2791 
2792 	if (idx == DB_NIL) {
2793 		return (NULL);
2794 	}
2795 
2796 	if (!VALID_INDEX(hdp, seg, idx)) {
2797 		(void) dprintf(DBG_ERR, "map_seg: seg(%d): invalid idx(%u)\n",
2798 		    seg, idx);
2799 		return (NULL);
2800 	}
2801 
2802 	/*
2803 	 * If the seg is already mapped in, use it if the access type is
2804 	 * valid.
2805 	 */
2806 	if (DB_SEG(hdp, seg) != NULL) {
2807 		if (DB_SEG_PROT(hdp, seg) != prot) {
2808 			(void) dprintf(DBG_ERR, "map_seg: illegal access: "
2809 			    "seg[%d]: idx=%u, seg_prot=%d, access=%d\n",
2810 			    seg, idx, DB_SEG_PROT(hdp, seg), prot);
2811 			return (NULL);
2812 		}
2813 		return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]);
2814 	}
2815 
2816 	/*
2817 	 * Segment is not mapped. Mmap() the segment.
2818 	 */
2819 	off = seg_size(hdp, DB_HEADER);
2820 	for (s = 0; s < seg; s++) {
2821 		off += seg_size(hdp, s);
2822 	}
2823 	slen = seg_size(hdp, seg);
2824 
2825 	addr = mmap(0, slen, prot, MAP_SHARED, DB(hdp)->db_fd, off);
2826 	if (addr == MAP_FAILED) {
2827 		(void) dprintf(DBG_ERR, "map_seg: seg[%d]: mmap failed: %s\n",
2828 		    seg, strerror(errno));
2829 		(void) dprintf(DBG_ERR, "map_seg: args: len=%lu, prot=%d,"
2830 		    " fd=%d, off=%ld\n", (ulong_t)slen, prot, DB(hdp)->db_fd,
2831 		    off);
2832 		return (NULL);
2833 	}
2834 
2835 	DB_SEG(hdp, seg) = addr;
2836 	DB_SEG_PROT(hdp, seg) = prot;
2837 
2838 	(void) dprintf(DBG_STEP, "map_seg: seg[%d]: len=%lu, prot=%d, fd=%d, "
2839 	    "off=%ld, seg_base=%p\n", seg, (ulong_t)slen, prot, DB(hdp)->db_fd,
2840 	    off, (void *)addr);
2841 
2842 	return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]);
2843 }
2844 
2845 /*
2846  * Computes the size of a segment rounded up to the nearest page boundary.
2847  */
2848 static size_t
2849 seg_size(struct di_devlink_handle *hdp, int seg)
2850 {
2851 	size_t sz;
2852 
2853 	assert(DB_HDR(hdp)->page_sz);
2854 
2855 	if (seg == DB_HEADER) {
2856 		sz = HDR_LEN;
2857 	} else {
2858 		assert(DB_NUM(hdp, seg) >= 1);
2859 		sz = DB_NUM(hdp, seg) * elem_sizes[seg];
2860 	}
2861 
2862 	sz = (sz / DB_HDR(hdp)->page_sz) + 1;
2863 
2864 	sz *= DB_HDR(hdp)->page_sz;
2865 
2866 	return (sz);
2867 }
2868 
2869 static size_t
2870 size_db(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
2871 {
2872 	int i;
2873 	size_t sz;
2874 	cache_link_t *clp;
2875 
2876 	assert(page_sz > 0);
2877 
2878 	/* Take "NIL" element into account */
2879 	for (i = 0; i < DB_TYPES; i++) {
2880 		count[i] = 1;
2881 	}
2882 
2883 	count_node(CACHE(hdp)->root, count);
2884 
2885 	for (clp = CACHE(hdp)->dngl; clp != NULL; clp = clp->sib) {
2886 		count_link(clp, count);
2887 	}
2888 
2889 	sz = ((HDR_LEN / page_sz) + 1) * page_sz;
2890 	for (i = 0; i < DB_TYPES; i++) {
2891 		assert(count[i] >= 1);
2892 		sz += (((count[i] * elem_sizes[i]) / page_sz) + 1) * page_sz;
2893 		(void) dprintf(DBG_INFO, "N[%u]=%u\n", i, count[i]);
2894 	}
2895 	(void) dprintf(DBG_INFO, "DB size=%lu\n", (ulong_t)sz);
2896 
2897 	return (sz);
2898 }
2899 
2900 
2901 static void
2902 count_node(cache_node_t *cnp, uint32_t *count)
2903 {
2904 	cache_minor_t *cmnp;
2905 
2906 	if (cnp == NULL)
2907 		return;
2908 
2909 	count[DB_NODE]++;
2910 	count_string(cnp->path, count);
2911 
2912 	for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) {
2913 		count_minor(cmnp, count);
2914 	}
2915 
2916 	for (cnp = cnp->child; cnp != NULL; cnp = cnp->sib) {
2917 		count_node(cnp, count);
2918 	}
2919 
2920 }
2921 
2922 static void
2923 count_minor(cache_minor_t *cmnp, uint32_t *count)
2924 {
2925 	cache_link_t *clp;
2926 
2927 	if (cmnp == NULL)
2928 		return;
2929 
2930 	count[DB_MINOR]++;
2931 	count_string(cmnp->name, count);
2932 	count_string(cmnp->nodetype, count);
2933 
2934 	for (clp = cmnp->link; clp != NULL; clp = clp->sib) {
2935 		count_link(clp, count);
2936 	}
2937 }
2938 
2939 static void
2940 count_link(cache_link_t *clp, uint32_t *count)
2941 {
2942 	if (clp == NULL)
2943 		return;
2944 
2945 	count[DB_LINK]++;
2946 	count_string(clp->path, count);
2947 	count_string(clp->content, count);
2948 }
2949 
2950 
2951 static void
2952 count_string(const char *str, uint32_t *count)
2953 {
2954 	if (str == NULL) {
2955 		(void) dprintf(DBG_ERR, "count_string: NULL argument\n");
2956 		return;
2957 	}
2958 
2959 	count[DB_STR] += strlen(str) + 1;
2960 }
2961 
2962 static uint_t
2963 hashfn(struct di_devlink_handle *hdp, const char *str)
2964 {
2965 	const char *cp;
2966 	ulong_t hval = 0;
2967 
2968 	if (str == NULL) {
2969 		return (0);
2970 	}
2971 
2972 	assert(CACHE(hdp)->hash_sz >= MIN_HASH_SIZE);
2973 
2974 	for (cp = str; *cp != '\0'; cp++) {
2975 		hval += *cp;
2976 	}
2977 
2978 	return (hval % CACHE(hdp)->hash_sz);
2979 }
2980 
2981 static int
2982 enter_update_lock(struct di_devlink_handle *hdp)
2983 {
2984 	int i, fd, rv;
2985 	struct flock lock;
2986 	char lockfile[PATH_MAX];
2987 
2988 	assert(hdp->lock_fd < 0);
2989 
2990 	get_db_path(hdp, DB_LOCK, lockfile, sizeof (lockfile));
2991 
2992 	/*
2993 	 * Record locks are per-process. Protect against multiple threads.
2994 	 */
2995 	(void) mutex_lock(&update_mutex);
2996 
2997 	if ((fd = open(lockfile, O_RDWR|O_CREAT, DB_LOCK_PERMS)) < 0) {
2998 		goto error;
2999 	}
3000 
3001 	lock.l_type = F_WRLCK;
3002 	lock.l_whence = SEEK_SET;
3003 	lock.l_start = 0;
3004 	lock.l_len = 0;
3005 
3006 	i = 1;
3007 	while ((rv = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) {
3008 		if (i < MAX_LOCK_RETRY) {
3009 			i++;
3010 		} else {
3011 			break;
3012 		}
3013 	}
3014 
3015 	if (rv == 0) {
3016 		hdp->lock_fd = fd;
3017 		return (0);
3018 	} else {
3019 		(void) close(fd);
3020 	}
3021 
3022 error:
3023 	(void) mutex_unlock(&update_mutex);
3024 
3025 	dprintf(DBG_ERR, "lockfile(%s): lock failed: %s\n", lockfile,
3026 	    strerror(errno));
3027 	return (-1);
3028 }
3029 
3030 /*
3031  * Close and re-open lock file every time so that it is recreated if deleted.
3032  */
3033 static void
3034 exit_update_lock(struct di_devlink_handle *hdp)
3035 {
3036 	struct flock unlock;
3037 
3038 	if (hdp->lock_fd < 0) {
3039 		return;
3040 	}
3041 
3042 	unlock.l_type = F_UNLCK;
3043 	unlock.l_whence = SEEK_SET;
3044 	unlock.l_start = 0;
3045 	unlock.l_len = 0;
3046 
3047 	if (fcntl(hdp->lock_fd, F_SETLK, &unlock) == -1) {
3048 		dprintf(DBG_ERR, "update lockfile: unlock failed: %s\n",
3049 		    strerror(errno));
3050 	}
3051 
3052 	(void) close(hdp->lock_fd);
3053 
3054 	hdp->lock_fd = -1;
3055 
3056 	(void) mutex_unlock(&update_mutex);
3057 }
3058 
3059 /*
3060  * returns 1 if contents is a minor node in /devices.
3061  * If mn_root is not NULL, mn_root is set to:
3062  *	if contents is a /dev node, mn_root = contents
3063  *			OR
3064  *	if contents is a /devices node, mn_root set to the '/'
3065  *	following /devices.
3066  */
3067 int
3068 is_minor_node(const char *contents, const char **mn_root)
3069 {
3070 	char *ptr, *prefix;
3071 
3072 	prefix = "../devices/";
3073 
3074 	if ((ptr = strstr(contents, prefix)) != NULL) {
3075 
3076 		/* mn_root should point to the / following /devices */
3077 		if (mn_root != NULL) {
3078 			*mn_root = ptr += strlen(prefix) - 1;
3079 		}
3080 		return (1);
3081 	}
3082 
3083 	prefix = "/devices/";
3084 
3085 	if (strncmp(contents, prefix, strlen(prefix)) == 0) {
3086 
3087 		/* mn_root should point to the / following /devices/ */
3088 		if (mn_root != NULL) {
3089 			*mn_root = contents + strlen(prefix) - 1;
3090 		}
3091 		return (1);
3092 	}
3093 
3094 	if (mn_root != NULL) {
3095 		*mn_root = contents;
3096 	}
3097 	return (0);
3098 }
3099 
3100 static int
3101 s_readlink(const char *link, char *buf, size_t blen)
3102 {
3103 	int rv;
3104 
3105 	if ((rv = readlink(link, buf, blen)) == -1)
3106 		goto bad;
3107 
3108 	if (rv >= blen && buf[blen - 1] != '\0') {
3109 		errno = ENAMETOOLONG;
3110 		goto bad;
3111 	} else if (rv < blen) {
3112 		buf[rv] = '\0';
3113 	}
3114 
3115 	return (0);
3116 bad:
3117 	dprintf(DBG_ERR, "s_readlink: %s: failed: %s\n",
3118 	    link, strerror(errno));
3119 	return (-1);
3120 }
3121 
3122 /*
3123  * Synchronous link creation interface routines
3124  * The scope of the operation is determined by the "name" arg.
3125  * "name" can be NULL, a driver name or a devfs pathname (without /devices)
3126  *
3127  *	"name"				creates
3128  *	======				=======
3129  *
3130  *	NULL		=>		All devlinks in system
3131  *	<driver>	=>		devlinks for named driver
3132  *	/pci@1		=>		devlinks for subtree rooted at pci@1
3133  *	/pseudo/foo@0:X	=>		devlinks for minor X
3134  *
3135  * devlink_create() returns 0 on success or an errno value on failure
3136  */
3137 
3138 #define	MAX_DAEMON_ATTEMPTS 2
3139 
3140 static int
3141 devlink_create(const char *root, const char *name)
3142 {
3143 	int i;
3144 	struct dca_off dca;
3145 
3146 	assert(root);
3147 
3148 	/*
3149 	 * Convert name into arg for door_call
3150 	 */
3151 	if (dca_init(name, &dca) != 0)
3152 		return (EINVAL);
3153 
3154 	/*
3155 	 * Attempt to use the daemon first
3156 	 */
3157 	i = 0;
3158 	do {
3159 		daemon_call(root, &dca);
3160 
3161 		dprintf(DBG_INFO, "daemon_call() retval=%d\n", dca.dca_error);
3162 
3163 		/*
3164 		 * Retry only if door server isn't running
3165 		 */
3166 		if (dca.dca_error != ENOENT && dca.dca_error != EBADF) {
3167 			return (dca.dca_error);
3168 		}
3169 
3170 		dca.dca_error = 0;
3171 
3172 		/*
3173 		 * To improve performance defer this check until the first
3174 		 * failure. Safe to defer as door server checks perms.
3175 		 */
3176 		if (geteuid() != 0)
3177 			return (EPERM);
3178 	/*
3179 	 * Daemon may not be running. Try to start it.
3180 	 */
3181 	} while ((++i < MAX_DAEMON_ATTEMPTS) && start_daemon(root) == 0);
3182 
3183 	dprintf(DBG_INFO, "devlink_create: can't start daemon\n");
3184 
3185 	assert(dca.dca_error == 0);
3186 
3187 	/*
3188 	 * If the daemon cannot be started execute the devfsadm command.
3189 	 */
3190 	exec_cmd(root, &dca);
3191 
3192 	return (dca.dca_error);
3193 }
3194 
3195 /*
3196  * The "name" member of "struct dca" contains data in the following order
3197  *	root'\0'minor'\0'driver'\0'
3198  * The root component is always present at offset 0 in the "name" field.
3199  * The driver and minor are optional. If present they have a non-zero
3200  * offset in the "name" member.
3201  */
3202 static int
3203 dca_init(const char *name, struct dca_off *dcp)
3204 {
3205 	char *cp;
3206 
3207 	dcp->dca_root = 0;
3208 	dcp->dca_minor = 0;
3209 	dcp->dca_driver = 0;
3210 	dcp->dca_error = 0;
3211 	dcp->dca_flags = 0;
3212 	dcp->dca_name[0] = '\0';
3213 
3214 	name = name ? name : "/";
3215 
3216 	/*
3217 	 *  Check if name is a driver name
3218 	 */
3219 	if (*name != '/') {
3220 		(void) snprintf(dcp->dca_name, sizeof (dcp->dca_name),
3221 		    "/ %s", name);
3222 		dcp->dca_root = 0;
3223 		*(dcp->dca_name + 1) = '\0';
3224 		dcp->dca_driver = 2;
3225 		return (0);
3226 	}
3227 
3228 	(void) snprintf(dcp->dca_name, sizeof (dcp->dca_name), "%s", name);
3229 
3230 	/*
3231 	 * "/devices" not allowed in devfs pathname
3232 	 */
3233 	if (is_minor_node(name, NULL))
3234 		return (-1);
3235 
3236 	dcp->dca_root = 0;
3237 	if (cp = strrchr(dcp->dca_name, ':')) {
3238 		*cp++ = '\0';
3239 		dcp->dca_minor = cp - dcp->dca_name;
3240 	}
3241 
3242 	return (0);
3243 }
3244 
3245 
3246 #define	DAEMON_STARTUP_TIME	1 /* 1 second. This may need to be adjusted */
3247 
3248 static void
3249 daemon_call(const char *root, struct dca_off *dcp)
3250 {
3251 	door_arg_t	arg;
3252 	int		fd, door_error;
3253 	sigset_t	oset, nset;
3254 	char		synch_door[PATH_MAX];
3255 	struct statvfs	svf;
3256 	char		*prefix;
3257 
3258 	/*
3259 	 * If readonly root, assume we are in install
3260 	 */
3261 	prefix =
3262 	    (statvfs("/etc/dev", &svf) == 0 && (svf.f_flag & ST_RDONLY)) ?
3263 		"/tmp" : (char *)root;
3264 	(void) snprintf(synch_door, sizeof (synch_door),
3265 	    "%s/etc/dev/%s", prefix, DEVFSADM_SYNCH_DOOR);
3266 
3267 	if ((fd = open(synch_door, O_RDONLY)) == -1) {
3268 		dcp->dca_error = errno;
3269 		dprintf(DBG_ERR, "open of %s failed: %s\n",
3270 		    synch_door, strerror(errno));
3271 		return;
3272 	}
3273 
3274 	arg.data_ptr = (char *)dcp;
3275 	arg.data_size = sizeof (*dcp);
3276 	arg.desc_ptr = NULL;
3277 	arg.desc_num = 0;
3278 	arg.rbuf = (char *)dcp;
3279 	arg.rsize = sizeof (*dcp);
3280 
3281 	/*
3282 	 * Block signals to this thread until door call
3283 	 * completes.
3284 	 */
3285 	(void) sigfillset(&nset);
3286 	(void) sigemptyset(&oset);
3287 	(void) sigprocmask(SIG_SETMASK, &nset, &oset);
3288 	if (door_call(fd, &arg)) {
3289 		door_error = 1;
3290 		dcp->dca_error = errno;
3291 	}
3292 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
3293 
3294 	(void) close(fd);
3295 
3296 	if (door_error)
3297 		return;
3298 
3299 	assert(arg.data_ptr);
3300 
3301 	/*LINTED*/
3302 	dcp->dca_error = ((struct dca_off *)arg.data_ptr)->dca_error;
3303 
3304 	/*
3305 	 * The doors interface may return data in a different buffer
3306 	 * If that happens, deallocate buffer via munmap()
3307 	 */
3308 	if (arg.rbuf != (char *)dcp)
3309 		(void) munmap(arg.rbuf, arg.rsize);
3310 }
3311 
3312 #define	DEVFSADM_PATH	"/usr/sbin/devfsadm"
3313 #define	DEVFSADM	"devfsadm"
3314 
3315 #define	DEVFSADMD_PATH	"/usr/lib/devfsadm/devfsadmd"
3316 #define	DEVFSADM_DAEMON	"devfsadmd"
3317 
3318 static int
3319 start_daemon(const char *root)
3320 {
3321 	int rv, i = 0;
3322 	char *argv[20];
3323 
3324 	argv[i++] = DEVFSADM_DAEMON;
3325 	if (strcmp(root, "/")) {
3326 		argv[i++] = "-r";
3327 		argv[i++] = (char *)root;
3328 	}
3329 	argv[i++] = NULL;
3330 
3331 	rv = do_exec(DEVFSADMD_PATH, argv);
3332 
3333 	(void) sleep(DAEMON_STARTUP_TIME);
3334 
3335 	return (rv);
3336 }
3337 
3338 static void
3339 exec_cmd(const char *root, struct dca_off *dcp)
3340 {
3341 	int i;
3342 	char *argv[20];
3343 
3344 	i = 0;
3345 	argv[i++] = DEVFSADM;
3346 
3347 	/*
3348 	 * Load drivers only if -i is specified
3349 	 */
3350 	if (dcp->dca_driver) {
3351 		argv[i++] = "-i";
3352 		argv[i++] = &dcp->dca_name[dcp->dca_driver];
3353 	} else {
3354 		argv[i++] = "-n";
3355 	}
3356 
3357 	if (root != NULL && strcmp(root, "/") != 0) {
3358 		argv[i++] = "-r";
3359 		argv[i++] = (char *)root;
3360 	}
3361 
3362 	argv[i] = NULL;
3363 
3364 	if (do_exec(DEVFSADM_PATH, argv))
3365 		dcp->dca_error = errno;
3366 }
3367 
3368 static int
3369 do_exec(const char *path, char *const argv[])
3370 {
3371 	int i;
3372 	pid_t cpid;
3373 
3374 #ifdef	DEBUG
3375 	dprintf(DBG_INFO, "Executing %s\n\tArgument list:", path);
3376 	for (i = 0; argv[i] != NULL; i++) {
3377 		dprintf(DBG_INFO, " %s", argv[i]);
3378 	}
3379 	dprintf(DBG_INFO, "\n");
3380 #endif
3381 
3382 	if ((cpid = fork1()) == -1) {
3383 		dprintf(DBG_ERR, "fork1 failed: %s\n", strerror(errno));
3384 		return (-1);
3385 	}
3386 
3387 	if (cpid == 0) { /* child process */
3388 		int fd;
3389 
3390 		if ((fd = open("/dev/null", O_RDWR)) >= 0) {
3391 			(void) dup2(fd, fileno(stdout));
3392 			(void) dup2(fd, fileno(stderr));
3393 			(void) close(fd);
3394 
3395 			(void) execv(path, argv);
3396 		} else {
3397 			dprintf(DBG_ERR, "open of /dev/null failed: %s\n",
3398 			    strerror(errno));
3399 		}
3400 
3401 		_exit(-1);
3402 	}
3403 
3404 	/* Parent process */
3405 	if (waitpid(cpid, &i, 0) == cpid) {
3406 		if (WIFEXITED(i)) {
3407 			if (WEXITSTATUS(i) == 0) {
3408 				dprintf(DBG_STEP,
3409 				    "do_exec: child exited normally\n");
3410 				return (0);
3411 			} else
3412 				errno = EINVAL;
3413 		} else {
3414 			/*
3415 			 * The child was interrupted by a signal
3416 			 */
3417 			errno = EINTR;
3418 		}
3419 		dprintf(DBG_ERR, "child terminated abnormally: %s\n",
3420 		    strerror(errno));
3421 	} else {
3422 		dprintf(DBG_ERR, "waitpid failed: %s\n", strerror(errno));
3423 	}
3424 
3425 	return (-1);
3426 }
3427 
3428 static int
3429 walk_cache_links(di_devlink_handle_t hdp, cache_link_t *clp, link_desc_t *linkp)
3430 {
3431 	int i;
3432 
3433 	assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp));
3434 
3435 	dprintf(DBG_INFO, "walk_cache_links: initial link: %s\n",
3436 	    clp ? clp->path : "<NULL>");
3437 
3438 	/*
3439 	 * First search the links under the specified minor. On the
3440 	 * 2nd pass, search the dangling list - secondary links may
3441 	 * exist on this list since they are not resolved during the
3442 	 * /dev walk.
3443 	 */
3444 	for (i = 0; i < 2; i++) {
3445 		for (; clp != NULL; clp = clp->sib) {
3446 			struct di_devlink vlink = {NULL};
3447 
3448 			assert(clp->path[0] != '/');
3449 
3450 			vlink.rel_path = clp->path;
3451 			vlink.content = clp->content;
3452 			vlink.type = attr2type(clp->attr);
3453 
3454 			if (visit_link(hdp, linkp, &vlink)
3455 			    != DI_WALK_CONTINUE) {
3456 				dprintf(DBG_INFO, "walk_cache_links: "
3457 				    "terminating at link: %s\n", clp->path);
3458 				goto out;
3459 			}
3460 		}
3461 
3462 		clp = CACHE(hdp)->dngl;
3463 	}
3464 
3465 out:
3466 
3467 	/* If i < 2, we terminated the walk prematurely */
3468 	return (i < 2 ? DI_WALK_TERMINATE : DI_WALK_CONTINUE);
3469 }
3470 
3471 static void
3472 walk_all_cache(di_devlink_handle_t hdp, link_desc_t *linkp)
3473 {
3474 	int i;
3475 	cache_link_t *clp;
3476 
3477 	dprintf(DBG_INFO, "walk_all_cache: entered\n");
3478 
3479 	for (i = 0; i < CACHE(hdp)->hash_sz; i++) {
3480 		clp = CACHE_HASH(hdp, i);
3481 		for (; clp; clp = clp->hash) {
3482 			struct di_devlink vlink = {NULL};
3483 
3484 			assert(clp->path[0] != '/');
3485 
3486 			vlink.rel_path = clp->path;
3487 			vlink.content = clp->content;
3488 			vlink.type = attr2type(clp->attr);
3489 			if (visit_link(hdp, linkp, &vlink) !=
3490 			    DI_WALK_CONTINUE) {
3491 				dprintf(DBG_INFO, "walk_all_cache: terminating "
3492 				    "walk at link: %s\n", clp->path);
3493 				return;
3494 			}
3495 		}
3496 	}
3497 }
3498 
3499 static void
3500 walk_cache_minor(di_devlink_handle_t hdp, const char *mpath, link_desc_t *linkp)
3501 {
3502 	cache_minor_t *cmnp;
3503 
3504 	assert(mpath);
3505 
3506 	if ((cmnp = lookup_minor(hdp, mpath, NULL, TYPE_CACHE)) != NULL) {
3507 		(void) walk_cache_links(hdp, cmnp->link, linkp);
3508 	} else {
3509 		dprintf(DBG_ERR, "lookup minor failed: %s\n", mpath);
3510 	}
3511 }
3512 
3513 static void
3514 walk_cache_node(di_devlink_handle_t hdp, const char *path, link_desc_t *linkp)
3515 {
3516 	cache_minor_t *cmnp;
3517 	cache_node_t *cnp;
3518 
3519 	assert(path);
3520 
3521 	if ((cnp = lookup_node(hdp, (char *)path, TYPE_CACHE)) == NULL) {
3522 		dprintf(DBG_ERR, "lookup node failed: %s\n", path);
3523 		return;
3524 	}
3525 
3526 	for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) {
3527 		if (walk_cache_links(hdp, cmnp->link, linkp)
3528 		    == DI_WALK_TERMINATE)
3529 			break;
3530 	}
3531 }
3532 
3533 /*
3534  * Private function
3535  *
3536  * Walk cached links corresponding to the given path.
3537  *
3538  * path		path to a node or minor node.
3539  *
3540  * flags	specifies the type of devlinks to be selected.
3541  *		If DI_PRIMARY_LINK is used, only primary links are selected.
3542  *		If DI_SECONDARY_LINK is specified, only secondary links
3543  *		are selected.
3544  *		If neither flag is specified, all devlinks are selected.
3545  *
3546  * re		An extended regular expression in regex(5) format which
3547  *		selects the /dev links to be returned. The regular
3548  *		expression should use link pathnames relative to
3549  *		/dev. i.e. without the leading "/dev/" prefix.
3550  *		A NULL value matches all devlinks.
3551  */
3552 int
3553 di_devlink_cache_walk(di_devlink_handle_t hdp,
3554 	const char *re,
3555 	const char *path,
3556 	uint_t flags,
3557 	void *arg,
3558 	int (*devlink_callback)(di_devlink_t, void *))
3559 {
3560 	regex_t reg;
3561 	link_desc_t linkd = {NULL};
3562 
3563 	if (hdp == NULL || path == NULL || !link_flag(flags) ||
3564 	    !HDL_RDWR(hdp) || devlink_callback == NULL) {
3565 		errno = EINVAL;
3566 		return (-1);
3567 	}
3568 
3569 	linkd.flags = flags;
3570 	linkd.arg = arg;
3571 	linkd.fcn = devlink_callback;
3572 
3573 	if (re) {
3574 		if (regcomp(&reg, re, REG_EXTENDED) != 0)
3575 			return (-1);
3576 		linkd.regp = &reg;
3577 	}
3578 
3579 	if (minor_colon(path) == NULL) {
3580 		walk_cache_node(hdp, path, &linkd);
3581 	} else {
3582 		walk_cache_minor(hdp, path, &linkd);
3583 	}
3584 
3585 	if (re)
3586 		regfree(&reg);
3587 
3588 	return (0);
3589 }
3590 
3591 #define	DEBUG_ENV_VAR	"_DEVLINK_DEBUG"
3592 static int _devlink_debug = -1;
3593 
3594 /*
3595  * debug level is initialized to -1.
3596  * On first call into this routine, debug level is set.
3597  * If debug level is zero, debugging msgs are disabled.
3598  */
3599 static void
3600 debug_print(debug_level_t msglevel, const char *fmt, va_list ap)
3601 {
3602 	char	*cp;
3603 	int	save;
3604 
3605 	/*
3606 	 * We shouldn't be here if debug is disabled
3607 	 */
3608 	assert(_devlink_debug != 0);
3609 
3610 	/*
3611 	 * Set debug level on first call into this routine
3612 	 */
3613 	if (_devlink_debug < 0) {
3614 		if ((cp = getenv(DEBUG_ENV_VAR)) == NULL) {
3615 			_devlink_debug = 0;
3616 			return;
3617 		}
3618 
3619 		save = errno;
3620 		errno = 0;
3621 		_devlink_debug = strtol(cp, NULL, 10);
3622 		if (errno != 0 || _devlink_debug < 0)  {
3623 			_devlink_debug = 0;
3624 			errno = save;
3625 			return;
3626 		}
3627 		errno = save;
3628 
3629 		if (!_devlink_debug)
3630 			return;
3631 	}
3632 
3633 	/* debug msgs are enabled */
3634 	assert(_devlink_debug > 0);
3635 
3636 	if (_devlink_debug < msglevel)
3637 		return;
3638 
3639 
3640 	/* Print a distinctive label for error msgs */
3641 	if (msglevel == DBG_ERR) {
3642 		(void) fprintf(stderr, "[ERROR]: ");
3643 	}
3644 
3645 	(void) vfprintf(stderr, fmt, ap);
3646 }
3647 
3648 /* ARGSUSED */
3649 /* PRINTFLIKE2 */
3650 void
3651 dprintf(debug_level_t msglevel, const char *fmt, ...)
3652 {
3653 	va_list ap;
3654 
3655 	assert(msglevel > 0);
3656 
3657 	if (!_devlink_debug)
3658 		return;
3659 
3660 	va_start(ap, fmt);
3661 	debug_print(msglevel, fmt, ap);
3662 	va_end(ap);
3663 }
3664