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