amfs_generic.c revision 278155
1/* 2 * Copyright (c) 1997-2006 Erez Zadok 3 * Copyright (c) 1990 Jan-Simon Pendry 4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * 40 * File: am-utils/amd/amfs_generic.c 41 * 42 */ 43 44/* 45 * generic functions used by amfs filesystems, ripped out of amfs_auto.c. 46 */ 47 48#ifdef HAVE_CONFIG_H 49# include <config.h> 50#endif /* HAVE_CONFIG_H */ 51#include <am_defs.h> 52#include <amd.h> 53 54 55/**************************************************************************** 56 *** MACROS *** 57 ****************************************************************************/ 58#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) 59 60 61/**************************************************************************** 62 *** STRUCTURES *** 63 ****************************************************************************/ 64/* 65 * Mounting a file system may take a significant period of time. The 66 * problem is that if this is done in the main process thread then the 67 * entire automounter could be blocked, possibly hanging lots of processes 68 * on the system. Instead we use a continuation scheme to allow mounts to 69 * be attempted in a sub-process. When the sub-process exits we pick up the 70 * exit status (by convention a UN*X error number) and continue in a 71 * notifier. The notifier gets handed a data structure and can then 72 * determine whether the mount was successful or not. If not, it updates 73 * the data structure and tries again until there are no more ways to try 74 * the mount, or some other permanent error occurs. In the mean time no RPC 75 * reply is sent, even after the mount is successful. We rely on the RPC 76 * retry mechanism to resend the lookup request which can then be handled. 77 */ 78struct continuation { 79 am_node *mp; /* Node we are trying to mount */ 80 int retry; /* Try again? */ 81 time_t start; /* Time we started this mount */ 82 int callout; /* Callout identifier */ 83 mntfs **mf; /* Current mntfs */ 84}; 85 86 87/**************************************************************************** 88 *** FORWARD DEFINITIONS *** 89 ****************************************************************************/ 90static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return); 91static mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, 92 char *def_opts, char *pfname); 93static mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return); 94static void amfs_cont(int rc, int term, opaque_t arg); 95static void amfs_retry(int rc, int term, opaque_t arg); 96static void free_continuation(struct continuation *cp); 97static int amfs_bgmount(struct continuation *cp); 98static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts); 99 100 101/**************************************************************************** 102 *** FUNCTIONS *** 103 ****************************************************************************/ 104static am_node * 105amfs_lookup_node(am_node *mp, char *fname, int *error_return) 106{ 107 am_node *new_mp; 108 int error = 0; /* Error so far */ 109 int in_progress = 0; /* # of (un)mount in progress */ 110 mntfs *mf; 111 char *expanded_fname = 0; 112 113 dlog("in amfs_lookup_node"); 114 115 /* 116 * If the server is shutting down 117 * then don't return information 118 * about the mount point. 119 */ 120 if (amd_state == Finishing) { 121 if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) { 122 dlog("%s mount ignored - going down", fname); 123 } else { 124 dlog("%s/%s mount ignored - going down", mp->am_path, fname); 125 } 126 ereturn(ENOENT); 127 } 128 129 /* 130 * Handle special case of "." and ".." 131 */ 132 if (fname[0] == '.') { 133 if (fname[1] == '\0') 134 return mp; /* "." is the current node */ 135 if (fname[1] == '.' && fname[2] == '\0') { 136 if (mp->am_parent) { 137 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 138 return mp->am_parent; /* ".." is the parent node */ 139 } 140 ereturn(ESTALE); 141 } 142 } 143 144 /* 145 * Check for valid key name. 146 * If it is invalid then pretend it doesn't exist. 147 */ 148 if (!valid_key(fname)) { 149 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 150 ereturn(ENOENT); 151 } 152 153 /* 154 * Expand key name. 155 * expanded_fname is now a private copy. 156 */ 157 expanded_fname = expand_selectors(fname); 158 159 /* 160 * Search children of this node 161 */ 162 for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) { 163 if (FSTREQ(new_mp->am_name, expanded_fname)) { 164 if (new_mp->am_error) { 165 error = new_mp->am_error; 166 continue; 167 } 168 169 /* 170 * If the error code is undefined then it must be 171 * in progress. 172 */ 173 mf = new_mp->am_mnt; 174 if (mf->mf_error < 0) 175 goto in_progrss; 176 177 /* 178 * If there was a previous error with this node 179 * then return that error code. 180 */ 181 if (mf->mf_flags & MFF_ERROR) { 182 error = mf->mf_error; 183 continue; 184 } 185 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { 186 in_progrss: 187 /* 188 * If the fs is not mounted or it is unmounting then there 189 * is a background (un)mount in progress. In this case 190 * we just drop the RPC request (return nil) and 191 * wait for a retry, by which time the (un)mount may 192 * have completed. 193 */ 194 dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x", 195 expanded_fname, mf->mf_mount, 196 (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags); 197 in_progress++; 198 if (mf->mf_flags & MFF_UNMOUNTING) { 199 dlog("will remount later"); 200 new_mp->am_flags |= AMF_REMOUNT; 201 } 202 continue; 203 } 204 205 /* 206 * Otherwise we have a hit: return the current mount point. 207 */ 208 dlog("matched %s in %s", expanded_fname, new_mp->am_path); 209 XFREE(expanded_fname); 210 return new_mp; 211 } 212 } 213 214 if (in_progress) { 215 dlog("Waiting while %d mount(s) in progress", in_progress); 216 XFREE(expanded_fname); 217 ereturn(-1); 218 } 219 220 /* 221 * If an error occurred then return it. 222 */ 223 if (error) { 224 dlog("Returning error: %s", strerror(error)); 225 XFREE(expanded_fname); 226 ereturn(error); 227 } 228 229 /* 230 * If the server is going down then just return, 231 * don't try to mount any more file systems 232 */ 233 if ((int) amd_state >= (int) Finishing) { 234 dlog("not found - server going down anyway"); 235 ereturn(ENOENT); 236 } 237 238 /* 239 * Allocate a new map 240 */ 241 new_mp = get_ap_child(mp, expanded_fname); 242 XFREE(expanded_fname); 243 if (new_mp == 0) 244 ereturn(ENOSPC); 245 246 *error_return = -1; 247 return new_mp; 248} 249 250 251 252static mntfs * 253amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, 254 char *def_opts, char *pfname) 255{ 256 am_ops *p; 257 am_opts *fs_opts; 258 mntfs *new_mf; 259 char *mp_dir = 0; 260#ifdef HAVE_FS_AUTOFS 261 int on_autofs = 1; 262#endif /* HAVE_FS_AUTOFS */ 263 264 /* match the operators */ 265 fs_opts = CALLOC(am_opts); 266 p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path, 267 pfname, mf->mf_info); 268#ifdef HAVE_FS_AUTOFS 269 /* XXX: this should be factored out into an autofs-specific function */ 270 if (new_mp->am_flags & AMF_AUTOFS) { 271 /* ignore user-provided fs if we're using autofs */ 272 if (fs_opts->opt_sublink) { 273 /* 274 * For sublinks we need to use a hack with autofs: 275 * mount the filesystem on the original opt_fs (which is NOT an 276 * autofs mountpoint) and symlink (or lofs-mount) to it from 277 * the autofs mountpoint. 278 */ 279 on_autofs = 0; 280 mp_dir = fs_opts->opt_fs; 281 } else { 282 if (p->autofs_fs_flags & FS_ON_AUTOFS) { 283 mp_dir = new_mp->am_path; 284 } else { 285 mp_dir = fs_opts->opt_fs; 286 on_autofs = 0; 287 } 288 } 289 } else 290#endif /* HAVE_FS_AUTOFS */ 291 mp_dir = fs_opts->opt_fs; 292 293 /* 294 * Find or allocate a filesystem for this node. 295 */ 296 new_mf = find_mntfs(p, fs_opts, 297 mp_dir, 298 fs_opts->fs_mtab, 299 def_opts, 300 fs_opts->opt_opts, 301 fs_opts->opt_remopts); 302 303 /* 304 * See whether this is a real filesystem 305 */ 306 p = new_mf->mf_ops; 307 if (p == &amfs_error_ops) { 308 plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path); 309 free_mntfs(new_mf); 310 return NULL; 311 } 312 313 dlog("Got a hit with %s", p->fs_type); 314 315#ifdef HAVE_FS_AUTOFS 316 if (new_mp->am_flags & AMF_AUTOFS && on_autofs) { 317 new_mf->mf_flags |= MFF_ON_AUTOFS; 318 new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags; 319 } 320 /* 321 * A new filesystem is an autofs filesystems if: 322 * 1. it claims it can be one (has the FS_AUTOFS flag) 323 * 2. autofs is enabled system-wide 324 * 3. either has an autofs parent, 325 * or it is explicitly requested to be autofs. 326 */ 327 if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS && 328 amd_use_autofs && 329 ((mf->mf_flags & MFF_IS_AUTOFS) || 330 (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type && 331 STREQ(new_mf->mf_fo->opt_mount_type, "autofs")))) 332 new_mf->mf_flags |= MFF_IS_AUTOFS; 333#endif /* HAVE_FS_AUTOFS */ 334 335 return new_mf; 336} 337 338 339static mntfs ** 340amfs_lookup_mntfs(am_node *new_mp, int *error_return) 341{ 342 am_node *mp; 343 char *info; /* Mount info - where to get the file system */ 344 char **ivecs, **cur_ivec; /* Split version of info */ 345 int num_ivecs; 346 char *orig_def_opts; /* Original Automount options */ 347 char *def_opts; /* Automount options */ 348 int error = 0; /* Error so far */ 349 char path_name[MAXPATHLEN]; /* General path name buffer */ 350 char *pfname; /* Path for database lookup */ 351 mntfs *mf, **mf_array; 352 int count; 353 354 dlog("in amfs_lookup_mntfs"); 355 356 mp = new_mp->am_parent; 357 358 /* 359 * If we get here then this is a reference to an, 360 * as yet, unknown name so we need to search the mount 361 * map for it. 362 */ 363 if (mp->am_pref) { 364 if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name)) 365 ereturn(ENAMETOOLONG); 366 xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name); 367 pfname = path_name; 368 } else { 369 pfname = new_mp->am_name; 370 } 371 372 mf = mp->am_mnt; 373 374 dlog("will search map info in %s to find %s", mf->mf_info, pfname); 375 /* 376 * Consult the oracle for some mount information. 377 * info is malloc'ed and belongs to this routine. 378 * It ends up being free'd in free_continuation(). 379 * 380 * Note that this may return -1 indicating that information 381 * is not yet available. 382 */ 383 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); 384 if (error) { 385 if (error > 0) 386 plog(XLOG_MAP, "No map entry for %s", pfname); 387 else 388 plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 389 ereturn(error); 390 } 391 dlog("mount info is %s", info); 392 393 /* 394 * Split info into an argument vector. 395 * The vector is malloc'ed and belongs to 396 * this routine. It is free'd further down. 397 * 398 * Note: the vector pointers point into info, so don't free it! 399 */ 400 ivecs = strsplit(info, ' ', '\"'); 401 402 if (mf->mf_auto) 403 def_opts = mf->mf_auto; 404 else 405 def_opts = ""; 406 407 orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts)); 408 def_opts = strdup(orig_def_opts); 409 410 /* first build our defaults */ 411 num_ivecs = 0; 412 for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) { 413 if (**cur_ivec == '-') { 414 /* 415 * Pick up new defaults 416 */ 417 char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1); 418 XFREE(def_opts); 419 def_opts = new_def_opts; 420 dlog("Setting def_opts to \"%s\"", def_opts); 421 continue; 422 } else 423 num_ivecs++; 424 } 425 426 mf_array = calloc(num_ivecs + 1, sizeof(mntfs *)); 427 428 /* construct the array of struct mntfs for this mount point */ 429 for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) { 430 mntfs *new_mf; 431 432 if (**cur_ivec == '-') { 433 XFREE(def_opts); 434 if ((*cur_ivec)[1] == '\0') { 435 /* 436 * If we have a single dash '-' than we need to reset the 437 * default options. 438 */ 439 def_opts = strdup(orig_def_opts); 440 dlog("Resetting the default options, a single dash '-' was found."); 441 } else { 442 /* append options to /default options */ 443 def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1); 444 dlog("Resetting def_opts to \"%s\"", def_opts); 445 } 446 continue; 447 } 448 449 /* 450 * If a mntfs has already been found, and we find 451 * a cut then don't try any more locations. 452 * 453 * XXX: we do not know when the "/" was added as an equivalent for "||". 454 * It's undocumented, it might go away at any time. Caveat emptor. 455 */ 456 if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) { 457 if (count > 0) { 458 dlog("Cut: not trying any more locations for %s", mp->am_path); 459 break; 460 } 461 continue; 462 } 463 464 new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname); 465 if (new_mf == NULL) 466 continue; 467 mf_array[count++] = new_mf; 468 } 469 470 /* We're done with ivecs */ 471 XFREE(ivecs); 472 XFREE(info); 473 XFREE(orig_def_opts); 474 XFREE(def_opts); 475 if (count == 0) { /* no match */ 476 XFREE(mf_array); 477 ereturn(ENOENT); 478 } 479 480 return mf_array; 481} 482 483 484/* 485 * The continuation function. This is called by 486 * the task notifier when a background mount attempt 487 * completes. 488 */ 489static void 490amfs_cont(int rc, int term, opaque_t arg) 491{ 492 struct continuation *cp = (struct continuation *) arg; 493 am_node *mp = cp->mp; 494 mntfs *mf = mp->am_mnt; 495 496 dlog("amfs_cont: '%s'", mp->am_path); 497 498 /* 499 * Definitely not trying to mount at the moment 500 */ 501 mf->mf_flags &= ~MFF_MOUNTING; 502 503 /* 504 * While we are mounting - try to avoid race conditions 505 */ 506 new_ttl(mp); 507 508 /* 509 * Wakeup anything waiting for this mount 510 */ 511 wakeup(get_mntfs_wchan(mf)); 512 513 /* 514 * Check for termination signal or exit status... 515 */ 516 if (rc || term) { 517#ifdef HAVE_FS_AUTOFS 518 if (mf->mf_flags & MFF_IS_AUTOFS && 519 !(mf->mf_flags & MFF_MOUNTED)) 520 autofs_release_fh(mp); 521#endif /* HAVE_FS_AUTOFS */ 522 523 if (term) { 524 /* 525 * Not sure what to do for an error code. 526 */ 527 mf->mf_error = EIO; /* XXX ? */ 528 mf->mf_flags |= MFF_ERROR; 529 plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term); 530 } else { 531 /* 532 * Check for exit status... 533 */ 534#ifdef __linux__ 535 /* 536 * HACK ALERT! 537 * 538 * On Linux (and maybe not only) it's possible to run 539 * an amd which "knows" how to mount certain combinations 540 * of nfs_proto/nfs_version which the kernel doesn't grok. 541 * So if we got an EINVAL and we have a server that's not 542 * using NFSv2/UDP, try again with NFSv2/UDP. 543 * 544 * Too bad that there is no way to dynamically determine 545 * what combinations the _client_ supports, as opposed to 546 * what the _server_ supports... 547 */ 548 if (rc == EINVAL && 549 mf->mf_server && 550 (mf->mf_server->fs_version != 2 || 551 !STREQ(mf->mf_server->fs_proto, "udp"))) 552 mf->mf_flags |= MFF_NFS_SCALEDOWN; 553 else 554#endif /* __linux__ */ 555 { 556 mf->mf_error = rc; 557 mf->mf_flags |= MFF_ERROR; 558 errno = rc; /* XXX */ 559 if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx")) 560 plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path); 561 } 562 } 563 564 if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) { 565 /* 566 * If we get here then that attempt didn't work, so 567 * move the info vector pointer along by one and 568 * call the background mount routine again 569 */ 570 amd_stats.d_merr++; 571 cp->mf++; 572 } 573 amfs_bgmount(cp); 574 if (mp->am_error > 0) 575 assign_error_mntfs(mp); 576 } else { 577 /* 578 * The mount worked. 579 */ 580 dlog("Mounting %s returned success", cp->mp->am_path); 581 am_mounted(cp->mp); 582 free_continuation(cp); 583 } 584 585 reschedule_timeout_mp(); 586} 587 588 589/* 590 * Retry a mount 591 */ 592static void 593amfs_retry(int rc, int term, opaque_t arg) 594{ 595 struct continuation *cp = (struct continuation *) arg; 596 am_node *mp = cp->mp; 597 int error = 0; 598 599 dlog("Commencing retry for mount of %s", mp->am_path); 600 601 new_ttl(mp); 602 603 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) { 604 /* 605 * The entire mount has timed out. Set the error code and skip past all 606 * the mntfs's so that amfs_bgmount will not have any more 607 * ways to try the mount, thus causing an error. 608 */ 609 plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path); 610 error = ETIMEDOUT; 611 while (*cp->mf) 612 cp->mf++; 613 /* explicitly forbid further retries after timeout */ 614 cp->retry = FALSE; 615 } 616 if (error || !IN_PROGRESS(cp)) 617 error = amfs_bgmount(cp); 618 619 reschedule_timeout_mp(); 620} 621 622 623/* 624 * Discard an old continuation 625 */ 626static void 627free_continuation(struct continuation *cp) 628{ 629 mntfs **mfp; 630 631 dlog("free_continuation"); 632 if (cp->callout) 633 untimeout(cp->callout); 634 /* 635 * we must free the mntfs's in the list. 636 * so free all of them if there was an error, 637 * or free all but the used one, if the mount succeeded. 638 */ 639 for (mfp = cp->mp->am_mfarray; *mfp; mfp++) { 640 free_mntfs(*mfp); 641 } 642 XFREE(cp->mp->am_mfarray); 643 cp->mp->am_mfarray = 0; 644 XFREE(cp); 645} 646 647 648/* 649 * Pick a file system to try mounting and 650 * do that in the background if necessary 651 * 652For each location: 653 discard previous mount location if required 654 fetch next mount location 655 if the filesystem failed to be mounted then 656 this_error = error from filesystem 657 goto failed 658 if the filesystem is mounting or unmounting then 659 goto retry; 660 if the fileserver is down then 661 this_error = EIO 662 continue; 663 if the filesystem is already mounted 664 break 665 fi 666 667 this_error = initialize mount point 668 669 if no error on this mount and mount is delayed then 670 this_error = -1 671 fi 672 if this_error < 0 then 673 retry = true 674 fi 675 if no error on this mount then 676 if mount in background then 677 run mount in background 678 return -1 679 else 680 this_error = mount in foreground 681 fi 682 fi 683 if an error occurred on this mount then 684 update stats 685 save error in mount point 686 fi 687endfor 688 */ 689static int 690amfs_bgmount(struct continuation *cp) 691{ 692 am_node *mp = cp->mp; 693 mntfs *mf; /* Current mntfs */ 694 int this_error = -1; /* Per-mount error */ 695 int hard_error = -1; /* Cumulative per-node error */ 696 697 if (mp->am_mnt) 698 free_mntfs(mp->am_mnt); 699 700 /* 701 * Try to mount each location. 702 * At the end: 703 * hard_error == 0 indicates something was mounted. 704 * hard_error > 0 indicates everything failed with a hard error 705 * hard_error < 0 indicates nothing could be mounted now 706 */ 707 for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) { 708 am_ops *p; 709 710 mf = dup_mntfs(mp->am_mnt); 711 p = mf->mf_ops; 712 713 if (hard_error < 0) 714 hard_error = this_error; 715 this_error = 0; 716 717 if (mf->mf_error > 0) { 718 this_error = mf->mf_error; 719 goto failed; 720 } 721 722 if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { 723 /* 724 * Still mounting - retry later 725 */ 726 dlog("mount of \"%s\" already pending", mf->mf_info); 727 goto retry; 728 } 729 730 if (FSRV_ISDOWN(mf->mf_server)) { 731 /* 732 * Would just mount from the same place 733 * as a hung mount - so give up 734 */ 735 dlog("%s is already hung - giving up", mf->mf_server->fs_host); 736 this_error = EIO; 737 goto failed; 738 } 739 740 if (mp->am_link) { 741 XFREE(mp->am_link); 742 mp->am_link = NULL; 743 } 744 if (mf->mf_fo && mf->mf_fo->opt_sublink) 745 mp->am_link = strdup(mf->mf_fo->opt_sublink); 746 747 /* 748 * Will usually need to play around with the mount nodes 749 * file attribute structure. This must be done here. 750 * Try and get things initialized, even if the fileserver 751 * is not known to be up. In the common case this will 752 * progress things faster. 753 */ 754 755 /* 756 * Fill in attribute fields. 757 */ 758 if (mf->mf_fsflags & FS_DIRECTORY) 759 mk_fattr(&mp->am_fattr, NFDIR); 760 else 761 mk_fattr(&mp->am_fattr, NFLNK); 762 763 if (mf->mf_flags & MFF_MOUNTED) { 764 dlog("duplicate mount of \"%s\" ...", mf->mf_info); 765 /* 766 * Skip initial processing of the mountpoint if already mounted. 767 * This could happen if we have multiple sublinks into the same f/s, 768 * or if we are restarting an already-mounted filesystem. 769 */ 770 goto already_mounted; 771 } 772 773 if (mf->mf_fo && mf->mf_fo->fs_mtab) { 774 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s", 775 mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type, 776 mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs"); 777 } 778 779 if (p->fs_init && !(mf->mf_flags & MFF_RESTART)) 780 this_error = p->fs_init(mf); 781 782 if (this_error > 0) 783 goto failed; 784 if (this_error < 0) 785 goto retry; 786 787 if (mf->mf_fo && mf->mf_fo->opt_delay) { 788 /* 789 * If there is a delay timer on the mount 790 * then don't try to mount if the timer 791 * has not expired. 792 */ 793 int i = atoi(mf->mf_fo->opt_delay); 794 time_t now = clocktime(NULL); 795 if (i > 0 && now < (cp->start + i)) { 796 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start)); 797 goto retry; 798 } 799 } 800 801 /* 802 * If the directory is not yet made and it needs to be made, then make it! 803 */ 804 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) { 805 plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount); 806 this_error = mkdirs(mf->mf_mount, 0555); 807 if (this_error) { 808 plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error)); 809 goto failed; 810 } 811 mf->mf_flags |= MFF_MKMNT; 812 } 813 814#ifdef HAVE_FS_AUTOFS 815 if (mf->mf_flags & MFF_IS_AUTOFS) 816 if ((this_error = autofs_get_fh(mp))) 817 goto failed; 818#endif /* HAVE_FS_AUTOFS */ 819 820 already_mounted: 821 mf->mf_flags |= MFF_MOUNTING; 822 if (mf->mf_fsflags & FS_MBACKGROUND) { 823 dlog("backgrounding mount of \"%s\"", mf->mf_mount); 824 if (cp->callout) { 825 untimeout(cp->callout); 826 cp->callout = 0; 827 } 828 829 /* actually run the task, backgrounding as necessary */ 830 run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp); 831 return -1; 832 } else { 833 dlog("foreground mount of \"%s\" ...", mf->mf_mount); 834 this_error = mount_node((opaque_t) mp); 835 } 836 837 mf->mf_flags &= ~MFF_MOUNTING; 838 if (this_error > 0) 839 goto failed; 840 if (this_error == 0) { 841 am_mounted(mp); 842 break; /* Success */ 843 } 844 845 retry: 846 if (!cp->retry) 847 continue; 848 dlog("will retry ...\n"); 849 850 /* 851 * Arrange that amfs_bgmount is called 852 * after anything else happens. 853 */ 854 dlog("Arranging to retry mount of %s", mp->am_path); 855 sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf)); 856 if (cp->callout) 857 untimeout(cp->callout); 858 cp->callout = timeout(RETRY_INTERVAL, wakeup, 859 (opaque_t) get_mntfs_wchan(mf)); 860 861 mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL; 862 863 /* 864 * Not done yet - so don't return anything 865 */ 866 return -1; 867 868 failed: 869 amd_stats.d_merr++; 870 mf->mf_error = this_error; 871 mf->mf_flags |= MFF_ERROR; 872#ifdef HAVE_FS_AUTOFS 873 if (mp->am_autofs_fh) 874 autofs_release_fh(mp); 875#endif /* HAVE_FS_AUTOFS */ 876 if (mf->mf_flags & MFF_MKMNT) { 877 rmdirs(mf->mf_mount); 878 mf->mf_flags &= ~MFF_MKMNT; 879 } 880 /* 881 * Wakeup anything waiting for this mount 882 */ 883 wakeup(get_mntfs_wchan(mf)); 884 free_mntfs(mf); 885 /* continue */ 886 } 887 888 /* 889 * If we get here, then either the mount succeeded or 890 * there is no more mount information available. 891 */ 892 if (this_error) { 893 mp->am_mnt = mf = new_mntfs(); 894 895#ifdef HAVE_FS_AUTOFS 896 if (mp->am_flags & AMF_AUTOFS) 897 autofs_mount_failed(mp); 898 else 899#endif /* HAVE_FS_AUTOFS */ 900 nfs_quick_reply(mp, this_error); 901 902 if (hard_error <= 0) 903 hard_error = this_error; 904 if (hard_error < 0) 905 hard_error = ETIMEDOUT; 906 907 /* 908 * Set a small(ish) timeout on an error node if 909 * the error was not a time out. 910 */ 911 switch (hard_error) { 912 case ETIMEDOUT: 913 case EWOULDBLOCK: 914 case EIO: 915 mp->am_timeo = 17; 916 break; 917 default: 918 mp->am_timeo = 5; 919 break; 920 } 921 new_ttl(mp); 922 } else { 923 mf = mp->am_mnt; 924 /* 925 * Wakeup anything waiting for this mount 926 */ 927 wakeup(get_mntfs_wchan(mf)); 928 hard_error = 0; 929 } 930 931 /* 932 * Make sure that the error value in the mntfs has a 933 * reasonable value. 934 */ 935 if (mf->mf_error < 0) { 936 mf->mf_error = hard_error; 937 if (hard_error) 938 mf->mf_flags |= MFF_ERROR; 939 } 940 941 /* 942 * In any case we don't need the continuation any more 943 */ 944 free_continuation(cp); 945 946 return hard_error; 947} 948 949 950static char * 951amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts) 952{ 953 char *dflts; 954 char *dfl; 955 char **rvec = NULL; 956 struct mnt_map *mm = (mnt_map *) mf->mf_private; 957 958 dlog("determining /defaults entry value"); 959 960 /* 961 * Find out if amd.conf overrode any map-specific /defaults. 962 * 963 * HACK ALERT: there's no easy way to find out what the map mount point is 964 * at this point, so I am forced to initialize the mnt_map->cfm field here 965 * for the first time, upon the very first search for a /defaults entry in 966 * this map. This initialization is much better done in mapc_create(), 967 * but it's impossible to do that there with the current code structure. 968 */ 969 if (mm->cfm == NULL) { /* then initialize it for first time */ 970 mm->cfm = find_cf_map(mf->mf_mount); 971 } 972 if (mm->cfm && mm->cfm->cfm_defaults) { 973 dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults); 974 dflts = strdup(mm->cfm->cfm_defaults); 975 } else if (mapc_search(mm, "/defaults", &dflts) == 0) { 976 dlog("/defaults gave %s", dflts); 977 } else { 978 return def_opts; /* if nothing found */ 979 } 980 981 /* trim leading '-' in case thee's one */ 982 if (*dflts == '-') 983 dfl = dflts + 1; 984 else 985 dfl = dflts; 986 987 /* 988 * Chop the defaults up 989 */ 990 rvec = strsplit(dfl, ' ', '\"'); 991 992 if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) { 993 /* 994 * Pick whichever first entry matched the list of selectors. 995 * Strip the selectors from the string, and assign to dfl the 996 * rest of the string. 997 */ 998 if (rvec) { 999 am_opts ap; 1000 am_ops *pt; 1001 char **sp = rvec; 1002 while (*sp) { /* loop until you find something, if any */ 1003 memset((char *) &ap, 0, sizeof(am_opts)); 1004 /* 1005 * This next routine cause many spurious "expansion of ... is" 1006 * messages, which are ignored, b/c all we need out of this 1007 * routine is to match selectors. These spurious messages may 1008 * be wrong, esp. if they try to expand ${key} b/c it will 1009 * get expanded to "/defaults" 1010 */ 1011 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", 1012 mp->am_parent->am_mnt->mf_info); 1013 free_opts(&ap); /* don't leak */ 1014 if (pt == &amfs_error_ops) { 1015 plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); 1016 } else { 1017 dfl = strip_selectors(*sp, "/defaults"); 1018 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); 1019 break; 1020 } 1021 ++sp; 1022 } 1023 } 1024 } else { /* not selectors_in_defaults */ 1025 /* 1026 * Extract first value 1027 */ 1028 dfl = rvec[0]; 1029 } 1030 1031 /* 1032 * If there were any values at all... 1033 */ 1034 if (dfl) { 1035 /* 1036 * Log error if there were other values 1037 */ 1038 if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) { 1039 dlog("/defaults chopped into %s", dfl); 1040 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 1041 } 1042 1043 /* 1044 * Prepend to existing defaults if they exist, 1045 * otherwise just use these defaults. 1046 */ 1047 if (*def_opts && *dfl) { 1048 size_t l = strlen(def_opts) + strlen(dfl) + 2; 1049 char *nopts = (char *) xmalloc(l); 1050 xsnprintf(nopts, l, "%s;%s", dfl, def_opts); 1051 XFREE(def_opts); 1052 def_opts = nopts; 1053 } else if (*dfl) { 1054 def_opts = strealloc(def_opts, dfl); 1055 } 1056 } 1057 1058 XFREE(dflts); 1059 1060 /* don't need info vector any more */ 1061 if (rvec) 1062 XFREE(rvec); 1063 1064 return def_opts; 1065} 1066 1067 1068am_node * 1069amfs_generic_mount_child(am_node *new_mp, int *error_return) 1070{ 1071 int error; 1072 struct continuation *cp; /* Continuation structure if need to mount */ 1073 1074 dlog("in amfs_generic_mount_child"); 1075 1076 *error_return = error = 0; /* Error so far */ 1077 1078 /* we have an errorfs attached to the am_node, free it */ 1079 free_mntfs(new_mp->am_mnt); 1080 new_mp->am_mnt = 0; 1081 1082 /* 1083 * Construct a continuation 1084 */ 1085 cp = ALLOC(struct continuation); 1086 cp->callout = 0; 1087 cp->mp = new_mp; 1088 cp->retry = TRUE; 1089 cp->start = clocktime(NULL); 1090 cp->mf = new_mp->am_mfarray; 1091 1092 /* 1093 * Try and mount the file system. If this succeeds immediately (possible 1094 * for a ufs file system) then return the attributes, otherwise just 1095 * return an error. 1096 */ 1097 error = amfs_bgmount(cp); 1098 reschedule_timeout_mp(); 1099 if (!error) 1100 return new_mp; 1101 1102 /* 1103 * Code for quick reply. If current_transp is set, then it's the 1104 * transp that's been passed down from nfs_program_2() or from 1105 * autofs_program_[123](). 1106 * If new_mp->am_transp is not already set, set it by copying in 1107 * current_transp. Once am_transp is set, nfs_quick_reply() and 1108 * autofs_mount_succeeded() can use it to send a reply to the 1109 * client that requested this mount. 1110 */ 1111 if (current_transp && !new_mp->am_transp) { 1112 dlog("Saving RPC transport for %s", new_mp->am_path); 1113 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); 1114 *(new_mp->am_transp) = *current_transp; 1115 } 1116 if (error && new_mp->am_mnt && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) 1117 new_mp->am_error = error; 1118 1119 if (new_mp->am_error > 0) 1120 assign_error_mntfs(new_mp); 1121 1122 ereturn(error); 1123} 1124 1125 1126/* 1127 * Automount interface to RPC lookup routine 1128 * Find the corresponding entry and return 1129 * the file handle for it. 1130 */ 1131am_node * 1132amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op) 1133{ 1134 am_node *new_mp; 1135 mntfs **mf_array; 1136 int mp_error; 1137 1138 dlog("in amfs_generic_lookup_child"); 1139 1140 *error_return = 0; 1141 new_mp = amfs_lookup_node(mp, fname, error_return); 1142 1143 /* return if we got an error */ 1144 if (!new_mp || *error_return > 0) 1145 return new_mp; 1146 1147 /* also return if it's already mounted and known to be up */ 1148 if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server)) 1149 return new_mp; 1150 1151 switch (op) { 1152 case VLOOK_DELETE: 1153 /* 1154 * If doing a delete then don't create again! 1155 */ 1156 ereturn(ENOENT); 1157 case VLOOK_LOOKUP: 1158 return new_mp; 1159 } 1160 1161 /* save error_return */ 1162 mp_error = *error_return; 1163 1164 mf_array = amfs_lookup_mntfs(new_mp, error_return); 1165 if (!mf_array) { 1166 new_mp->am_error = new_mp->am_mnt->mf_error = *error_return; 1167 free_map(new_mp); 1168 return NULL; 1169 } 1170 1171 /* 1172 * Already mounted but known to be down: 1173 * check if we have any alternatives to mount 1174 */ 1175 if (mp_error == 0) { 1176 mntfs **mfp; 1177 for (mfp = mf_array; *mfp; mfp++) 1178 if (*mfp != new_mp->am_mnt) 1179 break; 1180 if (*mfp != NULL) { 1181 /* 1182 * we found an alternative, so try mounting again. 1183 */ 1184 *error_return = -1; 1185 } else { 1186 for (mfp = mf_array; *mfp; mfp++) 1187 free_mntfs(*mfp); 1188 XFREE(mf_array); 1189 if (new_mp->am_flags & AMF_SOFTLOOKUP) { 1190 ereturn(EIO); 1191 } else { 1192 *error_return = 0; 1193 return new_mp; 1194 } 1195 } 1196 } 1197 1198 /* store the array inside the am_node */ 1199 new_mp->am_mfarray = mf_array; 1200 1201 /* 1202 * Note: while it might seem like a good idea to prioritize 1203 * the list of mntfs's we got here, it probably isn't. 1204 * It would ignore the ordering of entries specified by the user, 1205 * which is counterintuitive and confusing. 1206 */ 1207 return new_mp; 1208} 1209 1210 1211void 1212amfs_generic_mounted(mntfs *mf) 1213{ 1214 amfs_mkcacheref(mf); 1215} 1216 1217 1218/* 1219 * Unmount an automount sub-node 1220 */ 1221int 1222amfs_generic_umount(am_node *mp, mntfs *mf) 1223{ 1224 int error = 0; 1225 1226#ifdef HAVE_FS_AUTOFS 1227 int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; 1228 if (mf->mf_flags & MFF_IS_AUTOFS) 1229 error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); 1230#endif /* HAVE_FS_AUTOFS */ 1231 1232 return error; 1233} 1234 1235 1236char * 1237amfs_generic_match(am_opts *fo) 1238{ 1239 char *p; 1240 1241 if (!fo->opt_rfs) { 1242 plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)"); 1243 return 0; 1244 } 1245 if (!fo->opt_fs) { 1246 plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)"); 1247 return 0; 1248 } 1249 1250 /* 1251 * Swap round fs:= and rfs:= options 1252 * ... historical (jsp) 1253 */ 1254 p = fo->opt_rfs; 1255 fo->opt_rfs = fo->opt_fs; 1256 fo->opt_fs = p; 1257 1258 /* 1259 * mtab entry turns out to be the name of the mount map 1260 */ 1261 return strdup(fo->opt_rfs ? fo->opt_rfs : "."); 1262} 1263