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