dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fs.d / nfs / nfslog / dbtab.c
blobf6842cbf37bf0229d439d7c4e9de311088280f69
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Code to maintain the runtime and on-disk filehandle mapping table for
28 * nfslog.
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"
51 #define ROUNDUP32(val) (((val) + 3) & ~3)
54 * It is important that this string not match the length of the
55 * file handle key length NFS_FHMAXDATA
57 #define DB_VERSION_STRING "NFSLOG_DB_VERSION"
58 #define DB_VERSION "1"
60 #define MAX_PRUNE_REC_CNT 100000
62 fhandle_t public_fh = { 0 };
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 */
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;
79 extern int debug;
80 extern time_t mapping_update_interval;
81 extern time_t prune_timeout;
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);
133 * The following functions do the actual database I/O. Currently use DBM.
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:
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).
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.
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.
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.
177 * Renaming a file: Add the new link and then delete the old one.
179 * Lookup by file handle (read, write, lookup, etc.) - fetch primary rec.
180 * Lookup by dir info (delete, link, rename) - fetch secondary rec.
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).
193 * Create a link key given directory fh and name
195 static int
196 fill_link_key(char *linkkey, fhandle_t *dfh, char *name)
198 int linksize, linksize32;
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);
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.
214 static struct db_list *
215 db_get_db(char *fhpath, fsid_t *fsid, int *errorp, int create_flag)
217 struct db_list *p, *newp;
218 char fsidstr[30];
219 datum key, data;
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);
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);
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;
246 (void) sprintf(newp->path, "%s.%s", fhpath, fsidstr);
248 * The open mode is masked by UMASK.
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;
261 * Add the version identifier (have to check first in the
262 * case the db exists)
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);
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);
279 return (newp);
281 err_exit:
282 if (newp != NULL) {
283 if (newp->db != NULL) {
284 dbm_close(newp->db);
286 if (newp->path != NULL) {
287 free(newp->path);
289 free(newp);
291 return (NULL);
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.
302 static struct db_list *
303 db_get_all_databases(char *fhpath, bool_t getall)
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;
312 for (dbp = db_fs_list; dbp != NULL; dbp = dbp->next) {
313 if (strncmp(fhpath, dbp->path, strlen(fhpath)) == 0)
314 break;
316 if (dbp != NULL) {
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
322 if (!getall || dbp->getall)
323 return (dbp);
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);
331 fhpathname = NULL;
332 ret_dbp = NULL;
333 if ((dirptr = strrchr(fhdir, '/')) == NULL) {
334 /* no directory */
335 goto exit;
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;
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);
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;
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;
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;
374 (void) closedir(dirp);
375 exit:
376 free(fhpathname);
377 free(fhdir);
378 return (ret_dbp);
381 static void
382 debug_print_key(FILE *fp, char *str1, char *str2, char *key, int ksize)
384 (void) fprintf(fp, "%s: %s key (%d) ", str1, str2, ksize);
385 debug_opaque_print(fp, key, ksize);
386 /* may be inode,name - try to print the fields */
387 if (ksize >= NFS_FHMAXDATA) {
388 (void) fprintf(fp, ": inode ");
389 debug_opaque_print(fp, &key[2], sizeof (int));
390 (void) fprintf(fp, ", gen ");
391 debug_opaque_print(fp, &key[2 + sizeof (int)], sizeof (int));
392 if (ksize > NFS_FHMAXDATA) {
393 (void) fprintf(fp, ", name '%s'", &key[NFS_FHMAXDATA]);
396 (void) fprintf(fp, "\n");
399 static void
400 debug_print_linkinfo(FILE *fp, linkinfo_ent *linkp)
402 if (linkp == NULL)
403 return;
404 (void) fprintf(fp, "linkinfo:\ndfh: ");
405 debug_opaque_print(fp, (void *)&linkp->dfh, sizeof (linkp->dfh));
406 (void) fprintf(fp, "\nname: '%s'", LN_NAME(linkp));
407 (void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n",
408 linkp->mtime, linkp->atime, linkp->flags, linkp->reclen);
409 (void) fprintf(fp, "offsets: fhkey %d, name %d, next %d, prev %d\n",
410 linkp->fhkey_offset, linkp->name_offset, linkp->next_offset,
411 linkp->prev_offset);
412 debug_print_key(fp, "fhkey", "", LN_FHKEY(linkp), LN_FHKEY_LEN(linkp));
413 debug_print_key(fp, "next", "", LN_NEXT(linkp), LN_NEXT_LEN(linkp));
414 debug_print_key(fp, "prev", "", LN_PREV(linkp), LN_PREV_LEN(linkp));
417 static void
418 debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp)
420 if (fhrecp == NULL)
421 return;
422 (void) fprintf(fp, "fhrec:\nfh: ");
423 debug_opaque_print(fp, (void *)&fhrecp->fh, sizeof (fhrecp->fh));
424 (void) fprintf(fp, "name '%s', dfh: ", fhrecp->name);
425 debug_opaque_print(fp, (void *)&fhrecp->dfh, sizeof (fhrecp->dfh));
426 (void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n",
427 fhrecp->mtime, fhrecp->atime, fhrecp->flags, fhrecp->reclen);
430 static void
431 debug_print_key_and_data(FILE *fp, char *str1, char *str2, char *key,
432 int ksize, char *data, int dsize)
434 debug_print_key(fp, str1, str2, key, ksize);
435 (void) fprintf(fp, " ==> (%p,%d)\n", (void *)data, dsize);
436 if (ksize > NFS_FHMAXDATA) {
437 linkinfo_ent inf;
438 /* probably a link struct */
439 (void) memcpy(&inf, data, sizeof (linkinfo_ent));
440 debug_print_linkinfo(fp, &inf);
441 } else if (ksize == NFS_FHMAXDATA) {
442 fhlist_ent inf;
443 /* probably an fhlist struct */
444 (void) memcpy(&inf, data, sizeof (linkinfo_ent));
445 debug_print_fhlist(fp, &inf);
446 } else {
447 /* don't know... */
448 debug_opaque_print(fp, data, dsize);
453 * store_record - store the record in the database and return 0 for success
454 * or error code otherwise.
456 static int
457 store_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr,
458 int datasize, char *str)
460 datum key, data;
461 int error;
462 char *errfmt = "store_record: dbm_store failed, Error: %s\n";
463 char *err;
465 errno = 0;
466 key.dptr = keyaddr;
467 key.dsize = keysize;
468 data.dptr = dataaddr;
469 data.dsize = datasize;
471 if (debug > 2) {
472 debug_print_key_and_data(stdout, str, "dbm_store:\n ",
473 key.dptr, key.dsize, data.dptr, data.dsize);
475 if (dbm_store(dbp->db, key, data, DBM_REPLACE) < 0) {
476 /* Could not store */
477 error = dbm_error(dbp->db);
478 dbm_clearerr(dbp->db);
480 if (error) {
481 if (errno)
482 err = strerror(errno);
483 else {
484 err = err_str;
485 errno = EIO;
487 } else { /* should not happen but sometimes does */
488 err = err_str;
489 errno = -1;
491 if (debug) {
492 debug_print_key(stderr, str, "store_record:"
493 "dbm_store:\n", key.dptr, key.dsize);
494 (void) fprintf(stderr, errfmt, err);
495 } else
496 syslog(LOG_ERR, gettext(errfmt), err);
497 return (errno);
499 return (0);
503 * fetch_record - fetch the record from the database and return 0 for success
504 * and errno for failure.
505 * dataaddr is an optional valid address for the result. If dataaddr
506 * is non-null, then that memory is already alloc'd. Else, alloc it, and
507 * the caller must free the returned struct when done.
509 static void *
510 fetch_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr,
511 int *errorp, char *str)
513 datum key, data;
514 char *errfmt = "fetch_record: dbm_fetch failed, Error: %s\n";
515 char *err;
517 errno = 0;
518 *errorp = 0;
519 key.dptr = keyaddr;
520 key.dsize = keysize;
522 data = dbm_fetch(dbp->db, key);
523 if (data.dptr == NULL) {
524 /* see if there is a database error */
525 if (dbm_error(dbp->db)) {
526 /* clear and report the database error */
527 dbm_clearerr(dbp->db);
528 *errorp = EIO;
529 err = strerror(*errorp);
530 syslog(LOG_ERR, gettext(errfmt), err);
531 } else {
532 /* primary record not in database */
533 *errorp = ENOENT;
535 if (debug > 3) {
536 err = strerror(*errorp);
537 debug_print_key(stderr, str, "fetch_record:"
538 "dbm_fetch:\n", key.dptr, key.dsize);
539 (void) fprintf(stderr, errfmt, err);
541 return (NULL);
544 /* copy to local struct because dbm may return non-aligned pointers */
545 if ((dataaddr == NULL) &&
546 ((dataaddr = malloc(data.dsize)) == NULL)) {
547 *errorp = errno;
548 syslog(LOG_ERR, gettext(
549 "%s: dbm_fetch - malloc %ld: Error %s"),
550 str, data.dsize, strerror(*errorp));
551 return (NULL);
553 (void) memcpy(dataaddr, data.dptr, data.dsize);
554 if (debug > 3) {
555 debug_print_key_and_data(stdout, str, "fetch_record:"
556 "dbm_fetch:\n", key.dptr, key.dsize,
557 dataaddr, data.dsize);
559 *errorp = 0;
560 return (dataaddr);
564 * delete_record - delete the record from the database and return 0 for success
565 * or error code for failure.
567 static int
568 delete_record(struct db_list *dbp, void *keyaddr, int keysize, char *str)
570 datum key;
571 int error = 0;
572 char *errfmt = "delete_record: dbm_delete failed, Error: %s\n";
573 char *err;
575 errno = 0;
576 key.dptr = keyaddr;
577 key.dsize = keysize;
579 if (debug > 2) {
580 debug_print_key(stdout, str, "delete_record:"
581 "dbm_delete:\n", key.dptr, key.dsize);
583 if (dbm_delete(dbp->db, key) < 0) {
584 error = dbm_error(dbp->db);
585 dbm_clearerr(dbp->db);
587 if (error) {
588 if (errno)
589 err = strerror(errno);
590 else {
591 err = err_str;
592 errno = EIO;
594 } else { /* should not happen but sometimes does */
595 err = err_str;
596 errno = -1;
598 if (debug) {
599 debug_print_key(stderr, str, "delete_record:"
600 "dbm_delete:\n", key.dptr, key.dsize);
601 (void) fprintf(stderr, errfmt, err);
602 } else
603 syslog(LOG_ERR, gettext(errfmt), err);
605 return (errno);
609 * db_update_fhrec - puts fhrec in db with updated atime if more than
610 * mapping_update_interval seconds passed. Return 0 if success, error otherwise.
612 static int
613 db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize,
614 fhlist_ent *fhrecp, char *str)
616 time_t cur_time = time(0);
618 if (difftime(cur_time, fhrecp->atime) >= mapping_update_interval) {
619 fhrecp->atime = cur_time;
620 return (store_record(dbp, keyaddr, keysize,
621 fhrecp, fhrecp->reclen, str));
623 return (0);
627 * db_update_linkinfo - puts linkinfo in db with updated atime if more than
628 * mapping_update_interval seconds passed. Return 0 if success, error otherwise.
630 static int
631 db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize,
632 linkinfo_ent *linkp, char *str)
634 time_t cur_time = time(0);
636 if (difftime(cur_time, linkp->atime) >= mapping_update_interval) {
637 linkp->atime = cur_time;
638 return (store_record(dbp, keyaddr, keysize,
639 linkp, linkp->reclen, str));
641 return (0);
645 * create_primary_struct - add primary record to the database.
646 * Database must be open when this function is called.
647 * If success, return the added database entry. fhrecp may be used to
648 * provide an existing memory area, else malloc it. If failed, *errorp
649 * contains the error code and return NULL.
651 static fhlist_ent *
652 create_primary_struct(struct db_list *dbp, fhandle_t *dfh, char *name,
653 fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp)
655 int reclen, reclen1;
656 fhlist_ent *new_fhrecp = fhrecp;
658 reclen1 = offsetof(fhlist_ent, name) + strlen(name) + 1;
659 reclen = ROUNDUP32(reclen1);
660 if (fhrecp == NULL) { /* allocated the memory */
661 if ((new_fhrecp = malloc(reclen)) == NULL) {
662 *errorp = errno;
663 syslog(LOG_ERR, gettext(
664 "create_primary_struct: malloc %d Error %s"),
665 reclen, strerror(*errorp));
666 return (NULL);
669 /* Fill in the fields */
670 (void) memcpy(&new_fhrecp->fh, fh, sizeof (*fh));
671 (void) memcpy(&new_fhrecp->dfh, dfh, sizeof (*dfh));
672 new_fhrecp->flags = flags;
673 if (dfh == &public_fh)
674 new_fhrecp->flags |= PUBLIC_PATH;
675 else
676 new_fhrecp->flags &= ~PUBLIC_PATH;
677 new_fhrecp->mtime = time(0);
678 new_fhrecp->atime = new_fhrecp->mtime;
679 (void) strcpy(new_fhrecp->name, name);
680 if (reclen1 < reclen) {
681 bzero((char *)((uintptr_t)new_fhrecp + reclen1),
682 reclen - reclen1);
684 new_fhrecp->reclen = reclen;
685 *errorp = store_record(dbp, &fh->fh_data, fh->fh_len, new_fhrecp,
686 new_fhrecp->reclen, "create_primary_struct");
687 if (*errorp != 0) {
688 /* Could not store */
689 if (fhrecp == NULL) /* caller did not supply pointer */
690 free(new_fhrecp);
691 return (NULL);
693 return (new_fhrecp);
697 * db_add_primary - add primary record to the database.
698 * If record already in and live, return it (even if for a different link).
699 * If in database but marked deleted, replace it. If not in database, add it.
700 * Database must be open when this function is called.
701 * If success, return the added database entry. fhrecp may be used to
702 * provide an existing memory area, else malloc it. If failed, *errorp
703 * contains the error code and return NULL.
705 static fhlist_ent *
706 db_add_primary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh,
707 uint_t flags, fhlist_ent *fhrecp, int *errorp)
709 fhlist_ent *new_fhrecp;
710 fh_primary_key fhkey;
712 if (debug > 2)
713 (void) printf("db_add_primary entered: name '%s'\n", name);
715 bcopy(&fh->fh_data, fhkey, fh->fh_len);
716 new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, (void *)fhrecp,
717 errorp, "db_add_primary");
718 if (new_fhrecp != NULL) {
719 /* primary record is in the database */
720 /* Update atime if needed */
721 *errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp,
722 "db_add_primary put fhrec");
723 if (debug > 2)
724 (void) printf("db_add_primary exits(2): name '%s'\n",
725 name);
726 return (new_fhrecp);
728 /* primary record not in database - create it */
729 new_fhrecp = create_primary_struct(dbp, dfh, name, fh, flags,
730 fhrecp, errorp);
731 if (new_fhrecp == NULL) {
732 /* Could not store */
733 if (debug > 2)
734 (void) printf(
735 "db_add_primary exits(1): name '%s' Error %s\n",
736 name, ((*errorp >= 0) ? strerror(*errorp) :
737 "Unknown"));
739 return (NULL);
741 if (debug > 2)
742 (void) printf("db_add_primary exits(0): name '%s'\n", name);
743 return (new_fhrecp);
747 * get_next_link - get and check the next link in the chain.
748 * Re-use space if linkp param non-null. Also set *linkkey and *linksizep
749 * to values for next link (*linksizep set to 0 if last link).
750 * cookie is used to detect corrupted link entries XXXXXXX
751 * Return the link pointer or NULL if none.
753 static linkinfo_ent *
754 get_next_link(struct db_list *dbp, char *linkkey, int *linksizep,
755 linkinfo_ent *linkp, void **cookiep, int *errorp, char *msg)
757 int linksize, nextsize;
758 char *nextkey;
759 linkinfo_ent *new_linkp = linkp;
760 struct link_keys *lnp;
762 linksize = *linksizep;
763 if (linksize == 0)
764 return (NULL);
765 *linksizep = 0;
766 new_linkp = fetch_record(dbp, linkkey, linksize, (void *)linkp,
767 errorp, msg);
768 if (new_linkp == NULL)
769 return (NULL);
771 /* Set linkkey to point to next record */
772 nextsize = LN_NEXT_LEN(new_linkp);
773 if (nextsize == 0)
774 return (new_linkp);
776 /* Add this key to the cookie list */
777 if ((lnp = malloc(sizeof (struct link_keys))) == NULL) {
778 syslog(LOG_ERR, gettext("get_next_key: malloc error %s\n"),
779 strerror(errno));
780 if ((new_linkp != NULL) && (linkp == NULL))
781 free(new_linkp);
782 return (NULL);
784 (void) memcpy(lnp->lnkey, linkkey, linksize);
785 lnp->lnsize = linksize;
786 lnp->next = *(struct link_keys **)cookiep;
787 *cookiep = (void *)lnp;
789 /* Make sure record does not point to itself or other internal loops */
790 nextkey = LN_NEXT(new_linkp);
791 for (; lnp != NULL; lnp = lnp->next) {
792 if ((nextsize == lnp->lnsize) && (memcmp(
793 lnp->lnkey, nextkey, nextsize) == 0)) {
796 * XXX This entry's next pointer points to
797 * itself. This is only a work-around, remove
798 * this check once bug 4203186 is fixed.
800 if (debug) {
801 (void) fprintf(stderr,
802 "%s: get_next_link: last record invalid.\n",
803 msg);
804 debug_print_key_and_data(stderr, msg,
805 "invalid rec:\n ", linkkey, linksize,
806 (char *)new_linkp, new_linkp->reclen);
808 /* Return as if this is the last link */
809 return (new_linkp);
812 (void) memcpy(linkkey, nextkey, nextsize);
813 *linksizep = nextsize;
814 return (new_linkp);
818 * free_link_cookies - free the cookie list
820 static void
821 free_link_cookies(void *cookie)
823 struct link_keys *dellnp, *lnp;
825 lnp = (struct link_keys *)cookie;
826 while (lnp != NULL) {
827 dellnp = lnp;
828 lnp = lnp->next;
829 free(dellnp);
834 * add_mc_path - add a mc link to a file that has other links. Add it at end
835 * of linked list. Called when it's known there are other links.
837 static void
838 add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name,
839 fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp)
841 fh_secondary_key linkkey;
842 int linksize, len;
843 linkinfo_ent lastlink, *lastlinkp;
844 void *cookie;
846 linksize = fill_link_key(linkkey, &fhrecp->dfh, fhrecp->name);
847 cookie = NULL;
848 do {
849 lastlinkp = get_next_link(dbp, linkkey, &linksize, &lastlink,
850 &cookie, errorp, "add_mc_path");
851 } while (linksize > 0);
852 free_link_cookies(cookie);
853 /* reached end of list */
854 if (lastlinkp == NULL) {
855 /* nothing to do */
856 if (debug > 1) {
857 (void) fprintf(stderr, "add_mc_path link is null\n");
859 return;
861 /* Add new link after last link */
863 * next - link key for the next in the list - add at end so null.
864 * prev - link key for the previous link in the list.
866 linkp->prev_offset = linkp->next_offset; /* aligned */
867 linksize = fill_link_key(LN_PREV(linkp), &lastlinkp->dfh,
868 LN_NAME(lastlinkp));
869 linkp->reclen = linkp->prev_offset + linksize; /* aligned */
871 /* Add the link information to the database */
872 linksize = fill_link_key(linkkey, dfh, name);
873 *errorp = store_record(dbp, linkkey, linksize,
874 linkp, linkp->reclen, "add_mc_path");
875 if (*errorp != 0)
876 return;
878 /* Now update previous last link to point forward to new link */
879 /* Copy prev link out since it's going to be overwritten */
880 linksize = LN_PREV_LEN(lastlinkp);
881 (void) memcpy(linkkey, LN_PREV(lastlinkp), linksize);
882 /* Update previous last link to point to new one */
883 len = fill_link_key(LN_NEXT(lastlinkp), dfh, name);
884 lastlinkp->prev_offset = lastlinkp->next_offset + len; /* aligned */
885 (void) memcpy(LN_PREV(lastlinkp), linkkey, linksize);
886 lastlinkp->reclen = lastlinkp->prev_offset + linksize;
887 /* Update the link information to the database */
888 linksize = fill_link_key(linkkey, &lastlinkp->dfh, LN_NAME(lastlinkp));
889 *errorp = store_record(dbp, linkkey, linksize,
890 lastlinkp, lastlinkp->reclen, "add_mc_path prev");
894 * create_link_struct - create the secondary struct.
895 * (dfh,name) is the secondary key, fhrec is the primary record for the file
896 * and linkpp is a place holder for the record (could be null).
897 * Insert the record to the database.
898 * Return 0 if success, error otherwise.
900 static linkinfo_ent *
901 create_link_struct(struct db_list *dbp, fhandle_t *dfh, char *name,
902 fhlist_ent *fhrecp, int *errorp)
904 fh_secondary_key linkkey;
905 int len, linksize;
906 linkinfo_ent *linkp;
908 if ((linkp = malloc(sizeof (linkinfo_ent))) == NULL) {
909 *errorp = errno;
910 syslog(LOG_ERR, gettext(
911 "create_link_struct: malloc failed: Error %s"),
912 strerror(*errorp));
913 return (NULL);
915 if (dfh == &public_fh)
916 linkp->flags |= PUBLIC_PATH;
917 else
918 linkp->flags &= ~PUBLIC_PATH;
919 (void) memcpy(&linkp->dfh, dfh, sizeof (*dfh));
920 linkp->mtime = time(0);
921 linkp->atime = linkp->mtime;
922 /* Calculate offsets of variable fields */
923 /* fhkey - primary key (inode/gen) */
924 /* name - component name (in directory dfh) */
925 linkp->fhkey_offset = ROUNDUP32(offsetof(linkinfo_ent, varbuf));
926 len = fill_link_key(LN_FHKEY(linkp), &fhrecp->fh, name);
927 linkp->name_offset = linkp->fhkey_offset + fhrecp->fh.fh_len;
928 linkp->next_offset = linkp->fhkey_offset + len; /* aligned */
930 * next - link key for the next link in the list - NULL if it's
931 * the first link. If this is the public fs, only one link allowed.
932 * Avoid setting a multi-component path as primary path,
933 * unless no choice.
935 len = 0;
936 if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) ||
937 strcmp(fhrecp->name, name)) {
938 /* different link than the one that's in the record */
939 if (dfh == &public_fh) {
940 /* parent is public fh - either multi-comp or root */
941 if (memcmp(&fhrecp->fh, &public_fh,
942 sizeof (public_fh))) {
943 /* multi-comp path */
944 add_mc_path(dbp, dfh, name, fhrecp, linkp,
945 errorp);
946 if (*errorp != 0) {
947 free(linkp);
948 return (NULL);
950 return (linkp);
952 } else {
953 /* new link to a file with a different one already */
954 len = fill_link_key(LN_NEXT(linkp), &fhrecp->dfh,
955 fhrecp->name);
959 * prev - link key for the previous link in the list - since we
960 * always insert at the front of the list, it's always initially NULL.
962 linkp->prev_offset = linkp->next_offset + len; /* aligned */
963 linkp->reclen = linkp->prev_offset;
965 /* Add the link information to the database */
966 linksize = fill_link_key(linkkey, dfh, name);
967 *errorp = store_record(dbp, linkkey, linksize, linkp, linkp->reclen,
968 "create_link_struct");
969 if (*errorp != 0) {
970 free(linkp);
971 return (NULL);
973 return (linkp);
977 * db_add_secondary - add secondary record to the database (for the directory
978 * information).
979 * Assumes this is a new link, not yet in the database, and that the primary
980 * record is already in.
981 * If fhrecp is non-null, then fhrecp is the primary record.
982 * Database must be open when this function is called.
983 * Return 0 if success, error code otherwise.
985 static int
986 db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name,
987 fhandle_t *fh, fhlist_ent *fhrecp)
989 int nextsize, len, error;
990 linkinfo_ent nextlink, *newlinkp, *nextlinkp;
991 uint_t fhflags;
992 char *nextaddr;
993 fhlist_ent *new_fhrecp = fhrecp;
994 fh_primary_key fhkey;
996 if (debug > 2)
997 (void) printf("db_add_secondary entered: name '%s'\n", name);
999 bcopy(&fh->fh_data, fhkey, fh->fh_len);
1000 if (fhrecp == NULL) {
1001 /* Fetch the primary record */
1002 new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, NULL,
1003 &error, "db_add_secondary primary");
1004 if (new_fhrecp == NULL) {
1005 return (error);
1008 /* Update fhrec atime if needed */
1009 error = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp,
1010 "db_add_secondary primary");
1011 fhflags = new_fhrecp->flags;
1012 /* now create and insert the secondary record */
1013 newlinkp = create_link_struct(dbp, dfh, name, new_fhrecp, &error);
1014 if (fhrecp == NULL) {
1015 free(new_fhrecp);
1016 new_fhrecp = NULL;
1018 if (newlinkp == NULL) {
1019 if (debug > 2)
1020 (void) printf("create_link_struct '%s' Error %s\n",
1021 name, ((error >= 0) ? strerror(error) :
1022 "Unknown"));
1023 return (error);
1025 nextsize = LN_NEXT_LEN(newlinkp);
1026 if (nextsize == 0) {
1027 /* No next - can exit now */
1028 if (debug > 2)
1029 (void) printf("db_add_secondary: no next link\n");
1030 free(newlinkp);
1031 return (0);
1035 * Update the linked list to point to new head: replace head of
1036 * list in the primary record, then update previous secondary record
1037 * to point to new head
1039 new_fhrecp = create_primary_struct(dbp, dfh, name, fh, fhflags,
1040 new_fhrecp, &error);
1041 if (new_fhrecp == NULL) {
1042 if (debug > 2)
1043 (void) printf(
1044 "db_add_secondary: replace primary failed\n");
1045 free(newlinkp);
1046 return (error);
1047 } else if (fhrecp == NULL) {
1048 free(new_fhrecp);
1052 * newlink is the new head of the list, with its "next" pointing to
1053 * the old head, and its "prev" pointing to NULL. We now need to
1054 * modify the "next" entry to have its "prev" point to the new entry.
1056 nextaddr = LN_NEXT(newlinkp);
1057 if (debug > 2) {
1058 debug_print_key(stderr, "db_add_secondary", "next key\n ",
1059 nextaddr, nextsize);
1061 /* Get the next link entry from the database */
1062 nextlinkp = fetch_record(dbp, nextaddr, nextsize, (void *)&nextlink,
1063 &error, "db_add_secondary next link");
1064 if (nextlinkp == NULL) {
1065 if (debug > 2)
1066 (void) printf(
1067 "db_add_secondary: fetch next link failed\n");
1068 free(newlinkp);
1069 return (error);
1073 * since the "prev" field is the only field to be changed, and it's
1074 * the last in the link record, we only need to modify it (and reclen).
1075 * Re-use link to update the next record.
1077 len = fill_link_key(LN_PREV(nextlinkp), dfh, name);
1078 nextlinkp->reclen = nextlinkp->prev_offset + len;
1079 error = store_record(dbp, nextaddr, nextsize, nextlinkp,
1080 nextlinkp->reclen, "db_add_secondary");
1081 if (debug > 2)
1082 (void) printf(
1083 "db_add_secondary exits(%d): name '%s'\n", error, name);
1084 free(newlinkp);
1085 return (error);
1089 * Update the next link to point to the new prev.
1090 * Return 0 for success, error code otherwise.
1091 * If successful, and nextlinkpp is non-null,
1092 * *nextlinkpp contains the record for the next link, since
1093 * we may will it if the primary record should be updated.
1095 static linkinfo_ent *
1096 update_next_link(struct db_list *dbp, char *nextkey, int nextsize,
1097 char *prevkey, int prevsize, int *errorp)
1099 linkinfo_ent *nextlinkp, *linkp1;
1101 if ((nextlinkp = malloc(sizeof (linkinfo_ent))) == NULL) {
1102 *errorp = errno;
1103 syslog(LOG_ERR, gettext(
1104 "update_next_link: malloc next Error %s"),
1105 strerror(*errorp));
1106 return (NULL);
1108 linkp1 = nextlinkp;
1109 nextlinkp = fetch_record(dbp, nextkey, nextsize, nextlinkp,
1110 errorp, "update next");
1111 /* if there is no next record - ok */
1112 if (nextlinkp == NULL) {
1113 /* Return no error */
1114 *errorp = 0;
1115 free(linkp1);
1116 return (NULL);
1118 /* Set its prev to the prev of the deleted record */
1119 nextlinkp->reclen = ROUNDUP32(nextlinkp->reclen -
1120 LN_PREV_LEN(nextlinkp) + prevsize);
1121 /* Change the len and set prev */
1122 if (prevsize > 0) {
1123 (void) memcpy(LN_PREV(nextlinkp), prevkey, prevsize);
1125 /* No other changes needed because prev is last field */
1126 *errorp = store_record(dbp, nextkey, nextsize, nextlinkp,
1127 nextlinkp->reclen, "update_next");
1128 if (*errorp != 0) {
1129 free(nextlinkp);
1130 nextlinkp = NULL;
1132 return (nextlinkp);
1136 * Update the prev link to point to the new next.
1137 * Return 0 for success, error code otherwise.
1139 static int
1140 update_prev_link(struct db_list *dbp, char *nextkey, int nextsize,
1141 char *prevkey, int prevsize)
1143 linkinfo_ent prevlink, *prevlinkp;
1144 int diff, error;
1146 /* Update its next to the given one */
1147 prevlinkp = fetch_record(dbp, prevkey, prevsize, &prevlink, &error,
1148 "update prev");
1149 /* if error there is no next record - ok */
1150 if (prevlinkp == NULL) {
1151 return (0);
1153 diff = nextsize - LN_NEXT_LEN(prevlinkp);
1154 prevlinkp->reclen = ROUNDUP32(prevlinkp->reclen + diff);
1155 /* Change the len and set next - may push prev */
1156 if (diff != 0) {
1157 char *ptr = LN_PREV(prevlinkp);
1159 prevlinkp->prev_offset += diff;
1160 (void) memcpy(LN_PREV(prevlinkp), ptr, LN_PREV_LEN(prevlinkp));
1162 if (nextsize > 0) {
1163 (void) memcpy(LN_NEXT(prevlinkp), nextkey, nextsize);
1165 /* Store updated record */
1166 error = store_record(dbp, prevkey, prevsize, prevlinkp,
1167 prevlinkp->reclen, "update_prev");
1168 return (error);
1172 * update_linked_list - update the next link to point back to prev, and vice
1173 * versa. Normally called by delete_link to drop the deleted link from the
1174 * linked list of hard links for the file. next and prev are the keys of next
1175 * and previous links for the deleted link in the list (could be NULL).
1176 * Return 0 for success, error code otherwise.
1177 * If successful, and nextlinkpp is non-null,
1178 * return the record for the next link, since
1179 * if the primary record should be updated we'll need it. In this case,
1180 * actually allocate the space for it because we can't tell otherwise.
1182 static linkinfo_ent *
1183 update_linked_list(struct db_list *dbp, char *nextkey, int nextsize,
1184 char *prevkey, int prevsize, int *errorp)
1186 linkinfo_ent *nextlinkp = NULL;
1188 *errorp = 0;
1189 if (nextsize > 0) {
1190 nextlinkp = update_next_link(dbp, nextkey, nextsize,
1191 prevkey, prevsize, errorp);
1192 if (nextlinkp == NULL) {
1193 /* not an error if no next link */
1194 if (*errorp != 0) {
1195 if (debug > 1) {
1196 (void) fprintf(stderr,
1197 "update_next_link Error %s\n",
1198 ((*errorp >= 0) ? strerror(*errorp) :
1199 "Unknown"));
1201 return (NULL);
1205 if (prevsize > 0) {
1206 *errorp = update_prev_link(dbp, nextkey, nextsize,
1207 prevkey, prevsize);
1208 if (*errorp != 0) {
1209 if (debug > 1) {
1210 (void) fprintf(stderr,
1211 "update_prev_link Error %s\n",
1212 ((*errorp >= 0) ? strerror(*errorp) :
1213 "Unknown"));
1215 free(nextlinkp);
1216 nextlinkp = NULL;
1219 return (nextlinkp);
1223 * db_update_primary_new_head - Update a primary record that the head of
1224 * the list is deleted. Similar to db_add_primary, but the primary record
1225 * must exist, and is always replaced with one pointing to the new link,
1226 * unless it does not point to the deleted link. If the link we deleted
1227 * was the last link, the delete the primary record as well.
1228 * Return 0 for success, error code otherwise.
1230 static int
1231 db_update_primary_new_head(struct db_list *dbp, linkinfo_ent *dellinkp,
1232 linkinfo_ent *nextlinkp, fhlist_ent *fhrecp)
1234 int error;
1235 char *name, *next_name;
1236 fhandle_t *dfh;
1237 fh_primary_key fhkey;
1239 dfh = &dellinkp->dfh;
1240 name = LN_NAME(dellinkp);
1241 /* If the deleted link was not the head of the list, we are done */
1242 if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) ||
1243 strcmp(fhrecp->name, name)) {
1244 /* should never be here... */
1245 if (debug > 1) {
1246 (void) fprintf(stderr,
1247 "db_update_primary_new_head: primary "
1248 "is for [%s,", name);
1249 debug_opaque_print(stderr, (void *)dfh, sizeof (*dfh));
1250 (void) fprintf(stderr, "], not [%s,", fhrecp->name);
1251 debug_opaque_print(stderr, (void *)&fhrecp->dfh,
1252 sizeof (fhrecp->dfh));
1253 (void) fprintf(stderr, "]\n");
1255 return (0); /* not head of list so done */
1257 /* Set the head to nextkey if exists. Otherwise, mark file as deleted */
1258 bcopy(&fhrecp->fh.fh_data, fhkey, fhrecp->fh.fh_len);
1259 if (nextlinkp == NULL) {
1260 /* last link */
1261 /* remove primary record from database */
1262 (void) delete_record(dbp,
1263 fhkey, fhrecp->fh.fh_len,
1264 "db_update_primary_new_head: fh delete");
1265 return (0);
1266 } else {
1268 * There are still "live" links, so update the primary record.
1270 next_name = LN_NAME(nextlinkp);
1271 fhrecp->reclen = ROUNDUP32(offsetof(fhlist_ent, name) +
1272 strlen(next_name) + 1);
1273 /* Replace link data with the info for the next link */
1274 (void) memcpy(&fhrecp->dfh, &nextlinkp->dfh,
1275 sizeof (nextlinkp->dfh));
1276 (void) strcpy(fhrecp->name, next_name);
1278 /* not last link */
1279 fhrecp->mtime = time(0);
1280 fhrecp->atime = fhrecp->mtime;
1281 error = store_record(dbp,
1282 fhkey, fhrecp->fh.fh_len, fhrecp,
1283 fhrecp->reclen, "db_update_primary_new_head: fh");
1284 return (error);
1288 * Exported functions
1292 * db_add - add record to the database. If dfh, fh and name are all here,
1293 * add both primary and secondary records. If fh is not available, don't
1294 * add anything...
1295 * Assumes this is a new file, not yet in the database and that the record
1296 * for fh is already in.
1297 * Return 0 for success, error code otherwise.
1300 db_add(char *fhpath, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags)
1302 struct db_list *dbp = NULL;
1303 fhlist_ent fhrec, *fhrecp;
1304 int error = 0;
1306 if (fh == NULL) {
1307 /* nothing to add */
1308 return (EINVAL);
1310 if (fh == &public_fh) {
1311 dbp = db_get_all_databases(fhpath, FALSE);
1312 } else {
1313 dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT);
1315 for (; dbp != NULL; dbp = ((fh != &public_fh) ? NULL : dbp->next)) {
1316 if (debug > 3) {
1317 (void) printf("db_add: name '%s', db '%s'\n",
1318 name, dbp->path);
1320 fhrecp = db_add_primary(dbp, dfh, name, fh, flags,
1321 &fhrec, &error);
1322 if (fhrecp == NULL) {
1323 continue;
1325 if ((dfh == NULL) || (name == NULL)) {
1326 /* Can't add link information */
1327 syslog(LOG_ERR, gettext(
1328 "db_add: dfh %p, name %p - invalid"),
1329 (void *)dfh, (void *)name);
1330 error = EINVAL;
1331 continue;
1333 if (fh == &public_fh) {
1334 while ((fhrecp != NULL) && strcmp(name, fhrecp->name)) {
1335 /* Replace the public fh rather than add link */
1336 error = db_delete_link(fhpath, dfh,
1337 fhrecp->name);
1338 fhrecp = db_add_primary(dbp, dfh, name, fh,
1339 flags, &fhrec, &error);
1341 if (fhrecp == NULL) {
1342 continue;
1345 error = db_add_secondary(dbp, dfh, name, fh, fhrecp);
1346 if (fhrecp != &fhrec) {
1347 free(fhrecp);
1350 return (error);
1354 * db_lookup - search the database for the file identified by fh.
1355 * Return the entry in *fhrecpp if found, or NULL with error set otherwise.
1357 fhlist_ent *
1358 db_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp, int *errorp)
1360 struct db_list *dbp;
1361 fh_primary_key fhkey;
1363 if ((fhpath == NULL) || (fh == NULL) || (errorp == NULL)) {
1364 if (errorp != NULL)
1365 *errorp = EINVAL;
1366 return (NULL);
1368 *errorp = 0;
1369 if (fh == &public_fh) {
1370 dbp = db_get_all_databases(fhpath, FALSE);
1371 } else {
1372 dbp = db_get_db(fhpath, &fh->fh_fsid, errorp, O_CREAT);
1374 if (dbp == NULL) {
1375 /* Could not get or create database */
1376 return (NULL);
1378 bcopy(&fh->fh_data, fhkey, fh->fh_len);
1379 fhrecp = fetch_record(dbp, fhkey, fh->fh_len, fhrecp,
1380 errorp, "db_lookup");
1381 /* Update fhrec atime if needed */
1382 if (fhrecp != NULL) {
1383 *errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, fhrecp,
1384 "db_lookup");
1386 return (fhrecp);
1390 * db_lookup_link - search the database for the file identified by (dfh,name).
1391 * If the link was found, use it to search for the primary record.
1392 * Return 0 and set the entry in *fhrecpp if found, return error otherwise.
1394 fhlist_ent *
1395 db_lookup_link(char *fhpath, fhandle_t *dfh, char *name, fhlist_ent *fhrecp,
1396 int *errorp)
1398 struct db_list *dbp;
1399 fh_secondary_key linkkey;
1400 linkinfo_ent *linkp;
1401 int linksize, fhkeysize;
1402 char *fhkey;
1404 if ((fhpath == NULL) || (dfh == NULL) || (name == NULL) ||
1405 (errorp == NULL)) {
1406 if (errorp != NULL)
1407 *errorp = EINVAL;
1408 return (NULL);
1410 *errorp = 0;
1411 if (dfh == &public_fh) {
1412 dbp = db_get_all_databases(fhpath, FALSE);
1413 } else {
1414 dbp = db_get_db(fhpath, &dfh->fh_fsid, errorp, O_CREAT);
1416 if (dbp == NULL) {
1417 /* Could not get or create database */
1418 return (NULL);
1420 /* Get the link record */
1421 linksize = fill_link_key(linkkey, dfh, name);
1422 linkp = fetch_record(dbp, linkkey, linksize, NULL, errorp,
1423 "db_lookup_link link");
1424 if (linkp != NULL) {
1425 /* Now use link to search for fh entry */
1426 fhkeysize = LN_FHKEY_LEN(linkp);
1427 fhkey = LN_FHKEY(linkp);
1428 fhrecp = fetch_record(dbp, fhkey, fhkeysize,
1429 (void *)fhrecp, errorp, "db_lookup_link fh");
1430 /* Update fhrec atime if needed */
1431 if (fhrecp != NULL) {
1432 *errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp,
1433 "db_lookup_link fhrec");
1435 /* Update link atime if needed */
1436 *errorp = db_update_linkinfo(dbp, linkkey, linksize, linkp,
1437 "db_lookup_link link");
1438 free(linkp);
1439 } else {
1440 fhrecp = NULL;
1442 return (fhrecp);
1446 * delete_link - delete the requested link from the database. If it's the
1447 * last link in the database for that file then remove the primary record
1448 * as well. *errorp contains the returned error code.
1449 * Return ENOENT if link not in database and 0 otherwise.
1451 static int
1452 delete_link_by_key(struct db_list *dbp, char *linkkey, int *linksizep,
1453 int *errorp, char *errstr)
1455 int nextsize, prevsize, fhkeysize, linksize;
1456 char *nextkey, *prevkey, *fhkey;
1457 linkinfo_ent *dellinkp, *nextlinkp;
1458 fhlist_ent *fhrecp, fhrec;
1460 *errorp = 0;
1461 linksize = *linksizep;
1462 /* Get the link record */
1463 dellinkp = fetch_record(dbp, linkkey, linksize, NULL, errorp, errstr);
1464 if (dellinkp == NULL) {
1466 * Link not in database.
1468 if (debug > 2) {
1469 debug_print_key(stderr, errstr,
1470 "link not in database\n",
1471 linkkey, linksize);
1473 *linksizep = 0;
1474 return (ENOENT);
1477 * Possibilities:
1478 * 1. Normal case - only one link to delete: the link next and
1479 * prev should be NULL, and fhrec's name/dfh are same
1480 * as the link. Remove the link and fhrec.
1481 * 2. Multiple hard links, and the deleted link is the head of
1482 * the list. Remove the link and replace the link key in
1483 * the primary record to point to the new head.
1484 * 3. Multiple hard links, and the deleted link is not the
1485 * head of the list (not the same as in fhrec) - just
1486 * delete the link and update the previous and next records
1487 * in the links linked list.
1490 /* Get next and prev keys for linked list updates */
1491 nextsize = LN_NEXT_LEN(dellinkp);
1492 nextkey = ((nextsize > 0) ? LN_NEXT(dellinkp) : NULL);
1493 prevsize = LN_PREV_LEN(dellinkp);
1494 prevkey = ((prevsize > 0) ? LN_PREV(dellinkp) : NULL);
1495 /* Update the linked list for the file */
1496 nextlinkp = update_linked_list(dbp, nextkey, nextsize,
1497 prevkey, prevsize, errorp);
1498 if ((nextlinkp == NULL) && (*errorp != 0)) {
1499 free(dellinkp);
1500 *linksizep = 0;
1501 return (0);
1503 /* Delete link record */
1504 *errorp = delete_record(dbp, linkkey, linksize, errstr);
1505 /* Get the primary key */
1506 fhkeysize = LN_FHKEY_LEN(dellinkp);
1507 fhkey = LN_FHKEY(dellinkp);
1508 fhrecp = fetch_record(dbp, fhkey, fhkeysize,
1509 &fhrec, errorp, errstr);
1510 if (fhrecp == NULL) {
1511 /* Should never happen */
1512 if (debug > 1) {
1513 debug_print_key(stderr, errstr,
1514 "fetch primary for ", linkkey, linksize);
1515 (void) fprintf(stderr, " Error %s\n",
1516 ((*errorp >= 0) ? strerror(*errorp) : "Unknown"));
1518 } else if ((*errorp == 0) && (prevsize <= 0)) {
1519 /* This is the head of the list update primary record */
1520 *errorp = db_update_primary_new_head(dbp, dellinkp,
1521 nextlinkp, fhrecp);
1522 } else {
1523 /* Update fhrec atime if needed */
1524 *errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp,
1525 errstr);
1527 *linksizep = nextsize;
1528 if (nextsize > 0)
1529 (void) memcpy(linkkey, nextkey, nextsize);
1530 free(nextlinkp);
1531 free(dellinkp);
1532 return (0);
1536 * delete_link - delete the requested link from the database. If it's the
1537 * last link in the database for that file then remove the primary record
1538 * as well. If nextlinkkey/sizep are non-null, copy the key and key size of
1539 * the next link in the chain into them (this would save a dbm_fetch op).
1540 * Return ENOENT if link not in database and 0 otherwise, with *errorp
1541 * containing the returned error if any from the delete_link ops.
1543 static int
1544 delete_link(struct db_list *dbp, fhandle_t *dfh, char *name,
1545 char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr)
1547 int linkerr;
1549 *errorp = 0;
1550 if ((nextlinkkey != NULL) && (nextlinksizep != NULL)) {
1551 *nextlinksizep = fill_link_key(nextlinkkey, dfh, name);
1552 linkerr = delete_link_by_key(dbp, nextlinkkey, nextlinksizep,
1553 errorp, errstr);
1554 } else {
1555 int linksize;
1556 fh_secondary_key linkkey;
1558 linksize = fill_link_key(linkkey, dfh, name);
1559 linkerr = delete_link_by_key(dbp, linkkey, &linksize,
1560 errorp, errstr);
1562 return (linkerr);
1566 * db_delete_link - search the database for the file system for link.
1567 * Delete the link from the database. If this is the "primary" link,
1568 * set the primary record for the next link. If it's the last one,
1569 * delete the primary record.
1570 * Return 0 for success, error code otherwise.
1573 db_delete_link(char *fhpath, fhandle_t *dfh, char *name)
1575 struct db_list *dbp;
1576 int error = 0;
1578 if ((fhpath == NULL) || (dfh == NULL) || (name == NULL)) {
1579 return (EINVAL);
1581 if (dfh == &public_fh) {
1582 dbp = db_get_all_databases(fhpath, TRUE);
1583 } else {
1584 dbp = db_get_db(fhpath, &dfh->fh_fsid, &error, O_CREAT);
1586 for (; dbp != NULL; dbp = ((dfh == &public_fh) ? dbp->next : NULL)) {
1587 (void) delete_link(dbp, dfh, name, NULL, NULL, &error,
1588 "db_delete_link link");
1590 return (error);
1593 #ifdef DEBUG
1595 * db_delete - Deletes the fhrec corresponding to the fh. Use only
1596 * for repairing the fhtable, not for normal handling.
1597 * Return 0 for success, error code otherwise.
1600 db_delete(char *fhpath, fhandle_t *fh)
1602 struct db_list *dbp;
1603 int error = 0;
1605 if ((fhpath == NULL) || (fh == NULL)) {
1606 return (EINVAL);
1608 if (fh == &public_fh) {
1609 dbp = db_get_all_databases(fhpath, TRUE);
1610 } else {
1611 dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT);
1613 for (; dbp != NULL; dbp = ((fh == &public_fh) ? dbp->next : NULL)) {
1614 /* Get the link record */
1615 (void) delete_record(dbp, &fh->fh_data, fh->fh_len,
1616 "db_delete: fh delete");
1618 return (error);
1620 #endif /* DEBUG */
1623 * db_rename_link - search the database for the file system for link.
1624 * Add the new link and delete the old link from the database.
1625 * Return 0 for success, error code otherwise.
1628 db_rename_link(char *fhpath, fhandle_t *from_dfh, char *from_name,
1629 fhandle_t *to_dfh, char *to_name)
1631 int error;
1632 struct db_list *dbp;
1633 fhlist_ent fhrec, *fhrecp;
1635 if ((fhpath == NULL) || (from_dfh == NULL) || (from_name == NULL) ||
1636 (to_dfh == NULL) || (to_name == NULL)) {
1637 return (EINVAL);
1639 if (from_dfh == &public_fh) {
1640 dbp = db_get_all_databases(fhpath, FALSE);
1641 } else {
1642 dbp = db_get_db(fhpath, &from_dfh->fh_fsid, &error, O_CREAT);
1644 for (; dbp != NULL;
1645 dbp = ((from_dfh != &public_fh) ? NULL : dbp->next)) {
1646 /* find existing link */
1647 fhrecp = db_lookup_link(fhpath, from_dfh, from_name, &fhrec,
1648 &error);
1649 if (fhrecp == NULL) {
1650 /* Could not find the link */
1651 continue;
1653 /* Delete the old link (if last primary record not deleted) */
1654 error = db_delete_link(fhpath, from_dfh, from_name);
1655 if (error == 0) {
1656 error = db_add(fhpath, to_dfh, to_name, &fhrecp->fh,
1657 fhrecp->flags);
1660 return (error);
1664 * db_print_all_keys: prints all keys for a given filesystem. If fsidp is
1665 * NULL, print for all filesystems covered by fhpath.
1667 void
1668 db_print_all_keys(char *fhpath, fsid_t *fsidp, FILE *fp)
1670 struct db_list *dbp;
1671 datum key;
1672 int error, len;
1673 char strkey[NFS_FHMAXDATA + MAXNAMELEN];
1674 db_record rec;
1675 void *ptr;
1677 if ((fhpath == NULL) ||
1678 ((fsidp != NULL) && (fsidp == &public_fh.fh_fsid)))
1679 return;
1680 if (fsidp == NULL) {
1681 (void) db_get_all_databases(fhpath, TRUE);
1682 dbp = db_fs_list;
1683 } else {
1684 dbp = db_get_db(fhpath, fsidp, &error, 0);
1686 if (dbp == NULL) {
1687 /* Could not get or create database */
1688 return;
1690 len = strlen(fhpath);
1691 for (; dbp != NULL; dbp = ((fsidp != NULL) ? NULL : dbp->next)) {
1692 if (strncmp(fhpath, dbp->path, len))
1693 continue;
1694 (void) fprintf(fp,
1695 "\nStart print database for fsid 0x%x 0x%x\n",
1696 dbp->fsid.val[0], dbp->fsid.val[1]);
1697 (void) fprintf(fp, "=============================\n");
1698 for (key = dbm_firstkey(dbp->db); key.dptr != NULL;
1699 key = dbm_nextkey(dbp->db)) {
1700 (void) memcpy(strkey, key.dptr, key.dsize);
1701 debug_print_key(fp, "", "", strkey, key.dsize);
1702 if (debug < 2)
1703 continue;
1704 ptr = fetch_record(dbp, key.dptr, key.dsize,
1705 (void *)&rec, &error, "db_prt_keys");
1706 if (ptr == NULL)
1707 continue;
1708 if (key.dsize == NFS_FHMAXDATA) {
1709 /* fhrec */
1710 debug_print_fhlist(fp, &rec.fhlist_rec);
1711 } else if (key.dsize > NFS_FHMAXDATA) {
1712 /* linkinfo */
1713 debug_print_linkinfo(fp, &rec.link_rec);
1715 (void) fprintf(fp, "-----------------------------\n");
1717 (void) fprintf(fp, "End print database for fsid 0x%x 0x%x\n",
1718 dbp->fsid.val[0], dbp->fsid.val[1]);
1722 void
1723 debug_opaque_print(FILE *fp, void *buf, int size)
1725 int bufoffset = 0;
1726 char debug_str[200];
1728 if ((buf == NULL) || (size <= 0))
1729 return;
1731 nfslog_opaque_print_buf(buf, size, debug_str, &bufoffset, 200);
1732 (void) fprintf(fp, debug_str);
1736 * links_timedout() takes a primary records and searches all of its
1737 * links to see if they all have access times that are older than
1738 * the 'prune_timeout' value. TRUE if all links are old and FALSE
1739 * if there is just one link that has an access time which is recent.
1741 static int
1742 links_timedout(struct db_list *pdb, fhlist_ent *pfe, time_t ts)
1744 fh_secondary_key linkkey;
1745 linkinfo_ent *linkp, link_st;
1746 int error;
1747 int linksize;
1748 void *cookie;
1750 /* Get the link record */
1751 linksize = fill_link_key(linkkey, &pfe->dfh, pfe->name);
1752 cookie = NULL;
1753 do {
1754 linkp = get_next_link(pdb, linkkey, &linksize, &link_st,
1755 &cookie, &error, "links_timedout");
1756 if ((linkp != NULL) &&
1757 (difftime(ts, linkp->atime) <= prune_timeout)) {
1758 /* update primary record to have an uptodate time */
1759 pfe = fetch_record(pdb, (void *)&pfe->fh.fh_data,
1760 pfe->fh.fh_len, NULL, &error,
1761 "links_timedout");
1762 if (pfe == NULL) {
1763 syslog(LOG_ERR, gettext(
1764 "links_timedout: fetch fhrec error %s\n"),
1765 strerror(error));
1766 } else {
1767 if (difftime(pfe->atime, linkp->atime) < 0) {
1768 /* update fhrec atime */
1769 pfe->atime = linkp->atime;
1770 (void) store_record(pdb,
1771 (void *)&pfe->fh.fh_data,
1772 pfe->fh.fh_len, pfe,
1773 pfe->reclen, "links_timedout");
1775 free(pfe);
1777 free_link_cookies(cookie);
1778 return (FALSE);
1780 } while (linksize > 0);
1782 free_link_cookies(cookie);
1783 return (TRUE);
1787 * prune_dbs() will search all of the open databases looking for records
1788 * that have not been accessed in the last 'prune_timeout' seconds.
1789 * This search is done on the primary records and a list of potential
1790 * timeout candidates is built. The reason for doing this is to not
1791 * disturb the underlying dbm_firstkey()/dbm_nextkey() sequence; we
1792 * want to search all of the records in the database.
1793 * Once we have our candidate list built, we examine each of those
1794 * item's links to check if the links have been accessed within the
1795 * 'prune_timeout' seconds. If neither the primary nor any its links
1796 * have been accessed, then all of those records are removed/deleted
1797 * from the database.
1800 prune_dbs(char *fhpath)
1802 struct db_list *pdb;
1803 datum key;
1804 db_record *ptr;
1805 struct fhlist_ent *pfe;
1806 int error, linkerr, linksize;
1807 time_t cur_time = time(0);
1808 fh_secondary_key linkkey;
1809 struct thelist {
1810 struct thelist *next;
1811 db_record *ptr;
1812 } thelist, *ptl;
1813 int cnt = 0;
1815 if (fhpath != NULL)
1816 (void) db_get_all_databases(fhpath, TRUE);
1818 thelist.next = NULL;
1820 * Search each of the open databases
1822 for (pdb = db_fs_list; pdb; pdb = pdb->next) {
1823 do {
1824 /* Check each record in the database */
1825 for (key = dbm_firstkey(pdb->db); key.dptr != NULL;
1826 key = dbm_nextkey(pdb->db)) {
1827 /* We're only interested in primary records */
1828 if (key.dsize != NFS_FHMAXDATA)
1829 continue; /* probably a link record */
1830 ptr = fetch_record(pdb, key.dptr, key.dsize,
1831 NULL, &error, "dump_db");
1832 if (ptr == NULL)
1833 continue;
1835 * If this record is a primary record and it is
1836 * not an export point or a public file handle path,
1837 * check it for a ancient access time.
1839 if ((ptr->fhlist_rec.flags &
1840 (EXPORT_POINT | PUBLIC_PATH)) ||
1841 (difftime(cur_time, ptr->fhlist_rec.atime) <=
1842 prune_timeout)) {
1843 /* Keep this record in the database */
1844 free(ptr);
1845 } else {
1846 /* Found one? Save off info about it */
1847 ptl = malloc(sizeof (struct thelist));
1848 if (ptl == NULL) {
1849 syslog(LOG_ERR, gettext(
1850 "prune_dbs: malloc failed, error %s\n"),
1851 strerror(errno));
1852 break;
1854 ptl->ptr = ptr;
1855 ptl->next = thelist.next;
1856 thelist.next = ptl;
1857 cnt++; /* count how many records allocated */
1858 if (cnt > MAX_PRUNE_REC_CNT) {
1859 /* Limit number of records malloc'd */
1860 if (debug)
1861 (void) fprintf(stderr,
1862 "prune_dbs: halt search - too many records\n");
1863 break;
1869 * Take the saved records and check their links to make
1870 * sure that they have not been accessed as well.
1872 for (ptl = thelist.next; ptl; ptl = thelist.next) {
1873 thelist.next = ptl->next;
1874 /* Everything timed out? */
1875 pfe = &(ptl->ptr->fhlist_rec);
1876 if (links_timedout(pdb, pfe, cur_time)) {
1879 * Iterate until we run out of links.
1880 * We have to do this since there can be
1881 * multiple links to a primary record and
1882 * we need to delete one at a time.
1884 /* Delete the link and get the next */
1885 linkerr = delete_link(pdb,
1886 &pfe->dfh, pfe->name, linkkey,
1887 &linksize, &error, "dump_db");
1888 while ((linksize > 0) && !(error || linkerr)) {
1889 /* Delete the link and get the next */
1890 linkerr = delete_link_by_key(pdb,
1891 linkkey, &linksize,
1892 &error, "dump_db");
1893 if (error || linkerr) {
1894 break;
1897 if (linkerr) {
1898 /* link not in database, primary is */
1899 /* Should never happen */
1900 if (debug > 1) {
1901 (void) fprintf(stderr,
1902 "prune_dbs: Error primary exists ");
1903 debug_opaque_print(stderr,
1904 (void *)&pfe->fh,
1905 sizeof (pfe->fh));
1906 (void) fprintf(stderr, "\n");
1908 if (debug)
1909 syslog(LOG_ERR, gettext(
1910 "prune_dbs: Error primary exists\n"));
1911 (void) delete_record(pdb,
1912 &pfe->fh.fh_data, pfe->fh.fh_len,
1913 "prune_dbs: fh delete");
1916 /* Make sure to free the pointers used in the list */
1917 free(ptl->ptr);
1918 free(ptl);
1919 cnt--;
1921 thelist.next = NULL;
1922 } while (key.dptr != NULL);
1924 return (0);