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