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