xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfslog/dbtab.c (revision 03831d35)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Code to maintain the runtime and on-disk filehandle mapping table for
31  * nfslog.
32  */
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <ctype.h>
37 #include <nfs/nfs.h>
38 #include <stdlib.h>
39 #include <stddef.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <syslog.h>
43 #include <unistd.h>
44 #include <dirent.h>
45 #include <ndbm.h>
46 #include <time.h>
47 #include <libintl.h>
48 #include <sys/types.h>
49 #include <nfs/nfs.h>
50 #include <nfs/nfs_log.h>
51 #include "fhtab.h"
52 #include "nfslogd.h"
53 
54 #define	ROUNDUP32(val)		(((val) + 3) & ~3)
55 
56 /*
57  * It is important that this string not match the length of the
58  * file handle key length NFS_FHMAXDATA
59  */
60 #define	DB_VERSION_STRING	"NFSLOG_DB_VERSION"
61 #define	DB_VERSION		"1"
62 
63 #define	MAX_PRUNE_REC_CNT	100000
64 
65 fhandle_t	public_fh = { 0 };
66 
67 struct db_list {
68 	fsid_t		fsid;		/* filesystem fsid */
69 	char		*path;		/* dbm filepair path */
70 	DBM		*db;		/* open dbm database */
71 	bool_t		getall;		/* TRUE if all dbm for prefix open */
72 	struct db_list	*next;		/* next db */
73 };
74 
75 static struct db_list *db_fs_list = NULL;
76 static	char	err_str[] = "DB I/O error has occured";
77 struct link_keys {
78 	fh_secondary_key	lnkey;
79 	int			lnsize;
80 	struct link_keys	*next;
81 };
82 extern int debug;
83 extern time_t mapping_update_interval;
84 extern time_t prune_timeout;
85 
86 static int fill_link_key(char *linkkey, fhandle_t *dfh, char *name);
87 static struct db_list *db_get_db(char *fhpath, fsid_t *fsid, int *errorp,
88 	int create_flag);
89 static struct db_list *db_get_all_databases(char *fhpath, bool_t getall);
90 static void debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp);
91 static void debug_print_linkinfo(FILE *fp, linkinfo_ent *fhrecp);
92 static void debug_print_key(FILE *fp, char *str1, char *str2, char *key,
93 	int ksize);
94 static void debug_print_key_and_data(FILE *fp, char *str1, char *str2,
95 	char *key, int ksize, char *data, int dsize);
96 static int store_record(struct db_list *dbp, void *keyaddr, int keysize,
97 	void *dataaddr, int datasize, char *str);
98 static void *fetch_record(struct db_list *dbp, void *keyaddr, int keysize,
99 	void *dataaddr, int *errorp, char *str);
100 static int delete_record(struct db_list *dbp, void *keyaddr, int keysize,
101 	char *str);
102 static int db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize,
103 	fhlist_ent *fhrecp, char *str);
104 static int db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize,
105 	linkinfo_ent *linkp, char *str);
106 static fhlist_ent *create_primary_struct(struct db_list *dbp, fhandle_t *dfh,
107 	char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp,
108 	int *errorp);
109 static fhlist_ent *db_add_primary(struct db_list *dbp, fhandle_t *dfh,
110 	char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp,
111 	int *errorp);
112 static linkinfo_ent *get_next_link(struct db_list *dbp, char *linkkey,
113 	int *linksizep, linkinfo_ent *linkp, void **cookiep,
114 	int *errorp, char *msg);
115 static void free_link_cookies(void *cookie);
116 static void add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name,
117 	fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp);
118 static linkinfo_ent *create_link_struct(struct db_list *dbp, fhandle_t *dfh,
119 	char *name, fhlist_ent *fhrecp, int *errorp);
120 static int db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name,
121 	fhandle_t *fh, fhlist_ent *fhrecp);
122 static linkinfo_ent *update_next_link(struct db_list *dbp, char *nextkey,
123 	int nextsize, char *prevkey, int prevsize, int *errorp);
124 static int update_prev_link(struct db_list *dbp, char *nextkey, int nextsize,
125 	char *prevkey, int prevsize);
126 static linkinfo_ent *update_linked_list(struct db_list *dbp, char *nextkey,
127 	int nextsize, char *prevkey, int prevsize, int *errorp);
128 static int db_update_primary_new_head(struct db_list *dbp,
129 	linkinfo_ent *dellinkp, linkinfo_ent *nextlinkp, fhlist_ent *fhrecp);
130 static int delete_link_by_key(struct db_list *dbp, char *linkkey,
131 	int *linksizep, int *errorp, char *errstr);
132 static int delete_link(struct db_list *dbp, fhandle_t *dfh, char *name,
133 	char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr);
134 
135 /*
136  * The following functions do the actual database I/O. Currently use DBM.
137  */
138 
139 /*
140  * The "db_*" functions are functions that access the database using
141  * database-specific calls. Currently the only database supported is
142  * dbm. Because of the limitations of this database, in particular when
143  * it comes to manipulating records with the same key, or using multiple keys,
144  * the following design decisions have been made:
145  *
146  *	Each file system has a separate dbm file, which are kept open as
147  *		accessed, listed in a linked list.
148  *	Two possible access mode are available for each file - either by
149  *		file handle, or by directory file handle and name. Since
150  *		dbm does not allow multiple keys, we will have a primary
151  *		and secondary key for each file/link.
152  *	The primary key is the pair (inode,gen) which can be obtained
153  *		from the file handle. This points to a record with
154  *		the full file handle and the secondary key (dfh-key,name)
155  *		for one of the links.
156  *	The secondary key is the pair (dfh-key,name) where dfh-key is
157  *		the primary key for the directory and the name is the
158  *		link name. It points to a record that contains the primary
159  *		key for the file and to the previous and next hard link
160  *		found for this file (if they exist).
161  *
162  * Summary of operations:
163  *	Adding a new file: Create the primary record and secondary (link)
164  *		record and add both to the database. The link record
165  *		would have prev and next links set to NULL.
166  *
167  *	Adding a link to a file in the database: Add the link record,
168  *		to the head of the links list (i.e. prev = NULL, next =
169  *		secondary key recorded in the primary record). Update
170  *		the primary record to point to the new link, and the
171  *		secondary record for the old head of list to point to new.
172  *
173  *	Deleting a file: Delete the link record. If it is the last link
174  *		then mark the primary record as deleted but don't delete
175  *		that one from the database (in case some clients still
176  *		hold the file handle). If there are other links, and the
177  *		deleted link is the head of the list (in the primary
178  *		record), update the primary record with the new head.
179  *
180  *	Renaming a file: Add the new link and then delete the old one.
181  *
182  *	Lookup by file handle (read, write, lookup, etc.) - fetch primary rec.
183  *	Lookup by dir info (delete, link, rename) - fetch secondary rec.
184  *
185  *	XXX NOTE: The code is written single-threaded. To make it multi-
186  *	threaded, the following considerations must be made:
187  *	1. Changes/access to the db list must be atomic.
188  *	2. Changes/access for a specific file handle must be atomic
189  *	   (example: deleting a link may affect up to 4 separate database
190  *	   entries: the deleted link, the prev and next links if exist,
191  *	   and the filehandle entry, if it points to the deleted link -
192  *	   these changes must be atomic).
193  */
194 
195 /*
196  * Create a link key given directory fh and name
197  */
198 static int
199 fill_link_key(char *linkkey, fhandle_t *dfh, char *name)
200 {
201 	int	linksize, linksize32;
202 
203 	(void) memcpy(linkkey, &dfh->fh_data, dfh->fh_len);
204 	(void) strcpy(&linkkey[dfh->fh_len], name);
205 	linksize = dfh->fh_len + strlen(name) + 1;
206 	linksize32 = ROUNDUP32(linksize);
207 	if (linksize32 > linksize)
208 		bzero(&linkkey[linksize], linksize32 - linksize);
209 	return (linksize32);
210 }
211 
212 /*
213  * db_get_db - gets the database for the filesystem, or creates one
214  * if none exists. Return the pointer for the database in *dbpp if success.
215  * Return 0 for success, error code otherwise.
216  */
217 static struct db_list *
218 db_get_db(char *fhpath, fsid_t *fsid, int *errorp, int create_flag)
219 {
220 	struct db_list	*p, *newp;
221 	char		fsidstr[30];
222 	datum		key, data;
223 
224 	*errorp = 0;
225 	for (p = db_fs_list;
226 		(p != NULL) && memcmp(&p->fsid, fsid, sizeof (*fsid));
227 		p = p->next);
228 	if (p != NULL) {
229 		/* Found it */
230 		return (p);
231 	}
232 	/* Create it */
233 	if ((newp = calloc(1, sizeof (*newp))) == NULL) {
234 		*errorp = errno;
235 		syslog(LOG_ERR, gettext(
236 			"db_get_db: malloc db failed: Error %s"),
237 			strerror(*errorp));
238 		return (NULL);
239 	}
240 	(void) sprintf(fsidstr, "%08x%08x", fsid->val[0], fsid->val[1]);
241 	if ((newp->path = malloc(strlen(fhpath) + 2 + strlen(fsidstr)))
242 		== NULL) {
243 		*errorp = errno;
244 		syslog(LOG_ERR, gettext(
245 			"db_get_db: malloc dbpath failed: Error %s"),
246 			strerror(*errorp));
247 		goto err_exit;
248 	}
249 	(void) sprintf(newp->path, "%s.%s", fhpath, fsidstr);
250 	/*
251 	 * The open mode is masked by UMASK.
252 	 */
253 	if ((newp->db = dbm_open(newp->path, create_flag | O_RDWR, 0666))
254 		== NULL) {
255 		*errorp = errno;
256 		syslog(LOG_ERR, gettext(
257 			"db_get_db: dbm_open db '%s' failed: Error %s"),
258 			newp->path, strerror(*errorp));
259 		if (*errorp == 0)	/* should not happen but may */
260 			*errorp = -1;
261 		goto err_exit;
262 	}
263 	/*
264 	 * Add the version identifier (have to check first in the
265 	 * case the db exists)
266 	 */
267 	key.dptr = DB_VERSION_STRING;
268 	key.dsize = strlen(DB_VERSION_STRING);
269 	data = dbm_fetch(newp->db, key);
270 	if (data.dptr == NULL) {
271 		data.dptr = DB_VERSION;
272 		data.dsize = strlen(DB_VERSION);
273 		(void) dbm_store(newp->db, key, data, DBM_INSERT);
274 	}
275 
276 	(void) memcpy(&newp->fsid, fsid, sizeof (*fsid));
277 	newp->next = db_fs_list;
278 	db_fs_list = newp;
279 	if (debug > 1) {
280 		(void) printf("db_get_db: db %s opened\n", newp->path);
281 	}
282 	return (newp);
283 
284 err_exit:
285 	if (newp != NULL) {
286 		if (newp->db != NULL) {
287 			dbm_close(newp->db);
288 		}
289 		if (newp->path != NULL) {
290 			free(newp->path);
291 		}
292 		free(newp);
293 	}
294 	return (NULL);
295 }
296 
297 /*
298  * db_get_all_databases - gets the database for any filesystem. This is used
299  * when any database will do - typically to retrieve the path for the
300  * public filesystem. If any database is open - return the first one,
301  * otherwise, search for it using fhpath. If getall is TRUE, open all
302  * matching databases, and mark them (to indicate that all such were opened).
303  * Return the pointer for a matching database if success.
304  */
305 static struct db_list *
306 db_get_all_databases(char *fhpath, bool_t getall)
307 {
308 	char		*dirptr, *fhdir, *fhpathname;
309 	int		len, error;
310 	DIR		*dirp;
311 	struct dirent	*dp;
312 	fsid_t		fsid;
313 	struct db_list	*dbp, *ret_dbp;
314 
315 	for (dbp = db_fs_list; dbp != NULL; dbp = dbp->next) {
316 		if (strncmp(fhpath, dbp->path, strlen(fhpath)) == 0)
317 			break;
318 	}
319 	if (dbp != NULL) {
320 		/*
321 		 * if one database for that prefix is open, and  either only
322 		 * one is needed, or already opened all such databases,
323 		 * return here without exhaustive search
324 		 */
325 		if (!getall || dbp->getall)
326 			return (dbp);
327 	}
328 	if ((fhdir = strdup(fhpath)) == NULL) {
329 		syslog(LOG_ERR, gettext(
330 			"db_get_all_databases: strdup '%s' Error '%s*'"),
331 			fhpath, strerror(errno));
332 		return (NULL);
333 	}
334 	fhpathname = NULL;
335 	ret_dbp = NULL;
336 	if ((dirptr = strrchr(fhdir, '/')) == NULL) {
337 		/* no directory */
338 		goto exit;
339 	}
340 	if ((fhpathname = strdup(&dirptr[1])) == NULL) {
341 		syslog(LOG_ERR, gettext(
342 			"db_get_all_databases: strdup '%s' Error '%s*'"),
343 			&dirptr[1], strerror(errno));
344 		goto exit;
345 	}
346 	/* Terminate fhdir string at last '/' */
347 	dirptr[1] = '\0';
348 	/* Search the directory */
349 	if (debug > 2) {
350 		(void) printf("db_get_all_databases: search '%s' for '%s*'\n",
351 			fhdir, fhpathname);
352 	}
353 	if ((dirp = opendir(fhdir)) == NULL) {
354 		syslog(LOG_ERR, gettext(
355 			"db_get_all_databases: opendir '%s' Error '%s*'"),
356 			fhdir, strerror(errno));
357 		goto exit;
358 	}
359 	len = strlen(fhpathname);
360 	while ((dp = readdir(dirp)) != NULL) {
361 		if (strncmp(fhpathname, dp->d_name, len) == 0) {
362 			dirptr = &dp->d_name[len + 1];
363 			if (*(dirptr - 1) != '.') {
364 				continue;
365 			}
366 			(void) sscanf(dirptr, "%08lx%08lx",
367 			    (ulong_t *)&fsid.val[0], (ulong_t *)&fsid.val[1]);
368 			dbp = db_get_db(fhpath, &fsid, &error, 0);
369 			if (dbp != NULL) {
370 				ret_dbp = dbp;
371 				if (!getall)
372 					break;
373 				dbp->getall = TRUE;
374 			}
375 		}
376 	}
377 	(void) closedir(dirp);
378 exit:
379 	if (fhpathname != NULL)
380 		free(fhpathname);
381 	if (fhdir != NULL)
382 		free(fhdir);
383 	return (ret_dbp);
384 }
385 
386 static void
387 debug_print_key(FILE *fp, char *str1, char *str2, char *key, int ksize)
388 {
389 	(void) fprintf(fp, "%s: %s key (%d) ", str1, str2, ksize);
390 	debug_opaque_print(fp, key, ksize);
391 	/* may be inode,name - try to print the fields */
392 	if (ksize >= NFS_FHMAXDATA) {
393 		(void) fprintf(fp, ": inode ");
394 		debug_opaque_print(fp, &key[2], sizeof (int));
395 		(void) fprintf(fp, ", gen ");
396 		debug_opaque_print(fp, &key[2 + sizeof (int)], sizeof (int));
397 		if (ksize > NFS_FHMAXDATA) {
398 			(void) fprintf(fp, ", name '%s'", &key[NFS_FHMAXDATA]);
399 		}
400 	}
401 	(void) fprintf(fp, "\n");
402 }
403 
404 static void
405 debug_print_linkinfo(FILE *fp, linkinfo_ent *linkp)
406 {
407 	if (linkp == NULL)
408 		return;
409 	(void) fprintf(fp, "linkinfo:\ndfh: ");
410 	debug_opaque_print(fp, (void *)&linkp->dfh, sizeof (linkp->dfh));
411 	(void) fprintf(fp, "\nname: '%s'", LN_NAME(linkp));
412 	(void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n",
413 		linkp->mtime, linkp->atime, linkp->flags, linkp->reclen);
414 	(void) fprintf(fp, "offsets: fhkey %d, name %d, next %d, prev %d\n",
415 		linkp->fhkey_offset, linkp->name_offset, linkp->next_offset,
416 		linkp->prev_offset);
417 	debug_print_key(fp, "fhkey", "", LN_FHKEY(linkp), LN_FHKEY_LEN(linkp));
418 	debug_print_key(fp, "next", "", LN_NEXT(linkp), LN_NEXT_LEN(linkp));
419 	debug_print_key(fp, "prev", "", LN_PREV(linkp), LN_PREV_LEN(linkp));
420 }
421 
422 static void
423 debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp)
424 {
425 	if (fhrecp == NULL)
426 		return;
427 	(void) fprintf(fp, "fhrec:\nfh: ");
428 	debug_opaque_print(fp, (void *)&fhrecp->fh, sizeof (fhrecp->fh));
429 	(void) fprintf(fp, "name '%s', dfh: ", fhrecp->name);
430 	debug_opaque_print(fp, (void *)&fhrecp->dfh, sizeof (fhrecp->dfh));
431 	(void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n",
432 		fhrecp->mtime, fhrecp->atime, fhrecp->flags, fhrecp->reclen);
433 }
434 
435 static void
436 debug_print_key_and_data(FILE *fp, char *str1, char *str2, char *key,
437 	int ksize, char *data, int dsize)
438 {
439 	debug_print_key(fp, str1, str2, key, ksize);
440 	(void) fprintf(fp, " ==> (%p,%d)\n", (void *)data, dsize);
441 	if (ksize > NFS_FHMAXDATA) {
442 		linkinfo_ent inf;
443 		/* probably a link struct */
444 		(void) memcpy(&inf, data, sizeof (linkinfo_ent));
445 		debug_print_linkinfo(fp, &inf);
446 	} else if (ksize == NFS_FHMAXDATA) {
447 		fhlist_ent inf;
448 		/* probably an fhlist struct */
449 		(void) memcpy(&inf, data, sizeof (linkinfo_ent));
450 		debug_print_fhlist(fp, &inf);
451 	} else {
452 		/* don't know... */
453 		debug_opaque_print(fp, data, dsize);
454 	}
455 }
456 
457 /*
458  * store_record - store the record in the database and return 0 for success
459  * or error code otherwise.
460  */
461 static int
462 store_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr,
463 	int datasize, char *str)
464 {
465 	datum	key, data;
466 	int	error;
467 	char	*errfmt = "store_record: dbm_store failed, Error: %s\n";
468 	char	*err;
469 
470 	errno = 0;
471 	key.dptr = keyaddr;
472 	key.dsize = keysize;
473 	data.dptr = dataaddr;
474 	data.dsize = datasize;
475 
476 	if (debug > 2) {
477 		debug_print_key_and_data(stdout, str, "dbm_store:\n    ",
478 			key.dptr, key.dsize, data.dptr, data.dsize);
479 	}
480 	if (dbm_store(dbp->db, key, data, DBM_REPLACE) < 0) {
481 		/* Could not store */
482 		error = dbm_error(dbp->db);
483 		dbm_clearerr(dbp->db);
484 
485 		if (error) {
486 			if (errno)
487 				err = strerror(errno);
488 			else {
489 				err = err_str;
490 				errno = EIO;
491 			}
492 		} else { /* should not happen but sometimes does */
493 			err = err_str;
494 			errno = -1;
495 		}
496 		if (debug) {
497 			debug_print_key(stderr, str, "store_record:"
498 				"dbm_store:\n", key.dptr, key.dsize);
499 			(void) fprintf(stderr, errfmt, err);
500 		} else
501 			syslog(LOG_ERR, gettext(errfmt), err);
502 		return (errno);
503 	}
504 	return (0);
505 }
506 
507 /*
508  * fetch_record - fetch the record from the database and return 0 for success
509  * and errno for failure.
510  * dataaddr is an optional valid address for the result. If dataaddr
511  * is non-null, then that memory is already alloc'd. Else, alloc it, and
512  * the caller must free the returned struct when done.
513  */
514 static void *
515 fetch_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr,
516 	int *errorp, char *str)
517 {
518 	datum	key, data;
519 	char	*errfmt = "fetch_record: dbm_fetch failed, Error: %s\n";
520 	char	*err;
521 
522 	errno = 0;
523 	*errorp = 0;
524 	key.dptr = keyaddr;
525 	key.dsize = keysize;
526 
527 	data = dbm_fetch(dbp->db, key);
528 	if (data.dptr == NULL) {
529 		/* primary record not in database */
530 		*errorp = dbm_error(dbp->db);
531 		dbm_clearerr(dbp->db);
532 
533 		if (*errorp) {
534 			if (errno)
535 				err = strerror(errno);
536 			else {
537 				err = err_str;
538 				errno = EIO;
539 			}
540 		} else { /* should not happen but sometimes does */
541 			err = err_str;
542 			errno = -1;
543 		}
544 		*errorp = errno;
545 
546 		if (debug > 3) {
547 			debug_print_key(stderr, str, "fetch_record:"
548 				"dbm_fetch:\n", key.dptr, key.dsize);
549 			(void) fprintf(stderr, errfmt, err);
550 		} else
551 			syslog(LOG_ERR, gettext(errfmt), err);
552 		return (NULL);
553 	}
554 
555 	/* copy to local struct because dbm may return non-aligned pointers */
556 	if ((dataaddr == NULL) &&
557 	    ((dataaddr = malloc(data.dsize)) == NULL)) {
558 		*errorp = errno;
559 		syslog(LOG_ERR, gettext(
560 			"%s: dbm_fetch - malloc %ld: Error %s"),
561 			str, data.dsize, strerror(*errorp));
562 		return (NULL);
563 	}
564 	(void) memcpy(dataaddr, data.dptr, data.dsize);
565 	if (debug > 3) {
566 		debug_print_key_and_data(stdout, str, "fetch_record:"
567 			"dbm_fetch:\n", key.dptr, key.dsize,
568 			dataaddr, data.dsize);
569 	}
570 	*errorp = 0;
571 	return (dataaddr);
572 }
573 
574 /*
575  * delete_record - delete the record from the database and return 0 for success
576  * or error code for failure.
577  */
578 static int
579 delete_record(struct db_list *dbp, void *keyaddr, int keysize, char *str)
580 {
581 	datum	key;
582 	int	error = 0;
583 	char	*errfmt = "delete_record: dbm_delete failed, Error: %s\n";
584 	char	*err;
585 
586 	errno = 0;
587 	key.dptr = keyaddr;
588 	key.dsize = keysize;
589 
590 	if (debug > 2) {
591 		debug_print_key(stdout, str, "delete_record:"
592 			"dbm_delete:\n", key.dptr, key.dsize);
593 	}
594 	if (dbm_delete(dbp->db, key) < 0) {
595 		error = dbm_error(dbp->db);
596 		dbm_clearerr(dbp->db);
597 
598 		if (error) {
599 			if (errno)
600 				err = strerror(errno);
601 			else {
602 				err = err_str;
603 				errno = EIO;
604 			}
605 		} else { /* should not happen but sometimes does */
606 			err = err_str;
607 			errno = -1;
608 		}
609 		if (debug) {
610 			debug_print_key(stderr, str, "delete_record:"
611 				"dbm_delete:\n", key.dptr, key.dsize);
612 			(void) fprintf(stderr, errfmt, err);
613 		} else
614 			syslog(LOG_ERR, gettext(errfmt), err);
615 	}
616 	return (errno);
617 }
618 
619 /*
620  * db_update_fhrec - puts fhrec in db with updated atime if more than
621  * mapping_update_interval seconds passed. Return 0 if success, error otherwise.
622  */
623 static int
624 db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize,
625 	fhlist_ent *fhrecp, char *str)
626 {
627 	time_t	cur_time = time(0);
628 
629 	if (difftime(cur_time, fhrecp->atime) >= mapping_update_interval) {
630 		fhrecp->atime = cur_time;
631 		return (store_record(dbp, keyaddr, keysize,
632 				fhrecp, fhrecp->reclen, str));
633 	}
634 	return (0);
635 }
636 
637 /*
638  * db_update_linkinfo - puts linkinfo in db with updated atime if more than
639  * mapping_update_interval seconds passed. Return 0 if success, error otherwise.
640  */
641 static int
642 db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize,
643 	linkinfo_ent *linkp, char *str)
644 {
645 	time_t	cur_time = time(0);
646 
647 	if (difftime(cur_time, linkp->atime) >= mapping_update_interval) {
648 		linkp->atime = cur_time;
649 		return (store_record(dbp, keyaddr, keysize,
650 				linkp, linkp->reclen, str));
651 	}
652 	return (0);
653 }
654 
655 /*
656  * create_primary_struct - add primary record to the database.
657  * Database must be open when this function is called.
658  * If success, return the added database entry. fhrecp may be used to
659  * provide an existing memory area, else malloc it. If failed, *errorp
660  * contains the error code and return NULL.
661  */
662 static fhlist_ent *
663 create_primary_struct(struct db_list *dbp, fhandle_t *dfh, char *name,
664 	fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp)
665 {
666 	int		reclen, reclen1;
667 	fhlist_ent	*new_fhrecp = fhrecp;
668 
669 	reclen1 = offsetof(fhlist_ent, name) + strlen(name) + 1;
670 	reclen = ROUNDUP32(reclen1);
671 	if (fhrecp == NULL) {	/* allocated the memory */
672 		if ((new_fhrecp = malloc(reclen)) == NULL) {
673 			*errorp = errno;
674 			syslog(LOG_ERR, gettext(
675 				"create_primary_struct: malloc %d Error %s"),
676 				reclen, strerror(*errorp));
677 			return (NULL);
678 		}
679 	}
680 	/* Fill in the fields */
681 	(void) memcpy(&new_fhrecp->fh, fh, sizeof (*fh));
682 	(void) memcpy(&new_fhrecp->dfh, dfh, sizeof (*dfh));
683 	new_fhrecp->flags = flags;
684 	if (dfh == &public_fh)
685 		new_fhrecp->flags |= PUBLIC_PATH;
686 	else
687 		new_fhrecp->flags &= ~PUBLIC_PATH;
688 	new_fhrecp->mtime = time(0);
689 	new_fhrecp->atime = new_fhrecp->mtime;
690 	(void) strcpy(new_fhrecp->name, name);
691 	if (reclen1 < reclen) {
692 		bzero((char *)((uintptr_t)new_fhrecp + reclen1),
693 			reclen - reclen1);
694 	}
695 	new_fhrecp->reclen = reclen;
696 	*errorp = store_record(dbp, &fh->fh_data, fh->fh_len, new_fhrecp,
697 			new_fhrecp->reclen, "create_primary_struct");
698 	if (*errorp != 0) {
699 		/* Could not store */
700 		if (fhrecp == NULL)	/* caller did not supply pointer */
701 			free(new_fhrecp);
702 		return (NULL);
703 	}
704 	return (new_fhrecp);
705 }
706 
707 /*
708  * db_add_primary - add primary record to the database.
709  * If record already in and live, return it (even if for a different link).
710  * If in database but marked deleted, replace it. If not in database, add it.
711  * Database must be open when this function is called.
712  * If success, return the added database entry. fhrecp may be used to
713  * provide an existing memory area, else malloc it. If failed, *errorp
714  * contains the error code and return NULL.
715  */
716 static fhlist_ent *
717 db_add_primary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh,
718 	uint_t flags, fhlist_ent *fhrecp, int *errorp)
719 {
720 	fhlist_ent	*new_fhrecp;
721 	fh_primary_key	fhkey;
722 
723 	if (debug > 2)
724 		(void) printf("db_add_primary entered: name '%s'\n", name);
725 
726 	bcopy(&fh->fh_data, fhkey, fh->fh_len);
727 	new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, (void *)fhrecp,
728 			errorp, "db_add_primary");
729 	if (new_fhrecp != NULL) {
730 		/* primary record is in the database */
731 		/* Update atime if needed */
732 		*errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp,
733 				"db_add_primary put fhrec");
734 		if (debug > 2)
735 			(void) printf("db_add_primary exits(2): name '%s'\n",
736 				name);
737 		return (new_fhrecp);
738 	}
739 	/* primary record not in database - create it */
740 	new_fhrecp = create_primary_struct(dbp, dfh, name, fh, flags,
741 			fhrecp, errorp);
742 	if (new_fhrecp == NULL) {
743 		/* Could not store */
744 		if (debug > 2)
745 			(void) printf(
746 				"db_add_primary exits(1): name '%s' Error %s\n",
747 				name, ((*errorp >= 0) ? strerror(*errorp) :
748 					"Unknown"));
749 
750 		return (NULL);
751 	}
752 	if (debug > 2)
753 		(void) printf("db_add_primary exits(0): name '%s'\n", name);
754 	return (new_fhrecp);
755 }
756 
757 /*
758  * get_next_link - get and check the next link in the chain.
759  * Re-use space if linkp param non-null. Also set *linkkey and *linksizep
760  * to values for next link (*linksizep set to 0 if last link).
761  * cookie is used to detect corrupted link entries XXXXXXX
762  * Return the link pointer or NULL if none.
763  */
764 static linkinfo_ent *
765 get_next_link(struct db_list *dbp, char *linkkey, int *linksizep,
766 	linkinfo_ent *linkp, void **cookiep, int *errorp, char *msg)
767 {
768 	int	linksize, nextsize;
769 	char	*nextkey;
770 	linkinfo_ent *new_linkp = linkp;
771 	struct link_keys *lnp;
772 
773 	linksize = *linksizep;
774 	if (linksize == 0)
775 		return (NULL);
776 	*linksizep = 0;
777 	new_linkp = fetch_record(dbp, linkkey, linksize, (void *)linkp,
778 			errorp, msg);
779 	if (new_linkp == NULL)
780 		return (NULL);
781 
782 	/* Set linkkey to point to next record */
783 	nextsize = LN_NEXT_LEN(new_linkp);
784 	if (nextsize == 0)
785 		return (new_linkp);
786 
787 	/* Add this key to the cookie list */
788 	if ((lnp = malloc(sizeof (struct link_keys))) == NULL) {
789 		syslog(LOG_ERR, gettext("get_next_key: malloc error %s\n"),
790 			strerror(errno));
791 		if ((new_linkp != NULL) && (linkp == NULL))
792 			free(new_linkp);
793 		return (NULL);
794 	}
795 	(void) memcpy(lnp->lnkey, linkkey, linksize);
796 	lnp->lnsize = linksize;
797 	lnp->next = *(struct link_keys **)cookiep;
798 	*cookiep = (void *)lnp;
799 
800 	/* Make sure record does not point to itself or other internal loops */
801 	nextkey = LN_NEXT(new_linkp);
802 	for (; lnp != NULL; lnp = lnp->next) {
803 		if ((nextsize == lnp->lnsize) && (memcmp(
804 			lnp->lnkey, nextkey, nextsize) == 0)) {
805 
806 			/*
807 			 * XXX This entry's next pointer points to
808 			 * itself. This is only a work-around, remove
809 			 * this check once bug 4203186 is fixed.
810 			 */
811 			if (debug) {
812 				(void) fprintf(stderr,
813 				"%s: get_next_link: last record invalid.\n",
814 					msg);
815 				debug_print_key_and_data(stderr, msg,
816 					"invalid rec:\n ", linkkey, linksize,
817 					(char *)new_linkp, new_linkp->reclen);
818 			}
819 			/* Return as if this is the last link */
820 			return (new_linkp);
821 		}
822 	}
823 	(void) memcpy(linkkey, nextkey, nextsize);
824 	*linksizep = nextsize;
825 	return (new_linkp);
826 }
827 
828 /*
829  * free_link_cookies - free the cookie list
830  */
831 static void
832 free_link_cookies(void *cookie)
833 {
834 	struct link_keys *dellnp, *lnp;
835 
836 	lnp = (struct link_keys *)cookie;
837 	while (lnp != NULL) {
838 		dellnp = lnp;
839 		lnp = lnp->next;
840 		free(dellnp);
841 	}
842 }
843 
844 /*
845  * add_mc_path - add a mc link to a file that has other links. Add it at end
846  * of linked list. Called when it's known there are other links.
847  */
848 static void
849 add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name,
850 	fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp)
851 {
852 	fh_secondary_key	linkkey;
853 	int			linksize, len;
854 	linkinfo_ent		lastlink, *lastlinkp;
855 	void			*cookie;
856 
857 	linksize = fill_link_key(linkkey, &fhrecp->dfh, fhrecp->name);
858 	cookie = NULL;
859 	do {
860 		lastlinkp = get_next_link(dbp, linkkey, &linksize, &lastlink,
861 				&cookie, errorp, "add_mc_path");
862 	} while (linksize > 0);
863 	free_link_cookies(cookie);
864 	/* reached end of list */
865 	if (lastlinkp == NULL) {
866 		/* nothing to do */
867 		if (debug > 1) {
868 			(void) fprintf(stderr, "add_mc_path link is null\n");
869 		}
870 		return;
871 	}
872 	/* Add new link after last link */
873 	/*
874 	 * next - link key for the next in the list - add at end so null.
875 	 * prev - link key for the previous link in the list.
876 	 */
877 	linkp->prev_offset = linkp->next_offset;	/* aligned */
878 	linksize = fill_link_key(LN_PREV(linkp), &lastlinkp->dfh,
879 				LN_NAME(lastlinkp));
880 	linkp->reclen = linkp->prev_offset + linksize;	/* aligned */
881 
882 	/* Add the link information to the database */
883 	linksize = fill_link_key(linkkey, dfh, name);
884 	*errorp = store_record(dbp, linkkey, linksize,
885 			linkp, linkp->reclen, "add_mc_path");
886 	if (*errorp != 0)
887 		return;
888 
889 	/* Now update previous last link to point forward to new link */
890 	/* Copy prev link out since it's going to be overwritten */
891 	linksize = LN_PREV_LEN(lastlinkp);
892 	(void) memcpy(linkkey, LN_PREV(lastlinkp), linksize);
893 	/* Update previous last link to point to new one */
894 	len = fill_link_key(LN_NEXT(lastlinkp), dfh, name);
895 	lastlinkp->prev_offset = lastlinkp->next_offset + len;	/* aligned */
896 	(void) memcpy(LN_PREV(lastlinkp), linkkey, linksize);
897 	lastlinkp->reclen = lastlinkp->prev_offset + linksize;
898 	/* Update the link information to the database */
899 	linksize = fill_link_key(linkkey, &lastlinkp->dfh, LN_NAME(lastlinkp));
900 	*errorp = store_record(dbp, linkkey, linksize,
901 			lastlinkp, lastlinkp->reclen, "add_mc_path prev");
902 }
903 
904 /*
905  * create_link_struct - create the secondary struct.
906  * (dfh,name) is the secondary key, fhrec is the primary record for the file
907  * and linkpp is a place holder for the record (could be null).
908  * Insert the record to the database.
909  * Return 0 if success, error otherwise.
910  */
911 static linkinfo_ent *
912 create_link_struct(struct db_list *dbp, fhandle_t *dfh, char *name,
913 	fhlist_ent *fhrecp, int *errorp)
914 {
915 	fh_secondary_key	linkkey;
916 	int			len, linksize;
917 	linkinfo_ent		*linkp;
918 
919 	if ((linkp = malloc(sizeof (linkinfo_ent))) == NULL) {
920 		*errorp = errno;
921 		syslog(LOG_ERR, gettext(
922 			"create_link_struct: malloc failed: Error %s"),
923 			strerror(*errorp));
924 		return (NULL);
925 	}
926 	if (dfh == &public_fh)
927 		linkp->flags |= PUBLIC_PATH;
928 	else
929 		linkp->flags &= ~PUBLIC_PATH;
930 	(void) memcpy(&linkp->dfh, dfh, sizeof (*dfh));
931 	linkp->mtime = time(0);
932 	linkp->atime = linkp->mtime;
933 	/* Calculate offsets of variable fields */
934 	/* fhkey - primary key (inode/gen) */
935 	/* name - component name (in directory dfh) */
936 	linkp->fhkey_offset = ROUNDUP32(offsetof(linkinfo_ent, varbuf));
937 	len = fill_link_key(LN_FHKEY(linkp), &fhrecp->fh, name);
938 	linkp->name_offset = linkp->fhkey_offset + fhrecp->fh.fh_len;
939 	linkp->next_offset = linkp->fhkey_offset + len;	/* aligned */
940 	/*
941 	 * next - link key for the next link in the list - NULL if it's
942 	 * the first link. If this is the public fs, only one link allowed.
943 	 * Avoid setting a multi-component path as primary path,
944 	 * unless no choice.
945 	 */
946 	len = 0;
947 	if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) ||
948 	    strcmp(fhrecp->name, name)) {
949 		/* different link than the one that's in the record */
950 		if (dfh == &public_fh) {
951 			/* parent is public fh - either multi-comp or root */
952 			if (memcmp(&fhrecp->fh, &public_fh,
953 				sizeof (public_fh))) {
954 				/* multi-comp path */
955 				add_mc_path(dbp, dfh, name, fhrecp, linkp,
956 						errorp);
957 				if (*errorp != 0) {
958 					free(linkp);
959 					return (NULL);
960 				}
961 				return (linkp);
962 			}
963 		} else {
964 			/* new link to a file with a different one already */
965 			len = fill_link_key(LN_NEXT(linkp), &fhrecp->dfh,
966 				fhrecp->name);
967 		}
968 	}
969 	/*
970 	 * prev - link key for the previous link in the list - since we
971 	 * always insert at the front of the list, it's always initially NULL.
972 	 */
973 	linkp->prev_offset = linkp->next_offset + len;	/* aligned */
974 	linkp->reclen = linkp->prev_offset;
975 
976 	/* Add the link information to the database */
977 	linksize = fill_link_key(linkkey, dfh, name);
978 	*errorp = store_record(dbp, linkkey, linksize, linkp, linkp->reclen,
979 			"create_link_struct");
980 	if (*errorp != 0) {
981 		free(linkp);
982 		return (NULL);
983 	}
984 	return (linkp);
985 }
986 
987 /*
988  * db_add_secondary - add secondary record to the database (for the directory
989  * information).
990  * Assumes this is a new link, not yet in the database, and that the primary
991  * record is already in.
992  * If fhrecp is non-null, then fhrecp is the primary record.
993  * Database must be open when this function is called.
994  * Return 0 if success, error code otherwise.
995  */
996 static int
997 db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name,
998 	fhandle_t *fh, fhlist_ent *fhrecp)
999 {
1000 	int			nextsize, len, error;
1001 	linkinfo_ent		nextlink, *newlinkp, *nextlinkp;
1002 	uint_t			fhflags;
1003 	char			*nextaddr;
1004 	fhlist_ent		*new_fhrecp = fhrecp;
1005 	fh_primary_key		fhkey;
1006 
1007 	if (debug > 2)
1008 		(void) printf("db_add_secondary entered: name '%s'\n", name);
1009 
1010 	bcopy(&fh->fh_data, fhkey, fh->fh_len);
1011 	if (fhrecp == NULL) {
1012 		/* Fetch the primary record */
1013 		new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, NULL,
1014 				&error, "db_add_secondary primary");
1015 		if (new_fhrecp == NULL) {
1016 			return (error);
1017 		}
1018 	}
1019 	/* Update fhrec atime if needed */
1020 	error = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp,
1021 			"db_add_secondary primary");
1022 	fhflags = new_fhrecp->flags;
1023 	/* now create and insert the secondary record */
1024 	newlinkp = create_link_struct(dbp, dfh, name, new_fhrecp, &error);
1025 	if (fhrecp == NULL) {
1026 		free(new_fhrecp);
1027 		new_fhrecp = NULL;
1028 	}
1029 	if (newlinkp == NULL) {
1030 		if (debug > 2)
1031 			(void) printf("create_link_struct '%s' Error %s\n",
1032 				name, ((error >= 0) ? strerror(error) :
1033 					"Unknown"));
1034 		return (error);
1035 	}
1036 	nextsize = LN_NEXT_LEN(newlinkp);
1037 	if (nextsize == 0) {
1038 		/* No next - can exit now */
1039 		if (debug > 2)
1040 			(void) printf("db_add_secondary: no next link\n");
1041 		free(newlinkp);
1042 		return (0);
1043 	}
1044 
1045 	/*
1046 	 * Update the linked list to point to new head: replace head of
1047 	 * list in the primary record, then update previous secondary record
1048 	 * to point to new head
1049 	 */
1050 	new_fhrecp = create_primary_struct(dbp, dfh, name, fh, fhflags,
1051 			new_fhrecp, &error);
1052 	if (new_fhrecp == NULL) {
1053 		if (debug > 2)
1054 			(void) printf(
1055 				"db_add_secondary: replace primary failed\n");
1056 		free(newlinkp);
1057 		return (error);
1058 	} else if (fhrecp == NULL) {
1059 		free(new_fhrecp);
1060 	}
1061 
1062 	/*
1063 	 * newlink is the new head of the list, with its "next" pointing to
1064 	 * the old head, and its "prev" pointing to NULL. We now need to
1065 	 * modify the "next" entry to have its "prev" point to the new entry.
1066 	 */
1067 	nextaddr = LN_NEXT(newlinkp);
1068 	if (debug > 2) {
1069 		debug_print_key(stderr, "db_add_secondary", "next key\n    ",
1070 			nextaddr, nextsize);
1071 	}
1072 	/* Get the next link entry from the database */
1073 	nextlinkp = fetch_record(dbp, nextaddr, nextsize, (void *)&nextlink,
1074 			&error, "db_add_secondary next link");
1075 	if (nextlinkp == NULL) {
1076 		if (debug > 2)
1077 			(void) printf(
1078 				"db_add_secondary: fetch next link failed\n");
1079 		free(newlinkp);
1080 		return (error);
1081 	}
1082 
1083 	/*
1084 	 * since the "prev" field is the only field to be changed, and it's
1085 	 * the last in the link record, we only need to modify it (and reclen).
1086 	 * Re-use link to update the next record.
1087 	 */
1088 	len = fill_link_key(LN_PREV(nextlinkp), dfh, name);
1089 	nextlinkp->reclen = nextlinkp->prev_offset + len;
1090 	error = store_record(dbp, nextaddr, nextsize, nextlinkp,
1091 			nextlinkp->reclen, "db_add_secondary");
1092 	if (debug > 2)
1093 		(void) printf(
1094 			"db_add_secondary exits(%d): name '%s'\n", error, name);
1095 	free(newlinkp);
1096 	return (error);
1097 }
1098 
1099 /*
1100  * Update the next link to point to the new prev.
1101  * Return 0 for success, error code otherwise.
1102  * If successful, and nextlinkpp is non-null,
1103  * *nextlinkpp contains the record for the next link, since
1104  * we may will it if the primary record should be updated.
1105  */
1106 static linkinfo_ent *
1107 update_next_link(struct db_list *dbp, char *nextkey, int nextsize,
1108 	char *prevkey, int prevsize, int *errorp)
1109 {
1110 	linkinfo_ent	*nextlinkp, *linkp1;
1111 
1112 	if ((nextlinkp = malloc(sizeof (linkinfo_ent))) == NULL) {
1113 		*errorp = errno;
1114 		syslog(LOG_ERR, gettext(
1115 			"update_next_link: malloc next Error %s"),
1116 			strerror(*errorp));
1117 		return (NULL);
1118 	}
1119 	linkp1 = nextlinkp;
1120 	nextlinkp = fetch_record(dbp, nextkey, nextsize, nextlinkp,
1121 			errorp, "update next");
1122 	/* if there is no next record - ok */
1123 	if (nextlinkp == NULL) {
1124 		/* Return no error */
1125 		*errorp = 0;
1126 		free(linkp1);
1127 		return (NULL);
1128 	}
1129 	/* Set its prev to the prev of the deleted record */
1130 	nextlinkp->reclen = ROUNDUP32(nextlinkp->reclen -
1131 				LN_PREV_LEN(nextlinkp) + prevsize);
1132 	/* Change the len and set prev */
1133 	if (prevsize > 0) {
1134 		(void) memcpy(LN_PREV(nextlinkp), prevkey, prevsize);
1135 	}
1136 	/* No other changes needed because prev is last field */
1137 	*errorp = store_record(dbp, nextkey, nextsize, nextlinkp,
1138 			nextlinkp->reclen, "update_next");
1139 	if (*errorp != 0) {
1140 		free(nextlinkp);
1141 		nextlinkp = NULL;
1142 	}
1143 	return (nextlinkp);
1144 }
1145 
1146 /*
1147  * Update the prev link to point to the new next.
1148  * Return 0 for success, error code otherwise.
1149  */
1150 static int
1151 update_prev_link(struct db_list *dbp, char *nextkey, int nextsize,
1152 	char *prevkey, int prevsize)
1153 {
1154 	linkinfo_ent	prevlink, *prevlinkp;
1155 	int		diff, error;
1156 
1157 	/* Update its next to the given one */
1158 	prevlinkp = fetch_record(dbp, prevkey, prevsize, &prevlink, &error,
1159 			"update prev");
1160 	/* if error there is no next record - ok */
1161 	if (prevlinkp == NULL) {
1162 		return (0);
1163 	}
1164 	diff = nextsize - LN_NEXT_LEN(prevlinkp);
1165 	prevlinkp->reclen = ROUNDUP32(prevlinkp->reclen + diff);
1166 	/* Change the len and set next - may push prev */
1167 	if (diff != 0) {
1168 		char	*ptr = LN_PREV(prevlinkp);
1169 
1170 		prevlinkp->prev_offset += diff;
1171 		(void) memcpy(LN_PREV(prevlinkp), ptr, LN_PREV_LEN(prevlinkp));
1172 	}
1173 	if (nextsize > 0) {
1174 		(void) memcpy(LN_NEXT(prevlinkp), nextkey, nextsize);
1175 	}
1176 	/* Store updated record */
1177 	error = store_record(dbp, prevkey, prevsize, prevlinkp,
1178 			prevlinkp->reclen, "update_prev");
1179 	return (error);
1180 }
1181 
1182 /*
1183  * update_linked_list - update the next link to point back to prev, and vice
1184  * versa. Normally called by delete_link to drop the deleted link from the
1185  * linked list of hard links for the file. next and prev are the keys of next
1186  * and previous links for the deleted link in the list (could be NULL).
1187  * Return 0 for success, error code otherwise.
1188  * If successful, and nextlinkpp is non-null,
1189  * return the record for the next link, since
1190  * if the primary record should be updated we'll need it. In this case,
1191  * actually allocate the space for it because we can't tell otherwise.
1192  */
1193 static linkinfo_ent *
1194 update_linked_list(struct db_list *dbp, char *nextkey, int nextsize,
1195 	char *prevkey, int prevsize, int *errorp)
1196 {
1197 	linkinfo_ent	*nextlinkp = NULL;
1198 
1199 	*errorp = 0;
1200 	if (nextsize > 0) {
1201 		nextlinkp = update_next_link(dbp, nextkey, nextsize,
1202 				prevkey, prevsize, errorp);
1203 		if (nextlinkp == NULL) {
1204 			/* not an error if no next link */
1205 			if (*errorp != 0) {
1206 				if (debug > 1) {
1207 					(void) fprintf(stderr,
1208 						"update_next_link Error %s\n",
1209 					((*errorp >= 0) ? strerror(*errorp) :
1210 						"Unknown"));
1211 				}
1212 				return (NULL);
1213 			}
1214 		}
1215 	}
1216 	if (prevsize > 0) {
1217 		*errorp = update_prev_link(dbp, nextkey, nextsize,
1218 				prevkey, prevsize);
1219 		if (*errorp != 0) {
1220 			if (debug > 1) {
1221 				(void) fprintf(stderr,
1222 					"update_prev_link Error %s\n",
1223 					((*errorp >= 0) ? strerror(*errorp) :
1224 					"Unknown"));
1225 			}
1226 			if (nextlinkp != NULL)
1227 				free(nextlinkp);
1228 			nextlinkp = NULL;
1229 		}
1230 	}
1231 	return (nextlinkp);
1232 }
1233 
1234 /*
1235  * db_update_primary_new_head - Update a primary record that the head of
1236  * the list is deleted. Similar to db_add_primary, but the primary record
1237  * must exist, and is always replaced with one pointing to the new link,
1238  * unless it does not point to the deleted link. If the link we deleted
1239  * was the last link, the delete the primary record as well.
1240  * Return 0 for success, error code otherwise.
1241  */
1242 static int
1243 db_update_primary_new_head(struct db_list *dbp, linkinfo_ent *dellinkp,
1244 	linkinfo_ent *nextlinkp, fhlist_ent *fhrecp)
1245 {
1246 	int			error;
1247 	char			*name, *next_name;
1248 	fhandle_t		*dfh;
1249 	fh_primary_key		fhkey;
1250 
1251 	dfh = &dellinkp->dfh;
1252 	name = LN_NAME(dellinkp);
1253 	/* If the deleted link was not the head of the list, we are done */
1254 	if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) ||
1255 	    strcmp(fhrecp->name, name)) {
1256 		/* should never be here... */
1257 		if (debug > 1) {
1258 			(void) fprintf(stderr,
1259 				"db_update_primary_new_head: primary "
1260 				"is for [%s,", name);
1261 			debug_opaque_print(stderr, (void *)dfh, sizeof (*dfh));
1262 			(void) fprintf(stderr, "], not [%s,", fhrecp->name);
1263 			debug_opaque_print(stderr, (void *)&fhrecp->dfh,
1264 				sizeof (fhrecp->dfh));
1265 			(void) fprintf(stderr, "]\n");
1266 		}
1267 		return (0);	/* not head of list so done */
1268 	}
1269 	/* Set the head to nextkey if exists. Otherwise, mark file as deleted */
1270 	bcopy(&fhrecp->fh.fh_data, fhkey, fhrecp->fh.fh_len);
1271 	if (nextlinkp == NULL) {
1272 		/* last link */
1273 		/* remove primary record from database */
1274 		(void) delete_record(dbp,
1275 			fhkey, fhrecp->fh.fh_len,
1276 			"db_update_primary_new_head: fh delete");
1277 		return (0);
1278 	} else {
1279 		/*
1280 		 * There are still "live" links, so update the primary record.
1281 		 */
1282 		next_name = LN_NAME(nextlinkp);
1283 		fhrecp->reclen = ROUNDUP32(offsetof(fhlist_ent, name) +
1284 					strlen(next_name) + 1);
1285 		/* Replace link data with the info for the next link */
1286 		(void) memcpy(&fhrecp->dfh, &nextlinkp->dfh,
1287 			sizeof (nextlinkp->dfh));
1288 		(void) strcpy(fhrecp->name, next_name);
1289 	}
1290 	/* not last link */
1291 	fhrecp->mtime = time(0);
1292 	fhrecp->atime = fhrecp->mtime;
1293 	error = store_record(dbp,
1294 			fhkey, fhrecp->fh.fh_len, fhrecp,
1295 			fhrecp->reclen, "db_update_primary_new_head: fh");
1296 	return (error);
1297 }
1298 
1299 /*
1300  * Exported functions
1301  */
1302 
1303 /*
1304  * db_add - add record to the database. If dfh, fh and name are all here,
1305  * add both primary and secondary records. If fh is not available, don't
1306  * add anything...
1307  * Assumes this is a new file, not yet in the database and that the record
1308  * for fh is already in.
1309  * Return 0 for success, error code otherwise.
1310  */
1311 int
1312 db_add(char *fhpath, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags)
1313 {
1314 	struct db_list	*dbp = NULL;
1315 	fhlist_ent	fhrec, *fhrecp;
1316 	int		error = 0;
1317 
1318 	if (fh == NULL) {
1319 		/* nothing to add */
1320 		return (EINVAL);
1321 	}
1322 	if (fh == &public_fh) {
1323 		dbp = db_get_all_databases(fhpath, FALSE);
1324 	} else {
1325 		dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT);
1326 	}
1327 	for (; dbp != NULL; dbp = ((fh != &public_fh) ? NULL : dbp->next)) {
1328 		if (debug > 3) {
1329 			(void) printf("db_add: name '%s', db '%s'\n",
1330 				name, dbp->path);
1331 		}
1332 		fhrecp = db_add_primary(dbp, dfh, name, fh, flags,
1333 				&fhrec, &error);
1334 		if (fhrecp == NULL) {
1335 			continue;
1336 		}
1337 		if ((dfh == NULL) || (name == NULL)) {
1338 			/* Can't add link information */
1339 			syslog(LOG_ERR, gettext(
1340 				"db_add: dfh %p, name %p - invalid"),
1341 				(void *)dfh, (void *)name);
1342 			error = EINVAL;
1343 			continue;
1344 		}
1345 		if (fh == &public_fh) {
1346 			while ((fhrecp != NULL) && strcmp(name, fhrecp->name)) {
1347 				/* Replace the public fh rather than add link */
1348 				error = db_delete_link(fhpath, dfh,
1349 						fhrecp->name);
1350 				fhrecp = db_add_primary(dbp, dfh, name, fh,
1351 						flags, &fhrec, &error);
1352 			}
1353 			if (fhrecp == NULL) {
1354 				continue;
1355 			}
1356 		}
1357 		error = db_add_secondary(dbp, dfh, name, fh, fhrecp);
1358 		if (fhrecp != &fhrec) {
1359 			free(fhrecp);
1360 		}
1361 	}
1362 	return (error);
1363 }
1364 
1365 /*
1366  * db_lookup - search the database for the file identified by fh.
1367  * Return the entry in *fhrecpp if found, or NULL with error set otherwise.
1368  */
1369 fhlist_ent *
1370 db_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp, int *errorp)
1371 {
1372 	struct db_list	*dbp;
1373 	fh_primary_key	fhkey;
1374 
1375 	if ((fhpath == NULL) || (fh == NULL) || (errorp == NULL)) {
1376 		if (errorp != NULL)
1377 			*errorp = EINVAL;
1378 		return (NULL);
1379 	}
1380 	*errorp = 0;
1381 	if (fh == &public_fh) {
1382 		dbp = db_get_all_databases(fhpath, FALSE);
1383 	} else {
1384 		dbp = db_get_db(fhpath, &fh->fh_fsid, errorp, O_CREAT);
1385 	}
1386 	if (dbp == NULL) {
1387 		/* Could not get or create database */
1388 		return (NULL);
1389 	}
1390 	bcopy(&fh->fh_data, fhkey, fh->fh_len);
1391 	fhrecp = fetch_record(dbp, fhkey, fh->fh_len, fhrecp,
1392 			errorp, "db_lookup");
1393 	/* Update fhrec atime if needed */
1394 	if (fhrecp != NULL) {
1395 		*errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, fhrecp,
1396 				"db_lookup");
1397 	}
1398 	return (fhrecp);
1399 }
1400 
1401 /*
1402  * db_lookup_link - search the database for the file identified by (dfh,name).
1403  * If the link was found, use it to search for the primary record.
1404  * Return 0 and set the entry in *fhrecpp if found, return error otherwise.
1405  */
1406 fhlist_ent *
1407 db_lookup_link(char *fhpath, fhandle_t *dfh, char *name, fhlist_ent *fhrecp,
1408 	int *errorp)
1409 {
1410 	struct db_list		*dbp;
1411 	fh_secondary_key	linkkey;
1412 	linkinfo_ent		*linkp;
1413 	int			linksize, fhkeysize;
1414 	char			*fhkey;
1415 
1416 	if ((fhpath == NULL) || (dfh == NULL) || (name == NULL) ||
1417 		(errorp == NULL)) {
1418 		if (errorp != NULL)
1419 			*errorp = EINVAL;
1420 		return (NULL);
1421 	}
1422 	*errorp = 0;
1423 	if (dfh == &public_fh) {
1424 		dbp = db_get_all_databases(fhpath, FALSE);
1425 	} else {
1426 		dbp = db_get_db(fhpath, &dfh->fh_fsid, errorp, O_CREAT);
1427 	}
1428 	if (dbp == NULL) {
1429 		/* Could not get or create database */
1430 		return (NULL);
1431 	}
1432 	/* Get the link record */
1433 	linksize = fill_link_key(linkkey, dfh, name);
1434 	linkp = fetch_record(dbp, linkkey, linksize, NULL, errorp,
1435 			"db_lookup_link link");
1436 	if (linkp != NULL) {
1437 		/* Now use link to search for fh entry */
1438 		fhkeysize = LN_FHKEY_LEN(linkp);
1439 		fhkey = LN_FHKEY(linkp);
1440 		fhrecp = fetch_record(dbp, fhkey, fhkeysize,
1441 				(void *)fhrecp, errorp, "db_lookup_link fh");
1442 		/* Update fhrec atime if needed */
1443 		if (fhrecp != NULL) {
1444 			*errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp,
1445 				"db_lookup_link fhrec");
1446 		}
1447 		/* Update link atime if needed */
1448 		*errorp = db_update_linkinfo(dbp, linkkey, linksize, linkp,
1449 			"db_lookup_link link");
1450 		free(linkp);
1451 	} else {
1452 		fhrecp = NULL;
1453 	}
1454 	return (fhrecp);
1455 }
1456 
1457 /*
1458  * delete_link - delete the requested link from the database. If it's the
1459  * last link in the database for that file then remove the primary record
1460  * as well. *errorp contains the returned error code.
1461  * Return ENOENT if link not in database and 0 otherwise.
1462  */
1463 static int
1464 delete_link_by_key(struct db_list *dbp, char *linkkey, int *linksizep,
1465 	int *errorp, char *errstr)
1466 {
1467 	int			nextsize, prevsize, fhkeysize, linksize;
1468 	char			*nextkey, *prevkey, *fhkey;
1469 	linkinfo_ent		*dellinkp, *nextlinkp;
1470 	fhlist_ent		*fhrecp, fhrec;
1471 
1472 	*errorp = 0;
1473 	linksize = *linksizep;
1474 	/* Get the link record */
1475 	dellinkp = fetch_record(dbp, linkkey, linksize, NULL, errorp, errstr);
1476 	if (dellinkp == NULL) {
1477 		/*
1478 		 * Link not in database.
1479 		 */
1480 		if (debug > 2) {
1481 			debug_print_key(stderr, errstr,
1482 				"link not in database\n",
1483 				linkkey, linksize);
1484 		}
1485 		*linksizep = 0;
1486 		return (ENOENT);
1487 	}
1488 	/*
1489 	 * Possibilities:
1490 	 * 1. Normal case - only one link to delete: the link next and
1491 	 *    prev should be NULL, and fhrec's name/dfh are same
1492 	 *    as the link. Remove the link and fhrec.
1493 	 * 2. Multiple hard links, and the deleted link is the head of
1494 	 *    the list. Remove the link and replace the link key in
1495 	 *    the primary record to point to the new head.
1496 	 * 3. Multiple hard links, and the deleted link is not the
1497 	 *    head of the list (not the same as in fhrec) - just
1498 	 *    delete the link and update the previous and next records
1499 	 *    in the links linked list.
1500 	 */
1501 
1502 	/* Get next and prev keys for linked list updates */
1503 	nextsize = LN_NEXT_LEN(dellinkp);
1504 	nextkey = ((nextsize > 0) ? LN_NEXT(dellinkp) : NULL);
1505 	prevsize = LN_PREV_LEN(dellinkp);
1506 	prevkey = ((prevsize > 0) ? LN_PREV(dellinkp) : NULL);
1507 	/* Update the linked list for the file */
1508 	nextlinkp = update_linked_list(dbp, nextkey, nextsize,
1509 			prevkey, prevsize, errorp);
1510 	if ((nextlinkp == NULL) && (*errorp != 0)) {
1511 		free(dellinkp);
1512 		*linksizep = 0;
1513 		return (0);
1514 	}
1515 	/* Delete link record */
1516 	*errorp = delete_record(dbp, linkkey, linksize, errstr);
1517 	/* Get the primary key */
1518 	fhkeysize = LN_FHKEY_LEN(dellinkp);
1519 	fhkey = LN_FHKEY(dellinkp);
1520 	fhrecp = fetch_record(dbp, fhkey, fhkeysize,
1521 		&fhrec, errorp, errstr);
1522 	if (fhrecp == NULL) {
1523 		/* Should never happen */
1524 		if (debug > 1) {
1525 			debug_print_key(stderr, errstr,
1526 				"fetch primary for ", linkkey, linksize);
1527 			(void) fprintf(stderr, " Error %s\n",
1528 			((*errorp >= 0) ? strerror(*errorp) : "Unknown"));
1529 		}
1530 	} else if ((*errorp == 0) && (prevsize <= 0)) {
1531 		/* This is the head of the list update primary record */
1532 		*errorp = db_update_primary_new_head(dbp, dellinkp,
1533 				nextlinkp, fhrecp);
1534 	} else {
1535 		/* Update fhrec atime if needed */
1536 		*errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp,
1537 				errstr);
1538 	}
1539 	*linksizep = nextsize;
1540 	if (nextsize > 0)
1541 		(void) memcpy(linkkey, nextkey, nextsize);
1542 	if (nextlinkp != NULL)
1543 		free(nextlinkp);
1544 	free(dellinkp);
1545 	return (0);
1546 }
1547 
1548 /*
1549  * delete_link - delete the requested link from the database. If it's the
1550  * last link in the database for that file then remove the primary record
1551  * as well. If nextlinkkey/sizep are non-null, copy the key and key size of
1552  * the next link in the chain into them (this would save a dbm_fetch op).
1553  * Return ENOENT if link not in database and 0 otherwise, with *errorp
1554  * containing the returned error if any from the delete_link ops.
1555  */
1556 static int
1557 delete_link(struct db_list *dbp, fhandle_t *dfh, char *name,
1558 	char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr)
1559 {
1560 	int	linkerr;
1561 
1562 	*errorp = 0;
1563 	if ((nextlinkkey != NULL) && (nextlinksizep != NULL)) {
1564 		*nextlinksizep = fill_link_key(nextlinkkey, dfh, name);
1565 		linkerr = delete_link_by_key(dbp, nextlinkkey, nextlinksizep,
1566 				errorp, errstr);
1567 	} else {
1568 		int			linksize;
1569 		fh_secondary_key	linkkey;
1570 
1571 		linksize = fill_link_key(linkkey, dfh, name);
1572 		linkerr = delete_link_by_key(dbp, linkkey, &linksize,
1573 				errorp, errstr);
1574 	}
1575 	return (linkerr);
1576 }
1577 
1578 /*
1579  * db_delete_link - search the database for the file system for link.
1580  * Delete the link from the database. If this is the "primary" link,
1581  * set the primary record for the next link. If it's the last one,
1582  * delete the primary record.
1583  * Return 0 for success, error code otherwise.
1584  */
1585 int
1586 db_delete_link(char *fhpath, fhandle_t *dfh, char *name)
1587 {
1588 	struct db_list		*dbp;
1589 	int			error = 0;
1590 
1591 	if ((fhpath == NULL) || (dfh == NULL) || (name == NULL)) {
1592 		return (EINVAL);
1593 	}
1594 	if (dfh == &public_fh) {
1595 		dbp = db_get_all_databases(fhpath, TRUE);
1596 	} else {
1597 		dbp = db_get_db(fhpath, &dfh->fh_fsid, &error, O_CREAT);
1598 	}
1599 	for (; dbp != NULL; dbp = ((dfh == &public_fh) ? dbp->next : NULL)) {
1600 		(void) delete_link(dbp, dfh, name, NULL, NULL, &error,
1601 			"db_delete_link link");
1602 	}
1603 	return (error);
1604 }
1605 
1606 #ifdef DEBUG
1607 /*
1608  * db_delete - Deletes the fhrec corresponding to the fh. Use only
1609  * for repairing the fhtable, not for normal handling.
1610  * Return 0 for success, error code otherwise.
1611  */
1612 int
1613 db_delete(char *fhpath, fhandle_t *fh)
1614 {
1615 	struct db_list		*dbp;
1616 	int			error = 0;
1617 
1618 	if ((fhpath == NULL) || (fh == NULL)) {
1619 		return (EINVAL);
1620 	}
1621 	if (fh == &public_fh) {
1622 		dbp = db_get_all_databases(fhpath, TRUE);
1623 	} else {
1624 		dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT);
1625 	}
1626 	for (; dbp != NULL; dbp = ((fh == &public_fh) ? dbp->next : NULL)) {
1627 		/* Get the link record */
1628 		(void) delete_record(dbp, &fh->fh_data, fh->fh_len,
1629 			"db_delete: fh delete");
1630 	}
1631 	return (error);
1632 }
1633 #endif  /* DEBUG */
1634 
1635 /*
1636  * db_rename_link - search the database for the file system for link.
1637  * Add the new link and delete the old link from the database.
1638  * Return 0 for success, error code otherwise.
1639  */
1640 int
1641 db_rename_link(char *fhpath, fhandle_t *from_dfh, char *from_name,
1642 	fhandle_t *to_dfh, char *to_name)
1643 {
1644 	int			error;
1645 	struct db_list		*dbp;
1646 	fhlist_ent		fhrec, *fhrecp;
1647 
1648 	if ((fhpath == NULL) || (from_dfh == NULL) || (from_name == NULL) ||
1649 		(to_dfh == NULL) || (to_name == NULL)) {
1650 		return (EINVAL);
1651 	}
1652 	if (from_dfh == &public_fh) {
1653 		dbp = db_get_all_databases(fhpath, FALSE);
1654 	} else {
1655 		dbp = db_get_db(fhpath, &from_dfh->fh_fsid, &error, O_CREAT);
1656 	}
1657 	for (; dbp != NULL;
1658 		dbp = ((from_dfh != &public_fh) ? NULL : dbp->next)) {
1659 		/* find existing link */
1660 		fhrecp = db_lookup_link(fhpath, from_dfh, from_name, &fhrec,
1661 				&error);
1662 		if (fhrecp == NULL) {
1663 			/* Could not find the link */
1664 			continue;
1665 		}
1666 		/* Delete the old link (if last primary record not deleted) */
1667 		error = db_delete_link(fhpath, from_dfh, from_name);
1668 		if (error == 0) {
1669 			error = db_add(fhpath, to_dfh, to_name, &fhrecp->fh,
1670 					fhrecp->flags);
1671 		}
1672 	}
1673 	return (error);
1674 }
1675 
1676 /*
1677  * db_print_all_keys: prints all keys for a given filesystem. If fsidp is
1678  * NULL, print for all filesystems covered by fhpath.
1679  */
1680 void
1681 db_print_all_keys(char *fhpath, fsid_t *fsidp, FILE *fp)
1682 {
1683 	struct db_list	*dbp;
1684 	datum		key;
1685 	int		error, len;
1686 	char		strkey[NFS_FHMAXDATA + MAXNAMELEN];
1687 	db_record	rec;
1688 	void		*ptr;
1689 
1690 	if ((fhpath == NULL) ||
1691 	    ((fsidp != NULL) && (fsidp == &public_fh.fh_fsid)))
1692 		return;
1693 	if (fsidp == NULL) {
1694 		(void) db_get_all_databases(fhpath, TRUE);
1695 		dbp = db_fs_list;
1696 	} else {
1697 		dbp = db_get_db(fhpath, fsidp, &error, 0);
1698 	}
1699 	if (dbp == NULL) {
1700 		/* Could not get or create database */
1701 		return;
1702 	}
1703 	len = strlen(fhpath);
1704 	for (; dbp != NULL; dbp = ((fsidp != NULL) ? NULL : dbp->next)) {
1705 		if (strncmp(fhpath, dbp->path, len))
1706 			continue;
1707 		(void) fprintf(fp,
1708 			"\nStart print database for fsid 0x%x 0x%x\n",
1709 			dbp->fsid.val[0], dbp->fsid.val[1]);
1710 		(void) fprintf(fp, "=============================\n");
1711 		for (key = dbm_firstkey(dbp->db); key.dptr != NULL;
1712 			key = dbm_nextkey(dbp->db)) {
1713 			(void) memcpy(strkey, key.dptr, key.dsize);
1714 			debug_print_key(fp, "", "", strkey, key.dsize);
1715 			if (debug < 2)
1716 				continue;
1717 			ptr = fetch_record(dbp, key.dptr, key.dsize,
1718 					(void *)&rec, &error, "db_prt_keys");
1719 			if (ptr == NULL)
1720 				continue;
1721 			if (key.dsize == NFS_FHMAXDATA) {
1722 				/* fhrec */
1723 				debug_print_fhlist(fp, &rec.fhlist_rec);
1724 			} else if (key.dsize > NFS_FHMAXDATA) {
1725 				/* linkinfo */
1726 				debug_print_linkinfo(fp, &rec.link_rec);
1727 			}
1728 			(void) fprintf(fp, "-----------------------------\n");
1729 		}
1730 		(void) fprintf(fp, "End print database for fsid 0x%x 0x%x\n",
1731 			dbp->fsid.val[0], dbp->fsid.val[1]);
1732 	}
1733 }
1734 
1735 void
1736 debug_opaque_print(FILE *fp, void *buf, int size)
1737 {
1738 	int		bufoffset = 0;
1739 	char		debug_str[200];
1740 
1741 	if ((buf == NULL) || (size <= 0))
1742 		return;
1743 
1744 	nfslog_opaque_print_buf(buf, size, debug_str, &bufoffset, 200);
1745 	(void) fprintf(fp, debug_str);
1746 }
1747 
1748 /*
1749  * links_timedout() takes a primary records and searches all of its
1750  * links to see if they all have access times that are older than
1751  * the 'prune_timeout' value.  TRUE if all links are old and FALSE
1752  * if there is just one link that has an access time which is recent.
1753  */
1754 static int
1755 links_timedout(struct db_list *pdb, fhlist_ent *pfe, time_t ts)
1756 {
1757 	fh_secondary_key	linkkey;
1758 	linkinfo_ent		*linkp, link_st;
1759 	int			error;
1760 	int			linksize;
1761 	void			*cookie;
1762 
1763 	/* Get the link record */
1764 	linksize = fill_link_key(linkkey, &pfe->dfh, pfe->name);
1765 	cookie = NULL;
1766 	do {
1767 		linkp = get_next_link(pdb, linkkey, &linksize, &link_st,
1768 				&cookie, &error, "links_timedout");
1769 		if ((linkp != NULL) &&
1770 			(difftime(ts, linkp->atime) <= prune_timeout)) {
1771 			/* update primary record to have an uptodate time */
1772 			pfe = fetch_record(pdb, (void *)&pfe->fh.fh_data,
1773 					pfe->fh.fh_len, NULL, &error,
1774 					"links_timedout");
1775 			if (pfe == NULL) {
1776 				syslog(LOG_ERR, gettext(
1777 				"links_timedout: fetch fhrec error %s\n"),
1778 				strerror(error));
1779 			} else {
1780 				if (difftime(pfe->atime, linkp->atime) < 0) {
1781 					/* update fhrec atime */
1782 					pfe->atime = linkp->atime;
1783 					(void) store_record(pdb,
1784 						(void *)&pfe->fh.fh_data,
1785 						pfe->fh.fh_len, pfe,
1786 						pfe->reclen, "links_timedout");
1787 				}
1788 				free(pfe);
1789 			}
1790 			free_link_cookies(cookie);
1791 			return (FALSE);
1792 		}
1793 	} while (linksize > 0);
1794 
1795 	free_link_cookies(cookie);
1796 	return (TRUE);
1797 }
1798 
1799 /*
1800  * prune_dbs() will search all of the open databases looking for records
1801  * that have not been accessed in the last 'prune_timeout' seconds.
1802  * This search is done on the primary records and a list of potential
1803  * timeout candidates is built.  The reason for doing this is to not
1804  * disturb the underlying dbm_firstkey()/dbm_nextkey() sequence; we
1805  * want to search all of the records in the database.
1806  * Once we have our candidate list built, we examine each of those
1807  * item's links to check if the links have been accessed within the
1808  * 'prune_timeout' seconds.  If neither the primary nor any its links
1809  * have been accessed, then all of those records are removed/deleted
1810  * from the database.
1811  */
1812 int
1813 prune_dbs(char *fhpath)
1814 {
1815 	struct db_list		*pdb;
1816 	datum			key;
1817 	db_record		*ptr;
1818 	struct fhlist_ent 	*pfe;
1819 	int			error, linkerr, linksize;
1820 	time_t			cur_time = time(0);
1821 	fh_secondary_key	linkkey;
1822 	struct thelist {
1823 		struct thelist *next;
1824 		db_record *ptr;
1825 	} 			thelist, *ptl;
1826 	int	cnt = 0;
1827 
1828 	if (fhpath != NULL)
1829 		(void) db_get_all_databases(fhpath, TRUE);
1830 
1831 	thelist.next = NULL;
1832 	/*
1833 	 * Search each of the open databases
1834 	 */
1835 	for (pdb = db_fs_list; pdb; pdb = pdb->next) {
1836 	    do {
1837 		/* Check each record in the database */
1838 		for (key = dbm_firstkey(pdb->db); key.dptr != NULL;
1839 		    key = dbm_nextkey(pdb->db)) {
1840 			/* We're only interested in primary records */
1841 			if (key.dsize != NFS_FHMAXDATA)
1842 				continue;	/* probably a link record */
1843 			ptr = fetch_record(pdb, key.dptr, key.dsize,
1844 					NULL, &error, "dump_db");
1845 			if (ptr == NULL)
1846 				continue;
1847 			/*
1848 			 * If this record is a primary record and it is
1849 			 * not an export point or a public file handle path,
1850 			 * check it for a ancient access time.
1851 			 */
1852 			if ((ptr->fhlist_rec.flags &
1853 				    (EXPORT_POINT | PUBLIC_PATH)) ||
1854 			    (difftime(cur_time, ptr->fhlist_rec.atime) <=
1855 					prune_timeout)) {
1856 				/* Keep this record in the database */
1857 				free(ptr);
1858 			} else {
1859 				/* Found one?  Save off info about it */
1860 				ptl = malloc(sizeof (struct thelist));
1861 				if (ptl == NULL) {
1862 					syslog(LOG_ERR, gettext(
1863 				"prune_dbs: malloc failed, error %s\n"),
1864 						strerror(errno));
1865 					break;
1866 				}
1867 				ptl->ptr = ptr;
1868 				ptl->next = thelist.next;
1869 				thelist.next = ptl;
1870 				cnt++;	/* count how many records allocated */
1871 				if (cnt > MAX_PRUNE_REC_CNT) {
1872 					/* Limit number of records malloc'd */
1873 					if (debug)
1874 						(void) fprintf(stderr,
1875 				"prune_dbs: halt search - too many records\n");
1876 					break;
1877 				}
1878 			}
1879 		}
1880 
1881 		/*
1882 		 * Take the saved records and check their links to make
1883 		 * sure that they have not been accessed as well.
1884 		 */
1885 		for (ptl = thelist.next; ptl; ptl = thelist.next) {
1886 			thelist.next = ptl->next;
1887 			/* Everything timed out? */
1888 			pfe = &(ptl->ptr->fhlist_rec);
1889 			if (links_timedout(pdb,	pfe, cur_time)) {
1890 
1891 				/*
1892 				 * Iterate until we run out of links.
1893 				 * We have to do this since there can be
1894 				 * multiple links to a primary record and
1895 				 * we need to delete one at a time.
1896 				 */
1897 				/* Delete the link and get the next */
1898 				linkerr = delete_link(pdb,
1899 						&pfe->dfh, pfe->name, linkkey,
1900 						&linksize, &error, "dump_db");
1901 				while ((linksize > 0) && !(error || linkerr)) {
1902 					/* Delete the link and get the next */
1903 					linkerr = delete_link_by_key(pdb,
1904 						linkkey, &linksize,
1905 						&error, "dump_db");
1906 					if (error || linkerr) {
1907 						break;
1908 					}
1909 				}
1910 				if (linkerr) {
1911 					/* link not in database, primary is */
1912 					/* Should never happen */
1913 					if (debug > 1) {
1914 						(void) fprintf(stderr,
1915 					"prune_dbs: Error primary exists ");
1916 						debug_opaque_print(stderr,
1917 							(void *)&pfe->fh,
1918 							sizeof (pfe->fh));
1919 						(void) fprintf(stderr, "\n");
1920 					}
1921 					if (debug)
1922 						syslog(LOG_ERR, gettext(
1923 					"prune_dbs: Error primary exists\n"));
1924 					(void) delete_record(pdb,
1925 					&pfe->fh.fh_data, pfe->fh.fh_len,
1926 					"prune_dbs: fh delete");
1927 				}
1928 			}
1929 			/* Make sure to free the pointers used in the list */
1930 			free(ptl->ptr);
1931 			free(ptl);
1932 			cnt--;
1933 		}
1934 		thelist.next = NULL;
1935 	    } while (key.dptr != NULL);
1936 	}
1937 	return (0);
1938 }
1939