17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5facf4a8dSllai  * Common Development and Distribution License (the "License").
6facf4a8dSllai  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2274ceea2dSVikram Hegde  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
27facf4a8dSllai #include "libdevinfo.h"
287c478bd9Sstevel@tonic-gate #include "devinfo_devlink.h"
29ff2aee48Scth #include "device_info.h"
307c478bd9Sstevel@tonic-gate 
31ff2aee48Scth #undef	DEBUG
32ff2aee48Scth #ifndef	DEBUG
337c478bd9Sstevel@tonic-gate #define	NDEBUG 1
347c478bd9Sstevel@tonic-gate #else
357c478bd9Sstevel@tonic-gate #undef	NDEBUG
367c478bd9Sstevel@tonic-gate #endif
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include <assert.h>
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate static mutex_t update_mutex = DEFAULTMUTEX; /* Protects update record lock */
41568e756aSvikram static mutex_t temp_file_mutex = DEFAULTMUTEX; /* for file creation tests */
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate static const size_t elem_sizes[DB_TYPES] = {
447c478bd9Sstevel@tonic-gate 	sizeof (struct db_node),
457c478bd9Sstevel@tonic-gate 	sizeof (struct db_minor),
467c478bd9Sstevel@tonic-gate 	sizeof (struct db_link),
477c478bd9Sstevel@tonic-gate 	sizeof (char)
487c478bd9Sstevel@tonic-gate };
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate  * List of directories/files skipped while physically walking /dev
527c478bd9Sstevel@tonic-gate  * Paths are relative to "<root>/dev/"
537c478bd9Sstevel@tonic-gate  */
547c478bd9Sstevel@tonic-gate static const char *skip_dirs[] = {"fd"};
557c478bd9Sstevel@tonic-gate static const char *skip_files[] = {
567c478bd9Sstevel@tonic-gate 	"stdout",
577c478bd9Sstevel@tonic-gate 	"stdin",
587c478bd9Sstevel@tonic-gate 	"stderr"
597c478bd9Sstevel@tonic-gate };
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #define	N_SKIP_DIRS	(sizeof (skip_dirs) / sizeof (skip_dirs[0]))
627c478bd9Sstevel@tonic-gate #define	N_SKIP_FILES	(sizeof (skip_files) / sizeof (skip_files[0]))
637c478bd9Sstevel@tonic-gate 
64568e756aSvikram #define	DI_TEST_DB	ETCDEV "di_test_db"
65568e756aSvikram 
667c478bd9Sstevel@tonic-gate /*
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  * This file contains two sets of interfaces which operate on the reverse
697c478bd9Sstevel@tonic-gate  * links database. One set (which includes di_devlink_open()/_close())
70*bbf21555SRichard Lowe  * allows link generators like devfsadm(8) and ucblinks(1B) (writers) to
717c478bd9Sstevel@tonic-gate  * populate the database with /devices -> /dev mappings. Another set
727c478bd9Sstevel@tonic-gate  * of interfaces (which includes di_devlink_init()/_fini()) allows
737c478bd9Sstevel@tonic-gate  * applications (readers) to lookup the database for /dev links corresponding
747c478bd9Sstevel@tonic-gate  * to a given minor.
757c478bd9Sstevel@tonic-gate  *
767c478bd9Sstevel@tonic-gate  * Writers operate on a cached version of the database. The cache is created
777c478bd9Sstevel@tonic-gate  * when di_devlink_open() is called. As links in /dev are created and removed,
787c478bd9Sstevel@tonic-gate  * the cache is updated to keep it in synch with /dev. When the /dev updates
797c478bd9Sstevel@tonic-gate  * are complete, the link generator calls di_devlink_close() which writes
807c478bd9Sstevel@tonic-gate  * out the cache to the database.
817c478bd9Sstevel@tonic-gate  *
827c478bd9Sstevel@tonic-gate  * Applications which need to lookup the database, call di_devlink_init().
837c478bd9Sstevel@tonic-gate  * di_devlink_init() checks the database file (if one exists). If the
847c478bd9Sstevel@tonic-gate  * database is valid, it is mapped into the address space of the
857c478bd9Sstevel@tonic-gate  * application. The database file consists of several segments. Each
867c478bd9Sstevel@tonic-gate  * segment can be mapped in independently and is mapped on demand.
877c478bd9Sstevel@tonic-gate  *
887c478bd9Sstevel@tonic-gate  *		   Database Layout
897c478bd9Sstevel@tonic-gate  *
907c478bd9Sstevel@tonic-gate  *		---------------------
917c478bd9Sstevel@tonic-gate  *		|	Magic #     |
927c478bd9Sstevel@tonic-gate  *		| ----------------- |
937c478bd9Sstevel@tonic-gate  *		|       Version	    |	HEADER
947c478bd9Sstevel@tonic-gate  *		| ----------------- |
957c478bd9Sstevel@tonic-gate  *		|        ...        |
967c478bd9Sstevel@tonic-gate  *		---------------------
977c478bd9Sstevel@tonic-gate  *		|		    |
987c478bd9Sstevel@tonic-gate  *		|		    |	NODES
997c478bd9Sstevel@tonic-gate  *		|	            |
1007c478bd9Sstevel@tonic-gate  *		|		    |
1017c478bd9Sstevel@tonic-gate  *		---------------------
1027c478bd9Sstevel@tonic-gate  *		|		    |
1037c478bd9Sstevel@tonic-gate  *		|		    |	MINORS
1047c478bd9Sstevel@tonic-gate  *		|	            |
1057c478bd9Sstevel@tonic-gate  *		|		    |
1067c478bd9Sstevel@tonic-gate  *		---------------------
1077c478bd9Sstevel@tonic-gate  *		|		    |
1087c478bd9Sstevel@tonic-gate  *		|		    |   LINKS
1097c478bd9Sstevel@tonic-gate  *		|	            |
1107c478bd9Sstevel@tonic-gate  *		|		    |
1117c478bd9Sstevel@tonic-gate  *		---------------------
1127c478bd9Sstevel@tonic-gate  *		|		    |
1137c478bd9Sstevel@tonic-gate  *		|		    |	STRINGS
1147c478bd9Sstevel@tonic-gate  *		|	            |
1157c478bd9Sstevel@tonic-gate  *		|		    |
1167c478bd9Sstevel@tonic-gate  *		---------------------
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  * Readers can lookup /dev links for a specific minor or
1197c478bd9Sstevel@tonic-gate  * lookup all /dev links. In the latter case, the node
1207c478bd9Sstevel@tonic-gate  * and minor segments are not mapped in and the reader
1217c478bd9Sstevel@tonic-gate  * walks through every link in the link segment.
1227c478bd9Sstevel@tonic-gate  *
1237c478bd9Sstevel@tonic-gate  */
1247c478bd9Sstevel@tonic-gate di_devlink_handle_t
di_devlink_open(const char * root_dir,uint_t flags)1257c478bd9Sstevel@tonic-gate di_devlink_open(const char *root_dir, uint_t flags)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	int err;
1287c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
1297c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp;
1307c478bd9Sstevel@tonic-gate 	int retried = 0;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate retry:
1337c478bd9Sstevel@tonic-gate 	/*
1347c478bd9Sstevel@tonic-gate 	 * Allocate a read-write handle but open the DB in readonly
1357c478bd9Sstevel@tonic-gate 	 * mode. We do writes only to a temporary copy of the database.
1367c478bd9Sstevel@tonic-gate 	 */
1377c478bd9Sstevel@tonic-gate 	if ((hdp = handle_alloc(root_dir, OPEN_RDWR)) == NULL) {
1387c478bd9Sstevel@tonic-gate 		return (NULL);
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	err = open_db(hdp, OPEN_RDONLY);
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	/*
144f7b6b8cfScth 	 * We don't want to unlink the db at this point - if we did we
145f7b6b8cfScth 	 * would be creating a window where consumers would take a slow
146f7b6b8cfScth 	 * code path (and those consumers might also trigger requests for
147f7b6b8cfScth 	 * db creation, which we are already in the process of doing).
148f7b6b8cfScth 	 * When we are done with our update, we use rename to install the
149f7b6b8cfScth 	 * latest version of the db file.
1507c478bd9Sstevel@tonic-gate 	 */
1517c478bd9Sstevel@tonic-gate 	get_db_path(hdp, DB_FILE, path, sizeof (path));
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	/*
1547c478bd9Sstevel@tonic-gate 	 * The flags argument is reserved for future use.
1557c478bd9Sstevel@tonic-gate 	 */
1567c478bd9Sstevel@tonic-gate 	if (flags != 0) {
1577c478bd9Sstevel@tonic-gate 		handle_free(&hdp); /* also closes the DB */
1587c478bd9Sstevel@tonic-gate 		errno = EINVAL;
1597c478bd9Sstevel@tonic-gate 		return (NULL);
1607c478bd9Sstevel@tonic-gate 	}
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	if (cache_alloc(hdp) != 0) {
1637c478bd9Sstevel@tonic-gate 		handle_free(&hdp);
1647c478bd9Sstevel@tonic-gate 		return (NULL);
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	if (err) {
1687c478bd9Sstevel@tonic-gate 		/*
1697c478bd9Sstevel@tonic-gate 		 * Failed to open DB.
1707c478bd9Sstevel@tonic-gate 		 * The most likely cause is that DB file did not exist.
1717c478bd9Sstevel@tonic-gate 		 * Call di_devlink_close() to recreate the DB file and
1727c478bd9Sstevel@tonic-gate 		 * retry di_devlink_open().
1737c478bd9Sstevel@tonic-gate 		 */
1747c478bd9Sstevel@tonic-gate 		if (retried == 0) {
1757c478bd9Sstevel@tonic-gate 			(void) di_devlink_close(&hdp, 0);
1767c478bd9Sstevel@tonic-gate 			retried = 1;
1777c478bd9Sstevel@tonic-gate 			goto retry;
1787c478bd9Sstevel@tonic-gate 		}
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 		/*
1817c478bd9Sstevel@tonic-gate 		 * DB cannot be opened, just return the
1827c478bd9Sstevel@tonic-gate 		 * handle. We will recreate the DB later.
1837c478bd9Sstevel@tonic-gate 		 */
1847c478bd9Sstevel@tonic-gate 		return (hdp);
1857c478bd9Sstevel@tonic-gate 	}
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	/* Read the database into the cache */
1887c478bd9Sstevel@tonic-gate 	CACHE(hdp)->update_count = DB_HDR(hdp)->update_count;
1897c478bd9Sstevel@tonic-gate 	(void) read_nodes(hdp, NULL, DB_HDR(hdp)->root_idx);
1907c478bd9Sstevel@tonic-gate 	(void) read_links(hdp, NULL, DB_HDR(hdp)->dngl_idx);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	(void) close_db(hdp);
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	return (hdp);
1957c478bd9Sstevel@tonic-gate }
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate static void
get_db_path(struct di_devlink_handle * hdp,const char * fname,char * buf,size_t blen)1987c478bd9Sstevel@tonic-gate get_db_path(
1997c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp,
2007c478bd9Sstevel@tonic-gate 	const char *fname,
2017c478bd9Sstevel@tonic-gate 	char *buf,
2027c478bd9Sstevel@tonic-gate 	size_t blen)
2037c478bd9Sstevel@tonic-gate {
2047c478bd9Sstevel@tonic-gate 	char *dir = NULL;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate #ifdef	DEBUG
2077c478bd9Sstevel@tonic-gate 	if (dir = getenv(ALT_DB_DIR)) {
2087c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_INFO, "get_db_path: alternate db dir: %s\n",
2097c478bd9Sstevel@tonic-gate 		    dir);
2107c478bd9Sstevel@tonic-gate 	}
2117c478bd9Sstevel@tonic-gate #endif
2127c478bd9Sstevel@tonic-gate 	if (dir == NULL) {
213facf4a8dSllai 		dir = hdp->db_dir;
2147c478bd9Sstevel@tonic-gate 	}
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, blen, "%s/%s", dir, fname);
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate static int
open_db(struct di_devlink_handle * hdp,int flags)2207c478bd9Sstevel@tonic-gate open_db(struct di_devlink_handle *hdp, int flags)
2217c478bd9Sstevel@tonic-gate {
2227c478bd9Sstevel@tonic-gate 	size_t sz;
2237c478bd9Sstevel@tonic-gate 	long page_sz;
2247c478bd9Sstevel@tonic-gate 	int fd, rv, flg;
2257c478bd9Sstevel@tonic-gate 	struct stat sbuf;
2267c478bd9Sstevel@tonic-gate 	uint32_t count[DB_TYPES] = {0};
2277c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
2287c478bd9Sstevel@tonic-gate 	void *cp;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	assert(!DB_OPEN(hdp));
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate #ifdef	DEBUG
2337c478bd9Sstevel@tonic-gate 	if (getenv(SKIP_DB)) {
2347c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_INFO, "open_db: skipping database\n");
2357c478bd9Sstevel@tonic-gate 		return (-1);
2367c478bd9Sstevel@tonic-gate 	}
2377c478bd9Sstevel@tonic-gate #endif
2387c478bd9Sstevel@tonic-gate 	if ((page_sz = sysconf(_SC_PAGE_SIZE)) == -1) {
2397c478bd9Sstevel@tonic-gate 		return (-1);
2407c478bd9Sstevel@tonic-gate 	}
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	/*
2437c478bd9Sstevel@tonic-gate 	 * Use O_TRUNC flag for write access, so that the subsequent ftruncate()
2447c478bd9Sstevel@tonic-gate 	 * call will zero-fill the entire file
2457c478bd9Sstevel@tonic-gate 	 */
2467c478bd9Sstevel@tonic-gate 	if (IS_RDONLY(flags)) {
2477c478bd9Sstevel@tonic-gate 		flg = O_RDONLY;
2487c478bd9Sstevel@tonic-gate 		get_db_path(hdp, DB_FILE, path, sizeof (path));
2497c478bd9Sstevel@tonic-gate 	} else {
2507c478bd9Sstevel@tonic-gate 		flg = O_RDWR|O_CREAT|O_TRUNC;
2517c478bd9Sstevel@tonic-gate 		get_db_path(hdp, DB_TMP, path, sizeof (path));
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate 
254facf4a8dSllai 	/*
255facf4a8dSllai 	 * Avoid triggering /dev reconfigure for read when not present
256facf4a8dSllai 	 */
257facf4a8dSllai 	if (IS_RDONLY(flags) &&
258facf4a8dSllai 	    (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) {
259facf4a8dSllai 		return (-1);
260facf4a8dSllai 	}
261facf4a8dSllai 
2627c478bd9Sstevel@tonic-gate 	if ((fd = open(path, flg, DB_PERMS)) == -1) {
2637c478bd9Sstevel@tonic-gate 		return (-1);
2647c478bd9Sstevel@tonic-gate 	}
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	if (IS_RDONLY(flags)) {
2677c478bd9Sstevel@tonic-gate 		flg = PROT_READ;
2687c478bd9Sstevel@tonic-gate 		rv = fstat(fd, &sbuf);
2697c478bd9Sstevel@tonic-gate 		sz = sbuf.st_size;
2707c478bd9Sstevel@tonic-gate 	} else {
2717c478bd9Sstevel@tonic-gate 		flg = PROT_READ | PROT_WRITE;
2727c478bd9Sstevel@tonic-gate 		sz = size_db(hdp, page_sz, count);
2737c478bd9Sstevel@tonic-gate 		rv = ftruncate(fd, sz);
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	if (rv == -1 || sz < HDR_LEN) {
2777c478bd9Sstevel@tonic-gate 		if (rv != -1)
2787c478bd9Sstevel@tonic-gate 			errno = EINVAL;
2797c478bd9Sstevel@tonic-gate 		(void) close(fd);
2807c478bd9Sstevel@tonic-gate 		return (-1);
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	cp = mmap(0, HDR_LEN, flg, MAP_SHARED, fd, 0);
2847c478bd9Sstevel@tonic-gate 	if (cp == MAP_FAILED) {
2857c478bd9Sstevel@tonic-gate 		(void) close(fd);
2867c478bd9Sstevel@tonic-gate 		return (-1);
2877c478bd9Sstevel@tonic-gate 	}
2887c478bd9Sstevel@tonic-gate 	DB(hdp)->hdr = (struct db_hdr *)cp;
2897c478bd9Sstevel@tonic-gate 	DB(hdp)->db_fd = fd;
2907c478bd9Sstevel@tonic-gate 	DB(hdp)->flags = flags;
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	if (IS_RDONLY(flags)) {
2937c478bd9Sstevel@tonic-gate 		rv = invalid_db(hdp, sz, page_sz);
2947c478bd9Sstevel@tonic-gate 	} else {
2957c478bd9Sstevel@tonic-gate 		rv = init_hdr(hdp, page_sz, count);
2967c478bd9Sstevel@tonic-gate 	}
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	if (rv) {
2997c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "open_db: invalid DB(%s)\n", path);
3007c478bd9Sstevel@tonic-gate 		(void) close_db(hdp);
3017c478bd9Sstevel@tonic-gate 		return (-1);
3027c478bd9Sstevel@tonic-gate 	} else {
3037c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "open_db: DB(%s): opened\n", path);
3047c478bd9Sstevel@tonic-gate 		return (0);
3057c478bd9Sstevel@tonic-gate 	}
3067c478bd9Sstevel@tonic-gate }
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate /*
3097c478bd9Sstevel@tonic-gate  * A handle can be allocated for read-only or read-write access
3107c478bd9Sstevel@tonic-gate  */
3117c478bd9Sstevel@tonic-gate static struct di_devlink_handle *
handle_alloc(const char * root_dir,uint_t flags)3127c478bd9Sstevel@tonic-gate handle_alloc(const char *root_dir, uint_t flags)
3137c478bd9Sstevel@tonic-gate {
314facf4a8dSllai 	char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX];
3157c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp, proto = {0};
316568e756aSvikram 	int install = 0;
317568e756aSvikram 	int isroot = 0;
318568e756aSvikram 	struct stat sb;
319568e756aSvikram 	char can_path[PATH_MAX];
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	assert(flags == OPEN_RDWR || flags == OPEN_RDONLY);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	dev_dir[0] = '\0';
324facf4a8dSllai 	db_dir[0] = '\0';
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	/*
3277c478bd9Sstevel@tonic-gate 	 * NULL and the empty string are equivalent to "/"
3287c478bd9Sstevel@tonic-gate 	 */
3297c478bd9Sstevel@tonic-gate 	if (root_dir && root_dir[0] != '\0') {
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 		if (root_dir[0] != '/') {
3327c478bd9Sstevel@tonic-gate 			errno = EINVAL;
3337c478bd9Sstevel@tonic-gate 			return (NULL);
3347c478bd9Sstevel@tonic-gate 		}
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate #ifdef	DEBUG
3377c478bd9Sstevel@tonic-gate 		/*LINTED*/
3387c478bd9Sstevel@tonic-gate 		assert(sizeof (dev_dir) >= PATH_MAX);
3397c478bd9Sstevel@tonic-gate #endif
340facf4a8dSllai 		if ((realpath(root_dir, dev_dir) == NULL) ||
341facf4a8dSllai 		    (realpath(root_dir, db_dir) == NULL)) {
3427c478bd9Sstevel@tonic-gate 			return (NULL);
3437c478bd9Sstevel@tonic-gate 		}
344568e756aSvikram 	} else {
345568e756aSvikram 		/*
346568e756aSvikram 		 * The dev dir is at /dev i.e. we are not doing a -r /altroot
347568e756aSvikram 		 */
348568e756aSvikram 		isroot = 1;
3497c478bd9Sstevel@tonic-gate 	}
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	if (strcmp(dev_dir, "/") == 0) {
352facf4a8dSllai 		dev_dir[0] = 0;
353facf4a8dSllai 		db_dir[0] = 0;
3547c478bd9Sstevel@tonic-gate 	} else {
355facf4a8dSllai 		(void) strlcpy(db_dir, dev_dir, sizeof (db_dir));
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
358facf4a8dSllai 	(void) strlcat(dev_dir, DEV, sizeof (dev_dir));
359facf4a8dSllai 	(void) strlcat(db_dir, ETCDEV, sizeof (db_dir));
360facf4a8dSllai 
361568e756aSvikram 	/*
362568e756aSvikram 	 * The following code is for install. Readers and writers need
363568e756aSvikram 	 * to be redirected to /tmp/etc/dev for the database file.
364568e756aSvikram 	 * Note that we test for readonly /etc by actually creating a
365568e756aSvikram 	 * file since statvfs is not a reliable method for determining
366568e756aSvikram 	 * readonly filesystems.
367568e756aSvikram 	 */
368568e756aSvikram 	install = 0;
369568e756aSvikram 	(void) snprintf(can_path, sizeof (can_path), "%s/%s", ETCDEV, DB_FILE);
370568e756aSvikram 	if (flags == OPEN_RDWR && isroot) {
371568e756aSvikram 		char di_test_db[PATH_MAX];
372568e756aSvikram 		int fd;
373568e756aSvikram 		(void) mutex_lock(&temp_file_mutex);
374568e756aSvikram 		(void) snprintf(di_test_db, sizeof (di_test_db), "%s.%d",
375568e756aSvikram 		    DI_TEST_DB, getpid());
376568e756aSvikram 		fd = open(di_test_db, O_CREAT|O_RDWR|O_EXCL, 0644);
377568e756aSvikram 		if (fd == -1 && errno == EROFS && stat(can_path, &sb) == -1)
378568e756aSvikram 			install = 1;
379568e756aSvikram 		if (fd != -1) {
380568e756aSvikram 			(void) close(fd);
381568e756aSvikram 			(void) unlink(di_test_db);
382568e756aSvikram 		}
383568e756aSvikram 		(void) mutex_unlock(&temp_file_mutex);
384568e756aSvikram 	} else if (isroot) {
385568e756aSvikram 		/*
386568e756aSvikram 		 * Readers can be non-privileged so we cannot test by creating
387568e756aSvikram 		 * a file in /etc/dev. Instead we check if the database
388568e756aSvikram 		 * file is missing in /etc/dev and is present in /tmp/etc/dev
389568e756aSvikram 		 * and is owned by root.
390568e756aSvikram 		 */
391568e756aSvikram 		char install_path[PATH_MAX];
392568e756aSvikram 
393568e756aSvikram 		(void) snprintf(install_path, sizeof (install_path),
394568e756aSvikram 		    "/tmp%s/%s", ETCDEV, DB_FILE);
395568e756aSvikram 		if (stat(can_path, &sb) == -1 && stat(install_path, &sb)
396568e756aSvikram 		    != -1 && sb.st_uid == 0) {
397568e756aSvikram 			install = 1;
398568e756aSvikram 		}
399568e756aSvikram 	}
400568e756aSvikram 
401568e756aSvikram 	/*
402568e756aSvikram 	 * Check if we are in install. If we are, the database will be in
403568e756aSvikram 	 * /tmp/etc/dev
404568e756aSvikram 	 */
405568e756aSvikram 	if (install)
406568e756aSvikram 		(void) snprintf(db_dir, sizeof (db_dir), "/tmp%s", ETCDEV);
407568e756aSvikram 
4087c478bd9Sstevel@tonic-gate 	proto.dev_dir = dev_dir;
409facf4a8dSllai 	proto.db_dir = db_dir;
4107c478bd9Sstevel@tonic-gate 	proto.flags = flags;
4117c478bd9Sstevel@tonic-gate 	proto.lock_fd = -1;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	/*
4147c478bd9Sstevel@tonic-gate 	 * Lock database if a read-write handle is being allocated.
4157c478bd9Sstevel@tonic-gate 	 * Locks are needed to protect against multiple writers.
416f7b6b8cfScth 	 * Readers don't need locks.
4177c478bd9Sstevel@tonic-gate 	 */
4187c478bd9Sstevel@tonic-gate 	if (HDL_RDWR(&proto)) {
419ff2aee48Scth 		if (enter_db_lock(&proto, root_dir) != 1) {
4207c478bd9Sstevel@tonic-gate 			return (NULL);
4217c478bd9Sstevel@tonic-gate 		}
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	DB(&proto)->db_fd = -1;
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	hdp = calloc(1, sizeof (struct di_devlink_handle));
4277c478bd9Sstevel@tonic-gate 	if (hdp == NULL) {
4287c478bd9Sstevel@tonic-gate 		goto error;
4297c478bd9Sstevel@tonic-gate 	}
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	*hdp = proto;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	/*
4347c478bd9Sstevel@tonic-gate 	 * The handle hdp now contains a pointer to local storage
4357c478bd9Sstevel@tonic-gate 	 * in the dev_dir field (obtained from the proto handle).
4367c478bd9Sstevel@tonic-gate 	 * In the following line, a dynamically allocated version
4377c478bd9Sstevel@tonic-gate 	 * is substituted.
4387c478bd9Sstevel@tonic-gate 	 */
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	if ((hdp->dev_dir = strdup(proto.dev_dir)) == NULL) {
4417c478bd9Sstevel@tonic-gate 		free(hdp);
4427c478bd9Sstevel@tonic-gate 		goto error;
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 
445facf4a8dSllai 	if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) {
446facf4a8dSllai 		free(hdp->dev_dir);
447facf4a8dSllai 		free(hdp);
448facf4a8dSllai 		goto error;
449facf4a8dSllai 	}
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	return (hdp);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate error:
4547c478bd9Sstevel@tonic-gate 	if (HDL_RDWR(&proto)) {
4557c478bd9Sstevel@tonic-gate 		/* Unlink DB file on error */
4567c478bd9Sstevel@tonic-gate 		get_db_path(&proto, DB_FILE, path, sizeof (path));
4577c478bd9Sstevel@tonic-gate 		(void) unlink(path);
458ff2aee48Scth 		exit_db_lock(&proto);
4597c478bd9Sstevel@tonic-gate 	}
4607c478bd9Sstevel@tonic-gate 	return (NULL);
4617c478bd9Sstevel@tonic-gate }
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate static int
cache_alloc(struct di_devlink_handle * hdp)4657c478bd9Sstevel@tonic-gate cache_alloc(struct di_devlink_handle *hdp)
4667c478bd9Sstevel@tonic-gate {
4677c478bd9Sstevel@tonic-gate 	size_t hash_sz = 0;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	if (DB_OPEN(hdp)) {
4727c478bd9Sstevel@tonic-gate 		hash_sz = DB_NUM(hdp, DB_LINK) / AVG_CHAIN_SIZE;
4737c478bd9Sstevel@tonic-gate 	}
4747c478bd9Sstevel@tonic-gate 	hash_sz = (hash_sz >= MIN_HASH_SIZE) ? hash_sz : MIN_HASH_SIZE;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	CACHE(hdp)->hash = calloc(hash_sz, sizeof (cache_link_t *));
4777c478bd9Sstevel@tonic-gate 	if (CACHE(hdp)->hash == NULL) {
4787c478bd9Sstevel@tonic-gate 		return (-1);
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 	CACHE(hdp)->hash_sz = hash_sz;
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	return (0);
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate static int
invalid_db(struct di_devlink_handle * hdp,size_t fsize,long page_sz)4877c478bd9Sstevel@tonic-gate invalid_db(struct di_devlink_handle *hdp, size_t fsize, long page_sz)
4887c478bd9Sstevel@tonic-gate {
4897c478bd9Sstevel@tonic-gate 	int i;
4907c478bd9Sstevel@tonic-gate 	char *cp;
4917c478bd9Sstevel@tonic-gate 	size_t sz;
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	if (DB_HDR(hdp)->magic != DB_MAGIC || DB_HDR(hdp)->vers != DB_VERSION) {
4947c478bd9Sstevel@tonic-gate 		return (1);
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	if (DB_HDR(hdp)->page_sz == 0 || DB_HDR(hdp)->page_sz != page_sz) {
4987c478bd9Sstevel@tonic-gate 		return (1);
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	sz = seg_size(hdp, DB_HEADER);
5027c478bd9Sstevel@tonic-gate 	for (i = 0; i < DB_TYPES; i++) {
5037c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_INFO, "N[%u] = %u\n", i, DB_NUM(hdp, i));
5047c478bd9Sstevel@tonic-gate 		/* There must be at least 1 element of each type */
5057c478bd9Sstevel@tonic-gate 		if (DB_NUM(hdp, i) < 1) {
5067c478bd9Sstevel@tonic-gate 			return (1);
5077c478bd9Sstevel@tonic-gate 		}
5087c478bd9Sstevel@tonic-gate 		sz += seg_size(hdp, i);
5097c478bd9Sstevel@tonic-gate 		assert(sz % page_sz == 0);
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	if (sz != fsize) {
5137c478bd9Sstevel@tonic-gate 		return (1);
5147c478bd9Sstevel@tonic-gate 	}
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	if (!VALID_INDEX(hdp, DB_NODE, DB_HDR(hdp)->root_idx)) {
5177c478bd9Sstevel@tonic-gate 		return (1);
5187c478bd9Sstevel@tonic-gate 	}
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	if (!VALID_INDEX(hdp, DB_LINK, DB_HDR(hdp)->dngl_idx)) {
5217c478bd9Sstevel@tonic-gate 		return (1);
5227c478bd9Sstevel@tonic-gate 	}
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	if (DB_EMPTY(hdp)) {
5257c478bd9Sstevel@tonic-gate 		return (1);
5267c478bd9Sstevel@tonic-gate 	}
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	/*
5297c478bd9Sstevel@tonic-gate 	 * The last character in the string segment must be a NUL char.
5307c478bd9Sstevel@tonic-gate 	 */
5317c478bd9Sstevel@tonic-gate 	cp = get_string(hdp, DB_NUM(hdp, DB_STR) - 1);
5327c478bd9Sstevel@tonic-gate 	if (cp == NULL || *cp != '\0') {
5337c478bd9Sstevel@tonic-gate 		return (1);
5347c478bd9Sstevel@tonic-gate 	}
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	return (0);
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate static int
read_nodes(struct di_devlink_handle * hdp,cache_node_t * pcnp,uint32_t nidx)5407c478bd9Sstevel@tonic-gate read_nodes(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
5417c478bd9Sstevel@tonic-gate {
5427c478bd9Sstevel@tonic-gate 	char *path;
5437c478bd9Sstevel@tonic-gate 	cache_node_t *cnp;
5447c478bd9Sstevel@tonic-gate 	struct db_node *dnp;
5457c478bd9Sstevel@tonic-gate 	const char *fcn = "read_nodes";
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	/*
5507c478bd9Sstevel@tonic-gate 	 * parent node should be NULL only for the root node
5517c478bd9Sstevel@tonic-gate 	 */
5527c478bd9Sstevel@tonic-gate 	if ((pcnp == NULL) ^ (nidx == DB_HDR(hdp)->root_idx)) {
5537c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "%s: invalid parent or index(%u)\n",
5547c478bd9Sstevel@tonic-gate 		    fcn, nidx);
5557c478bd9Sstevel@tonic-gate 		SET_DB_ERR(hdp);
5567c478bd9Sstevel@tonic-gate 		return (-1);
5577c478bd9Sstevel@tonic-gate 	}
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	for (; dnp = get_node(hdp, nidx); nidx = dnp->sib) {
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 		path = get_string(hdp, dnp->path);
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 		/*
5647c478bd9Sstevel@tonic-gate 		 * Insert at head of list to recreate original order
5657c478bd9Sstevel@tonic-gate 		 */
5667c478bd9Sstevel@tonic-gate 		cnp = node_insert(hdp, pcnp, path, INSERT_HEAD);
5677c478bd9Sstevel@tonic-gate 		if (cnp == NULL) {
5687c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
5697c478bd9Sstevel@tonic-gate 			break;
5707c478bd9Sstevel@tonic-gate 		}
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		assert(strcmp(path, "/") ^ (nidx == DB_HDR(hdp)->root_idx));
5737c478bd9Sstevel@tonic-gate 		assert(strcmp(path, "/") != 0 || dnp->sib == DB_NIL);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 		if (read_minors(hdp, cnp, dnp->minor) != 0 ||
5767c478bd9Sstevel@tonic-gate 		    read_nodes(hdp, cnp, dnp->child) != 0) {
5777c478bd9Sstevel@tonic-gate 			break;
5787c478bd9Sstevel@tonic-gate 		}
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, nidx,
5817c478bd9Sstevel@tonic-gate 		    cnp->path);
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	return (dnp ? -1 : 0);
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate static int
read_minors(struct di_devlink_handle * hdp,cache_node_t * pcnp,uint32_t nidx)5887c478bd9Sstevel@tonic-gate read_minors(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
5897c478bd9Sstevel@tonic-gate {
5907c478bd9Sstevel@tonic-gate 	cache_minor_t *cmnp;
5917c478bd9Sstevel@tonic-gate 	struct db_minor *dmp;
5927c478bd9Sstevel@tonic-gate 	char *name, *nodetype;
5937c478bd9Sstevel@tonic-gate 	const char *fcn = "read_minors";
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	if (pcnp == NULL) {
5987c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "%s: minor[%u]: orphan minor\n", fcn,
5997c478bd9Sstevel@tonic-gate 		    nidx);
6007c478bd9Sstevel@tonic-gate 		SET_DB_ERR(hdp);
6017c478bd9Sstevel@tonic-gate 		return (-1);
6027c478bd9Sstevel@tonic-gate 	}
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) {
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 		name = get_string(hdp, dmp->name);
6077c478bd9Sstevel@tonic-gate 		nodetype = get_string(hdp, dmp->nodetype);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 		cmnp = minor_insert(hdp, pcnp, name, nodetype, NULL);
6107c478bd9Sstevel@tonic-gate 		if (cmnp == NULL) {
6117c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
6127c478bd9Sstevel@tonic-gate 			break;
6137c478bd9Sstevel@tonic-gate 		}
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "%s: minor[%u]: %s\n", fcn, nidx,
6167c478bd9Sstevel@tonic-gate 		    cmnp->name);
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		if (read_links(hdp, cmnp, dmp->link) != 0) {
6197c478bd9Sstevel@tonic-gate 			break;
6207c478bd9Sstevel@tonic-gate 		}
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	return (dmp ? -1 : 0);
6247c478bd9Sstevel@tonic-gate }
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate /*
6277c478bd9Sstevel@tonic-gate  * If the link is dangling the corresponding minor will be absent.
6287c478bd9Sstevel@tonic-gate  */
6297c478bd9Sstevel@tonic-gate static int
read_links(struct di_devlink_handle * hdp,cache_minor_t * pcmp,uint32_t nidx)6307c478bd9Sstevel@tonic-gate read_links(struct di_devlink_handle *hdp, cache_minor_t *pcmp, uint32_t nidx)
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate 	cache_link_t *clp;
6337c478bd9Sstevel@tonic-gate 	struct db_link *dlp;
6347c478bd9Sstevel@tonic-gate 	char *path, *content;
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	if (nidx != DB_NIL &&
6397c478bd9Sstevel@tonic-gate 	    ((pcmp == NULL) ^ (nidx == DB_HDR(hdp)->dngl_idx))) {
6407c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "read_links: invalid minor or"
6417c478bd9Sstevel@tonic-gate 		    " index(%u)\n", nidx);
6427c478bd9Sstevel@tonic-gate 		SET_DB_ERR(hdp);
6437c478bd9Sstevel@tonic-gate 		return (-1);
6447c478bd9Sstevel@tonic-gate 	}
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) {
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 		path = get_string(hdp, dlp->path);
6497c478bd9Sstevel@tonic-gate 		content = get_string(hdp, dlp->content);
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 		clp = link_insert(hdp, pcmp, path, content, dlp->attr);
6527c478bd9Sstevel@tonic-gate 		if (clp == NULL) {
6537c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
6547c478bd9Sstevel@tonic-gate 			break;
6557c478bd9Sstevel@tonic-gate 		}
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "read_links: link[%u]: %s%s\n",
6587c478bd9Sstevel@tonic-gate 		    nidx, clp->path, pcmp == NULL ? "(DANGLING)" : "");
6597c478bd9Sstevel@tonic-gate 	}
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	return (dlp ? -1 : 0);
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate int
di_devlink_close(di_devlink_handle_t * pp,int flag)6657c478bd9Sstevel@tonic-gate di_devlink_close(di_devlink_handle_t *pp, int flag)
6667c478bd9Sstevel@tonic-gate {
6677c478bd9Sstevel@tonic-gate 	int i, rv;
6687c478bd9Sstevel@tonic-gate 	char tmp[PATH_MAX];
6697c478bd9Sstevel@tonic-gate 	char file[PATH_MAX];
6707c478bd9Sstevel@tonic-gate 	uint32_t next[DB_TYPES] = {0};
6717c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	if (pp == NULL || *pp == NULL || !HDL_RDWR(*pp)) {
6747c478bd9Sstevel@tonic-gate 		errno = EINVAL;
6757c478bd9Sstevel@tonic-gate 		return (-1);
6767c478bd9Sstevel@tonic-gate 	}
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	hdp = *pp;
6797c478bd9Sstevel@tonic-gate 	*pp = NULL;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	/*
6827c478bd9Sstevel@tonic-gate 	 * The caller encountered some error in their processing.
6837c478bd9Sstevel@tonic-gate 	 * so handle isn't valid. Discard it and return success.
6847c478bd9Sstevel@tonic-gate 	 */
6857c478bd9Sstevel@tonic-gate 	if (flag == DI_LINK_ERROR) {
6867c478bd9Sstevel@tonic-gate 		handle_free(&hdp);
6877c478bd9Sstevel@tonic-gate 		return (0);
6887c478bd9Sstevel@tonic-gate 	}
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	if (DB_ERR(hdp)) {
6917c478bd9Sstevel@tonic-gate 		handle_free(&hdp);
6927c478bd9Sstevel@tonic-gate 		errno = EINVAL;
6937c478bd9Sstevel@tonic-gate 		return (-1);
6947c478bd9Sstevel@tonic-gate 	}
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	/*
6977c478bd9Sstevel@tonic-gate 	 * Extract the DB path before the handle is freed.
6987c478bd9Sstevel@tonic-gate 	 */
6997c478bd9Sstevel@tonic-gate 	get_db_path(hdp, DB_FILE, file, sizeof (file));
7007c478bd9Sstevel@tonic-gate 	get_db_path(hdp, DB_TMP, tmp, sizeof (tmp));
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate 	/*
7037c478bd9Sstevel@tonic-gate 	 * update database with actual contents of /dev
7047c478bd9Sstevel@tonic-gate 	 */
7057c478bd9Sstevel@tonic-gate 	(void) dprintf(DBG_INFO, "di_devlink_close: update_count = %u\n",
7067c478bd9Sstevel@tonic-gate 	    CACHE(hdp)->update_count);
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	/*
7097c478bd9Sstevel@tonic-gate 	 * For performance reasons, synchronization of the database
7107c478bd9Sstevel@tonic-gate 	 * with /dev is turned off by default. However, applications
7117c478bd9Sstevel@tonic-gate 	 * with appropriate permissions can request a "sync" by
7127c478bd9Sstevel@tonic-gate 	 * calling di_devlink_update().
7137c478bd9Sstevel@tonic-gate 	 */
7147c478bd9Sstevel@tonic-gate 	if (CACHE(hdp)->update_count == 0) {
7157c478bd9Sstevel@tonic-gate 		CACHE(hdp)->update_count = 1;
7167c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_INFO,
7177c478bd9Sstevel@tonic-gate 		    "di_devlink_close: synchronizing DB\n");
7187c478bd9Sstevel@tonic-gate 		(void) synchronize_db(hdp);
7197c478bd9Sstevel@tonic-gate 	}
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 	/*
7227c478bd9Sstevel@tonic-gate 	 * Resolve dangling links AFTER synchronizing DB with /dev as the
7237c478bd9Sstevel@tonic-gate 	 * synchronization process may create dangling links.
7247c478bd9Sstevel@tonic-gate 	 */
7257c478bd9Sstevel@tonic-gate 	resolve_dangling_links(hdp);
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	/*
7287c478bd9Sstevel@tonic-gate 	 * All changes to the cache are complete. Write out the cache
7297c478bd9Sstevel@tonic-gate 	 * to the database only if it is not empty.
7307c478bd9Sstevel@tonic-gate 	 */
7317c478bd9Sstevel@tonic-gate 	if (CACHE_EMPTY(hdp)) {
7327c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_INFO, "di_devlink_close: skipping write\n");
7337c478bd9Sstevel@tonic-gate 		(void) unlink(file);
7347c478bd9Sstevel@tonic-gate 		handle_free(&hdp);
7357c478bd9Sstevel@tonic-gate 		return (0);
7367c478bd9Sstevel@tonic-gate 	}
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	if (open_db(hdp, OPEN_RDWR) != 0) {
7397c478bd9Sstevel@tonic-gate 		handle_free(&hdp);
7407c478bd9Sstevel@tonic-gate 		return (-1);
7417c478bd9Sstevel@tonic-gate 	}
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	/*
744ff2aee48Scth 	 * Keep track of array assignments. There is at least
7457c478bd9Sstevel@tonic-gate 	 * 1 element (the "NIL" element) per type.
7467c478bd9Sstevel@tonic-gate 	 */
7477c478bd9Sstevel@tonic-gate 	for (i = 0; i < DB_TYPES; i++) {
7487c478bd9Sstevel@tonic-gate 		next[i] = 1;
7497c478bd9Sstevel@tonic-gate 	}
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 	(void) write_nodes(hdp, NULL, CACHE_ROOT(hdp), next);
7527c478bd9Sstevel@tonic-gate 	(void) write_links(hdp, NULL, CACHE(hdp)->dngl, next);
7537c478bd9Sstevel@tonic-gate 	DB_HDR(hdp)->update_count = CACHE(hdp)->update_count;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	rv = close_db(hdp);
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	if (rv != 0 || DB_ERR(hdp) || rename(tmp, file) != 0) {
7587c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "di_devlink_close: %s error: %s\n",
7597c478bd9Sstevel@tonic-gate 		    rv ? "close_db" : "DB or rename", strerror(errno));
7607c478bd9Sstevel@tonic-gate 		(void) unlink(tmp);
7617c478bd9Sstevel@tonic-gate 		(void) unlink(file);
7627c478bd9Sstevel@tonic-gate 		handle_free(&hdp);
7637c478bd9Sstevel@tonic-gate 		return (-1);
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	handle_free(&hdp);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	(void) dprintf(DBG_INFO, "di_devlink_close: wrote DB(%s)\n", file);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	return (0);
7717c478bd9Sstevel@tonic-gate }
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate /*
7747c478bd9Sstevel@tonic-gate  * Inits the database header.
7757c478bd9Sstevel@tonic-gate  */
7767c478bd9Sstevel@tonic-gate static int
init_hdr(struct di_devlink_handle * hdp,long page_sz,uint32_t * count)7777c478bd9Sstevel@tonic-gate init_hdr(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
7787c478bd9Sstevel@tonic-gate {
7797c478bd9Sstevel@tonic-gate 	int i;
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 	DB_HDR(hdp)->magic = DB_MAGIC;
7827c478bd9Sstevel@tonic-gate 	DB_HDR(hdp)->vers = DB_VERSION;
7837c478bd9Sstevel@tonic-gate 	DB_HDR(hdp)->root_idx = DB_NIL;
7847c478bd9Sstevel@tonic-gate 	DB_HDR(hdp)->dngl_idx = DB_NIL;
7857c478bd9Sstevel@tonic-gate 	DB_HDR(hdp)->page_sz = (uint32_t)page_sz;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	for (i = 0; i < DB_TYPES; i++) {
7887c478bd9Sstevel@tonic-gate 		assert(count[i] >= 1);
7897c478bd9Sstevel@tonic-gate 		DB_NUM(hdp, i) = count[i];
7907c478bd9Sstevel@tonic-gate 	}
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	return (0);
7937c478bd9Sstevel@tonic-gate }
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate static int
write_nodes(struct di_devlink_handle * hdp,struct db_node * pdnp,cache_node_t * cnp,uint32_t * next)7967c478bd9Sstevel@tonic-gate write_nodes(
7977c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp,
7987c478bd9Sstevel@tonic-gate 	struct db_node *pdnp,
7997c478bd9Sstevel@tonic-gate 	cache_node_t *cnp,
8007c478bd9Sstevel@tonic-gate 	uint32_t *next)
8017c478bd9Sstevel@tonic-gate {
8027c478bd9Sstevel@tonic-gate 	uint32_t idx;
8037c478bd9Sstevel@tonic-gate 	struct db_node *dnp;
8047c478bd9Sstevel@tonic-gate 	const char *fcn = "write_nodes";
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 	for (; cnp != NULL; cnp = cnp->sib) {
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		assert(cnp->path != NULL);
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 		/* parent node should only be NULL for root node */
8137c478bd9Sstevel@tonic-gate 		if ((pdnp == NULL) ^ (cnp == CACHE_ROOT(hdp))) {
8147c478bd9Sstevel@tonic-gate 			(void) dprintf(DBG_ERR, "%s: invalid parent for: %s\n",
8157c478bd9Sstevel@tonic-gate 			    fcn, cnp->path);
8167c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
8177c478bd9Sstevel@tonic-gate 			break;
8187c478bd9Sstevel@tonic-gate 		}
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate 		assert((strcmp(cnp->path, "/") != 0) ^
8217c478bd9Sstevel@tonic-gate 		    (cnp == CACHE_ROOT(hdp)));
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 		idx = next[DB_NODE];
8247c478bd9Sstevel@tonic-gate 		if ((dnp = set_node(hdp, idx)) == NULL) {
8257c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
8267c478bd9Sstevel@tonic-gate 			break;
8277c478bd9Sstevel@tonic-gate 		}
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 		dnp->path = write_string(hdp, cnp->path, next);
8307c478bd9Sstevel@tonic-gate 		if (dnp->path == DB_NIL) {
8317c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
8327c478bd9Sstevel@tonic-gate 			break;
8337c478bd9Sstevel@tonic-gate 		}
8347c478bd9Sstevel@tonic-gate 		/* commit write for this node */
8357c478bd9Sstevel@tonic-gate 		next[DB_NODE]++;
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 		if (pdnp == NULL) {
8387c478bd9Sstevel@tonic-gate 			assert(DB_HDR(hdp)->root_idx == DB_NIL);
8397c478bd9Sstevel@tonic-gate 			DB_HDR(hdp)->root_idx = idx;
8407c478bd9Sstevel@tonic-gate 		} else {
8417c478bd9Sstevel@tonic-gate 			dnp->sib = pdnp->child;
8427c478bd9Sstevel@tonic-gate 			pdnp->child = idx;
8437c478bd9Sstevel@tonic-gate 		}
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, idx,
8467c478bd9Sstevel@tonic-gate 		    cnp->path);
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		if (write_minors(hdp, dnp, cnp->minor, next) != 0 ||
8497c478bd9Sstevel@tonic-gate 		    write_nodes(hdp, dnp, cnp->child, next) != 0) {
8507c478bd9Sstevel@tonic-gate 			break;
8517c478bd9Sstevel@tonic-gate 		}
8527c478bd9Sstevel@tonic-gate 	}
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 	return (cnp ? -1 : 0);
8557c478bd9Sstevel@tonic-gate }
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate static int
write_minors(struct di_devlink_handle * hdp,struct db_node * pdnp,cache_minor_t * cmnp,uint32_t * next)8587c478bd9Sstevel@tonic-gate write_minors(
8597c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp,
8607c478bd9Sstevel@tonic-gate 	struct db_node *pdnp,
8617c478bd9Sstevel@tonic-gate 	cache_minor_t *cmnp,
8627c478bd9Sstevel@tonic-gate 	uint32_t *next)
8637c478bd9Sstevel@tonic-gate {
8647c478bd9Sstevel@tonic-gate 	uint32_t idx;
8657c478bd9Sstevel@tonic-gate 	struct db_minor *dmp;
8667c478bd9Sstevel@tonic-gate 	const char *fcn = "write_minors";
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	if (pdnp == NULL) {
8717c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "%s: no node for minor: %s\n", fcn,
8727c478bd9Sstevel@tonic-gate 		    cmnp ? cmnp->name : "<NULL>");
8737c478bd9Sstevel@tonic-gate 		SET_DB_ERR(hdp);
8747c478bd9Sstevel@tonic-gate 		return (-1);
8757c478bd9Sstevel@tonic-gate 	}
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	for (; cmnp != NULL; cmnp = cmnp->sib) {
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 		assert(cmnp->name != NULL);
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 		idx = next[DB_MINOR];
8827c478bd9Sstevel@tonic-gate 		if ((dmp = set_minor(hdp, idx)) == NULL) {
8837c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
8847c478bd9Sstevel@tonic-gate 			break;
8857c478bd9Sstevel@tonic-gate 		}
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 		dmp->name = write_string(hdp, cmnp->name, next);
8887c478bd9Sstevel@tonic-gate 		dmp->nodetype = write_string(hdp, cmnp->nodetype, next);
8897c478bd9Sstevel@tonic-gate 		if (dmp->name == DB_NIL || dmp->nodetype == DB_NIL) {
8907c478bd9Sstevel@tonic-gate 			dmp->name = dmp->nodetype = DB_NIL;
8917c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
8927c478bd9Sstevel@tonic-gate 			break;
8937c478bd9Sstevel@tonic-gate 		}
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 		/* Commit writes to this minor */
8967c478bd9Sstevel@tonic-gate 		next[DB_MINOR]++;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 		dmp->sib = pdnp->minor;
8997c478bd9Sstevel@tonic-gate 		pdnp->minor = idx;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "%s: minor[%u]: %s\n", fcn, idx,
9027c478bd9Sstevel@tonic-gate 		    cmnp->name);
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 		if (write_links(hdp, dmp, cmnp->link, next) != 0) {
9057c478bd9Sstevel@tonic-gate 			break;
9067c478bd9Sstevel@tonic-gate 		}
9077c478bd9Sstevel@tonic-gate 	}
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	return (cmnp ? -1 : 0);
9107c478bd9Sstevel@tonic-gate }
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate static int
write_links(struct di_devlink_handle * hdp,struct db_minor * pdmp,cache_link_t * clp,uint32_t * next)9137c478bd9Sstevel@tonic-gate write_links(
9147c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp,
9157c478bd9Sstevel@tonic-gate 	struct db_minor *pdmp,
9167c478bd9Sstevel@tonic-gate 	cache_link_t *clp,
9177c478bd9Sstevel@tonic-gate 	uint32_t *next)
9187c478bd9Sstevel@tonic-gate {
9197c478bd9Sstevel@tonic-gate 	uint32_t idx;
9207c478bd9Sstevel@tonic-gate 	struct db_link *dlp;
9217c478bd9Sstevel@tonic-gate 	const char *fcn = "write_links";
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	/* A NULL minor if and only if the links are dangling */
9267c478bd9Sstevel@tonic-gate 	if (clp != NULL && ((pdmp == NULL) ^ (clp == CACHE(hdp)->dngl))) {
9277c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "%s: invalid minor for link\n", fcn);
9287c478bd9Sstevel@tonic-gate 		SET_DB_ERR(hdp);
9297c478bd9Sstevel@tonic-gate 		return (-1);
9307c478bd9Sstevel@tonic-gate 	}
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	for (; clp != NULL; clp = clp->sib) {
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 		assert(clp->path != NULL);
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 		if ((pdmp == NULL) ^ (clp->minor == NULL)) {
9377c478bd9Sstevel@tonic-gate 			(void) dprintf(DBG_ERR, "%s: invalid minor for link"
9387c478bd9Sstevel@tonic-gate 			    "(%s)\n", fcn, clp->path);
9397c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
9407c478bd9Sstevel@tonic-gate 			break;
9417c478bd9Sstevel@tonic-gate 		}
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 		idx = next[DB_LINK];
9447c478bd9Sstevel@tonic-gate 		if ((dlp = set_link(hdp, idx)) == NULL) {
9457c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
9467c478bd9Sstevel@tonic-gate 			break;
9477c478bd9Sstevel@tonic-gate 		}
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 		dlp->path = write_string(hdp, clp->path, next);
9507c478bd9Sstevel@tonic-gate 		dlp->content = write_string(hdp, clp->content, next);
9517c478bd9Sstevel@tonic-gate 		if (dlp->path == DB_NIL || dlp->content == DB_NIL) {
9527c478bd9Sstevel@tonic-gate 			dlp->path = dlp->content = DB_NIL;
9537c478bd9Sstevel@tonic-gate 			SET_DB_ERR(hdp);
9547c478bd9Sstevel@tonic-gate 			break;
9557c478bd9Sstevel@tonic-gate 		}
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		dlp->attr = clp->attr;
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 		/* Commit writes to this link */
9607c478bd9Sstevel@tonic-gate 		next[DB_LINK]++;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 		if (pdmp != NULL) {
9637c478bd9Sstevel@tonic-gate 			dlp->sib = pdmp->link;
9647c478bd9Sstevel@tonic-gate 			pdmp->link = idx;
9657c478bd9Sstevel@tonic-gate 		} else {
9667c478bd9Sstevel@tonic-gate 			dlp->sib = DB_HDR(hdp)->dngl_idx;
9677c478bd9Sstevel@tonic-gate 			DB_HDR(hdp)->dngl_idx = idx;
9687c478bd9Sstevel@tonic-gate 		}
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_STEP, "%s: link[%u]: %s%s\n", fcn, idx,
9717c478bd9Sstevel@tonic-gate 		    clp->path, pdmp == NULL ? "(DANGLING)" : "");
9727c478bd9Sstevel@tonic-gate 	}
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	return (clp ? -1 : 0);
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate static uint32_t
write_string(struct di_devlink_handle * hdp,const char * str,uint32_t * next)9797c478bd9Sstevel@tonic-gate write_string(struct di_devlink_handle *hdp, const char *str, uint32_t *next)
9807c478bd9Sstevel@tonic-gate {
9817c478bd9Sstevel@tonic-gate 	char *dstr;
9827c478bd9Sstevel@tonic-gate 	uint32_t idx;
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	assert(HDL_RDWR(hdp));
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	if (str == NULL) {
9877c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "write_string: NULL argument\n");
9887c478bd9Sstevel@tonic-gate 		return (DB_NIL);
9897c478bd9Sstevel@tonic-gate 	}
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	idx = next[DB_STR];
9927c478bd9Sstevel@tonic-gate 	if (!VALID_STR(hdp, idx, str)) {
9937c478bd9Sstevel@tonic-gate 		(void) dprintf(DBG_ERR, "write_string: invalid index[%u],"
9947c478bd9Sstevel@tonic-gate 		    " string(%s)\n", idx, str);
9957c478bd9Sstevel@tonic-gate 		return (DB_NIL);
9967c478bd9Sstevel@tonic-gate 	}
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 	if ((dstr = set_string(hdp, idx)) == NULL) {
9997c478bd9Sstevel@tonic-gate 		return (DB_NIL);
10007c478bd9Sstevel@tonic-gate 	}
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 	(void) strcpy(dstr, str);
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 	next[DB_STR] += strlen(dstr) + 1;
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	return (idx);
10077c478bd9Sstevel@tonic-gate }
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate static int
close_db(struct di_devlink_handle * hdp)10107c478bd9Sstevel@tonic-gate close_db(struct di_devlink_handle *hdp)
10117c478bd9Sstevel@tonic-gate {
10127c478bd9Sstevel@tonic-gate 	int i, rv = 0;
10137c478bd9Sstevel@tonic-gate 	size_t sz;
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	if (!DB_OPEN(hdp)) {
10167c478bd9Sstevel@tonic-gate #ifdef	DEBUG
10177c478bd9Sstevel@tonic-gate 		assert(DB(hdp)->db_fd == -1);
10187c478bd9Sstevel@tonic-gate 		assert(DB(hdp)->flags == 0);
10197c478bd9Sstevel@tonic-gate 		for (i = 0; i < DB_TYPES; i++) {
10207c478bd9Sstevel@tonic-gate 			assert(DB_SEG(hdp, i) == NULL);
10217c478bd9Sstevel@tonic-gate 			assert(DB_SEG_PROT(hdp, i) == 0);
10227c478bd9Sstevel@tonic-gate 		}
10237c478bd9Sstevel@tonic-gate #endif
10247c478bd9Sstevel@tonic-gate 		return (0);
10257c478bd9Sstevel@tonic-gate 	}
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	/* Unmap header after unmapping all other mapped segments */
10287c478bd9Sstevel@tonic-gate 	for (i = 0; i < DB_TYPES; i++) {
10297c478bd9Sstevel@tonic-gate 		if (DB_SEG(hdp, i)) {
10307c478bd9Sstevel@tonic-gate 			sz = seg_size(hdp, i);
10317c478bd9Sstevel@tonic-gate 			if (DB_RDWR(hdp))
10327c478bd9Sstevel@tonic-gate 				rv += msync(DB_SEG(hdp, i), sz, MS_SYNC);
10337c478bd9Sstevel@tonic-gate 			(void) munmap(DB_SEG(hdp, i), sz);
10347c478bd9Sstevel@tonic-gate 			DB_SEG(hdp, i) = NULL;
10357c478bd9Sstevel@tonic-gate 			DB_SEG_PROT(hdp, i) = 0;
10367c478bd9Sstevel@tonic-gate 		}
10377c478bd9Sstevel@tonic-gate 	}
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	if (DB_RDWR(hdp))
10407c478bd9Sstevel@tonic-gate 		rv += msync((caddr_t)DB_HDR(hdp), HDR_LEN, MS_SYNC);
10417c478bd9Sstevel@tonic-gate 	(void) munmap((caddr_t)DB_HDR(hdp), HDR_LEN);
10427c478bd9Sstevel@tonic-gate 	DB(hdp)->hdr = NULL;
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 	(void) close(DB(hdp)->db_fd);
10457c478bd9Sstevel@tonic-gate 	DB(hdp)->db_fd = -1;
10467c478bd9Sstevel@tonic-gate 	DB(hdp)->flags = 0;
10477c478bd9Sstevel@tonic-gate 
10487c478bd9Sstevel@tonic-gate 	return (rv ? -1 : 0);
10497c478bd9Sstevel@tonic-gate }
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate static void
cache_free(struct di_devlink_handle * hdp)10537c478bd9Sstevel@tonic-gate cache_free(struct di_devlink_handle *hdp)
10547c478bd9Sstevel@tonic-gate {
10557c478bd9Sstevel@tonic-gate 	cache_link_t *clp;
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	subtree_free(hdp, &(CACHE_ROOT(hdp)));
10587c478bd9Sstevel@tonic-gate 	assert(CACHE_LAST(hdp) == NULL);
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	/*
10617c478bd9Sstevel@tonic-gate 	 * Don't bother removing links from hash table chains,
10627c478bd9Sstevel@tonic-gate 	 * as we are freeing the hash table itself.
10637c478bd9Sstevel@tonic-gate 	 */
10647c478bd9Sstevel@tonic-gate 	while (CACHE(hdp)->dngl != NULL) {
10657c478bd9Sstevel@tonic-gate 		clp = CACHE(hdp)->dngl;
10667c478bd9Sstevel@tonic-gate 		CACHE(hdp)->dngl = clp->sib;
10677c478bd9Sstevel@tonic-gate 		assert(clp->minor == NULL);
10687c478bd9Sstevel@tonic-gate 		link_free(&clp);
10697c478bd9Sstevel@tonic-gate 	}
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 	assert((CACHE(hdp)->hash == NULL) ^ (CACHE(hdp)->hash_sz != 0));
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	free(CACHE(hdp)->hash);
10747c478bd9Sstevel@tonic-gate 	CACHE(hdp)->hash = NULL;
10757c478bd9Sstevel@tonic-gate 	CACHE(hdp)->hash_sz = 0;
10767c478bd9Sstevel@tonic-gate }
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate static void
handle_free(struct di_devlink_handle ** pp)10797c478bd9Sstevel@tonic-gate handle_free(struct di_devlink_handle **pp)
10807c478bd9Sstevel@tonic-gate {
10817c478bd9Sstevel@tonic-gate 	struct di_devlink_handle *hdp = *pp;
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	*pp = NULL;
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	if (hdp == NULL)
10867c478bd9Sstevel@tonic-gate 		return;
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 	(void) close_db(hdp);
10897c478bd9Sstevel@tonic-gate 	cache_free(hdp);
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	if (HDL_RDWR(hdp))
1092ff2aee48Scth 		exit_db_lock(hdp);
10937c478bd9Sstevel@tonic-gate 	assert(hdp->lock_fd == -1);
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 	free(hdp->dev_dir);
1096a08731ecScth 	free(hdp->db_dir);
10977c478bd9Sstevel@tonic-gate 	free(hdp);
10987c478bd9Sstevel@tonic-gate }
10997c478bd9Sstevel@tonic-gate 
11007c478bd9Sstevel@tonic-gate /*
11017c478bd9Sstevel@tonic-gate  * Frees the tree rooted at a node. Siblings of the subtree root
11027c478bd9Sstevel@tonic-gate  * have to be handled by the caller.
11037c478bd9Sstevel@tonic-gate  */
11047c478bd9Sstevel@tonic-gate static void
subtree_free(struct di_devlink_handle * hdp,cache_node_t ** pp)11057c478bd9Sstevel@tonic-gate subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp)
11067c478bd9Sstevel@tonic-gate {
11077c478bd9Sstevel@tonic-gate 	cache_node_t *np;
11087c478bd9Sstevel@tonic-gate 	cache_link_t *clp;
11097c478bd9Sstevel@tonic-gate 	cache_minor_t *cmnp;
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	if (pp == NULL || *pp == NULL)
11127c478bd9Sstevel@tonic-gate 		return;
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	while ((*pp)->child != NULL) {
11157c478bd9Sstevel@tonic-gate 		np = (*pp)->child;
11167c478bd9Sstevel@tonic-gate 		(*pp)->child = np->sib;
11177c478bd9Sstevel@tonic-gate 		subtree_free(hdp, &np);
11187c478bd9Sstevel@tonic-gate 	}
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 	while ((*pp)->minor != NULL) {
11217c478bd9Sstevel@tonic-gate 		cmnp = (*pp)->minor;
11227c478bd9Sstevel@tonic-gate 		(*pp)->minor = cmnp->sib;
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 		while (cmnp->link != NULL) {
11257c478bd9Sstevel@tonic-gate 			clp = cmnp->link;
11267c478bd9Sstevel@tonic-gate 			cmnp->link = clp->sib;
11277c478bd9Sstevel@tonic-gate 			rm_link_from_hash(hdp, clp);
11287c478bd9Sstevel@tonic-gate 			link_free(&clp);