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