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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27/* 28 * Portions Copyright 2007-2011 Apple Inc. 29 */ 30 31#include <stdio.h> 32#include <string.h> 33#include <ctype.h> 34#include <unistd.h> 35#include <stdlib.h> 36#include <sys/param.h> 37#include <pthread.h> 38#include <fstab.h> 39#include <errno.h> 40#include <assert.h> 41#include <mntopts.h> 42#include "autofs.h" 43#include "automount.h" 44#include "auto_mntopts.h" 45 46#define MIN_CACHE_TIME 30 // min cache time for fstab cache (sec) 47 48/* 49 * Structure for a server host in fstab. 50 */ 51struct fstabhost { 52 char *name; /* name of the host */ 53 struct fstabnode *fstab_ents; /* fstab entries for that host */ 54 struct fstabhost *next; /* next entry in hash table bucket */ 55}; 56 57#define HASHTABLESIZE 251 58 59/* 60 * Hash table of hosts with at least one "net" entry. 61 */ 62static struct fstabhost *fstab_hashtable[HASHTABLESIZE]; 63 64/* 65 * Hash table of static map entries. 66 */ 67static struct staticmap *static_hashtable[HASHTABLESIZE]; 68 69/* 70 * Read/write lock on the cache. 71 */ 72static pthread_rwlock_t fstab_cache_lock = PTHREAD_RWLOCK_INITIALIZER; 73 74static time_t min_cachetime; // Min time that the cache is valid 75static time_t max_cachetime; // Max time that the cache is valid 76 77static int 78process_fstab_mntopts(struct fstab *fs, char **mntops_outp, char **url) 79{ 80 char *mntops_out; 81 char *mntops_copy; 82 size_t optlen; 83 char *p, *pp; 84 char *optp; 85 int error = 0; 86 87 /* 88 * Remove "net", "bg", and "fg" from the mount options; 89 * "net" is irrelevant, and the automounter can't mount 90 * stuff in the background - it's not supposed to return 91 * until the mount has succeeded or failed - so "bg" 92 * should be ignored and "fg" is irrelevant. 93 * 94 * If "url==" appears, extract the URL and return it through 95 * "*url", and remove it from the mount options. 96 * 97 * If "fs->fs_type" isn't a null string, it's either "rw" or "ro"; 98 * add it to the end of the list of options. 99 */ 100 optlen = strlen(fs->fs_mntops) + strlen(fs->fs_type) + 1 + 1; 101 if (optlen > MAXOPTSLEN) 102 return (ENAMETOOLONG); 103 mntops_out = (char *)malloc(optlen); 104 if (mntops_out == NULL) 105 return (ENOMEM); 106 strcpy(mntops_out, ""); 107 108 /* 109 * Copy over mount options, except for "net", "bg", "fg", 110 * "browse", "nobrowse", or "url==". We discard "net", 111 * "bg", and "fg"; we map "browse" and "nobrowse" to 112 * "findervol" and "nofindervol"; we extract the URL from 113 * "url==" and return it through "*url". 114 */ 115 *url = NULL; /* haven't seen it yet */ 116 117 /* 118 * Copy option string, since it is about to be torn asunder ... 119 */ 120 if ((mntops_copy = strdup(fs->fs_mntops)) == NULL) { 121 free(mntops_out); 122 return (ENOMEM); 123 } 124 pp = mntops_copy; 125 126 while ((p = strsep(&pp, ",")) != NULL) { 127 if (strcmp(p, "net") == 0 || strcmp(p, "bg") == 0 || 128 strcmp(p, "bg") == 0) 129 continue; 130 131 if (strncmp(p, "url==", 5) == 0) { 132 /* 133 * Extract the URL. 134 */ 135 if (*url != NULL) 136 free(*url); 137 *url = strdup(p + 5); 138 if (*url == NULL) { 139 free(mntops_out); 140 free(mntops_copy); 141 return (ENOMEM); 142 } 143 continue; /* don't add it to the mount options */ 144 } 145 146 if (strcmp(p, "browse") == 0) 147 optp = "findervol"; 148 else if (strcmp(p, "nobrowse") == 0) 149 optp = "nofindervol"; 150 else 151 optp = p; 152 if (mntops_out[0] != '\0') { 153 /* 154 * We already have mount options; add 155 * a comma before this one. 156 */ 157 if ((error = CHECK_STRCAT(mntops_out, ",", optlen))) { 158 error = ENAMETOOLONG; 159 goto DONE; 160 } 161 } 162 if ((error = CHECK_STRCAT(mntops_out, optp, optlen))) { 163 error = ENAMETOOLONG; 164 goto DONE; 165 } 166 } 167 if (fs->fs_type[0] != '\0') { 168 /* 169 * We have a type, which is either "rw", "ro", or nothing. 170 * If it's "rw" or "ro", add that to the end of the 171 * options list, just as the old automounter did. 172 */ 173 if (mntops_out[0] != '\0') { 174 /* 175 * We already have mount options; add 176 * a comma before this one. 177 */ 178 if ((error = CHECK_STRCAT(mntops_out, ",", optlen))) { 179 error = ENAMETOOLONG; 180 goto DONE; 181 } 182 } 183 if ((error = CHECK_STRCAT(mntops_out, fs->fs_type, optlen))) { 184 error = ENAMETOOLONG; 185 goto DONE; 186 } 187 } 188DONE: 189 *mntops_outp = mntops_out; 190 free(mntops_copy); 191 return error; 192} 193 194static void 195freefst_ent(fst) 196 struct fstabnode *fst; 197{ 198 if (fst->fst_dir != NULL) 199 free(fst->fst_dir); 200 if (fst->fst_vfstype != NULL) 201 free(fst->fst_vfstype); 202 if (fst->fst_mntops != NULL) 203 free(fst->fst_mntops); 204 if (fst->fst_url != NULL) 205 free(fst->fst_url); 206 free((char *)fst); 207} 208 209static void 210freefst_list(fst) 211 struct fstabnode *fst; 212{ 213 struct fstabnode *tmpfst; 214 215 while (fst) { 216 tmpfst = fst->fst_next; 217 freefst_ent(fst); 218 fst = tmpfst; 219 } 220} 221 222static struct fstabhost * 223find_host_entry(const char *host, struct fstabhost ***bucketp) 224{ 225 size_t hash; 226 const unsigned char *hashp; 227 unsigned char c; 228 struct fstabhost **bucket; 229 struct fstabhost *hostent; 230 231 /* 232 * Cheesy hash function - just add all the characters in the 233 * host name together, after lower-casing all upper-case 234 * letters, and take it mod HASHTABLESIZE. 235 */ 236 hash = 0; 237 for (hashp = (const unsigned char *)host; (c = *hashp) != '\0'; hashp++) 238 hash += tolower(c); 239 bucket = &fstab_hashtable[hash % HASHTABLESIZE]; 240 if (bucketp != NULL) 241 *bucketp = bucket; 242 for (hostent = *bucket; hostent != NULL; hostent = hostent->next) { 243 if (strcasecmp(hostent->name, host) == 0) 244 return (hostent); 245 } 246 return NULL; 247} 248 249static struct staticmap * 250find_staticmap_entry(const char *dir, struct staticmap ***bucketp) 251{ 252 size_t hash; 253 const unsigned char *hashp; 254 unsigned char c; 255 struct staticmap **bucket; 256 struct staticmap *staticent; 257 258 /* 259 * Cheesy hash function - just add all the characters in the 260 * directory name together, and take it mod HASHTABLESIZE. 261 */ 262 hash = 0; 263 for (hashp = (const unsigned char *)dir; (c = *hashp) != '\0'; hashp++) 264 hash += c; 265 bucket = &static_hashtable[hash % HASHTABLESIZE]; 266 if (bucketp != NULL) 267 *bucketp = bucket; 268 for (staticent = *bucket; staticent != NULL; staticent = staticent->next) { 269 if (strcmp(staticent->dir, dir) == 0) 270 return (staticent); 271 } 272 return NULL; 273} 274 275/* 276 * This assumes that a write lock on the fstab cache lock is held. 277 */ 278static void 279clean_hashtables(void) 280{ 281 int i; 282 struct fstabhost *host_ent, *next_host_ent; 283 struct staticmap *static_ent, *next_static_ent; 284 285 for (i = 0; i < HASHTABLESIZE; i++) { 286 for (host_ent = fstab_hashtable[i]; host_ent != NULL; 287 host_ent = next_host_ent) { 288 next_host_ent = host_ent->next; 289 free(host_ent->name); 290 freefst_list(host_ent->fstab_ents); 291 free(host_ent); 292 } 293 fstab_hashtable[i] = NULL; 294 } 295 296 for (i = 0; i < HASHTABLESIZE; i++) { 297 for (static_ent = static_hashtable[i]; static_ent != NULL; 298 static_ent = next_static_ent) { 299 next_static_ent = static_ent->next; 300 free(static_ent->dir); 301 free(static_ent->vfstype); 302 free(static_ent->mntops); 303 free(static_ent->host); 304 free(static_ent->localpath); 305 free(static_ent->spec); 306 free(static_ent); 307 } 308 static_hashtable[i] = NULL; 309 } 310} 311 312static void 313sort_fstab_entries(void) 314{ 315 int i; 316 struct fstabhost *host_ent; 317 struct fstabnode *fst = NULL; 318 struct fstabnode *tfstlist, **tfstp, *fstnext; 319 size_t fstlen; 320 int duplicate; 321 322 for (i = 0; i < HASHTABLESIZE; i++) { 323 for (host_ent = fstab_hashtable[i]; host_ent != NULL; 324 host_ent = host_ent->next) { 325 tfstlist = NULL; 326 for (fst = host_ent->fstab_ents; fst; fst = fstnext) { 327 fstnext = fst->fst_next; 328 fstlen = strlen(fst->fst_dir); 329 duplicate = 0; 330 for (tfstp = &tfstlist; *tfstp; 331 tfstp = &((*tfstp)->fst_next)) { 332 if (fstlen < strlen((*tfstp)->fst_dir)) 333 break; 334 duplicate = (strcmp(fst->fst_dir, (*tfstp)->fst_dir) == 0); 335 if (duplicate) { 336 /* disregard duplicate entry */ 337 freefst_ent(fst); 338 break; 339 } 340 } 341 if (!duplicate) { 342 fst->fst_next = *tfstp; 343 *tfstp = fst; 344 } 345 } 346 host_ent->fstab_ents = tfstlist; 347 } 348 } 349} 350 351static const struct mntopt mopts_net[] = { 352 MOPT_NET, 353 { NULL, 0, 0, 0 } 354}; 355 356/* 357 * Flush out all information we got the last time we read all the 358 * fstab entries, and then read them and reconstruct that information. 359 * 360 * This assumes that a read lock is held on fstab_cache_lock. 361 * It will take a write lock on it; this means that all read 362 * locks will have been released before it gets the write lock. 363 * If it returns 0, the write lock is still held; if it returns an 364 * error, the write lock has been released. (One reason it can 365 * return an error is that, for whatever reason, the attempt to 366 * get the write lock failed....) 367 */ 368static int 369readfstab(void) 370{ 371 int err; 372 struct fstab *fs; 373 char *p; 374 int is_local_entry; 375 mntoptparse_t mop; 376 int flags; 377 int altflags; 378 char *mntops, *url, *host, *localpath; 379 380 if (trace > 1) 381 trace_prt(1, "readfstab called\n"); 382 383 /* 384 * Re-read fstab and rebuild the table. 385 * We assume we were called with a reader lock; release 386 * it and grab a writer lock, to ensure that nobody else 387 * will be looking at it while we're modifying it. 388 */ 389 pthread_rwlock_unlock(&fstab_cache_lock); 390 err = pthread_rwlock_wrlock(&fstab_cache_lock); 391 if (err != 0) { 392 pr_msg("Error attempting to get write lock on fstab cache: %m"); 393 return (err); /* use the cached data */ 394 } 395 396 /* 397 * If the cache was populated recently, i.e. less than 398 * MIN_CACHE_TIME seconds ago, then just ignore this 399 * request to purge/repopulate the cache. This avoids 400 * spurious cache purging by because a process is 401 * repeatedly looking up a name that's not cached. 402 */ 403 if (time(NULL) < min_cachetime) 404 return (0); 405 406 /* 407 * Clean out the old entries, in case this is being called 408 * because we failed to find an entry in a non-empty 409 * cache. 410 */ 411 clean_hashtables(); 412 413 /* 414 * OK, scan the fstab and build our data structure. 415 */ 416 setfsent(); 417 418 while ((fs = getfsent()) != NULL) { 419 char *vfstype; 420 421 /* 422 * Is this an entry with an empty type, an "rw", a "ro" 423 * entry, or none of those? 424 */ 425 if (fs->fs_type[0] != '\0' && 426 strcmp(fs->fs_type, FSTAB_RW) != 0 && 427 strcmp(fs->fs_type, FSTAB_RO) != 0) { 428 /* None of those - ignore it. */ 429 continue; 430 } 431 432 /* 433 * Does fs_spec begin with /? 434 */ 435 if (fs->fs_spec[0] == '/') { 436 /* 437 * This is an entry for a local file system; 438 * ignore it. 439 */ 440 continue; 441 } 442 443 /* 444 * Does it begin with some form of XXX=, where XXX 445 * is an identifier (letters, numbers, underscores, 446 * and, just for fun, hyphens)? 447 */ 448 is_local_entry = 1; 449 for (p = fs->fs_spec; *p != '\0' && *p != '='; p++) { 450 if ((*p & 0x80) != 0) { 451 /* 452 * Ow - non-ASCII. 453 * Assume it's not a local entry. 454 */ 455 is_local_entry = 0; 456 break; 457 } 458 if (!isalnum(*p) && *p != '-' && *p != '_') { 459 /* 460 * Something other than an identifier 461 * character. Not a local entry. 462 */ 463 is_local_entry = 0; 464 break; 465 } 466 } 467 if (is_local_entry) { 468 /* 469 * We assume anything beginning with XXX= is 470 * a local entry, along the lines of UUID= 471 * or LABEL=. 472 */ 473 continue; 474 } 475 476 /* 477 * Is "net" one of the mount options? 478 */ 479 flags = altflags = 0; 480 getmnt_silent = 1; 481 mop = getmntopts(fs->fs_mntops, mopts_net, &flags, &altflags); 482 if (mop == NULL) { 483 pr_msg("Couldn't parse mount options \"%s\" for %s: %m", 484 fs->fs_mntops, fs->fs_spec); 485 continue; /* give up on this */ 486 } 487 freemntopts(mop); 488 489 /* 490 * Extract the host name from fs_spec. 491 */ 492 p = strchr(fs->fs_spec, ':'); 493 if (p == NULL) { 494 /* 495 * No colon; one could consider that a path with 496 * no host name, or a host name with no path; 497 * in at least one case, the problem was that 498 * the path was missing, so report it as that. 499 */ 500 pr_msg("Mount for %s has no path for the directory to mount", fs->fs_spec); 501 continue; /* no path - ignore this */ 502 } 503 if (p == fs->fs_spec) { 504 pr_msg("Mount for %s has an empty host name", fs->fs_spec); 505 continue; /* empty host name - ignore this */ 506 } 507 *p = '\0'; /* split into host name and the rest */ 508 host = strdup(fs->fs_spec); 509 if (host == NULL) 510 goto outofmem; 511 localpath = strdup(p+1); 512 if (localpath == NULL) { 513 free(host); 514 goto outofmem; 515 } 516 /* 517 * Put fs->fs_spec back the way it was, so we can use it 518 * in error messages. 519 */ 520 *p = ':'; 521 522 /* 523 * Massage the mount options. 524 */ 525 err = process_fstab_mntopts(fs, &mntops, &url); 526 if (err == ENAMETOOLONG) { 527 pr_msg("Mount options for %s are too long", 528 fs->fs_spec); 529 free(localpath); 530 free(host); 531 continue; /* give up on this */ 532 } 533 if (err == ENOMEM) { 534 free(localpath); 535 free(host); 536 goto outofmem; 537 } 538 539 /* 540 * If the VFS type is empty, we treat it as "nfs", for 541 * backwards compatibility with the old automounter. 542 */ 543 vfstype = fs->fs_vfstype; 544 if (vfstype[0] == '\0') 545 vfstype = "nfs"; 546 if (strcmp(vfstype, "url") == 0) { 547 /* We must have a URL. */ 548 if (url == NULL) { 549 pr_msg("Mount for %s has type url but no URL", 550 fs->fs_spec); 551 free(mntops); 552 free(localpath); 553 free(host); 554 continue; 555 } 556 } else { 557 /* We must not have a URL. */ 558 if (url != NULL) { 559 pr_msg("Mount for %s has type %s but has a URL", 560 fs->fs_spec, vfstype); 561 free(mntops); 562 free(url); 563 free(localpath); 564 free(host); 565 continue; 566 } 567 } 568 569 if (altflags & FSTAB_MNT_NET) { 570 struct fstabnode *fst; 571 struct fstabhost *host_entry; 572 struct fstabhost **fstab_bucket; 573 574 /* 575 * Entry has "net" - it's for -fstab. 576 * 577 * Allocate an entry for this fstab record. 578 */ 579 fst = calloc(1, sizeof (struct fstabnode)); 580 if (fst == NULL) { 581 free(url); 582 free(mntops); 583 free(localpath); 584 free(host); 585 goto outofmem; 586 } 587 588 fst->fst_dir = localpath; 589 fst->fst_vfstype = strdup(vfstype); 590 if (fst->fst_vfstype == NULL) { 591 free(fst->fst_dir); 592 free(fst); 593 free(url); 594 free(mntops); 595 free(host); 596 goto outofmem; 597 } 598 fst->fst_mntops = mntops; /* this is mallocated */ 599 fst->fst_url = url; /* as is this */ 600 601 /* 602 * Now add an entry for the host if we haven't already 603 * done so. 604 */ 605 host_entry = find_host_entry(host, &fstab_bucket); 606 if (host_entry == NULL) { 607 /* 608 * We found no entry for the host; allocate 609 * one and add it to the hash table bucket. 610 */ 611 host_entry = malloc(sizeof (struct fstabhost)); 612 if (host_entry == NULL) { 613 free(fst->fst_vfstype); 614 free(fst->fst_dir); 615 free(fst); 616 free(url); 617 free(mntops); 618 free(host); 619 goto outofmem; 620 } 621 host_entry->name = host; 622 host_entry->fstab_ents = fst; 623 host_entry->next = *fstab_bucket; 624 *fstab_bucket = host_entry; 625 } else { 626 /* 627 * We found an entry; add the fstab 628 * entry to its list of fstab entries. 629 */ 630 fst->fst_next = host_entry->fstab_ents; 631 host_entry->fstab_ents = fst; 632 633 /* 634 * We don't need the host - we already 635 * have it in the host entry. 636 */ 637 free(host); 638 } 639 } else { 640 /* 641 * Entry doesn't have "net" - it's for -static. 642 * 643 * Do we already have an entry for this 644 * directory? 645 */ 646 struct staticmap *static_ent; 647 struct staticmap **static_bucket; 648 649 if (strlen(fs->fs_file) == 0) { 650 pr_msg("Mount for %s has an empty mount point path", 651 fs->fs_spec); 652 continue; /* empty mount point path - ignore this */ 653 } 654 655 static_ent = find_staticmap_entry(fs->fs_file, 656 &static_bucket); 657 if (static_ent == NULL) { 658 /* No - make one. */ 659 static_ent = malloc(sizeof (struct staticmap)); 660 if (static_ent == NULL) { 661 free(url); 662 free(mntops); 663 free(localpath); 664 free(host); 665 goto outofmem; 666 } 667 static_ent->dir = strdup(fs->fs_file); 668 if (static_ent->dir == NULL) { 669 free(static_ent); 670 free(url); 671 free(mntops); 672 free(localpath); 673 free(host); 674 goto outofmem; 675 } 676 static_ent->vfstype = strdup(vfstype); 677 if (static_ent->vfstype == NULL) { 678 free(static_ent->dir); 679 free(static_ent); 680 free(url); 681 free(mntops); 682 free(localpath); 683 free(host); 684 goto outofmem; 685 } 686 static_ent->mntops = mntops; 687 static_ent->host = host; 688 if (url != NULL) { 689 static_ent->localpath = localpath; 690 static_ent->spec = url; 691 } else { 692 static_ent->localpath = localpath; 693 static_ent->spec = strdup(localpath); 694 if (static_ent->spec == NULL) { 695 free(static_ent->localpath); 696 free(static_ent->host); 697 free(static_ent->vfstype); 698 free(static_ent->dir); 699 free(static_ent); 700 free(url); 701 free(mntops); 702 goto outofmem; 703 } 704 } 705 706 /* Add it to the hash bucket. */ 707 static_ent->next = *static_bucket; 708 *static_bucket = static_ent; 709 } else { 710 /* Yes - leave it. */ 711 free(mntops); 712 free(url); 713 free(localpath); 714 free(host); 715 } 716 } 717 } 718 endfsent(); 719 720 /* 721 * Now, for each host entry, sort the list of fstab entries 722 * by the length of the name; automountd expects that, so it 723 * can get the mount order right. 724 */ 725 sort_fstab_entries(); 726 727 /* 728 * Update the min and max cache times 729 */ 730 min_cachetime = time(NULL) + MIN_CACHE_TIME; 731 max_cachetime = time(NULL) + RDDIR_CACHE_TIME * 2; 732 733 return (0); 734 735outofmem: 736 endfsent(); 737 clean_hashtables(); 738 pthread_rwlock_unlock(&fstab_cache_lock); 739 pr_msg("Memory allocation failed while reading fstab"); 740 return (ENOMEM); 741} 742 743/* 744 * Look up a particular host in the fstab map hash table and, if we find it, 745 * run the callback routine on each entry in its fstab entry list. 746 */ 747int 748fstab_process_host(const char *host, int (*callback)(struct fstabnode *, void *), 749 void *callback_arg) 750{ 751 int err; 752 struct fstabhost *hostent; 753 struct fstabnode *fst; 754 755 /* 756 * Get a read lock, so the cache doesn't get modified out 757 * from under us. 758 */ 759 err = pthread_rwlock_rdlock(&fstab_cache_lock); 760 if (err != 0) 761 return (err); 762 763 hostent = find_host_entry(host, NULL); 764 if (hostent == NULL) { 765 /* 766 * We've seen no entries for that host, 767 * either because the cache has been purged 768 * or we didn't find any the last time we 769 * scanned the fstab entries. 770 * 771 * Try re-reading the fstab entries. 772 */ 773 err = readfstab(); 774 if (err != 0) { 775 /* 776 * That failed; give up. 777 */ 778 return (err); 779 } 780 781 /* 782 * Now see if we can find the host. 783 */ 784 hostent = find_host_entry(host, NULL); 785 if (hostent == NULL) { 786 /* 787 * No - give up. 788 */ 789 pthread_rwlock_unlock(&fstab_cache_lock); 790 return (-1); 791 } 792 } 793 794 /* 795 * Run the callback on every entry in the fstab node list. 796 * If the callback returns a non-zero value, stop and 797 * return that value as an error. 798 */ 799 err = 0; 800 for (fst = hostent->fstab_ents; fst != NULL; fst = fst->fst_next) { 801 err = (*callback)(fst, callback_arg); 802 if (err != 0) 803 break; 804 } 805 806 /* We're done processing the list; release the lock. */ 807 pthread_rwlock_unlock(&fstab_cache_lock); 808 809 return (err); 810} 811 812/* 813 * This assumes that a read or write lock on the fstab cache lock is held. 814 */ 815static int 816scan_fstab(struct dir_entry **list, struct dir_entry **lastp) 817{ 818 int i; 819 struct fstabhost *host_ent; 820 int err; 821 822 *lastp = NULL; 823 for (i = 0; i < HASHTABLESIZE; i++) { 824 for (host_ent = fstab_hashtable[i]; host_ent != NULL; 825 host_ent = host_ent->next) { 826 /* 827 * Add an entry for it if we haven't already 828 * done so. 829 * 830 * A return of -1 means the name isn't valid. 831 */ 832 err = add_dir_entry(host_ent->name, NULL, NULL, list, 833 lastp); 834 if (err != -1) { 835 if (err) 836 return (err); 837 assert(*lastp != NULL); 838 } 839 } 840 } 841 return (0); 842} 843 844/* 845 * Enumerate all the entries in the -fstab map. 846 * This is used by a readdir on the -fstab map; those are likely to 847 * be followed by stats on one or more of the entries in that map, so 848 * we populate the fstab map cache and return values from that. 849 */ 850int 851getfstabkeys(struct dir_entry **list, int *error, int *cache_time) 852{ 853 int err; 854 time_t timediff; 855 struct dir_entry *last; 856 char thishost[MAXHOSTNAMELEN]; 857 char *p; 858 859 /* 860 * Get a read lock, so the cache doesn't get modified out 861 * from under us. 862 */ 863 err = pthread_rwlock_rdlock(&fstab_cache_lock); 864 if (err != 0) { 865 *error = err; 866 return (__NSW_UNAVAIL); 867 } 868 869 /* 870 * Return the time until the current cache data times out. 871 */ 872 timediff = max_cachetime - time(NULL); 873 if (timediff < 0) 874 timediff = 0; 875 else if (timediff > INT_MAX) 876 timediff = INT_MAX; 877 *cache_time = (int)timediff; 878 *error = 0; 879 if (trace > 1) 880 trace_prt(1, "getfstabkeys called\n"); 881 882 /* 883 * Get what we have cached. 884 */ 885 *error = scan_fstab(list, &last); 886 if (*error == 0) { 887 if (*list == NULL) { 888 /* 889 * We've seen no fstab entries, either because 890 * the cache has been purged or we didn't find 891 * any the last time we scanned the fstab entries. 892 * 893 * Try re-reading the fstab entries. 894 */ 895 err = readfstab(); 896 if (err != 0) { 897 /* 898 * That failed; give up. 899 */ 900 *error = err; 901 return (__NSW_UNAVAIL); 902 } 903 904 /* 905 * Get what we now have cached. 906 */ 907 *error = scan_fstab(list, &last); 908 } 909 } 910 if (*list != NULL) { 911 /* 912 * list of entries found 913 */ 914 *error = 0; 915 } 916 917 if (*error == 0) { 918 /* 919 * If we're a server, add an entry for this host's FQDN and 920 * for the first component of its name; it will show up as 921 * a symbolic link to "/". 922 * 923 * We do this, just as the old automounter did, so that, on 924 * a server, you can refer to network home directories on the 925 * machine with a /Network/Server/... path even if you haven't 926 * yet made a mount record for the host, or if you're on an 927 * Active Directory network and mount records are synthesized 928 * when the user is looked up. (See 5479706.) 929 */ 930 if (we_are_a_server()) { 931 /* (presumed) FQDN */ 932 gethostname(thishost, MAXHOSTNAMELEN); 933 err = add_dir_entry(thishost, NULL, NULL, list, &last); 934 if (err != -1) { 935 if (err) 936 return (err); 937 assert(last != NULL); 938 } 939 940 /* First component. */ 941 p = strchr(thishost, '.'); 942 if (p != NULL) { 943 *p = '\0'; 944 err = add_dir_entry(thishost, NULL, NULL, list, 945 &last); 946 if (err != -1) { 947 if (err) 948 return (err); 949 assert(last != NULL); 950 } 951 } 952 } 953 } 954 955 /* We're done processing the list; release the lock. */ 956 pthread_rwlock_unlock(&fstab_cache_lock); 957 958 return (__NSW_SUCCESS); 959} 960 961/* 962 * Check whether we have any entries in the -fstab map. 963 * This is used by automount to decide whether to mount that map 964 * or not. 965 */ 966int 967havefstabkeys(void) 968{ 969 int err; 970 int ret; 971 int i; 972 973 /* 974 * Are we a server? If so, we always have -fstab entries, 975 * as there's always the selflink. 976 */ 977 if (we_are_a_server()) 978 return (1); 979 980 /* 981 * Get a read lock, so the cache doesn't get modified out 982 * from under us. 983 */ 984 err = pthread_rwlock_rdlock(&fstab_cache_lock); 985 if (err != 0) 986 return (0); 987 988 if (trace > 1) 989 trace_prt(1, "havefstabkeys called\n"); 990 991 /* 992 * Check what we have cached. 993 */ 994 for (i = 0; i < HASHTABLESIZE; i++) { 995 if (fstab_hashtable[i] != NULL) { 996 /* 997 * We have at least one entry. 998 */ 999 ret = 1; 1000 goto done; 1001 } 1002 } 1003 1004 /* 1005 * We've seen no entries, either because the cache has 1006 * been purged or we didn't find any the last time we 1007 * scanned the fstab entries. 1008 * 1009 * Try re-reading the fstab entries. 1010 */ 1011 err = readfstab(); 1012 if (err != 0) { 1013 /* 1014 * That failed; give up. 1015 */ 1016 ret = 0; 1017 goto done; 1018 } 1019 1020 /* 1021 * Check what we now have cached. 1022 */ 1023 for (i = 0; i < HASHTABLESIZE; i++) { 1024 if (fstab_hashtable[i] != NULL) { 1025 /* 1026 * We have at least one entry. 1027 */ 1028 ret = 1; 1029 goto done; 1030 } 1031 } 1032 1033 /* 1034 * Nothing found. 1035 */ 1036 ret = 0; 1037 1038done: 1039 1040 /* We're done processing the list; release the lock. */ 1041 pthread_rwlock_unlock(&fstab_cache_lock); 1042 1043 return (ret); 1044} 1045 1046/* 1047 * Load the -static direct map. 1048 */ 1049int 1050loaddirect_static(local_map, opts, stack, stkptr) 1051 char *local_map, *opts; 1052 char **stack, ***stkptr; 1053{ 1054 int done = 0; 1055 int i; 1056 struct staticmap *static_ent; 1057 1058 /* 1059 * Get a read lock, so the cache doesn't get modified out 1060 * from under us. 1061 */ 1062 if (pthread_rwlock_rdlock(&fstab_cache_lock) != 0) 1063 return (__NSW_UNAVAIL); 1064 1065 /* 1066 * Get what we have cached. 1067 */ 1068 for (i = 0; i < HASHTABLESIZE; i++) { 1069 for (static_ent = static_hashtable[i]; static_ent != NULL; 1070 static_ent = static_ent->next) { 1071 dirinit(static_ent->dir, local_map, opts, 1, stack, stkptr); 1072 done++; 1073 } 1074 } 1075 1076 if (!done) { 1077 /* 1078 * We've seen no entries, either because the cache has 1079 * been purged or we didn't find any the last time we 1080 * scanned the fstab entries. 1081 * 1082 * Try re-reading the fstab entries. 1083 */ 1084 if (readfstab() != 0) { 1085 /* 1086 * That failed; give up. 1087 */ 1088 return (__NSW_UNAVAIL); 1089 } 1090 1091 /* 1092 * Get what we now have cached. 1093 */ 1094 for (i = 0; i < HASHTABLESIZE; i++) { 1095 for (static_ent = static_hashtable[i]; 1096 static_ent != NULL; 1097 static_ent = static_ent->next) { 1098 dirinit(static_ent->dir, local_map, opts, 1, stack, stkptr); 1099 done++; 1100 } 1101 } 1102 } 1103 1104 pthread_rwlock_unlock(&fstab_cache_lock); 1105 return (done ? __NSW_SUCCESS : __NSW_NOTFOUND); 1106} 1107 1108/* 1109 * Find the -static map entry corresponding to a given mount point. 1110 * 1111 * We return with the read lock held, so that the cache doesn't 1112 * get cleared, and all the entries freed, while somebody's holding 1113 * onto an entry. The holder must call release_staticmap_entry() 1114 * with the entry; for now, all that will do is release the rwlock. 1115 */ 1116struct staticmap * 1117get_staticmap_entry(const char *dir) 1118{ 1119 struct staticmap *static_ent; 1120 1121 /* 1122 * Get a read lock, so the cache doesn't get modified out 1123 * from under us. 1124 */ 1125 if (pthread_rwlock_rdlock(&fstab_cache_lock) != 0) 1126 return (NULL); 1127 1128 /* 1129 * Get what we have cached. 1130 */ 1131 static_ent = find_staticmap_entry(dir, NULL); 1132 1133 if (static_ent == NULL) { 1134 /* 1135 * We've found no entry, either because the cache has 1136 * been purged or we didn't find any the last time we 1137 * scanned the fstab entries. 1138 * 1139 * Try re-reading the fstab entries. 1140 */ 1141 if (readfstab() != 0) { 1142 /* 1143 * That failed; give up. 1144 */ 1145 pthread_rwlock_unlock(&fstab_cache_lock); 1146 return (NULL); 1147 } 1148 1149 /* 1150 * Check what we now have cached. 1151 */ 1152 static_ent = find_staticmap_entry(dir, NULL); 1153 1154 /* 1155 * If we still have nothing, release the read lock, 1156 * as we're not returning a pointer to something 1157 * in the cache. 1158 */ 1159 if (static_ent == NULL) 1160 pthread_rwlock_unlock(&fstab_cache_lock); 1161 } 1162 1163 return (static_ent); 1164} 1165 1166/* 1167 * Indicate that we're done with a -static map entry returned by 1168 * get_staticmap_entry(). 1169 */ 1170void 1171release_staticmap_entry(__unused struct staticmap *static_ent) 1172{ 1173 pthread_rwlock_unlock(&fstab_cache_lock); 1174} 1175 1176/* 1177 * Purge the fstab cache; if scheduled is true, do so only if it's 1178 * stale, otherwise do it unconditionally. 1179 */ 1180void 1181clean_fstab_cache(int scheduled) 1182{ 1183 /* 1184 * If this is a scheduled cache cleanup, and the cache is still 1185 * valid, don't purge it. 1186 */ 1187 if (scheduled && max_cachetime > time(NULL)) 1188 return; 1189 1190 /* 1191 * Lock the cache against all operations. 1192 */ 1193 if (pthread_rwlock_wrlock(&fstab_cache_lock) != 0) 1194 return; 1195 1196 /* 1197 * Clean out the old entries. 1198 */ 1199 clean_hashtables(); 1200 1201 /* 1202 * Reset the minimum cache time, so that we'll reread the fstab 1203 * entries; we have to do so, as we discarded the results of 1204 * the last read. 1205 */ 1206 min_cachetime = 0; 1207 1208 pthread_rwlock_unlock(&fstab_cache_lock); 1209} 1210