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