Home | History | Annotate | Download | only in libdevinfo
      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 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include "libdevinfo.h"
     29 #include "devinfo_devlink.h"
     30 #include "device_info.h"
     31 
     32 #undef	DEBUG
     33 #ifndef	DEBUG
     34 #define	NDEBUG 1
     35 #else
     36 #undef	NDEBUG
     37 #endif
     38 
     39 #include <assert.h>
     40 
     41 static mutex_t update_mutex = DEFAULTMUTEX; /* Protects update record lock */
     42 static mutex_t temp_file_mutex = DEFAULTMUTEX; /* for file creation tests */
     43 
     44 static const size_t elem_sizes[DB_TYPES] = {
     45 	sizeof (struct db_node),
     46 	sizeof (struct db_minor),
     47 	sizeof (struct db_link),
     48 	sizeof (char)
     49 };
     50 
     51 /*
     52  * List of directories/files skipped while physically walking /dev
     53  * Paths are relative to "<root>/dev/"
     54  */
     55 static const char *skip_dirs[] = {"fd"};
     56 static const char *skip_files[] = {
     57 	"stdout",
     58 	"stdin",
     59 	"stderr"
     60 };
     61 
     62 #define	N_SKIP_DIRS	(sizeof (skip_dirs) / sizeof (skip_dirs[0]))
     63 #define	N_SKIP_FILES	(sizeof (skip_files) / sizeof (skip_files[0]))
     64 
     65 #define	DI_TEST_DB	ETCDEV "di_test_db"
     66 
     67 /*
     68  *
     69  * This file contains two sets of interfaces which operate on the reverse
     70  * links database. One set (which includes di_devlink_open()/_close())
     71  * allows link generators like devfsadm(1M) and ucblinks(1B) (writers) to
     72  * populate the database with /devices -> /dev mappings. Another set
     73  * of interfaces (which includes di_devlink_init()/_fini()) allows
     74  * applications (readers) to lookup the database for /dev links corresponding
     75  * to a given minor.
     76  *
     77  * Writers operate on a cached version of the database. The cache is created
     78  * when di_devlink_open() is called. As links in /dev are created and removed,
     79  * the cache is updated to keep it in synch with /dev. When the /dev updates
     80  * are complete, the link generator calls di_devlink_close() which writes
     81  * out the cache to the database.
     82  *
     83  * Applications which need to lookup the database, call di_devlink_init().
     84  * di_devlink_init() checks the database file (if one exists). If the
     85  * database is valid, it is mapped into the address space of the
     86  * application. The database file consists of several segments. Each
     87  * segment can be mapped in independently and is mapped on demand.
     88  *
     89  *		   Database Layout
     90  *
     91  *		---------------------
     92  *		|	Magic #     |
     93  *		| ----------------- |
     94  *		|       Version	    |	HEADER
     95  *		| ----------------- |
     96  *		|        ...        |
     97  *		---------------------
     98  *		|		    |
     99  *		|		    |	NODES
    100  *		|	            |
    101  *		|		    |
    102  *		---------------------
    103  *		|		    |
    104  *		|		    |	MINORS
    105  *		|	            |
    106  *		|		    |
    107  *		---------------------
    108  *		|		    |
    109  *		|		    |   LINKS
    110  *		|	            |
    111  *		|		    |
    112  *		---------------------
    113  *		|		    |
    114  *		|		    |	STRINGS
    115  *		|	            |
    116  *		|		    |
    117  *		---------------------
    118  *
    119  * Readers can lookup /dev links for a specific minor or
    120  * lookup all /dev links. In the latter case, the node
    121  * and minor segments are not mapped in and the reader
    122  * walks through every link in the link segment.
    123  *
    124  */
    125 di_devlink_handle_t
    126 di_devlink_open(const char *root_dir, uint_t flags)
    127 {
    128 	int err;
    129 	char path[PATH_MAX];
    130 	struct di_devlink_handle *hdp;
    131 	int retried = 0;
    132 
    133 retry:
    134 	/*
    135 	 * Allocate a read-write handle but open the DB in readonly
    136 	 * mode. We do writes only to a temporary copy of the database.
    137 	 */
    138 	if ((hdp = handle_alloc(root_dir, OPEN_RDWR)) == NULL) {
    139 		return (NULL);
    140 	}
    141 
    142 	err = open_db(hdp, OPEN_RDONLY);
    143 
    144 	/*
    145 	 * We don't want to unlink the db at this point - if we did we
    146 	 * would be creating a window where consumers would take a slow
    147 	 * code path (and those consumers might also trigger requests for
    148 	 * db creation, which we are already in the process of doing).
    149 	 * When we are done with our update, we use rename to install the
    150 	 * latest version of the db file.
    151 	 */
    152 	get_db_path(hdp, DB_FILE, path, sizeof (path));
    153 
    154 	/*
    155 	 * The flags argument is reserved for future use.
    156 	 */
    157 	if (flags != 0) {
    158 		handle_free(&hdp); /* also closes the DB */
    159 		errno = EINVAL;
    160 		return (NULL);
    161 	}
    162 
    163 	if (cache_alloc(hdp) != 0) {
    164 		handle_free(&hdp);
    165 		return (NULL);
    166 	}
    167 
    168 	if (err) {
    169 		/*
    170 		 * Failed to open DB.
    171 		 * The most likely cause is that DB file did not exist.
    172 		 * Call di_devlink_close() to recreate the DB file and
    173 		 * retry di_devlink_open().
    174 		 */
    175 		if (retried == 0) {
    176 			(void) di_devlink_close(&hdp, 0);
    177 			retried = 1;
    178 			goto retry;
    179 		}
    180 
    181 		/*
    182 		 * DB cannot be opened, just return the
    183 		 * handle. We will recreate the DB later.
    184 		 */
    185 		return (hdp);
    186 	}
    187 
    188 	/* Read the database into the cache */
    189 	CACHE(hdp)->update_count = DB_HDR(hdp)->update_count;
    190 	(void) read_nodes(hdp, NULL, DB_HDR(hdp)->root_idx);
    191 	(void) read_links(hdp, NULL, DB_HDR(hdp)->dngl_idx);
    192 
    193 	(void) close_db(hdp);
    194 
    195 	return (hdp);
    196 }
    197 
    198 static void
    199 get_db_path(
    200 	struct di_devlink_handle *hdp,
    201 	const char *fname,
    202 	char *buf,
    203 	size_t blen)
    204 {
    205 	char *dir = NULL;
    206 
    207 #ifdef	DEBUG
    208 	if (dir = getenv(ALT_DB_DIR)) {
    209 		(void) dprintf(DBG_INFO, "get_db_path: alternate db dir: %s\n",
    210 		    dir);
    211 	}
    212 #endif
    213 	if (dir == NULL) {
    214 		dir = hdp->db_dir;
    215 	}
    216 
    217 	(void) snprintf(buf, blen, "%s/%s", dir, fname);
    218 }
    219 
    220 static int
    221 open_db(struct di_devlink_handle *hdp, int flags)
    222 {
    223 	size_t sz;
    224 	long page_sz;
    225 	int fd, rv, flg;
    226 	struct stat sbuf;
    227 	uint32_t count[DB_TYPES] = {0};
    228 	char path[PATH_MAX];
    229 	void *cp;
    230 
    231 	assert(!DB_OPEN(hdp));
    232 
    233 #ifdef	DEBUG
    234 	if (getenv(SKIP_DB)) {
    235 		(void) dprintf(DBG_INFO, "open_db: skipping database\n");
    236 		return (-1);
    237 	}
    238 #endif
    239 	if ((page_sz = sysconf(_SC_PAGE_SIZE)) == -1) {
    240 		return (-1);
    241 	}
    242 
    243 	/*
    244 	 * Use O_TRUNC flag for write access, so that the subsequent ftruncate()
    245 	 * call will zero-fill the entire file
    246 	 */
    247 	if (IS_RDONLY(flags)) {
    248 		flg = O_RDONLY;
    249 		get_db_path(hdp, DB_FILE, path, sizeof (path));
    250 	} else {
    251 		flg = O_RDWR|O_CREAT|O_TRUNC;
    252 		get_db_path(hdp, DB_TMP, path, sizeof (path));
    253 	}
    254 
    255 	/*
    256 	 * Avoid triggering /dev reconfigure for read when not present
    257 	 */
    258 	if (IS_RDONLY(flags) &&
    259 	    (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) {
    260 		return (-1);
    261 	}
    262 
    263 	if ((fd = open(path, flg, DB_PERMS)) == -1) {
    264 		return (-1);
    265 	}
    266 
    267 	if (IS_RDONLY(flags)) {
    268 		flg = PROT_READ;
    269 		rv = fstat(fd, &sbuf);
    270 		sz = sbuf.st_size;
    271 	} else {
    272 		flg = PROT_READ | PROT_WRITE;
    273 		sz = size_db(hdp, page_sz, count);
    274 		rv = ftruncate(fd, sz);
    275 	}
    276 
    277 	if (rv == -1 || sz < HDR_LEN) {
    278 		if (rv != -1)
    279 			errno = EINVAL;
    280 		(void) close(fd);
    281 		return (-1);
    282 	}
    283 
    284 	cp = mmap(0, HDR_LEN, flg, MAP_SHARED, fd, 0);
    285 	if (cp == MAP_FAILED) {
    286 		(void) close(fd);
    287 		return (-1);
    288 	}
    289 	DB(hdp)->hdr = (struct db_hdr *)cp;
    290 	DB(hdp)->db_fd = fd;
    291 	DB(hdp)->flags = flags;
    292 
    293 	if (IS_RDONLY(flags)) {
    294 		rv = invalid_db(hdp, sz, page_sz);
    295 	} else {
    296 		rv = init_hdr(hdp, page_sz, count);
    297 	}
    298 
    299 	if (rv) {
    300 		(void) dprintf(DBG_ERR, "open_db: invalid DB(%s)\n", path);
    301 		(void) close_db(hdp);
    302 		return (-1);
    303 	} else {
    304 		(void) dprintf(DBG_STEP, "open_db: DB(%s): opened\n", path);
    305 		return (0);
    306 	}
    307 }
    308 
    309 /*
    310  * A handle can be allocated for read-only or read-write access
    311  */
    312 static struct di_devlink_handle *
    313 handle_alloc(const char *root_dir, uint_t flags)
    314 {
    315 	char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX];
    316 	struct di_devlink_handle *hdp, proto = {0};
    317 	int install = 0;
    318 	int isroot = 0;
    319 	struct stat sb;
    320 	char can_path[PATH_MAX];
    321 
    322 	assert(flags == OPEN_RDWR || flags == OPEN_RDONLY);
    323 
    324 	dev_dir[0] = '\0';
    325 	db_dir[0] = '\0';
    326 
    327 	/*
    328 	 * NULL and the empty string are equivalent to "/"
    329 	 */
    330 	if (root_dir && root_dir[0] != '\0') {
    331 
    332 		if (root_dir[0] != '/') {
    333 			errno = EINVAL;
    334 			return (NULL);
    335 		}
    336 
    337 #ifdef	DEBUG
    338 		/*LINTED*/
    339 		assert(sizeof (dev_dir) >= PATH_MAX);
    340 #endif
    341 		if ((realpath(root_dir, dev_dir) == NULL) ||
    342 		    (realpath(root_dir, db_dir) == NULL)) {
    343 			return (NULL);
    344 		}
    345 	} else {
    346 		/*
    347 		 * The dev dir is at /dev i.e. we are not doing a -r /altroot
    348 		 */
    349 		isroot = 1;
    350 	}
    351 
    352 	if (strcmp(dev_dir, "/") == 0) {
    353 		dev_dir[0] = 0;
    354 		db_dir[0] = 0;
    355 	} else {
    356 		(void) strlcpy(db_dir, dev_dir, sizeof (db_dir));
    357 	}
    358 
    359 	(void) strlcat(dev_dir, DEV, sizeof (dev_dir));
    360 	(void) strlcat(db_dir, ETCDEV, sizeof (db_dir));
    361 
    362 	/*
    363 	 * The following code is for install. Readers and writers need
    364 	 * to be redirected to /tmp/etc/dev for the database file.
    365 	 * Note that we test for readonly /etc by actually creating a
    366 	 * file since statvfs is not a reliable method for determining
    367 	 * readonly filesystems.
    368 	 */
    369 	install = 0;
    370 	(void) snprintf(can_path, sizeof (can_path), "%s/%s", ETCDEV, DB_FILE);
    371 	if (flags == OPEN_RDWR && isroot) {
    372 		char di_test_db[PATH_MAX];
    373 		int fd;
    374 		(void) mutex_lock(&temp_file_mutex);
    375 		(void) snprintf(di_test_db, sizeof (di_test_db), "%s.%d",
    376 		    DI_TEST_DB, getpid());
    377 		fd = open(di_test_db, O_CREAT|O_RDWR|O_EXCL, 0644);
    378 		if (fd == -1 && errno == EROFS && stat(can_path, &sb) == -1)
    379 			install = 1;
    380 		if (fd != -1) {
    381 			(void) close(fd);
    382 			(void) unlink(di_test_db);
    383 		}
    384 		(void) mutex_unlock(&temp_file_mutex);
    385 	} else if (isroot) {
    386 		/*
    387 		 * Readers can be non-privileged so we cannot test by creating
    388 		 * a file in /etc/dev. Instead we check if the database
    389 		 * file is missing in /etc/dev and is present in /tmp/etc/dev
    390 		 * and is owned by root.
    391 		 */
    392 		char install_path[PATH_MAX];
    393 
    394 		(void) snprintf(install_path, sizeof (install_path),
    395 		    "/tmp%s/%s", ETCDEV, DB_FILE);
    396 		if (stat(can_path, &sb) == -1 && stat(install_path, &sb)
    397 		    != -1 && sb.st_uid == 0) {
    398 			install = 1;
    399 		}
    400 	}
    401 
    402 	/*
    403 	 * Check if we are in install. If we are, the database will be in
    404 	 * /tmp/etc/dev
    405 	 */
    406 	if (install)
    407 		(void) snprintf(db_dir, sizeof (db_dir), "/tmp%s", ETCDEV);
    408 
    409 	proto.dev_dir = dev_dir;
    410 	proto.db_dir = db_dir;
    411 	proto.flags = flags;
    412 	proto.lock_fd = -1;
    413 
    414 	/*
    415 	 * Lock database if a read-write handle is being allocated.
    416 	 * Locks are needed to protect against multiple writers.
    417 	 * Readers don't need locks.
    418 	 */
    419 	if (HDL_RDWR(&proto)) {
    420 		if (enter_db_lock(&proto, root_dir) != 1) {
    421 			return (NULL);
    422 		}
    423 	}
    424 
    425 	DB(&proto)->db_fd = -1;
    426 
    427 	hdp = calloc(1, sizeof (struct di_devlink_handle));
    428 	if (hdp == NULL) {
    429 		goto error;
    430 	}
    431 
    432 	*hdp = proto;
    433 
    434 	/*
    435 	 * The handle hdp now contains a pointer to local storage
    436 	 * in the dev_dir field (obtained from the proto handle).
    437 	 * In the following line, a dynamically allocated version
    438 	 * is substituted.
    439 	 */
    440 
    441 	if ((hdp->dev_dir = strdup(proto.dev_dir)) == NULL) {
    442 		free(hdp);
    443 		goto error;
    444 	}
    445 
    446 	if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) {
    447 		free(hdp->dev_dir);
    448 		free(hdp);
    449 		goto error;
    450 	}
    451 
    452 	return (hdp);
    453 
    454 error:
    455 	if (HDL_RDWR(&proto)) {
    456 		/* Unlink DB file on error */
    457 		get_db_path(&proto, DB_FILE, path, sizeof (path));
    458 		(void) unlink(path);
    459 		exit_db_lock(&proto);
    460 	}
    461 	return (NULL);
    462 }
    463 
    464 
    465 static int
    466 cache_alloc(struct di_devlink_handle *hdp)
    467 {
    468 	size_t hash_sz = 0;
    469 
    470 	assert(HDL_RDWR(hdp));
    471 
    472 	if (DB_OPEN(hdp)) {
    473 		hash_sz = DB_NUM(hdp, DB_LINK) / AVG_CHAIN_SIZE;
    474 	}
    475 	hash_sz = (hash_sz >= MIN_HASH_SIZE) ? hash_sz : MIN_HASH_SIZE;
    476 
    477 	CACHE(hdp)->hash = calloc(hash_sz, sizeof (cache_link_t *));
    478 	if (CACHE(hdp)->hash == NULL) {
    479 		return (-1);
    480 	}
    481 	CACHE(hdp)->hash_sz = hash_sz;
    482 
    483 	return (0);
    484 }
    485 
    486 
    487 static int
    488 invalid_db(struct di_devlink_handle *hdp, size_t fsize, long page_sz)
    489 {
    490 	int i;
    491 	char *cp;
    492 	size_t sz;
    493 
    494 	if (DB_HDR(hdp)->magic != DB_MAGIC || DB_HDR(hdp)->vers != DB_VERSION) {
    495 		return (1);
    496 	}
    497 
    498 	if (DB_HDR(hdp)->page_sz == 0 || DB_HDR(hdp)->page_sz != page_sz) {
    499 		return (1);
    500 	}
    501 
    502 	sz = seg_size(hdp, DB_HEADER);
    503 	for (i = 0; i < DB_TYPES; i++) {
    504 		(void) dprintf(DBG_INFO, "N[%u] = %u\n", i, DB_NUM(hdp, i));
    505 		/* There must be at least 1 element of each type */
    506 		if (DB_NUM(hdp, i) < 1) {
    507 			return (1);
    508 		}
    509 		sz += seg_size(hdp, i);
    510 		assert(sz % page_sz == 0);
    511 	}
    512 
    513 	if (sz != fsize) {
    514 		return (1);
    515 	}
    516 
    517 	if (!VALID_INDEX(hdp, DB_NODE, DB_HDR(hdp)->root_idx)) {
    518 		return (1);
    519 	}
    520 
    521 	if (!VALID_INDEX(hdp, DB_LINK, DB_HDR(hdp)->dngl_idx)) {
    522 		return (1);
    523 	}
    524 
    525 	if (DB_EMPTY(hdp)) {
    526 		return (1);
    527 	}
    528 
    529 	/*
    530 	 * The last character in the string segment must be a NUL char.
    531 	 */
    532 	cp = get_string(hdp, DB_NUM(hdp, DB_STR) - 1);
    533 	if (cp == NULL || *cp != '\0') {
    534 		return (1);
    535 	}
    536 
    537 	return (0);
    538 }
    539 
    540 static int
    541 read_nodes(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
    542 {
    543 	char *path;
    544 	cache_node_t *cnp;
    545 	struct db_node *dnp;
    546 	const char *fcn = "read_nodes";
    547 
    548 	assert(HDL_RDWR(hdp));
    549 
    550 	/*
    551 	 * parent node should be NULL only for the root node
    552 	 */
    553 	if ((pcnp == NULL) ^ (nidx == DB_HDR(hdp)->root_idx)) {
    554 		(void) dprintf(DBG_ERR, "%s: invalid parent or index(%u)\n",
    555 		    fcn, nidx);
    556 		SET_DB_ERR(hdp);
    557 		return (-1);
    558 	}
    559 
    560 	for (; dnp = get_node(hdp, nidx); nidx = dnp->sib) {
    561 
    562 		path = get_string(hdp, dnp->path);
    563 
    564 		/*
    565 		 * Insert at head of list to recreate original order
    566 		 */
    567 		cnp = node_insert(hdp, pcnp, path, INSERT_HEAD);
    568 		if (cnp == NULL) {
    569 			SET_DB_ERR(hdp);
    570 			break;
    571 		}
    572 
    573 		assert(strcmp(path, "/") ^ (nidx == DB_HDR(hdp)->root_idx));
    574 		assert(strcmp(path, "/") != 0 || dnp->sib == DB_NIL);
    575 
    576 		if (read_minors(hdp, cnp, dnp->minor) != 0 ||
    577 		    read_nodes(hdp, cnp, dnp->child) != 0) {
    578 			break;
    579 		}
    580 
    581 		(void) dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, nidx,
    582 		    cnp->path);
    583 	}
    584 
    585 	return (dnp ? -1 : 0);
    586 }
    587 
    588 static int
    589 read_minors(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx)
    590 {
    591 	cache_minor_t *cmnp;
    592 	struct db_minor *dmp;
    593 	char *name, *nodetype;
    594 	const char *fcn = "read_minors";
    595 
    596 	assert(HDL_RDWR(hdp));
    597 
    598 	if (pcnp == NULL) {
    599 		(void) dprintf(DBG_ERR, "%s: minor[%u]: orphan minor\n", fcn,
    600 		    nidx);
    601 		SET_DB_ERR(hdp);
    602 		return (-1);
    603 	}
    604 
    605 	for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) {
    606 
    607 		name = get_string(hdp, dmp->name);
    608 		nodetype = get_string(hdp, dmp->nodetype);
    609 
    610 		cmnp = minor_insert(hdp, pcnp, name, nodetype, NULL);
    611 		if (cmnp == NULL) {
    612 			SET_DB_ERR(hdp);
    613 			break;
    614 		}
    615 
    616 		(void) dprintf(DBG_STEP, "%s: minor[%u]: %s\n", fcn, nidx,
    617 		    cmnp->name);
    618 
    619 		if (read_links(hdp, cmnp, dmp->link) != 0) {
    620 			break;
    621 		}
    622 	}
    623 
    624 	return (dmp ? -1 : 0);
    625 }
    626 
    627 /*
    628  * If the link is dangling the corresponding minor will be absent.
    629  */
    630 static int
    631 read_links(struct di_devlink_handle *hdp, cache_minor_t *pcmp, uint32_t nidx)
    632 {
    633 	cache_link_t *clp;
    634 	struct db_link *dlp;
    635 	char *path, *content;
    636 
    637 	assert(HDL_RDWR(hdp));
    638 
    639 	if (nidx != DB_NIL &&
    640 	    ((pcmp == NULL) ^ (nidx == DB_HDR(hdp)->dngl_idx))) {
    641 		(void) dprintf(DBG_ERR, "read_links: invalid minor or"
    642 		    " index(%u)\n", nidx);
    643 		SET_DB_ERR(hdp);
    644 		return (-1);
    645 	}
    646 
    647 	for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) {
    648 
    649 		path = get_string(hdp, dlp->path);
    650 		content = get_string(hdp, dlp->content);
    651 
    652 		clp = link_insert(hdp, pcmp, path, content, dlp->attr);
    653 		if (clp == NULL) {
    654 			SET_DB_ERR(hdp);
    655 			break;
    656 		}
    657 
    658 		(void) dprintf(DBG_STEP, "read_links: link[%u]: %s%s\n",
    659 		    nidx, clp->path, pcmp == NULL ? "(DANGLING)" : "");
    660 	}
    661 
    662 	return (dlp ? -1 : 0);
    663 }
    664 
    665 int
    666 di_devlink_close(di_devlink_handle_t *pp, int flag)
    667 {
    668 	int i, rv;
    669 	char tmp[PATH_MAX];
    670 	char file[PATH_MAX];
    671 	uint32_t next[DB_TYPES] = {0};
    672 	struct di_devlink_handle *hdp;
    673 
    674 	if (pp == NULL || *pp == NULL || !HDL_RDWR(*pp)) {
    675 		errno = EINVAL;
    676 		return (-1);
    677 	}
    678 
    679 	hdp = *pp;
    680 	*pp = NULL;
    681 
    682 	/*
    683 	 * The caller encountered some error in their processing.
    684 	 * so handle isn't valid. Discard it and return success.
    685 	 */
    686 	if (flag == DI_LINK_ERROR) {
    687 		handle_free(&hdp);
    688 		return (0);
    689 	}
    690 
    691 	if (DB_ERR(hdp)) {
    692 		handle_free(&hdp);
    693 		errno = EINVAL;
    694 		return (-1);
    695 	}
    696 
    697 	/*
    698 	 * Extract the DB path before the handle is freed.
    699 	 */
    700 	get_db_path(hdp, DB_FILE, file, sizeof (file));
    701 	get_db_path(hdp, DB_TMP, tmp, sizeof (tmp));
    702 
    703 	/*
    704 	 * update database with actual contents of /dev
    705 	 */
    706 	(void) dprintf(DBG_INFO, "di_devlink_close: update_count = %u\n",
    707 	    CACHE(hdp)->update_count);
    708 
    709 	/*
    710 	 * For performance reasons, synchronization of the database
    711 	 * with /dev is turned off by default. However, applications
    712 	 * with appropriate permissions can request a "sync" by
    713 	 * calling di_devlink_update().
    714 	 */
    715 	if (CACHE(hdp)->update_count == 0) {
    716 		CACHE(hdp)->update_count = 1;
    717 		(void) dprintf(DBG_INFO,
    718 		    "di_devlink_close: synchronizing DB\n");
    719 		(void) synchronize_db(hdp);
    720 	}
    721 
    722 	/*
    723 	 * Resolve dangling links AFTER synchronizing DB with /dev as the
    724 	 * synchronization process may create dangling links.
    725 	 */
    726 	resolve_dangling_links(hdp);
    727 
    728 	/*
    729 	 * All changes to the cache are complete. Write out the cache
    730 	 * to the database only if it is not empty.
    731 	 */
    732 	if (CACHE_EMPTY(hdp)) {
    733 		(void) dprintf(DBG_INFO, "di_devlink_close: skipping write\n");
    734 		(void) unlink(file);
    735 		handle_free(&hdp);
    736 		return (0);
    737 	}
    738 
    739 	if (open_db(hdp, OPEN_RDWR) != 0) {
    740 		handle_free(&hdp);
    741 		return (-1);
    742 	}
    743 
    744 	/*
    745 	 * Keep track of array assignments. There is at least
    746 	 * 1 element (the "NIL" element) per type.
    747 	 */
    748 	for (i = 0; i < DB_TYPES; i++) {
    749 		next[i] = 1;
    750 	}
    751 
    752 	(void) write_nodes(hdp, NULL, CACHE_ROOT(hdp), next);
    753 	(void) write_links(hdp, NULL, CACHE(hdp)->dngl, next);
    754 	DB_HDR(hdp)->update_count = CACHE(hdp)->update_count;
    755 
    756 	rv = close_db(hdp);
    757 
    758 	if (rv != 0 || DB_ERR(hdp) || rename(tmp, file) != 0) {
    759 		(void) dprintf(DBG_ERR, "di_devlink_close: %s error: %s\n",
    760 		    rv ? "close_db" : "DB or rename", strerror(errno));
    761 		(void) unlink(tmp);
    762 		(void) unlink(file);
    763 		handle_free(&hdp);
    764 		return (-1);
    765 	}
    766 
    767 	handle_free(&hdp);
    768 
    769 	(void) dprintf(DBG_INFO, "di_devlink_close: wrote DB(%s)\n", file);
    770 
    771 	return (0);
    772 }
    773 
    774 /*
    775  * Inits the database header.
    776  */
    777 static int
    778 init_hdr(struct di_devlink_handle *hdp, long page_sz, uint32_t *count)
    779 {
    780 	int i;
    781 
    782 	DB_HDR(hdp)->magic = DB_MAGIC;
    783 	DB_HDR(hdp)->vers = DB_VERSION;
    784 	DB_HDR(hdp)->root_idx = DB_NIL;
    785 	DB_HDR(hdp)->dngl_idx = DB_NIL;
    786 	DB_HDR(hdp)->page_sz = (uint32_t)page_sz;
    787 
    788 	for (i = 0; i < DB_TYPES; i++) {
    789 		assert(count[i] >= 1);
    790 		DB_NUM(hdp, i) = count[i];
    791 	}
    792 
    793 	return (0);
    794 }
    795 
    796 static int
    797 write_nodes(
    798 	struct di_devlink_handle *hdp,
    799 	struct db_node *pdnp,
    800 	cache_node_t *cnp,
    801 	uint32_t *next)
    802 {
    803 	uint32_t idx;
    804 	struct db_node *dnp;
    805 	const char *fcn = "write_nodes";
    806 
    807 	assert(HDL_RDWR(hdp));
    808 
    809 	for (; cnp != NULL; cnp = cnp->sib) {
    810 
    811 		assert(cnp->path != NULL);
    812 
    813 		/* parent node should only be NULL for root node */
    814 		if ((pdnp == NULL) ^ (cnp == CACHE_ROOT(hdp))) {
    815 			(void) dprintf(DBG_ERR, "%s: invalid parent for: %s\n",
    816 			    fcn, cnp->path);
    817 			SET_DB_ERR(hdp);
    818 			break;
    819 		}
    820 
    821 		assert((strcmp(cnp->path, "/") != 0) ^
    822 		    (cnp == CACHE_ROOT(hdp)));
    823 
    824 		idx = next[DB_NODE];
    825 		if ((dnp = set_node(hdp, idx)) == NULL) {
    826 			SET_DB_ERR(hdp);
    827 			break;
    828 		}
    829 
    830 		dnp->path = write_string(hdp, cnp->path, next);
    831 		if (dnp->path == DB_NIL) {
    832 			SET_DB_ERR(hdp);
    833 			break;
    834 		}
    835 		/* commit write for this node */
    836 		next[DB_NODE]++;
    837 
    838 		if (pdnp == NULL) {
    839 			assert(DB_HDR(hdp)->root_idx == DB_NIL);
    840 			DB_HDR(hdp)->root_idx = idx;
    841 		} else {
    842 			dnp->sib = pdnp->child;
    843 			pdnp->child = idx;
    844 		}
    845 
    846 		(void) dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, idx,
    847 		    cnp->path);
    848 
    849 		if (write_minors(hdp, dnp, cnp->minor, next) != 0 ||
    850 		    write_nodes(hdp, dnp, cnp->child, next) != 0) {
    851 			break;
    852 		}
    853 	}
    854 
    855 	return (cnp ? -1 : 0);
    856 }
    857 
    858 static int
    859 write_minors(
    860 	struct di_devlink_handle *hdp,
    861 	struct db_node *pdnp,
    862 	cache_minor_t *cmnp,
    863 	uint32_t *next)
    864 {
    865 	uint32_t idx;
    866 	struct db_minor *dmp;
    867 	const char *fcn = "write_minors";
    868 
    869 	assert(HDL_RDWR(hdp));
    870 
    871 	if (pdnp == NULL) {
    872 		(void) dprintf(DBG_ERR, "%s: no node for minor: %s\n", fcn,
    873 		    cmnp ? cmnp->name : "<NULL>");
    874 		SET_DB_ERR(hdp);
    875 		return (-1);
    876 	}
    877 
    878 	for (; cmnp != NULL; cmnp = cmnp->sib) {
    879 
    880 		assert(cmnp->name != NULL);
    881 
    882 		idx = next[DB_MINOR];
    883 		if ((dmp = set_minor(hdp, idx)) == NULL) {
    884 			SET_DB_ERR(hdp);
    885 			break;
    886 		}
    887 
    888 		dmp->name = write_string(hdp, cmnp->name, next);
    889 		dmp->nodetype = write_string(hdp, cmnp->nodetype, next);
    890 		if (dmp->name == DB_NIL || dmp->nodetype == DB_NIL) {
    891 			dmp->name = dmp->nodetype = DB_NIL;
    892 			SET_DB_ERR(hdp);
    893 			break;
    894 		}
    895 
    896 		/* Commit writes to this minor */
    897 		next[DB_MINOR]++;
    898 
    899 		dmp->sib = pdnp->minor;
    900 		pdnp->minor = idx;
    901 
    902 		(void) dprintf(DBG_STEP, "%s: minor[%u]: %s\n", fcn, idx,
    903 		    cmnp->name);
    904 
    905 		if (write_links(hdp, dmp, cmnp->link, next) != 0) {
    906 			break;
    907 		}
    908 	}
    909 
    910 	return (cmnp ? -1 : 0);
    911 }
    912 
    913 static int
    914 write_links(
    915 	struct di_devlink_handle *hdp,
    916 	struct db_minor *pdmp,
    917 	cache_link_t *clp,
    918 	uint32_t *next)
    919 {
    920 	uint32_t idx;
    921 	struct db_link *dlp;
    922 	const char *fcn = "write_links";
    923 
    924 	assert(HDL_RDWR(hdp));
    925 
    926 	/* A NULL minor if and only if the links are dangling */
    927 	if (clp != NULL && ((pdmp == NULL) ^ (clp == CACHE(hdp)->dngl))) {
    928 		(void) dprintf(DBG_ERR, "%s: invalid minor for link\n", fcn);
    929 		SET_DB_ERR(hdp);
    930 		return (-1);
    931 	}
    932 
    933 	for (; clp != NULL; clp = clp->sib) {
    934 
    935 		assert(clp->path != NULL);
    936 
    937 		if ((pdmp == NULL) ^ (clp->minor == NULL)) {
    938 			(void) dprintf(DBG_ERR, "%s: invalid minor for link"
    939 			    "(%s)\n", fcn, clp->path);
    940 			SET_DB_ERR(hdp);
    941 			break;
    942 		}
    943 
    944 		idx = next[DB_LINK];
    945 		if ((dlp = set_link(hdp, idx)) == NULL) {
    946 			SET_DB_ERR(hdp);
    947 			break;
    948 		}
    949 
    950 		dlp->path = write_string(hdp, clp->path, next);
    951 		dlp->content = write_string(hdp, clp->content, next);
    952 		if (dlp->path == DB_NIL || dlp->content == DB_NIL) {
    953 			dlp->path = dlp->content = DB_NIL;
    954 			SET_DB_ERR(hdp);
    955 			break;
    956 		}
    957 
    958 		dlp->attr = clp->attr;
    959 
    960 		/* Commit writes to this link */
    961 		next[DB_LINK]++;
    962 
    963 		if (pdmp != NULL) {
    964 			dlp->sib = pdmp->link;
    965 			pdmp->link = idx;
    966 		} else {
    967 			dlp->sib = DB_HDR(hdp)->dngl_idx;
    968 			DB_HDR(hdp)->dngl_idx = idx;
    969 		}
    970 
    971 		(void) dprintf(DBG_STEP, "%s: link[%u]: %s%s\n", fcn, idx,
    972 		    clp->path, pdmp == NULL ? "(DANGLING)" : "");
    973 	}
    974 
    975 	return (clp ? -1 : 0);
    976 }
    977 
    978 
    979 static uint32_t
    980 write_string(struct di_devlink_handle *hdp, const char *str, uint32_t *next)
    981 {
    982 	char *dstr;
    983 	uint32_t idx;
    984 
    985 	assert(HDL_RDWR(hdp));
    986 
    987 	if (str == NULL) {
    988 		(void) dprintf(DBG_ERR, "write_string: NULL argument\n");
    989 		return (DB_NIL);
    990 	}
    991 
    992 	idx = next[DB_STR];
    993 	if (!VALID_STR(hdp, idx, str)) {
    994 		(void) dprintf(DBG_ERR, "write_string: invalid index[%u],"
    995 		    " string(%s)\n", idx, str);
    996 		return (DB_NIL);
    997 	}
    998 
    999 	if ((dstr = set_string(hdp, idx)) == NULL) {
   1000 		return (DB_NIL);
   1001 	}
   1002 
   1003 	(void) strcpy(dstr, str);
   1004 
   1005 	next[DB_STR] += strlen(dstr) + 1;
   1006 
   1007 	return (idx);
   1008 }
   1009 
   1010 static int
   1011 close_db(struct di_devlink_handle *hdp)
   1012 {
   1013 	int i, rv = 0;
   1014 	size_t sz;
   1015 
   1016 	if (!DB_OPEN(hdp)) {
   1017 #ifdef	DEBUG
   1018 		assert(DB(hdp)->db_fd == -1);
   1019 		assert(DB(hdp)->flags == 0);
   1020 		for (i = 0; i < DB_TYPES; i++) {
   1021 			assert(DB_SEG(hdp, i) == NULL);
   1022 			assert(DB_SEG_PROT(hdp, i) == 0);
   1023 		}
   1024 #endif
   1025 		return (0);
   1026 	}
   1027 
   1028 	/* Unmap header after unmapping all other mapped segments */
   1029 	for (i = 0; i < DB_TYPES; i++) {
   1030 		if (DB_SEG(hdp, i)) {
   1031 			sz = seg_size(hdp, i);
   1032 			if (DB_RDWR(hdp))
   1033 				rv += msync(DB_SEG(hdp, i), sz, MS_SYNC);
   1034 			(void) munmap(DB_SEG(hdp, i), sz);
   1035 			DB_SEG(hdp, i) = NULL;
   1036 			DB_SEG_PROT(hdp, i) = 0;
   1037 		}
   1038 	}
   1039 
   1040 	if (DB_RDWR(hdp))
   1041 		rv += msync((caddr_t)DB_HDR(hdp), HDR_LEN, MS_SYNC);
   1042 	(void) munmap((caddr_t)DB_HDR(hdp), HDR_LEN);
   1043 	DB(hdp)->hdr = NULL;
   1044 
   1045 	(void) close(DB(hdp)->db_fd);
   1046 	DB(hdp)->db_fd = -1;
   1047 	DB(hdp)->flags = 0;
   1048 
   1049 	return (rv ? -1 : 0);
   1050 }
   1051 
   1052 
   1053 static void
   1054 cache_free(struct di_devlink_handle *hdp)
   1055 {
   1056 	cache_link_t *clp;
   1057 
   1058 	subtree_free(hdp, &(CACHE_ROOT(hdp)));
   1059 	assert(CACHE_LAST(hdp) == NULL);
   1060 
   1061 	/*
   1062 	 * Don't bother removing links from hash table chains,
   1063 	 * as we are freeing the hash table itself.
   1064 	 */
   1065 	while (CACHE(hdp)->dngl != NULL) {
   1066 		clp = CACHE(hdp)->dngl;
   1067 		CACHE(hdp)->dngl = clp->sib;
   1068 		assert(clp->minor == NULL);
   1069 		link_free(&clp);
   1070 	}
   1071 
   1072 	assert((CACHE(hdp)->hash == NULL) ^ (CACHE(hdp)->hash_sz != 0));
   1073 
   1074 	free(CACHE(hdp)->hash);
   1075 	CACHE(hdp)->hash = NULL;
   1076 	CACHE(hdp)->hash_sz = 0;
   1077 }
   1078 
   1079 static void
   1080 handle_free(struct di_devlink_handle **pp)
   1081 {
   1082 	struct di_devlink_handle *hdp = *pp;
   1083 
   1084 	*pp = NULL;
   1085 
   1086 	if (hdp == NULL)
   1087 		return;
   1088 
   1089 	(void) close_db(hdp);
   1090 	cache_free(hdp);
   1091 
   1092 	if (HDL_RDWR(hdp))
   1093 		exit_db_lock(hdp);
   1094 	assert(hdp->lock_fd == -1);
   1095 
   1096 	free(hdp->dev_dir);
   1097 	free(hdp->db_dir);
   1098 	free(hdp);
   1099 }
   1100 
   1101 /*
   1102  * Frees the tree rooted at a node. Siblings of the subtree root
   1103  * have to be handled by the caller.
   1104  */
   1105 static void
   1106 subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp)
   1107 {
   1108 	cache_node_t *np;
   1109 	cache_link_t *clp;
   1110 	cache_minor_t *cmnp;
   1111 
   1112 	if (pp == NULL || *pp == NULL)
   1113 		return;
   1114 
   1115 	while ((*pp)->child != NULL) {
   1116 		np = (*pp)->child;
   1117 		(*pp)->child = np->sib;
   1118 		subtree_free(hdp, &np);
   1119 	}
   1120 
   1121 	while ((*pp)->minor != NULL) {
   1122 		cmnp = (*pp)->minor;
   1123 		(*pp)->minor = cmnp->sib;
   1124 
   1125 		while (cmnp->link != NULL) {
   1126 			clp = cmnp->link;
   1127 			cmnp->link = clp->sib;
   1128 			rm_link_from_hash(hdp, clp);
   1129 			link_free(&clp);
   1130 		}
   1131 		minor_free(hdp, &cmnp);
   1132 	}
   1133 
   1134 	node_free(pp);
   1135 }
   1136 
   1137 static void
   1138 rm_link_from_hash(struct