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]
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
45 #include <sys/types.h>
47 #include <nfs/nfs_log.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 };
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";
75 fh_secondary_key lnkey
;
77 struct link_keys
*next
;
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
,
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
,
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
,
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
,
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
,
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
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
);
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
;
223 (p
!= NULL
) && memcmp(&p
->fsid
, fsid
, sizeof (*fsid
));
230 if ((newp
= calloc(1, sizeof (*newp
))) == NULL
) {
232 syslog(LOG_ERR
, gettext(
233 "db_get_db: malloc db failed: Error %s"),
237 (void) sprintf(fsidstr
, "%08x%08x", fsid
->val
[0], fsid
->val
[1]);
238 if ((newp
->path
= malloc(strlen(fhpath
) + 2 + strlen(fsidstr
)))
241 syslog(LOG_ERR
, gettext(
242 "db_get_db: malloc dbpath failed: Error %s"),
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))
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 */
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
;
277 (void) printf("db_get_db: db %s opened\n", newp
->path
);
283 if (newp
->db
!= NULL
) {
286 if (newp
->path
!= 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
;
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)
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
)
325 if ((fhdir
= strdup(fhpath
)) == NULL
) {
326 syslog(LOG_ERR
, gettext(
327 "db_get_all_databases: strdup '%s' Error '%s*'"),
328 fhpath
, strerror(errno
));
333 if ((dirptr
= strrchr(fhdir
, '/')) == NULL
) {
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
));
343 /* Terminate fhdir string at last '/' */
345 /* Search the directory */
347 (void) printf("db_get_all_databases: search '%s' for '%s*'\n",
350 if ((dirp
= opendir(fhdir
)) == NULL
) {
351 syslog(LOG_ERR
, gettext(
352 "db_get_all_databases: opendir '%s' Error '%s*'"),
353 fhdir
, strerror(errno
));
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) != '.') {
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);
374 (void) closedir(dirp
);
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");
400 debug_print_linkinfo(FILE *fp
, linkinfo_ent
*linkp
)
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
,
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
));
418 debug_print_fhlist(FILE *fp
, fhlist_ent
*fhrecp
)
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
);
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
) {
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
) {
443 /* probably an fhlist struct */
444 (void) memcpy(&inf
, data
, sizeof (linkinfo_ent
));
445 debug_print_fhlist(fp
, &inf
);
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.
457 store_record(struct db_list
*dbp
, void *keyaddr
, int keysize
, void *dataaddr
,
458 int datasize
, char *str
)
462 char *errfmt
= "store_record: dbm_store failed, Error: %s\n";
468 data
.dptr
= dataaddr
;
469 data
.dsize
= datasize
;
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
);
482 err
= strerror(errno
);
487 } else { /* should not happen but sometimes does */
492 debug_print_key(stderr
, str
, "store_record:"
493 "dbm_store:\n", key
.dptr
, key
.dsize
);
494 (void) fprintf(stderr
, errfmt
, err
);
496 syslog(LOG_ERR
, gettext(errfmt
), err
);
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.
510 fetch_record(struct db_list
*dbp
, void *keyaddr
, int keysize
, void *dataaddr
,
511 int *errorp
, char *str
)
514 char *errfmt
= "fetch_record: dbm_fetch failed, Error: %s\n";
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
);
529 err
= strerror(*errorp
);
530 syslog(LOG_ERR
, gettext(errfmt
), err
);
532 /* primary record not in database */
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
);
544 /* copy to local struct because dbm may return non-aligned pointers */
545 if ((dataaddr
== NULL
) &&
546 ((dataaddr
= malloc(data
.dsize
)) == NULL
)) {
548 syslog(LOG_ERR
, gettext(
549 "%s: dbm_fetch - malloc %ld: Error %s"),
550 str
, data
.dsize
, strerror(*errorp
));
553 (void) memcpy(dataaddr
, data
.dptr
, data
.dsize
);
555 debug_print_key_and_data(stdout
, str
, "fetch_record:"
556 "dbm_fetch:\n", key
.dptr
, key
.dsize
,
557 dataaddr
, data
.dsize
);
564 * delete_record - delete the record from the database and return 0 for success
565 * or error code for failure.
568 delete_record(struct db_list
*dbp
, void *keyaddr
, int keysize
, char *str
)
572 char *errfmt
= "delete_record: dbm_delete failed, Error: %s\n";
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
);
589 err
= strerror(errno
);
594 } else { /* should not happen but sometimes does */
599 debug_print_key(stderr
, str
, "delete_record:"
600 "dbm_delete:\n", key
.dptr
, key
.dsize
);
601 (void) fprintf(stderr
, errfmt
, err
);
603 syslog(LOG_ERR
, gettext(errfmt
), err
);
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.
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
));
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.
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
));
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.
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
)
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
) {
663 syslog(LOG_ERR
, gettext(
664 "create_primary_struct: malloc %d Error %s"),
665 reclen
, strerror(*errorp
));
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
;
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
),
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");
688 /* Could not store */
689 if (fhrecp
== NULL
) /* caller did not supply pointer */
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.
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
;
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");
724 (void) printf("db_add_primary exits(2): name '%s'\n",
728 /* primary record not in database - create it */
729 new_fhrecp
= create_primary_struct(dbp
, dfh
, name
, fh
, flags
,
731 if (new_fhrecp
== NULL
) {
732 /* Could not store */
735 "db_add_primary exits(1): name '%s' Error %s\n",
736 name
, ((*errorp
>= 0) ? strerror(*errorp
) :
742 (void) printf("db_add_primary exits(0): name '%s'\n", name
);
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
;
759 linkinfo_ent
*new_linkp
= linkp
;
760 struct link_keys
*lnp
;
762 linksize
= *linksizep
;
766 new_linkp
= fetch_record(dbp
, linkkey
, linksize
, (void *)linkp
,
768 if (new_linkp
== NULL
)
771 /* Set linkkey to point to next record */
772 nextsize
= LN_NEXT_LEN(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"),
780 if ((new_linkp
!= NULL
) && (linkp
== 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.
801 (void) fprintf(stderr
,
802 "%s: get_next_link: last record invalid.\n",
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 */
812 (void) memcpy(linkkey
, nextkey
, nextsize
);
813 *linksizep
= nextsize
;
818 * free_link_cookies - free the cookie list
821 free_link_cookies(void *cookie
)
823 struct link_keys
*dellnp
, *lnp
;
825 lnp
= (struct link_keys
*)cookie
;
826 while (lnp
!= NULL
) {
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.
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
;
843 linkinfo_ent lastlink
, *lastlinkp
;
846 linksize
= fill_link_key(linkkey
, &fhrecp
->dfh
, fhrecp
->name
);
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
) {
857 (void) fprintf(stderr
, "add_mc_path link is null\n");
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
,
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");
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
;
908 if ((linkp
= malloc(sizeof (linkinfo_ent
))) == NULL
) {
910 syslog(LOG_ERR
, gettext(
911 "create_link_struct: malloc failed: Error %s"),
915 if (dfh
== &public_fh
)
916 linkp
->flags
|= PUBLIC_PATH
;
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,
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
,
953 /* new link to a file with a different one already */
954 len
= fill_link_key(LN_NEXT(linkp
), &fhrecp
->dfh
,
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");
977 * db_add_secondary - add secondary record to the database (for the directory
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.
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
;
993 fhlist_ent
*new_fhrecp
= fhrecp
;
994 fh_primary_key fhkey
;
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
) {
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
) {
1018 if (newlinkp
== NULL
) {
1020 (void) printf("create_link_struct '%s' Error %s\n",
1021 name
, ((error
>= 0) ? strerror(error
) :
1025 nextsize
= LN_NEXT_LEN(newlinkp
);
1026 if (nextsize
== 0) {
1027 /* No next - can exit now */
1029 (void) printf("db_add_secondary: no next link\n");
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
) {
1044 "db_add_secondary: replace primary failed\n");
1047 } else if (fhrecp
== NULL
) {
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
);
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
) {
1067 "db_add_secondary: fetch next link failed\n");
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");
1083 "db_add_secondary exits(%d): name '%s'\n", error
, name
);
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
) {
1103 syslog(LOG_ERR
, gettext(
1104 "update_next_link: malloc next Error %s"),
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 */
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 */
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");
1136 * Update the prev link to point to the new next.
1137 * Return 0 for success, error code otherwise.
1140 update_prev_link(struct db_list
*dbp
, char *nextkey
, int nextsize
,
1141 char *prevkey
, int prevsize
)
1143 linkinfo_ent prevlink
, *prevlinkp
;
1146 /* Update its next to the given one */
1147 prevlinkp
= fetch_record(dbp
, prevkey
, prevsize
, &prevlink
, &error
,
1149 /* if error there is no next record - ok */
1150 if (prevlinkp
== NULL
) {
1153 diff
= nextsize
- LN_NEXT_LEN(prevlinkp
);
1154 prevlinkp
->reclen
= ROUNDUP32(prevlinkp
->reclen
+ diff
);
1155 /* Change the len and set next - may push prev */
1157 char *ptr
= LN_PREV(prevlinkp
);
1159 prevlinkp
->prev_offset
+= diff
;
1160 (void) memcpy(LN_PREV(prevlinkp
), ptr
, LN_PREV_LEN(prevlinkp
));
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");
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
;
1190 nextlinkp
= update_next_link(dbp
, nextkey
, nextsize
,
1191 prevkey
, prevsize
, errorp
);
1192 if (nextlinkp
== NULL
) {
1193 /* not an error if no next link */
1196 (void) fprintf(stderr
,
1197 "update_next_link Error %s\n",
1198 ((*errorp
>= 0) ? strerror(*errorp
) :
1206 *errorp
= update_prev_link(dbp
, nextkey
, nextsize
,
1210 (void) fprintf(stderr
,
1211 "update_prev_link Error %s\n",
1212 ((*errorp
>= 0) ? strerror(*errorp
) :
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.
1231 db_update_primary_new_head(struct db_list
*dbp
, linkinfo_ent
*dellinkp
,
1232 linkinfo_ent
*nextlinkp
, fhlist_ent
*fhrecp
)
1235 char *name
, *next_name
;
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... */
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
) {
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");
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
);
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");
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
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
;
1307 /* nothing to add */
1310 if (fh
== &public_fh
) {
1311 dbp
= db_get_all_databases(fhpath
, FALSE
);
1313 dbp
= db_get_db(fhpath
, &fh
->fh_fsid
, &error
, O_CREAT
);
1315 for (; dbp
!= NULL
; dbp
= ((fh
!= &public_fh
) ? NULL
: dbp
->next
)) {
1317 (void) printf("db_add: name '%s', db '%s'\n",
1320 fhrecp
= db_add_primary(dbp
, dfh
, name
, fh
, flags
,
1322 if (fhrecp
== NULL
) {
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
);
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
,
1338 fhrecp
= db_add_primary(dbp
, dfh
, name
, fh
,
1339 flags
, &fhrec
, &error
);
1341 if (fhrecp
== NULL
) {
1345 error
= db_add_secondary(dbp
, dfh
, name
, fh
, fhrecp
);
1346 if (fhrecp
!= &fhrec
) {
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.
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
)) {
1369 if (fh
== &public_fh
) {
1370 dbp
= db_get_all_databases(fhpath
, FALSE
);
1372 dbp
= db_get_db(fhpath
, &fh
->fh_fsid
, errorp
, O_CREAT
);
1375 /* Could not get or create database */
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
,
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.
1395 db_lookup_link(char *fhpath
, fhandle_t
*dfh
, char *name
, fhlist_ent
*fhrecp
,
1398 struct db_list
*dbp
;
1399 fh_secondary_key linkkey
;
1400 linkinfo_ent
*linkp
;
1401 int linksize
, fhkeysize
;
1404 if ((fhpath
== NULL
) || (dfh
== NULL
) || (name
== NULL
) ||
1411 if (dfh
== &public_fh
) {
1412 dbp
= db_get_all_databases(fhpath
, FALSE
);
1414 dbp
= db_get_db(fhpath
, &dfh
->fh_fsid
, errorp
, O_CREAT
);
1417 /* Could not get or create database */
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");
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.
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
;
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.
1469 debug_print_key(stderr
, errstr
,
1470 "link not in database\n",
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)) {
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 */
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
,
1523 /* Update fhrec atime if needed */
1524 *errorp
= db_update_fhrec(dbp
, fhkey
, fhkeysize
, fhrecp
,
1527 *linksizep
= nextsize
;
1529 (void) memcpy(linkkey
, nextkey
, nextsize
);
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.
1544 delete_link(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
1545 char *nextlinkkey
, int *nextlinksizep
, int *errorp
, char *errstr
)
1550 if ((nextlinkkey
!= NULL
) && (nextlinksizep
!= NULL
)) {
1551 *nextlinksizep
= fill_link_key(nextlinkkey
, dfh
, name
);
1552 linkerr
= delete_link_by_key(dbp
, nextlinkkey
, nextlinksizep
,
1556 fh_secondary_key linkkey
;
1558 linksize
= fill_link_key(linkkey
, dfh
, name
);
1559 linkerr
= delete_link_by_key(dbp
, linkkey
, &linksize
,
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
;
1578 if ((fhpath
== NULL
) || (dfh
== NULL
) || (name
== NULL
)) {
1581 if (dfh
== &public_fh
) {
1582 dbp
= db_get_all_databases(fhpath
, TRUE
);
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");
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
;
1605 if ((fhpath
== NULL
) || (fh
== NULL
)) {
1608 if (fh
== &public_fh
) {
1609 dbp
= db_get_all_databases(fhpath
, TRUE
);
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");
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
)
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
)) {
1639 if (from_dfh
== &public_fh
) {
1640 dbp
= db_get_all_databases(fhpath
, FALSE
);
1642 dbp
= db_get_db(fhpath
, &from_dfh
->fh_fsid
, &error
, O_CREAT
);
1645 dbp
= ((from_dfh
!= &public_fh
) ? NULL
: dbp
->next
)) {
1646 /* find existing link */
1647 fhrecp
= db_lookup_link(fhpath
, from_dfh
, from_name
, &fhrec
,
1649 if (fhrecp
== NULL
) {
1650 /* Could not find the link */
1653 /* Delete the old link (if last primary record not deleted) */
1654 error
= db_delete_link(fhpath
, from_dfh
, from_name
);
1656 error
= db_add(fhpath
, to_dfh
, to_name
, &fhrecp
->fh
,
1664 * db_print_all_keys: prints all keys for a given filesystem. If fsidp is
1665 * NULL, print for all filesystems covered by fhpath.
1668 db_print_all_keys(char *fhpath
, fsid_t
*fsidp
, FILE *fp
)
1670 struct db_list
*dbp
;
1673 char strkey
[NFS_FHMAXDATA
+ MAXNAMELEN
];
1677 if ((fhpath
== NULL
) ||
1678 ((fsidp
!= NULL
) && (fsidp
== &public_fh
.fh_fsid
)))
1680 if (fsidp
== NULL
) {
1681 (void) db_get_all_databases(fhpath
, TRUE
);
1684 dbp
= db_get_db(fhpath
, fsidp
, &error
, 0);
1687 /* Could not get or create database */
1690 len
= strlen(fhpath
);
1691 for (; dbp
!= NULL
; dbp
= ((fsidp
!= NULL
) ? NULL
: dbp
->next
)) {
1692 if (strncmp(fhpath
, dbp
->path
, len
))
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
);
1704 ptr
= fetch_record(dbp
, key
.dptr
, key
.dsize
,
1705 (void *)&rec
, &error
, "db_prt_keys");
1708 if (key
.dsize
== NFS_FHMAXDATA
) {
1710 debug_print_fhlist(fp
, &rec
.fhlist_rec
);
1711 } else if (key
.dsize
> NFS_FHMAXDATA
) {
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]);
1723 debug_opaque_print(FILE *fp
, void *buf
, int size
)
1726 char debug_str
[200];
1728 if ((buf
== NULL
) || (size
<= 0))
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.
1742 links_timedout(struct db_list
*pdb
, fhlist_ent
*pfe
, time_t ts
)
1744 fh_secondary_key linkkey
;
1745 linkinfo_ent
*linkp
, link_st
;
1750 /* Get the link record */
1751 linksize
= fill_link_key(linkkey
, &pfe
->dfh
, pfe
->name
);
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
,
1763 syslog(LOG_ERR
, gettext(
1764 "links_timedout: fetch fhrec error %s\n"),
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");
1777 free_link_cookies(cookie
);
1780 } while (linksize
> 0);
1782 free_link_cookies(cookie
);
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
;
1805 struct fhlist_ent
*pfe
;
1806 int error
, linkerr
, linksize
;
1807 time_t cur_time
= time(0);
1808 fh_secondary_key linkkey
;
1810 struct thelist
*next
;
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
) {
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");
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
) <=
1843 /* Keep this record in the database */
1846 /* Found one? Save off info about it */
1847 ptl
= malloc(sizeof (struct thelist
));
1849 syslog(LOG_ERR
, gettext(
1850 "prune_dbs: malloc failed, error %s\n"),
1855 ptl
->next
= thelist
.next
;
1857 cnt
++; /* count how many records allocated */
1858 if (cnt
> MAX_PRUNE_REC_CNT
) {
1859 /* Limit number of records malloc'd */
1861 (void) fprintf(stderr
,
1862 "prune_dbs: halt search - too many records\n");
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
,
1893 if (error
|| linkerr
) {
1898 /* link not in database, primary is */
1899 /* Should never happen */
1901 (void) fprintf(stderr
,
1902 "prune_dbs: Error primary exists ");
1903 debug_opaque_print(stderr
,
1906 (void) fprintf(stderr
, "\n");
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 */
1921 thelist
.next
= NULL
;
1922 } while (key
.dptr
!= NULL
);