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