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.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * Code to maintain the runtime and on-disk filehandle mapping table for
47 #include <sys/types.h>
49 #include <nfs/nfs_log.h>
53 #define ROUNDUP32(val) (((val) + 3) & ~3)
56 * It is important that this string not match the length of the
57 * file handle key length NFS_FHMAXDATA
59 #define DB_VERSION_STRING "NFSLOG_DB_VERSION"
60 #define DB_VERSION "1"
62 #define MAX_PRUNE_REC_CNT 100000
64 fhandle_t public_fh
= { 0 };
67 fsid_t fsid
; /* filesystem fsid */
68 char *path
; /* dbm filepair path */
69 DBM
*db
; /* open dbm database */
70 bool_t getall
; /* TRUE if all dbm for prefix open */
71 struct db_list
*next
; /* next db */
74 static struct db_list
*db_fs_list
= NULL
;
75 static char err_str
[] = "DB I/O error has occurred";
77 fh_secondary_key lnkey
;
79 struct link_keys
*next
;
82 extern time_t mapping_update_interval
;
83 extern time_t prune_timeout
;
85 static int fill_link_key(char *linkkey
, fhandle_t
*dfh
, char *name
);
86 static struct db_list
*db_get_db(char *fhpath
, fsid_t
*fsid
, int *errorp
,
88 static struct db_list
*db_get_all_databases(char *fhpath
, bool_t getall
);
89 static void debug_print_fhlist(FILE *fp
, fhlist_ent
*fhrecp
);
90 static void debug_print_linkinfo(FILE *fp
, linkinfo_ent
*fhrecp
);
91 static void debug_print_key(FILE *fp
, char *str1
, char *str2
, char *key
,
93 static void debug_print_key_and_data(FILE *fp
, char *str1
, char *str2
,
94 char *key
, int ksize
, char *data
, int dsize
);
95 static int store_record(struct db_list
*dbp
, void *keyaddr
, int keysize
,
96 void *dataaddr
, int datasize
, char *str
);
97 static void *fetch_record(struct db_list
*dbp
, void *keyaddr
, int keysize
,
98 void *dataaddr
, int *errorp
, char *str
);
99 static int delete_record(struct db_list
*dbp
, void *keyaddr
, int keysize
,
101 static int db_update_fhrec(struct db_list
*dbp
, void *keyaddr
, int keysize
,
102 fhlist_ent
*fhrecp
, char *str
);
103 static int db_update_linkinfo(struct db_list
*dbp
, void *keyaddr
, int keysize
,
104 linkinfo_ent
*linkp
, char *str
);
105 static fhlist_ent
*create_primary_struct(struct db_list
*dbp
, fhandle_t
*dfh
,
106 char *name
, fhandle_t
*fh
, uint_t flags
, fhlist_ent
*fhrecp
,
108 static fhlist_ent
*db_add_primary(struct db_list
*dbp
, fhandle_t
*dfh
,
109 char *name
, fhandle_t
*fh
, uint_t flags
, fhlist_ent
*fhrecp
,
111 static linkinfo_ent
*get_next_link(struct db_list
*dbp
, char *linkkey
,
112 int *linksizep
, linkinfo_ent
*linkp
, void **cookiep
,
113 int *errorp
, char *msg
);
114 static void free_link_cookies(void *cookie
);
115 static void add_mc_path(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
116 fhlist_ent
*fhrecp
, linkinfo_ent
*linkp
, int *errorp
);
117 static linkinfo_ent
*create_link_struct(struct db_list
*dbp
, fhandle_t
*dfh
,
118 char *name
, fhlist_ent
*fhrecp
, int *errorp
);
119 static int db_add_secondary(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
120 fhandle_t
*fh
, fhlist_ent
*fhrecp
);
121 static linkinfo_ent
*update_next_link(struct db_list
*dbp
, char *nextkey
,
122 int nextsize
, char *prevkey
, int prevsize
, int *errorp
);
123 static int update_prev_link(struct db_list
*dbp
, char *nextkey
, int nextsize
,
124 char *prevkey
, int prevsize
);
125 static linkinfo_ent
*update_linked_list(struct db_list
*dbp
, char *nextkey
,
126 int nextsize
, char *prevkey
, int prevsize
, int *errorp
);
127 static int db_update_primary_new_head(struct db_list
*dbp
,
128 linkinfo_ent
*dellinkp
, linkinfo_ent
*nextlinkp
, fhlist_ent
*fhrecp
);
129 static int delete_link_by_key(struct db_list
*dbp
, char *linkkey
,
130 int *linksizep
, int *errorp
, char *errstr
);
131 static int delete_link(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
132 char *nextlinkkey
, int *nextlinksizep
, int *errorp
, char *errstr
);
135 * The following functions do the actual database I/O. Currently use DBM.
139 * The "db_*" functions are functions that access the database using
140 * database-specific calls. Currently the only database supported is
141 * dbm. Because of the limitations of this database, in particular when
142 * it comes to manipulating records with the same key, or using multiple keys,
143 * the following design decisions have been made:
145 * Each file system has a separate dbm file, which are kept open as
146 * accessed, listed in a linked list.
147 * Two possible access mode are available for each file - either by
148 * file handle, or by directory file handle and name. Since
149 * dbm does not allow multiple keys, we will have a primary
150 * and secondary key for each file/link.
151 * The primary key is the pair (inode,gen) which can be obtained
152 * from the file handle. This points to a record with
153 * the full file handle and the secondary key (dfh-key,name)
154 * for one of the links.
155 * The secondary key is the pair (dfh-key,name) where dfh-key is
156 * the primary key for the directory and the name is the
157 * link name. It points to a record that contains the primary
158 * key for the file and to the previous and next hard link
159 * found for this file (if they exist).
161 * Summary of operations:
162 * Adding a new file: Create the primary record and secondary (link)
163 * record and add both to the database. The link record
164 * would have prev and next links set to NULL.
166 * Adding a link to a file in the database: Add the link record,
167 * to the head of the links list (i.e. prev = NULL, next =
168 * secondary key recorded in the primary record). Update
169 * the primary record to point to the new link, and the
170 * secondary record for the old head of list to point to new.
172 * Deleting a file: Delete the link record. If it is the last link
173 * then mark the primary record as deleted but don't delete
174 * that one from the database (in case some clients still
175 * hold the file handle). If there are other links, and the
176 * deleted link is the head of the list (in the primary
177 * record), update the primary record with the new head.
179 * Renaming a file: Add the new link and then delete the old one.
181 * Lookup by file handle (read, write, lookup, etc.) - fetch primary rec.
182 * Lookup by dir info (delete, link, rename) - fetch secondary rec.
184 * XXX NOTE: The code is written single-threaded. To make it multi-
185 * threaded, the following considerations must be made:
186 * 1. Changes/access to the db list must be atomic.
187 * 2. Changes/access for a specific file handle must be atomic
188 * (example: deleting a link may affect up to 4 separate database
189 * entries: the deleted link, the prev and next links if exist,
190 * and the filehandle entry, if it points to the deleted link -
191 * these changes must be atomic).
195 * Create a link key given directory fh and name
198 fill_link_key(char *linkkey
, fhandle_t
*dfh
, char *name
)
200 int linksize
, linksize32
;
202 (void) memcpy(linkkey
, &dfh
->fh_data
, dfh
->fh_len
);
203 (void) strcpy(&linkkey
[dfh
->fh_len
], name
);
204 linksize
= dfh
->fh_len
+ strlen(name
) + 1;
205 linksize32
= ROUNDUP32(linksize
);
206 if (linksize32
> linksize
)
207 bzero(&linkkey
[linksize
], linksize32
- linksize
);
212 * db_get_db - gets the database for the filesystem, or creates one
213 * if none exists. Return the pointer for the database in *dbpp if success.
214 * Return 0 for success, error code otherwise.
216 static struct db_list
*
217 db_get_db(char *fhpath
, fsid_t
*fsid
, int *errorp
, int create_flag
)
219 struct db_list
*p
, *newp
;
225 (p
!= NULL
) && memcmp(&p
->fsid
, fsid
, sizeof (*fsid
));
232 if ((newp
= calloc(1, sizeof (*newp
))) == NULL
) {
234 syslog(LOG_ERR
, gettext(
235 "db_get_db: malloc db failed: Error %s"),
239 (void) sprintf(fsidstr
, "%08x%08x", fsid
->val
[0], fsid
->val
[1]);
240 if ((newp
->path
= malloc(strlen(fhpath
) + 2 + strlen(fsidstr
)))
243 syslog(LOG_ERR
, gettext(
244 "db_get_db: malloc dbpath failed: Error %s"),
248 (void) sprintf(newp
->path
, "%s.%s", fhpath
, fsidstr
);
250 * The open mode is masked by UMASK.
252 if ((newp
->db
= dbm_open(newp
->path
, create_flag
| O_RDWR
, 0666))
255 syslog(LOG_ERR
, gettext(
256 "db_get_db: dbm_open db '%s' failed: Error %s"),
257 newp
->path
, strerror(*errorp
));
258 if (*errorp
== 0) /* should not happen but may */
263 * Add the version identifier (have to check first in the
264 * case the db exists)
266 key
.dptr
= DB_VERSION_STRING
;
267 key
.dsize
= strlen(DB_VERSION_STRING
);
268 data
= dbm_fetch(newp
->db
, key
);
269 if (data
.dptr
== NULL
) {
270 data
.dptr
= DB_VERSION
;
271 data
.dsize
= strlen(DB_VERSION
);
272 (void) dbm_store(newp
->db
, key
, data
, DBM_INSERT
);
275 (void) memcpy(&newp
->fsid
, fsid
, sizeof (*fsid
));
276 newp
->next
= db_fs_list
;
279 (void) printf("db_get_db: db %s opened\n", newp
->path
);
285 if (newp
->db
!= NULL
) {
288 if (newp
->path
!= NULL
) {
297 * db_get_all_databases - gets the database for any filesystem. This is used
298 * when any database will do - typically to retrieve the path for the
299 * public filesystem. If any database is open - return the first one,
300 * otherwise, search for it using fhpath. If getall is TRUE, open all
301 * matching databases, and mark them (to indicate that all such were opened).
302 * Return the pointer for a matching database if success.
304 static struct db_list
*
305 db_get_all_databases(char *fhpath
, bool_t getall
)
307 char *dirptr
, *fhdir
, *fhpathname
;
312 struct db_list
*dbp
, *ret_dbp
;
314 for (dbp
= db_fs_list
; dbp
!= NULL
; dbp
= dbp
->next
) {
315 if (strncmp(fhpath
, dbp
->path
, strlen(fhpath
)) == 0)
320 * if one database for that prefix is open, and either only
321 * one is needed, or already opened all such databases,
322 * return here without exhaustive search
324 if (!getall
|| dbp
->getall
)
327 if ((fhdir
= strdup(fhpath
)) == NULL
) {
328 syslog(LOG_ERR
, gettext(
329 "db_get_all_databases: strdup '%s' Error '%s*'"),
330 fhpath
, strerror(errno
));
335 if ((dirptr
= strrchr(fhdir
, '/')) == NULL
) {
339 if ((fhpathname
= strdup(&dirptr
[1])) == NULL
) {
340 syslog(LOG_ERR
, gettext(
341 "db_get_all_databases: strdup '%s' Error '%s*'"),
342 &dirptr
[1], strerror(errno
));
345 /* Terminate fhdir string at last '/' */
347 /* Search the directory */
349 (void) printf("db_get_all_databases: search '%s' for '%s*'\n",
352 if ((dirp
= opendir(fhdir
)) == NULL
) {
353 syslog(LOG_ERR
, gettext(
354 "db_get_all_databases: opendir '%s' Error '%s*'"),
355 fhdir
, strerror(errno
));
358 len
= strlen(fhpathname
);
359 while ((dp
= readdir(dirp
)) != NULL
) {
360 if (strncmp(fhpathname
, dp
->d_name
, len
) == 0) {
361 dirptr
= &dp
->d_name
[len
+ 1];
362 if (*(dirptr
- 1) != '.') {
365 (void) sscanf(dirptr
, "%08lx%08lx",
366 (ulong_t
*)&fsid
.val
[0], (ulong_t
*)&fsid
.val
[1]);
367 dbp
= db_get_db(fhpath
, &fsid
, &error
, 0);
376 (void) closedir(dirp
);
378 if (fhpathname
!= NULL
)
386 debug_print_key(FILE *fp
, char *str1
, char *str2
, char *key
, int ksize
)
388 (void) fprintf(fp
, "%s: %s key (%d) ", str1
, str2
, ksize
);
389 debug_opaque_print(fp
, key
, ksize
);
390 /* may be inode,name - try to print the fields */
391 if (ksize
>= NFS_FHMAXDATA
) {
392 (void) fprintf(fp
, ": inode ");
393 debug_opaque_print(fp
, &key
[2], sizeof (int));
394 (void) fprintf(fp
, ", gen ");
395 debug_opaque_print(fp
, &key
[2 + sizeof (int)], sizeof (int));
396 if (ksize
> NFS_FHMAXDATA
) {
397 (void) fprintf(fp
, ", name '%s'", &key
[NFS_FHMAXDATA
]);
400 (void) fprintf(fp
, "\n");
404 debug_print_linkinfo(FILE *fp
, linkinfo_ent
*linkp
)
408 (void) fprintf(fp
, "linkinfo:\ndfh: ");
409 debug_opaque_print(fp
, (void *)&linkp
->dfh
, sizeof (linkp
->dfh
));
410 (void) fprintf(fp
, "\nname: '%s'", LN_NAME(linkp
));
411 (void) fprintf(fp
, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n",
412 linkp
->mtime
, linkp
->atime
, linkp
->flags
, linkp
->reclen
);
413 (void) fprintf(fp
, "offsets: fhkey %d, name %d, next %d, prev %d\n",
414 linkp
->fhkey_offset
, linkp
->name_offset
, linkp
->next_offset
,
416 debug_print_key(fp
, "fhkey", "", LN_FHKEY(linkp
), LN_FHKEY_LEN(linkp
));
417 debug_print_key(fp
, "next", "", LN_NEXT(linkp
), LN_NEXT_LEN(linkp
));
418 debug_print_key(fp
, "prev", "", LN_PREV(linkp
), LN_PREV_LEN(linkp
));
422 debug_print_fhlist(FILE *fp
, fhlist_ent
*fhrecp
)
426 (void) fprintf(fp
, "fhrec:\nfh: ");
427 debug_opaque_print(fp
, (void *)&fhrecp
->fh
, sizeof (fhrecp
->fh
));
428 (void) fprintf(fp
, "name '%s', dfh: ", fhrecp
->name
);
429 debug_opaque_print(fp
, (void *)&fhrecp
->dfh
, sizeof (fhrecp
->dfh
));
430 (void) fprintf(fp
, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n",
431 fhrecp
->mtime
, fhrecp
->atime
, fhrecp
->flags
, fhrecp
->reclen
);
435 debug_print_key_and_data(FILE *fp
, char *str1
, char *str2
, char *key
,
436 int ksize
, char *data
, int dsize
)
438 debug_print_key(fp
, str1
, str2
, key
, ksize
);
439 (void) fprintf(fp
, " ==> (%p,%d)\n", (void *)data
, dsize
);
440 if (ksize
> NFS_FHMAXDATA
) {
442 /* probably a link struct */
443 (void) memcpy(&inf
, data
, sizeof (linkinfo_ent
));
444 debug_print_linkinfo(fp
, &inf
);
445 } else if (ksize
== NFS_FHMAXDATA
) {
447 /* probably an fhlist struct */
448 (void) memcpy(&inf
, data
, sizeof (linkinfo_ent
));
449 debug_print_fhlist(fp
, &inf
);
452 debug_opaque_print(fp
, data
, dsize
);
457 * store_record - store the record in the database and return 0 for success
458 * or error code otherwise.
461 store_record(struct db_list
*dbp
, void *keyaddr
, int keysize
, void *dataaddr
,
462 int datasize
, char *str
)
466 char *errfmt
= "store_record: dbm_store failed, Error: %s\n";
472 data
.dptr
= dataaddr
;
473 data
.dsize
= datasize
;
476 debug_print_key_and_data(stdout
, str
, "dbm_store:\n ",
477 key
.dptr
, key
.dsize
, data
.dptr
, data
.dsize
);
479 if (dbm_store(dbp
->db
, key
, data
, DBM_REPLACE
) < 0) {
480 /* Could not store */
481 error
= dbm_error(dbp
->db
);
482 dbm_clearerr(dbp
->db
);
486 err
= strerror(errno
);
491 } else { /* should not happen but sometimes does */
496 debug_print_key(stderr
, str
, "store_record:"
497 "dbm_store:\n", key
.dptr
, key
.dsize
);
498 (void) fprintf(stderr
, errfmt
, err
);
500 syslog(LOG_ERR
, gettext(errfmt
), err
);
507 * fetch_record - fetch the record from the database and return 0 for success
508 * and errno for failure.
509 * dataaddr is an optional valid address for the result. If dataaddr
510 * is non-null, then that memory is already alloc'd. Else, alloc it, and
511 * the caller must free the returned struct when done.
514 fetch_record(struct db_list
*dbp
, void *keyaddr
, int keysize
, void *dataaddr
,
515 int *errorp
, char *str
)
518 char *errfmt
= "fetch_record: dbm_fetch failed, Error: %s\n";
526 data
= dbm_fetch(dbp
->db
, key
);
527 if (data
.dptr
== NULL
) {
528 /* see if there is a database error */
529 if (dbm_error(dbp
->db
)) {
530 /* clear and report the database error */
531 dbm_clearerr(dbp
->db
);
533 err
= strerror(*errorp
);
534 syslog(LOG_ERR
, gettext(errfmt
), err
);
536 /* primary record not in database */
540 err
= strerror(*errorp
);
541 debug_print_key(stderr
, str
, "fetch_record:"
542 "dbm_fetch:\n", key
.dptr
, key
.dsize
);
543 (void) fprintf(stderr
, errfmt
, err
);
548 /* copy to local struct because dbm may return non-aligned pointers */
549 if ((dataaddr
== NULL
) &&
550 ((dataaddr
= malloc(data
.dsize
)) == NULL
)) {
552 syslog(LOG_ERR
, gettext(
553 "%s: dbm_fetch - malloc %ld: Error %s"),
554 str
, data
.dsize
, strerror(*errorp
));
557 (void) memcpy(dataaddr
, data
.dptr
, data
.dsize
);
559 debug_print_key_and_data(stdout
, str
, "fetch_record:"
560 "dbm_fetch:\n", key
.dptr
, key
.dsize
,
561 dataaddr
, data
.dsize
);
568 * delete_record - delete the record from the database and return 0 for success
569 * or error code for failure.
572 delete_record(struct db_list
*dbp
, void *keyaddr
, int keysize
, char *str
)
576 char *errfmt
= "delete_record: dbm_delete failed, Error: %s\n";
584 debug_print_key(stdout
, str
, "delete_record:"
585 "dbm_delete:\n", key
.dptr
, key
.dsize
);
587 if (dbm_delete(dbp
->db
, key
) < 0) {
588 error
= dbm_error(dbp
->db
);
589 dbm_clearerr(dbp
->db
);
593 err
= strerror(errno
);
598 } else { /* should not happen but sometimes does */
603 debug_print_key(stderr
, str
, "delete_record:"
604 "dbm_delete:\n", key
.dptr
, key
.dsize
);
605 (void) fprintf(stderr
, errfmt
, err
);
607 syslog(LOG_ERR
, gettext(errfmt
), err
);
613 * db_update_fhrec - puts fhrec in db with updated atime if more than
614 * mapping_update_interval seconds passed. Return 0 if success, error otherwise.
617 db_update_fhrec(struct db_list
*dbp
, void *keyaddr
, int keysize
,
618 fhlist_ent
*fhrecp
, char *str
)
620 time_t cur_time
= time(0);
622 if (difftime(cur_time
, fhrecp
->atime
) >= mapping_update_interval
) {
623 fhrecp
->atime
= cur_time
;
624 return (store_record(dbp
, keyaddr
, keysize
,
625 fhrecp
, fhrecp
->reclen
, str
));
631 * db_update_linkinfo - puts linkinfo in db with updated atime if more than
632 * mapping_update_interval seconds passed. Return 0 if success, error otherwise.
635 db_update_linkinfo(struct db_list
*dbp
, void *keyaddr
, int keysize
,
636 linkinfo_ent
*linkp
, char *str
)
638 time_t cur_time
= time(0);
640 if (difftime(cur_time
, linkp
->atime
) >= mapping_update_interval
) {
641 linkp
->atime
= cur_time
;
642 return (store_record(dbp
, keyaddr
, keysize
,
643 linkp
, linkp
->reclen
, str
));
649 * create_primary_struct - add primary record to the database.
650 * Database must be open when this function is called.
651 * If success, return the added database entry. fhrecp may be used to
652 * provide an existing memory area, else malloc it. If failed, *errorp
653 * contains the error code and return NULL.
656 create_primary_struct(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
657 fhandle_t
*fh
, uint_t flags
, fhlist_ent
*fhrecp
, int *errorp
)
660 fhlist_ent
*new_fhrecp
= fhrecp
;
662 reclen1
= offsetof(fhlist_ent
, name
) + strlen(name
) + 1;
663 reclen
= ROUNDUP32(reclen1
);
664 if (fhrecp
== NULL
) { /* allocated the memory */
665 if ((new_fhrecp
= malloc(reclen
)) == NULL
) {
667 syslog(LOG_ERR
, gettext(
668 "create_primary_struct: malloc %d Error %s"),
669 reclen
, strerror(*errorp
));
673 /* Fill in the fields */
674 (void) memcpy(&new_fhrecp
->fh
, fh
, sizeof (*fh
));
675 (void) memcpy(&new_fhrecp
->dfh
, dfh
, sizeof (*dfh
));
676 new_fhrecp
->flags
= flags
;
677 if (dfh
== &public_fh
)
678 new_fhrecp
->flags
|= PUBLIC_PATH
;
680 new_fhrecp
->flags
&= ~PUBLIC_PATH
;
681 new_fhrecp
->mtime
= time(0);
682 new_fhrecp
->atime
= new_fhrecp
->mtime
;
683 (void) strcpy(new_fhrecp
->name
, name
);
684 if (reclen1
< reclen
) {
685 bzero((char *)((uintptr_t)new_fhrecp
+ reclen1
),
688 new_fhrecp
->reclen
= reclen
;
689 *errorp
= store_record(dbp
, &fh
->fh_data
, fh
->fh_len
, new_fhrecp
,
690 new_fhrecp
->reclen
, "create_primary_struct");
692 /* Could not store */
693 if (fhrecp
== NULL
) /* caller did not supply pointer */
701 * db_add_primary - add primary record to the database.
702 * If record already in and live, return it (even if for a different link).
703 * If in database but marked deleted, replace it. If not in database, add it.
704 * Database must be open when this function is called.
705 * If success, return the added database entry. fhrecp may be used to
706 * provide an existing memory area, else malloc it. If failed, *errorp
707 * contains the error code and return NULL.
710 db_add_primary(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
, fhandle_t
*fh
,
711 uint_t flags
, fhlist_ent
*fhrecp
, int *errorp
)
713 fhlist_ent
*new_fhrecp
;
714 fh_primary_key fhkey
;
717 (void) printf("db_add_primary entered: name '%s'\n", name
);
719 bcopy(&fh
->fh_data
, fhkey
, fh
->fh_len
);
720 new_fhrecp
= fetch_record(dbp
, fhkey
, fh
->fh_len
, (void *)fhrecp
,
721 errorp
, "db_add_primary");
722 if (new_fhrecp
!= NULL
) {
723 /* primary record is in the database */
724 /* Update atime if needed */
725 *errorp
= db_update_fhrec(dbp
, fhkey
, fh
->fh_len
, new_fhrecp
,
726 "db_add_primary put fhrec");
728 (void) printf("db_add_primary exits(2): name '%s'\n",
732 /* primary record not in database - create it */
733 new_fhrecp
= create_primary_struct(dbp
, dfh
, name
, fh
, flags
,
735 if (new_fhrecp
== NULL
) {
736 /* Could not store */
739 "db_add_primary exits(1): name '%s' Error %s\n",
740 name
, ((*errorp
>= 0) ? strerror(*errorp
) :
746 (void) printf("db_add_primary exits(0): name '%s'\n", name
);
751 * get_next_link - get and check the next link in the chain.
752 * Re-use space if linkp param non-null. Also set *linkkey and *linksizep
753 * to values for next link (*linksizep set to 0 if last link).
754 * cookie is used to detect corrupted link entries XXXXXXX
755 * Return the link pointer or NULL if none.
757 static linkinfo_ent
*
758 get_next_link(struct db_list
*dbp
, char *linkkey
, int *linksizep
,
759 linkinfo_ent
*linkp
, void **cookiep
, int *errorp
, char *msg
)
761 int linksize
, nextsize
;
763 linkinfo_ent
*new_linkp
= linkp
;
764 struct link_keys
*lnp
;
766 linksize
= *linksizep
;
770 new_linkp
= fetch_record(dbp
, linkkey
, linksize
, (void *)linkp
,
772 if (new_linkp
== NULL
)
775 /* Set linkkey to point to next record */
776 nextsize
= LN_NEXT_LEN(new_linkp
);
780 /* Add this key to the cookie list */
781 if ((lnp
= malloc(sizeof (struct link_keys
))) == NULL
) {
782 syslog(LOG_ERR
, gettext("get_next_key: malloc error %s\n"),
784 if ((new_linkp
!= NULL
) && (linkp
== NULL
))
788 (void) memcpy(lnp
->lnkey
, linkkey
, linksize
);
789 lnp
->lnsize
= linksize
;
790 lnp
->next
= *(struct link_keys
**)cookiep
;
791 *cookiep
= (void *)lnp
;
793 /* Make sure record does not point to itself or other internal loops */
794 nextkey
= LN_NEXT(new_linkp
);
795 for (; lnp
!= NULL
; lnp
= lnp
->next
) {
796 if ((nextsize
== lnp
->lnsize
) && (memcmp(
797 lnp
->lnkey
, nextkey
, nextsize
) == 0)) {
800 * XXX This entry's next pointer points to
801 * itself. This is only a work-around, remove
802 * this check once bug 4203186 is fixed.
805 (void) fprintf(stderr
,
806 "%s: get_next_link: last record invalid.\n",
808 debug_print_key_and_data(stderr
, msg
,
809 "invalid rec:\n ", linkkey
, linksize
,
810 (char *)new_linkp
, new_linkp
->reclen
);
812 /* Return as if this is the last link */
816 (void) memcpy(linkkey
, nextkey
, nextsize
);
817 *linksizep
= nextsize
;
822 * free_link_cookies - free the cookie list
825 free_link_cookies(void *cookie
)
827 struct link_keys
*dellnp
, *lnp
;
829 lnp
= (struct link_keys
*)cookie
;
830 while (lnp
!= NULL
) {
838 * add_mc_path - add a mc link to a file that has other links. Add it at end
839 * of linked list. Called when it's known there are other links.
842 add_mc_path(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
843 fhlist_ent
*fhrecp
, linkinfo_ent
*linkp
, int *errorp
)
845 fh_secondary_key linkkey
;
847 linkinfo_ent lastlink
, *lastlinkp
;
850 linksize
= fill_link_key(linkkey
, &fhrecp
->dfh
, fhrecp
->name
);
853 lastlinkp
= get_next_link(dbp
, linkkey
, &linksize
, &lastlink
,
854 &cookie
, errorp
, "add_mc_path");
855 } while (linksize
> 0);
856 free_link_cookies(cookie
);
857 /* reached end of list */
858 if (lastlinkp
== NULL
) {
861 (void) fprintf(stderr
, "add_mc_path link is null\n");
865 /* Add new link after last link */
867 * next - link key for the next in the list - add at end so null.
868 * prev - link key for the previous link in the list.
870 linkp
->prev_offset
= linkp
->next_offset
; /* aligned */
871 linksize
= fill_link_key(LN_PREV(linkp
), &lastlinkp
->dfh
,
873 linkp
->reclen
= linkp
->prev_offset
+ linksize
; /* aligned */
875 /* Add the link information to the database */
876 linksize
= fill_link_key(linkkey
, dfh
, name
);
877 *errorp
= store_record(dbp
, linkkey
, linksize
,
878 linkp
, linkp
->reclen
, "add_mc_path");
882 /* Now update previous last link to point forward to new link */
883 /* Copy prev link out since it's going to be overwritten */
884 linksize
= LN_PREV_LEN(lastlinkp
);
885 (void) memcpy(linkkey
, LN_PREV(lastlinkp
), linksize
);
886 /* Update previous last link to point to new one */
887 len
= fill_link_key(LN_NEXT(lastlinkp
), dfh
, name
);
888 lastlinkp
->prev_offset
= lastlinkp
->next_offset
+ len
; /* aligned */
889 (void) memcpy(LN_PREV(lastlinkp
), linkkey
, linksize
);
890 lastlinkp
->reclen
= lastlinkp
->prev_offset
+ linksize
;
891 /* Update the link information to the database */
892 linksize
= fill_link_key(linkkey
, &lastlinkp
->dfh
, LN_NAME(lastlinkp
));
893 *errorp
= store_record(dbp
, linkkey
, linksize
,
894 lastlinkp
, lastlinkp
->reclen
, "add_mc_path prev");
898 * create_link_struct - create the secondary struct.
899 * (dfh,name) is the secondary key, fhrec is the primary record for the file
900 * and linkpp is a place holder for the record (could be null).
901 * Insert the record to the database.
902 * Return 0 if success, error otherwise.
904 static linkinfo_ent
*
905 create_link_struct(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
906 fhlist_ent
*fhrecp
, int *errorp
)
908 fh_secondary_key linkkey
;
912 if ((linkp
= malloc(sizeof (linkinfo_ent
))) == NULL
) {
914 syslog(LOG_ERR
, gettext(
915 "create_link_struct: malloc failed: Error %s"),
919 if (dfh
== &public_fh
)
920 linkp
->flags
|= PUBLIC_PATH
;
922 linkp
->flags
&= ~PUBLIC_PATH
;
923 (void) memcpy(&linkp
->dfh
, dfh
, sizeof (*dfh
));
924 linkp
->mtime
= time(0);
925 linkp
->atime
= linkp
->mtime
;
926 /* Calculate offsets of variable fields */
927 /* fhkey - primary key (inode/gen) */
928 /* name - component name (in directory dfh) */
929 linkp
->fhkey_offset
= ROUNDUP32(offsetof(linkinfo_ent
, varbuf
));
930 len
= fill_link_key(LN_FHKEY(linkp
), &fhrecp
->fh
, name
);
931 linkp
->name_offset
= linkp
->fhkey_offset
+ fhrecp
->fh
.fh_len
;
932 linkp
->next_offset
= linkp
->fhkey_offset
+ len
; /* aligned */
934 * next - link key for the next link in the list - NULL if it's
935 * the first link. If this is the public fs, only one link allowed.
936 * Avoid setting a multi-component path as primary path,
940 if (memcmp(&fhrecp
->dfh
, dfh
, sizeof (*dfh
)) ||
941 strcmp(fhrecp
->name
, name
)) {
942 /* different link than the one that's in the record */
943 if (dfh
== &public_fh
) {
944 /* parent is public fh - either multi-comp or root */
945 if (memcmp(&fhrecp
->fh
, &public_fh
,
946 sizeof (public_fh
))) {
947 /* multi-comp path */
948 add_mc_path(dbp
, dfh
, name
, fhrecp
, linkp
,
957 /* new link to a file with a different one already */
958 len
= fill_link_key(LN_NEXT(linkp
), &fhrecp
->dfh
,
963 * prev - link key for the previous link in the list - since we
964 * always insert at the front of the list, it's always initially NULL.
966 linkp
->prev_offset
= linkp
->next_offset
+ len
; /* aligned */
967 linkp
->reclen
= linkp
->prev_offset
;
969 /* Add the link information to the database */
970 linksize
= fill_link_key(linkkey
, dfh
, name
);
971 *errorp
= store_record(dbp
, linkkey
, linksize
, linkp
, linkp
->reclen
,
972 "create_link_struct");
981 * db_add_secondary - add secondary record to the database (for the directory
983 * Assumes this is a new link, not yet in the database, and that the primary
984 * record is already in.
985 * If fhrecp is non-null, then fhrecp is the primary record.
986 * Database must be open when this function is called.
987 * Return 0 if success, error code otherwise.
990 db_add_secondary(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
991 fhandle_t
*fh
, fhlist_ent
*fhrecp
)
993 int nextsize
, len
, error
;
994 linkinfo_ent nextlink
, *newlinkp
, *nextlinkp
;
997 fhlist_ent
*new_fhrecp
= fhrecp
;
998 fh_primary_key fhkey
;
1001 (void) printf("db_add_secondary entered: name '%s'\n", name
);
1003 bcopy(&fh
->fh_data
, fhkey
, fh
->fh_len
);
1004 if (fhrecp
== NULL
) {
1005 /* Fetch the primary record */
1006 new_fhrecp
= fetch_record(dbp
, fhkey
, fh
->fh_len
, NULL
,
1007 &error
, "db_add_secondary primary");
1008 if (new_fhrecp
== NULL
) {
1012 /* Update fhrec atime if needed */
1013 error
= db_update_fhrec(dbp
, fhkey
, fh
->fh_len
, new_fhrecp
,
1014 "db_add_secondary primary");
1015 fhflags
= new_fhrecp
->flags
;
1016 /* now create and insert the secondary record */
1017 newlinkp
= create_link_struct(dbp
, dfh
, name
, new_fhrecp
, &error
);
1018 if (fhrecp
== NULL
) {
1022 if (newlinkp
== NULL
) {
1024 (void) printf("create_link_struct '%s' Error %s\n",
1025 name
, ((error
>= 0) ? strerror(error
) :
1029 nextsize
= LN_NEXT_LEN(newlinkp
);
1030 if (nextsize
== 0) {
1031 /* No next - can exit now */
1033 (void) printf("db_add_secondary: no next link\n");
1039 * Update the linked list to point to new head: replace head of
1040 * list in the primary record, then update previous secondary record
1041 * to point to new head
1043 new_fhrecp
= create_primary_struct(dbp
, dfh
, name
, fh
, fhflags
,
1044 new_fhrecp
, &error
);
1045 if (new_fhrecp
== NULL
) {
1048 "db_add_secondary: replace primary failed\n");
1051 } else if (fhrecp
== NULL
) {
1056 * newlink is the new head of the list, with its "next" pointing to
1057 * the old head, and its "prev" pointing to NULL. We now need to
1058 * modify the "next" entry to have its "prev" point to the new entry.
1060 nextaddr
= LN_NEXT(newlinkp
);
1062 debug_print_key(stderr
, "db_add_secondary", "next key\n ",
1063 nextaddr
, nextsize
);
1065 /* Get the next link entry from the database */
1066 nextlinkp
= fetch_record(dbp
, nextaddr
, nextsize
, (void *)&nextlink
,
1067 &error
, "db_add_secondary next link");
1068 if (nextlinkp
== NULL
) {
1071 "db_add_secondary: fetch next link failed\n");
1077 * since the "prev" field is the only field to be changed, and it's
1078 * the last in the link record, we only need to modify it (and reclen).
1079 * Re-use link to update the next record.
1081 len
= fill_link_key(LN_PREV(nextlinkp
), dfh
, name
);
1082 nextlinkp
->reclen
= nextlinkp
->prev_offset
+ len
;
1083 error
= store_record(dbp
, nextaddr
, nextsize
, nextlinkp
,
1084 nextlinkp
->reclen
, "db_add_secondary");
1087 "db_add_secondary exits(%d): name '%s'\n", error
, name
);
1093 * Update the next link to point to the new prev.
1094 * Return 0 for success, error code otherwise.
1095 * If successful, and nextlinkpp is non-null,
1096 * *nextlinkpp contains the record for the next link, since
1097 * we may will it if the primary record should be updated.
1099 static linkinfo_ent
*
1100 update_next_link(struct db_list
*dbp
, char *nextkey
, int nextsize
,
1101 char *prevkey
, int prevsize
, int *errorp
)
1103 linkinfo_ent
*nextlinkp
, *linkp1
;
1105 if ((nextlinkp
= malloc(sizeof (linkinfo_ent
))) == NULL
) {
1107 syslog(LOG_ERR
, gettext(
1108 "update_next_link: malloc next Error %s"),
1113 nextlinkp
= fetch_record(dbp
, nextkey
, nextsize
, nextlinkp
,
1114 errorp
, "update next");
1115 /* if there is no next record - ok */
1116 if (nextlinkp
== NULL
) {
1117 /* Return no error */
1122 /* Set its prev to the prev of the deleted record */
1123 nextlinkp
->reclen
= ROUNDUP32(nextlinkp
->reclen
-
1124 LN_PREV_LEN(nextlinkp
) + prevsize
);
1125 /* Change the len and set prev */
1127 (void) memcpy(LN_PREV(nextlinkp
), prevkey
, prevsize
);
1129 /* No other changes needed because prev is last field */
1130 *errorp
= store_record(dbp
, nextkey
, nextsize
, nextlinkp
,
1131 nextlinkp
->reclen
, "update_next");
1140 * Update the prev link to point to the new next.
1141 * Return 0 for success, error code otherwise.
1144 update_prev_link(struct db_list
*dbp
, char *nextkey
, int nextsize
,
1145 char *prevkey
, int prevsize
)
1147 linkinfo_ent prevlink
, *prevlinkp
;
1150 /* Update its next to the given one */
1151 prevlinkp
= fetch_record(dbp
, prevkey
, prevsize
, &prevlink
, &error
,
1153 /* if error there is no next record - ok */
1154 if (prevlinkp
== NULL
) {
1157 diff
= nextsize
- LN_NEXT_LEN(prevlinkp
);
1158 prevlinkp
->reclen
= ROUNDUP32(prevlinkp
->reclen
+ diff
);
1159 /* Change the len and set next - may push prev */
1161 char *ptr
= LN_PREV(prevlinkp
);
1163 prevlinkp
->prev_offset
+= diff
;
1164 (void) memcpy(LN_PREV(prevlinkp
), ptr
, LN_PREV_LEN(prevlinkp
));
1167 (void) memcpy(LN_NEXT(prevlinkp
), nextkey
, nextsize
);
1169 /* Store updated record */
1170 error
= store_record(dbp
, prevkey
, prevsize
, prevlinkp
,
1171 prevlinkp
->reclen
, "update_prev");
1176 * update_linked_list - update the next link to point back to prev, and vice
1177 * versa. Normally called by delete_link to drop the deleted link from the
1178 * linked list of hard links for the file. next and prev are the keys of next
1179 * and previous links for the deleted link in the list (could be NULL).
1180 * Return 0 for success, error code otherwise.
1181 * If successful, and nextlinkpp is non-null,
1182 * return the record for the next link, since
1183 * if the primary record should be updated we'll need it. In this case,
1184 * actually allocate the space for it because we can't tell otherwise.
1186 static linkinfo_ent
*
1187 update_linked_list(struct db_list
*dbp
, char *nextkey
, int nextsize
,
1188 char *prevkey
, int prevsize
, int *errorp
)
1190 linkinfo_ent
*nextlinkp
= NULL
;
1194 nextlinkp
= update_next_link(dbp
, nextkey
, nextsize
,
1195 prevkey
, prevsize
, errorp
);
1196 if (nextlinkp
== NULL
) {
1197 /* not an error if no next link */
1200 (void) fprintf(stderr
,
1201 "update_next_link Error %s\n",
1202 ((*errorp
>= 0) ? strerror(*errorp
) :
1210 *errorp
= update_prev_link(dbp
, nextkey
, nextsize
,
1214 (void) fprintf(stderr
,
1215 "update_prev_link Error %s\n",
1216 ((*errorp
>= 0) ? strerror(*errorp
) :
1219 if (nextlinkp
!= NULL
)
1228 * db_update_primary_new_head - Update a primary record that the head of
1229 * the list is deleted. Similar to db_add_primary, but the primary record
1230 * must exist, and is always replaced with one pointing to the new link,
1231 * unless it does not point to the deleted link. If the link we deleted
1232 * was the last link, the delete the primary record as well.
1233 * Return 0 for success, error code otherwise.
1236 db_update_primary_new_head(struct db_list
*dbp
, linkinfo_ent
*dellinkp
,
1237 linkinfo_ent
*nextlinkp
, fhlist_ent
*fhrecp
)
1240 char *name
, *next_name
;
1242 fh_primary_key fhkey
;
1244 dfh
= &dellinkp
->dfh
;
1245 name
= LN_NAME(dellinkp
);
1246 /* If the deleted link was not the head of the list, we are done */
1247 if (memcmp(&fhrecp
->dfh
, dfh
, sizeof (*dfh
)) ||
1248 strcmp(fhrecp
->name
, name
)) {
1249 /* should never be here... */
1251 (void) fprintf(stderr
,
1252 "db_update_primary_new_head: primary "
1253 "is for [%s,", name
);
1254 debug_opaque_print(stderr
, (void *)dfh
, sizeof (*dfh
));
1255 (void) fprintf(stderr
, "], not [%s,", fhrecp
->name
);
1256 debug_opaque_print(stderr
, (void *)&fhrecp
->dfh
,
1257 sizeof (fhrecp
->dfh
));
1258 (void) fprintf(stderr
, "]\n");
1260 return (0); /* not head of list so done */
1262 /* Set the head to nextkey if exists. Otherwise, mark file as deleted */
1263 bcopy(&fhrecp
->fh
.fh_data
, fhkey
, fhrecp
->fh
.fh_len
);
1264 if (nextlinkp
== NULL
) {
1266 /* remove primary record from database */
1267 (void) delete_record(dbp
,
1268 fhkey
, fhrecp
->fh
.fh_len
,
1269 "db_update_primary_new_head: fh delete");
1273 * There are still "live" links, so update the primary record.
1275 next_name
= LN_NAME(nextlinkp
);
1276 fhrecp
->reclen
= ROUNDUP32(offsetof(fhlist_ent
, name
) +
1277 strlen(next_name
) + 1);
1278 /* Replace link data with the info for the next link */
1279 (void) memcpy(&fhrecp
->dfh
, &nextlinkp
->dfh
,
1280 sizeof (nextlinkp
->dfh
));
1281 (void) strcpy(fhrecp
->name
, next_name
);
1284 fhrecp
->mtime
= time(0);
1285 fhrecp
->atime
= fhrecp
->mtime
;
1286 error
= store_record(dbp
,
1287 fhkey
, fhrecp
->fh
.fh_len
, fhrecp
,
1288 fhrecp
->reclen
, "db_update_primary_new_head: fh");
1293 * Exported functions
1297 * db_add - add record to the database. If dfh, fh and name are all here,
1298 * add both primary and secondary records. If fh is not available, don't
1300 * Assumes this is a new file, not yet in the database and that the record
1301 * for fh is already in.
1302 * Return 0 for success, error code otherwise.
1305 db_add(char *fhpath
, fhandle_t
*dfh
, char *name
, fhandle_t
*fh
, uint_t flags
)
1307 struct db_list
*dbp
= NULL
;
1308 fhlist_ent fhrec
, *fhrecp
;
1312 /* nothing to add */
1315 if (fh
== &public_fh
) {
1316 dbp
= db_get_all_databases(fhpath
, FALSE
);
1318 dbp
= db_get_db(fhpath
, &fh
->fh_fsid
, &error
, O_CREAT
);
1320 for (; dbp
!= NULL
; dbp
= ((fh
!= &public_fh
) ? NULL
: dbp
->next
)) {
1322 (void) printf("db_add: name '%s', db '%s'\n",
1325 fhrecp
= db_add_primary(dbp
, dfh
, name
, fh
, flags
,
1327 if (fhrecp
== NULL
) {
1330 if ((dfh
== NULL
) || (name
== NULL
)) {
1331 /* Can't add link information */
1332 syslog(LOG_ERR
, gettext(
1333 "db_add: dfh %p, name %p - invalid"),
1334 (void *)dfh
, (void *)name
);
1338 if (fh
== &public_fh
) {
1339 while ((fhrecp
!= NULL
) && strcmp(name
, fhrecp
->name
)) {
1340 /* Replace the public fh rather than add link */
1341 error
= db_delete_link(fhpath
, dfh
,
1343 fhrecp
= db_add_primary(dbp
, dfh
, name
, fh
,
1344 flags
, &fhrec
, &error
);
1346 if (fhrecp
== NULL
) {
1350 error
= db_add_secondary(dbp
, dfh
, name
, fh
, fhrecp
);
1351 if (fhrecp
!= &fhrec
) {
1359 * db_lookup - search the database for the file identified by fh.
1360 * Return the entry in *fhrecpp if found, or NULL with error set otherwise.
1363 db_lookup(char *fhpath
, fhandle_t
*fh
, fhlist_ent
*fhrecp
, int *errorp
)
1365 struct db_list
*dbp
;
1366 fh_primary_key fhkey
;
1368 if ((fhpath
== NULL
) || (fh
== NULL
) || (errorp
== NULL
)) {
1374 if (fh
== &public_fh
) {
1375 dbp
= db_get_all_databases(fhpath
, FALSE
);
1377 dbp
= db_get_db(fhpath
, &fh
->fh_fsid
, errorp
, O_CREAT
);
1380 /* Could not get or create database */
1383 bcopy(&fh
->fh_data
, fhkey
, fh
->fh_len
);
1384 fhrecp
= fetch_record(dbp
, fhkey
, fh
->fh_len
, fhrecp
,
1385 errorp
, "db_lookup");
1386 /* Update fhrec atime if needed */
1387 if (fhrecp
!= NULL
) {
1388 *errorp
= db_update_fhrec(dbp
, fhkey
, fh
->fh_len
, fhrecp
,
1395 * db_lookup_link - search the database for the file identified by (dfh,name).
1396 * If the link was found, use it to search for the primary record.
1397 * Return 0 and set the entry in *fhrecpp if found, return error otherwise.
1400 db_lookup_link(char *fhpath
, fhandle_t
*dfh
, char *name
, fhlist_ent
*fhrecp
,
1403 struct db_list
*dbp
;
1404 fh_secondary_key linkkey
;
1405 linkinfo_ent
*linkp
;
1406 int linksize
, fhkeysize
;
1409 if ((fhpath
== NULL
) || (dfh
== NULL
) || (name
== NULL
) ||
1416 if (dfh
== &public_fh
) {
1417 dbp
= db_get_all_databases(fhpath
, FALSE
);
1419 dbp
= db_get_db(fhpath
, &dfh
->fh_fsid
, errorp
, O_CREAT
);
1422 /* Could not get or create database */
1425 /* Get the link record */
1426 linksize
= fill_link_key(linkkey
, dfh
, name
);
1427 linkp
= fetch_record(dbp
, linkkey
, linksize
, NULL
, errorp
,
1428 "db_lookup_link link");
1429 if (linkp
!= NULL
) {
1430 /* Now use link to search for fh entry */
1431 fhkeysize
= LN_FHKEY_LEN(linkp
);
1432 fhkey
= LN_FHKEY(linkp
);
1433 fhrecp
= fetch_record(dbp
, fhkey
, fhkeysize
,
1434 (void *)fhrecp
, errorp
, "db_lookup_link fh");
1435 /* Update fhrec atime if needed */
1436 if (fhrecp
!= NULL
) {
1437 *errorp
= db_update_fhrec(dbp
, fhkey
, fhkeysize
, fhrecp
,
1438 "db_lookup_link fhrec");
1440 /* Update link atime if needed */
1441 *errorp
= db_update_linkinfo(dbp
, linkkey
, linksize
, linkp
,
1442 "db_lookup_link link");
1451 * delete_link - delete the requested link from the database. If it's the
1452 * last link in the database for that file then remove the primary record
1453 * as well. *errorp contains the returned error code.
1454 * Return ENOENT if link not in database and 0 otherwise.
1457 delete_link_by_key(struct db_list
*dbp
, char *linkkey
, int *linksizep
,
1458 int *errorp
, char *errstr
)
1460 int nextsize
, prevsize
, fhkeysize
, linksize
;
1461 char *nextkey
, *prevkey
, *fhkey
;
1462 linkinfo_ent
*dellinkp
, *nextlinkp
;
1463 fhlist_ent
*fhrecp
, fhrec
;
1466 linksize
= *linksizep
;
1467 /* Get the link record */
1468 dellinkp
= fetch_record(dbp
, linkkey
, linksize
, NULL
, errorp
, errstr
);
1469 if (dellinkp
== NULL
) {
1471 * Link not in database.
1474 debug_print_key(stderr
, errstr
,
1475 "link not in database\n",
1483 * 1. Normal case - only one link to delete: the link next and
1484 * prev should be NULL, and fhrec's name/dfh are same
1485 * as the link. Remove the link and fhrec.
1486 * 2. Multiple hard links, and the deleted link is the head of
1487 * the list. Remove the link and replace the link key in
1488 * the primary record to point to the new head.
1489 * 3. Multiple hard links, and the deleted link is not the
1490 * head of the list (not the same as in fhrec) - just
1491 * delete the link and update the previous and next records
1492 * in the links linked list.
1495 /* Get next and prev keys for linked list updates */
1496 nextsize
= LN_NEXT_LEN(dellinkp
);
1497 nextkey
= ((nextsize
> 0) ? LN_NEXT(dellinkp
) : NULL
);
1498 prevsize
= LN_PREV_LEN(dellinkp
);
1499 prevkey
= ((prevsize
> 0) ? LN_PREV(dellinkp
) : NULL
);
1500 /* Update the linked list for the file */
1501 nextlinkp
= update_linked_list(dbp
, nextkey
, nextsize
,
1502 prevkey
, prevsize
, errorp
);
1503 if ((nextlinkp
== NULL
) && (*errorp
!= 0)) {
1508 /* Delete link record */
1509 *errorp
= delete_record(dbp
, linkkey
, linksize
, errstr
);
1510 /* Get the primary key */
1511 fhkeysize
= LN_FHKEY_LEN(dellinkp
);
1512 fhkey
= LN_FHKEY(dellinkp
);
1513 fhrecp
= fetch_record(dbp
, fhkey
, fhkeysize
,
1514 &fhrec
, errorp
, errstr
);
1515 if (fhrecp
== NULL
) {
1516 /* Should never happen */
1518 debug_print_key(stderr
, errstr
,
1519 "fetch primary for ", linkkey
, linksize
);
1520 (void) fprintf(stderr
, " Error %s\n",
1521 ((*errorp
>= 0) ? strerror(*errorp
) : "Unknown"));
1523 } else if ((*errorp
== 0) && (prevsize
<= 0)) {
1524 /* This is the head of the list update primary record */
1525 *errorp
= db_update_primary_new_head(dbp
, dellinkp
,
1528 /* Update fhrec atime if needed */
1529 *errorp
= db_update_fhrec(dbp
, fhkey
, fhkeysize
, fhrecp
,
1532 *linksizep
= nextsize
;
1534 (void) memcpy(linkkey
, nextkey
, nextsize
);
1535 if (nextlinkp
!= NULL
)
1542 * delete_link - delete the requested link from the database. If it's the
1543 * last link in the database for that file then remove the primary record
1544 * as well. If nextlinkkey/sizep are non-null, copy the key and key size of
1545 * the next link in the chain into them (this would save a dbm_fetch op).
1546 * Return ENOENT if link not in database and 0 otherwise, with *errorp
1547 * containing the returned error if any from the delete_link ops.
1550 delete_link(struct db_list
*dbp
, fhandle_t
*dfh
, char *name
,
1551 char *nextlinkkey
, int *nextlinksizep
, int *errorp
, char *errstr
)
1556 if ((nextlinkkey
!= NULL
) && (nextlinksizep
!= NULL
)) {
1557 *nextlinksizep
= fill_link_key(nextlinkkey
, dfh
, name
);
1558 linkerr
= delete_link_by_key(dbp
, nextlinkkey
, nextlinksizep
,
1562 fh_secondary_key linkkey
;
1564 linksize
= fill_link_key(linkkey
, dfh
, name
);
1565 linkerr
= delete_link_by_key(dbp
, linkkey
, &linksize
,
1572 * db_delete_link - search the database for the file system for link.
1573 * Delete the link from the database. If this is the "primary" link,
1574 * set the primary record for the next link. If it's the last one,
1575 * delete the primary record.
1576 * Return 0 for success, error code otherwise.
1579 db_delete_link(char *fhpath
, fhandle_t
*dfh
, char *name
)
1581 struct db_list
*dbp
;
1584 if ((fhpath
== NULL
) || (dfh
== NULL
) || (name
== NULL
)) {
1587 if (dfh
== &public_fh
) {
1588 dbp
= db_get_all_databases(fhpath
, TRUE
);
1590 dbp
= db_get_db(fhpath
, &dfh
->fh_fsid
, &error
, O_CREAT
);
1592 for (; dbp
!= NULL
; dbp
= ((dfh
== &public_fh
) ? dbp
->next
: NULL
)) {
1593 (void) delete_link(dbp
, dfh
, name
, NULL
, NULL
, &error
,
1594 "db_delete_link link");
1601 * db_delete - Deletes the fhrec corresponding to the fh. Use only
1602 * for repairing the fhtable, not for normal handling.
1603 * Return 0 for success, error code otherwise.
1606 db_delete(char *fhpath
, fhandle_t
*fh
)
1608 struct db_list
*dbp
;
1611 if ((fhpath
== NULL
) || (fh
== NULL
)) {
1614 if (fh
== &public_fh
) {
1615 dbp
= db_get_all_databases(fhpath
, TRUE
);
1617 dbp
= db_get_db(fhpath
, &fh
->fh_fsid
, &error
, O_CREAT
);
1619 for (; dbp
!= NULL
; dbp
= ((fh
== &public_fh
) ? dbp
->next
: NULL
)) {
1620 /* Get the link record */
1621 (void) delete_record(dbp
, &fh
->fh_data
, fh
->fh_len
,
1622 "db_delete: fh delete");
1629 * db_rename_link - search the database for the file system for link.
1630 * Add the new link and delete the old link from the database.
1631 * Return 0 for success, error code otherwise.
1634 db_rename_link(char *fhpath
, fhandle_t
*from_dfh
, char *from_name
,
1635 fhandle_t
*to_dfh
, char *to_name
)
1638 struct db_list
*dbp
;
1639 fhlist_ent fhrec
, *fhrecp
;
1641 if ((fhpath
== NULL
) || (from_dfh
== NULL
) || (from_name
== NULL
) ||
1642 (to_dfh
== NULL
) || (to_name
== NULL
)) {
1645 if (from_dfh
== &public_fh
) {
1646 dbp
= db_get_all_databases(fhpath
, FALSE
);
1648 dbp
= db_get_db(fhpath
, &from_dfh
->fh_fsid
, &error
, O_CREAT
);
1651 dbp
= ((from_dfh
!= &public_fh
) ? NULL
: dbp
->next
)) {
1652 /* find existing link */
1653 fhrecp
= db_lookup_link(fhpath
, from_dfh
, from_name
, &fhrec
,
1655 if (fhrecp
== NULL
) {
1656 /* Could not find the link */
1659 /* Delete the old link (if last primary record not deleted) */
1660 error
= db_delete_link(fhpath
, from_dfh
, from_name
);
1662 error
= db_add(fhpath
, to_dfh
, to_name
, &fhrecp
->fh
,
1670 * db_print_all_keys: prints all keys for a given filesystem. If fsidp is
1671 * NULL, print for all filesystems covered by fhpath.
1674 db_print_all_keys(char *fhpath
, fsid_t
*fsidp
, FILE *fp
)
1676 struct db_list
*dbp
;
1679 char strkey
[NFS_FHMAXDATA
+ MAXNAMELEN
];
1683 if ((fhpath
== NULL
) ||
1684 ((fsidp
!= NULL
) && (fsidp
== &public_fh
.fh_fsid
)))
1686 if (fsidp
== NULL
) {
1687 (void) db_get_all_databases(fhpath
, TRUE
);
1690 dbp
= db_get_db(fhpath
, fsidp
, &error
, 0);
1693 /* Could not get or create database */
1696 len
= strlen(fhpath
);
1697 for (; dbp
!= NULL
; dbp
= ((fsidp
!= NULL
) ? NULL
: dbp
->next
)) {
1698 if (strncmp(fhpath
, dbp
->path
, len
))
1701 "\nStart print database for fsid 0x%x 0x%x\n",
1702 dbp
->fsid
.val
[0], dbp
->fsid
.val
[1]);
1703 (void) fprintf(fp
, "=============================\n");
1704 for (key
= dbm_firstkey(dbp
->db
); key
.dptr
!= NULL
;
1705 key
= dbm_nextkey(dbp
->db
)) {
1706 (void) memcpy(strkey
, key
.dptr
, key
.dsize
);
1707 debug_print_key(fp
, "", "", strkey
, key
.dsize
);
1710 ptr
= fetch_record(dbp
, key
.dptr
, key
.dsize
,
1711 (void *)&rec
, &error
, "db_prt_keys");
1714 if (key
.dsize
== NFS_FHMAXDATA
) {
1716 debug_print_fhlist(fp
, &rec
.fhlist_rec
);
1717 } else if (key
.dsize
> NFS_FHMAXDATA
) {
1719 debug_print_linkinfo(fp
, &rec
.link_rec
);
1721 (void) fprintf(fp
, "-----------------------------\n");
1723 (void) fprintf(fp
, "End print database for fsid 0x%x 0x%x\n",
1724 dbp
->fsid
.val
[0], dbp
->fsid
.val
[1]);
1729 debug_opaque_print(FILE *fp
, void *buf
, int size
)
1732 char debug_str
[200];
1734 if ((buf
== NULL
) || (size
<= 0))
1737 nfslog_opaque_print_buf(buf
, size
, debug_str
, &bufoffset
, 200);
1738 (void) fprintf(fp
, debug_str
);
1742 * links_timedout() takes a primary records and searches all of its
1743 * links to see if they all have access times that are older than
1744 * the 'prune_timeout' value. TRUE if all links are old and FALSE
1745 * if there is just one link that has an access time which is recent.
1748 links_timedout(struct db_list
*pdb
, fhlist_ent
*pfe
, time_t ts
)
1750 fh_secondary_key linkkey
;
1751 linkinfo_ent
*linkp
, link_st
;
1756 /* Get the link record */
1757 linksize
= fill_link_key(linkkey
, &pfe
->dfh
, pfe
->name
);
1760 linkp
= get_next_link(pdb
, linkkey
, &linksize
, &link_st
,
1761 &cookie
, &error
, "links_timedout");
1762 if ((linkp
!= NULL
) &&
1763 (difftime(ts
, linkp
->atime
) <= prune_timeout
)) {
1764 /* update primary record to have an uptodate time */
1765 pfe
= fetch_record(pdb
, (void *)&pfe
->fh
.fh_data
,
1766 pfe
->fh
.fh_len
, NULL
, &error
,
1769 syslog(LOG_ERR
, gettext(
1770 "links_timedout: fetch fhrec error %s\n"),
1773 if (difftime(pfe
->atime
, linkp
->atime
) < 0) {
1774 /* update fhrec atime */
1775 pfe
->atime
= linkp
->atime
;
1776 (void) store_record(pdb
,
1777 (void *)&pfe
->fh
.fh_data
,
1778 pfe
->fh
.fh_len
, pfe
,
1779 pfe
->reclen
, "links_timedout");
1783 free_link_cookies(cookie
);
1786 } while (linksize
> 0);
1788 free_link_cookies(cookie
);
1793 * prune_dbs() will search all of the open databases looking for records
1794 * that have not been accessed in the last 'prune_timeout' seconds.
1795 * This search is done on the primary records and a list of potential
1796 * timeout candidates is built. The reason for doing this is to not
1797 * disturb the underlying dbm_firstkey()/dbm_nextkey() sequence; we
1798 * want to search all of the records in the database.
1799 * Once we have our candidate list built, we examine each of those
1800 * item's links to check if the links have been accessed within the
1801 * 'prune_timeout' seconds. If neither the primary nor any its links
1802 * have been accessed, then all of those records are removed/deleted
1803 * from the database.
1806 prune_dbs(char *fhpath
)
1808 struct db_list
*pdb
;
1811 struct fhlist_ent
*pfe
;
1812 int error
, linkerr
, linksize
;
1813 time_t cur_time
= time(0);
1814 fh_secondary_key linkkey
;
1816 struct thelist
*next
;
1822 (void) db_get_all_databases(fhpath
, TRUE
);
1824 thelist
.next
= NULL
;
1826 * Search each of the open databases
1828 for (pdb
= db_fs_list
; pdb
; pdb
= pdb
->next
) {
1830 /* Check each record in the database */
1831 for (key
= dbm_firstkey(pdb
->db
); key
.dptr
!= NULL
;
1832 key
= dbm_nextkey(pdb
->db
)) {
1833 /* We're only interested in primary records */
1834 if (key
.dsize
!= NFS_FHMAXDATA
)
1835 continue; /* probably a link record */
1836 ptr
= fetch_record(pdb
, key
.dptr
, key
.dsize
,
1837 NULL
, &error
, "dump_db");
1841 * If this record is a primary record and it is
1842 * not an export point or a public file handle path,
1843 * check it for a ancient access time.
1845 if ((ptr
->fhlist_rec
.flags
&
1846 (EXPORT_POINT
| PUBLIC_PATH
)) ||
1847 (difftime(cur_time
, ptr
->fhlist_rec
.atime
) <=
1849 /* Keep this record in the database */
1852 /* Found one? Save off info about it */
1853 ptl
= malloc(sizeof (struct thelist
));
1855 syslog(LOG_ERR
, gettext(
1856 "prune_dbs: malloc failed, error %s\n"),
1861 ptl
->next
= thelist
.next
;
1863 cnt
++; /* count how many records allocated */
1864 if (cnt
> MAX_PRUNE_REC_CNT
) {
1865 /* Limit number of records malloc'd */
1867 (void) fprintf(stderr
,
1868 "prune_dbs: halt search - too many records\n");
1875 * Take the saved records and check their links to make
1876 * sure that they have not been accessed as well.
1878 for (ptl
= thelist
.next
; ptl
; ptl
= thelist
.next
) {
1879 thelist
.next
= ptl
->next
;
1880 /* Everything timed out? */
1881 pfe
= &(ptl
->ptr
->fhlist_rec
);
1882 if (links_timedout(pdb
, pfe
, cur_time
)) {
1885 * Iterate until we run out of links.
1886 * We have to do this since there can be
1887 * multiple links to a primary record and
1888 * we need to delete one at a time.
1890 /* Delete the link and get the next */
1891 linkerr
= delete_link(pdb
,
1892 &pfe
->dfh
, pfe
->name
, linkkey
,
1893 &linksize
, &error
, "dump_db");
1894 while ((linksize
> 0) && !(error
|| linkerr
)) {
1895 /* Delete the link and get the next */
1896 linkerr
= delete_link_by_key(pdb
,
1899 if (error
|| linkerr
) {
1904 /* link not in database, primary is */
1905 /* Should never happen */
1907 (void) fprintf(stderr
,
1908 "prune_dbs: Error primary exists ");
1909 debug_opaque_print(stderr
,
1912 (void) fprintf(stderr
, "\n");
1915 syslog(LOG_ERR
, gettext(
1916 "prune_dbs: Error primary exists\n"));
1917 (void) delete_record(pdb
,
1918 &pfe
->fh
.fh_data
, pfe
->fh
.fh_len
,
1919 "prune_dbs: fh delete");
1922 /* Make sure to free the pointers used in the list */
1927 thelist
.next
= NULL
;
1928 } while (key
.dptr
!= NULL
);