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