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