amfs_generic.c revision 174295
1202719Sgabor/* 2202719Sgabor * Copyright (c) 1997-2006 Erez Zadok 3202719Sgabor * Copyright (c) 1990 Jan-Simon Pendry 4202719Sgabor * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5202719Sgabor * Copyright (c) 1990 The Regents of the University of California. 6202719Sgabor * All rights reserved. 7202719Sgabor * 8202719Sgabor * This code is derived from software contributed to Berkeley by 9202719Sgabor * Jan-Simon Pendry at Imperial College, London. 10202719Sgabor * 11202719Sgabor * Redistribution and use in source and binary forms, with or without 12202719Sgabor * modification, are permitted provided that the following conditions 13202719Sgabor * are met: 14202719Sgabor * 1. Redistributions of source code must retain the above copyright 15202719Sgabor * notice, this list of conditions and the following disclaimer. 16202719Sgabor * 2. Redistributions in binary form must reproduce the above copyright 17202719Sgabor * notice, this list of conditions and the following disclaimer in the 18202719Sgabor * documentation and/or other materials provided with the distribution. 19202719Sgabor * 3. All advertising materials mentioning features or use of this software 20202719Sgabor * must display the following acknowledgment: 21202719Sgabor * This product includes software developed by the University of 22202719Sgabor * California, Berkeley and its contributors. 23202719Sgabor * 4. Neither the name of the University nor the names of its contributors 24202719Sgabor * may be used to endorse or promote products derived from this software 25202719Sgabor * without specific prior written permission. 26202719Sgabor * 27202719Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28202719Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29202719Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30202719Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31202719Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32202719Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33202719Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34202719Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35202719Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36202719Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37202719Sgabor * SUCH DAMAGE. 38202719Sgabor * 39202719Sgabor * 40202719Sgabor * File: am-utils/amd/amfs_generic.c 41202719Sgabor * 42202719Sgabor */ 43202719Sgabor 44202719Sgabor/* 45202719Sgabor * generic functions used by amfs filesystems, ripped out of amfs_auto.c. 46202719Sgabor */ 47202719Sgabor 48202719Sgabor#ifdef HAVE_CONFIG_H 49202719Sgabor# include <config.h> 50202719Sgabor#endif /* HAVE_CONFIG_H */ 51202719Sgabor#include <am_defs.h> 52202719Sgabor#include <amd.h> 53202719Sgabor 54202719Sgabor 55202719Sgabor/**************************************************************************** 56202719Sgabor *** MACROS *** 57202719Sgabor ****************************************************************************/ 58202719Sgabor#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) 59202719Sgabor 60202719Sgabor 61202719Sgabor/**************************************************************************** 62202719Sgabor *** STRUCTURES *** 63202719Sgabor ****************************************************************************/ 64202719Sgabor/* 65202719Sgabor * Mounting a file system may take a significant period of time. The 66202719Sgabor * problem is that if this is done in the main process thread then the 67202719Sgabor * entire automounter could be blocked, possibly hanging lots of processes 68202719Sgabor * on the system. Instead we use a continuation scheme to allow mounts to 69202719Sgabor * be attempted in a sub-process. When the sub-process exits we pick up the 70202719Sgabor * exit status (by convention a UN*X error number) and continue in a 71202719Sgabor * notifier. The notifier gets handed a data structure and can then 72202719Sgabor * determine whether the mount was successful or not. If not, it updates 73202719Sgabor * the data structure and tries again until there are no more ways to try 74202719Sgabor * the mount, or some other permanent error occurs. In the mean time no RPC 75202719Sgabor * reply is sent, even after the mount is successful. We rely on the RPC 76202719Sgabor * retry mechanism to resend the lookup request which can then be handled. 77202719Sgabor */ 78202719Sgaborstruct continuation { 79202719Sgabor am_node *mp; /* Node we are trying to mount */ 80202719Sgabor int retry; /* Try again? */ 81202719Sgabor time_t start; /* Time we started this mount */ 82202719Sgabor int callout; /* Callout identifier */ 83202719Sgabor mntfs **mf; /* Current mntfs */ 84202719Sgabor}; 85202719Sgabor 86202719Sgabor 87202719Sgabor/**************************************************************************** 88202719Sgabor *** FORWARD DEFINITIONS *** 89202719Sgabor ****************************************************************************/ 90202719Sgaborstatic am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return); 91202719Sgaborstatic mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, 92202719Sgabor char *def_opts, char *pfname); 93202719Sgaborstatic mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return); 94202719Sgaborstatic void amfs_cont(int rc, int term, opaque_t arg); 95202719Sgaborstatic void amfs_retry(int rc, int term, opaque_t arg); 96202719Sgaborstatic void free_continuation(struct continuation *cp); 97202719Sgaborstatic int amfs_bgmount(struct continuation *cp); 98202719Sgaborstatic char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts); 99202719Sgabor 100202719Sgabor 101202719Sgabor/**************************************************************************** 102202719Sgabor *** FUNCTIONS *** 103202719Sgabor ****************************************************************************/ 104202719Sgaborstatic am_node * 105202719Sgaboramfs_lookup_node(am_node *mp, char *fname, int *error_return) 106202719Sgabor{ 107202719Sgabor am_node *new_mp; 108202719Sgabor int error = 0; /* Error so far */ 109202719Sgabor int in_progress = 0; /* # of (un)mount in progress */ 110202719Sgabor mntfs *mf; 111202719Sgabor char *expanded_fname = 0; 112202719Sgabor 113202719Sgabor dlog("in amfs_lookup_node"); 114202719Sgabor 115202719Sgabor /* 116202719Sgabor * If the server is shutting down 117202719Sgabor * then don't return information 118202719Sgabor * about the mount point. 119202719Sgabor */ 120202719Sgabor if (amd_state == Finishing) { 121202719Sgabor if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) { 122202719Sgabor dlog("%s mount ignored - going down", fname); 123202719Sgabor } else { 124202719Sgabor dlog("%s/%s mount ignored - going down", mp->am_path, fname); 125202719Sgabor } 126202719Sgabor ereturn(ENOENT); 127202719Sgabor } 128202719Sgabor 129202719Sgabor /* 130202719Sgabor * Handle special case of "." and ".." 131202719Sgabor */ 132202719Sgabor if (fname[0] == '.') { 133202719Sgabor if (fname[1] == '\0') 134202719Sgabor return mp; /* "." is the current node */ 135202719Sgabor if (fname[1] == '.' && fname[2] == '\0') { 136202719Sgabor if (mp->am_parent) { 137202719Sgabor dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 138202719Sgabor return mp->am_parent; /* ".." is the parent node */ 139202719Sgabor } 140202719Sgabor ereturn(ESTALE); 141202719Sgabor } 142202719Sgabor } 143202719Sgabor 144202719Sgabor /* 145202719Sgabor * Check for valid key name. 146202719Sgabor * If it is invalid then pretend it doesn't exist. 147202719Sgabor */ 148202719Sgabor if (!valid_key(fname)) { 149202719Sgabor plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 150202719Sgabor ereturn(ENOENT); 151202719Sgabor } 152202719Sgabor 153202719Sgabor /* 154202719Sgabor * Expand key name. 155202719Sgabor * expanded_fname is now a private copy. 156202719Sgabor */ 157202719Sgabor expanded_fname = expand_selectors(fname); 158202719Sgabor 159202719Sgabor /* 160202719Sgabor * Search children of this node 161202719Sgabor */ 162202719Sgabor for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) { 163202719Sgabor if (FSTREQ(new_mp->am_name, expanded_fname)) { 164202719Sgabor if (new_mp->am_error) { 165202719Sgabor error = new_mp->am_error; 166202719Sgabor continue; 167202719Sgabor } 168202719Sgabor 169202719Sgabor /* 170202719Sgabor * If the error code is undefined then it must be 171202719Sgabor * in progress. 172202719Sgabor */ 173202719Sgabor mf = new_mp->am_mnt; 174202719Sgabor if (mf->mf_error < 0) 175202719Sgabor goto in_progrss; 176202719Sgabor 177202719Sgabor /* 178202719Sgabor * If there was a previous error with this node 179202719Sgabor * then return that error code. 180202719Sgabor */ 181202719Sgabor if (mf->mf_flags & MFF_ERROR) { 182202719Sgabor error = mf->mf_error; 183202719Sgabor continue; 184202719Sgabor } 185202719Sgabor if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { 186202719Sgabor in_progrss: 187202719Sgabor /* 188202719Sgabor * If the fs is not mounted or it is unmounting then there 189202719Sgabor * is a background (un)mount in progress. In this case 190202719Sgabor * we just drop the RPC request (return nil) and 191202719Sgabor * wait for a retry, by which time the (un)mount may 192202719Sgabor * have completed. 193202719Sgabor */ 194202719Sgabor dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x", 195202719Sgabor expanded_fname, mf->mf_mount, 196202719Sgabor (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags); 197202719Sgabor in_progress++; 198202719Sgabor if (mf->mf_flags & MFF_UNMOUNTING) { 199202719Sgabor dlog("will remount later"); 200202719Sgabor new_mp->am_flags |= AMF_REMOUNT; 201202719Sgabor } 202202719Sgabor continue; 203202719Sgabor } 204202719Sgabor 205202719Sgabor /* 206202719Sgabor * Otherwise we have a hit: return the current mount point. 207202719Sgabor */ 208202719Sgabor dlog("matched %s in %s", expanded_fname, new_mp->am_path); 209202719Sgabor XFREE(expanded_fname); 210202719Sgabor return new_mp; 211202719Sgabor } 212202719Sgabor } 213202719Sgabor 214202719Sgabor if (in_progress) { 215202719Sgabor dlog("Waiting while %d mount(s) in progress", in_progress); 216202719Sgabor XFREE(expanded_fname); 217202719Sgabor ereturn(-1); 218202719Sgabor } 219202719Sgabor 220202719Sgabor /* 221202719Sgabor * If an error occurred then return it. 222202719Sgabor */ 223202719Sgabor if (error) { 224202719Sgabor dlog("Returning error: %s", strerror(error)); 225202719Sgabor XFREE(expanded_fname); 226202719Sgabor ereturn(error); 227202719Sgabor } 228202719Sgabor 229202719Sgabor /* 230202719Sgabor * If the server is going down then just return, 231202719Sgabor * don't try to mount any more file systems 232202719Sgabor */ 233202719Sgabor if ((int) amd_state >= (int) Finishing) { 234202719Sgabor dlog("not found - server going down anyway"); 235202719Sgabor ereturn(ENOENT); 236202719Sgabor } 237202719Sgabor 238202719Sgabor /* 239202719Sgabor * Allocate a new map 240202719Sgabor */ 241202719Sgabor new_mp = get_ap_child(mp, expanded_fname); 242202719Sgabor XFREE(expanded_fname); 243202719Sgabor if (new_mp == 0) 244202719Sgabor ereturn(ENOSPC); 245202719Sgabor 246202719Sgabor *error_return = -1; 247202719Sgabor return new_mp; 248202719Sgabor} 249202719Sgabor 250202719Sgabor 251202719Sgabor 252202719Sgaborstatic mntfs * 253202719Sgaboramfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, 254202719Sgabor char *def_opts, char *pfname) 255202719Sgabor{ 256202719Sgabor am_ops *p; 257202719Sgabor am_opts *fs_opts; 258202719Sgabor mntfs *new_mf; 259202719Sgabor char *mp_dir = 0; 260202719Sgabor#ifdef HAVE_FS_AUTOFS 261202719Sgabor int on_autofs = 1; 262202719Sgabor#endif /* HAVE_FS_AUTOFS */ 263202719Sgabor 264202719Sgabor /* match the operators */ 265202719Sgabor fs_opts = CALLOC(am_opts); 266202719Sgabor p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path, 267202719Sgabor pfname, mf->mf_info); 268202719Sgabor#ifdef HAVE_FS_AUTOFS 269202719Sgabor /* XXX: this should be factored out into an autofs-specific function */ 270202719Sgabor if (new_mp->am_flags & AMF_AUTOFS) { 271202719Sgabor /* ignore user-provided fs if we're using autofs */ 272202719Sgabor if (fs_opts->opt_sublink) { 273202719Sgabor /* 274202719Sgabor * For sublinks we need to use a hack with autofs: 275202719Sgabor * mount the filesystem on the original opt_fs (which is NOT an 276202719Sgabor * autofs mountpoint) and symlink (or lofs-mount) to it from 277202719Sgabor * the autofs mountpoint. 278202719Sgabor */ 279202719Sgabor on_autofs = 0; 280202719Sgabor mp_dir = fs_opts->opt_fs; 281202719Sgabor } else { 282202719Sgabor if (p->autofs_fs_flags & FS_ON_AUTOFS) { 283202719Sgabor mp_dir = new_mp->am_path; 284202719Sgabor } else { 285202719Sgabor mp_dir = fs_opts->opt_fs; 286202719Sgabor on_autofs = 0; 287202719Sgabor } 288202719Sgabor } 289202719Sgabor } else 290202719Sgabor#endif /* HAVE_FS_AUTOFS */ 291202719Sgabor mp_dir = fs_opts->opt_fs; 292202719Sgabor 293202719Sgabor /* 294202719Sgabor * Find or allocate a filesystem for this node. 295202719Sgabor */ 296202719Sgabor new_mf = find_mntfs(p, fs_opts, 297202719Sgabor mp_dir, 298202719Sgabor fs_opts->fs_mtab, 299202719Sgabor def_opts, 300202719Sgabor fs_opts->opt_opts, 301202719Sgabor fs_opts->opt_remopts); 302202719Sgabor 303202719Sgabor /* 304202719Sgabor * See whether this is a real filesystem 305202719Sgabor */ 306202719Sgabor p = new_mf->mf_ops; 307202719Sgabor if (p == &amfs_error_ops) { 308202719Sgabor plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path); 309202719Sgabor free_mntfs(new_mf); 310202719Sgabor return NULL; 311202719Sgabor } 312202719Sgabor 313202719Sgabor dlog("Got a hit with %s", p->fs_type); 314202719Sgabor 315202719Sgabor#ifdef HAVE_FS_AUTOFS 316202719Sgabor if (new_mp->am_flags & AMF_AUTOFS && on_autofs) { 317202719Sgabor new_mf->mf_flags |= MFF_ON_AUTOFS; 318202719Sgabor new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags; 319202719Sgabor } 320202719Sgabor /* 321202719Sgabor * A new filesystem is an autofs filesystems if: 322202719Sgabor * 1. it claims it can be one (has the FS_AUTOFS flag) 323202719Sgabor * 2. autofs is enabled system-wide 324202719Sgabor * 3. either has an autofs parent, 325202719Sgabor * or it is explicitly requested to be autofs. 326202719Sgabor */ 327202719Sgabor if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS && 328202719Sgabor amd_use_autofs && 329202719Sgabor ((mf->mf_flags & MFF_IS_AUTOFS) || 330202719Sgabor (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type && 331202719Sgabor STREQ(new_mf->mf_fo->opt_mount_type, "autofs")))) 332202719Sgabor new_mf->mf_flags |= MFF_IS_AUTOFS; 333202719Sgabor#endif /* HAVE_FS_AUTOFS */ 334202719Sgabor 335202719Sgabor return new_mf; 336202719Sgabor} 337202719Sgabor 338202719Sgabor 339202719Sgaborstatic mntfs ** 340202719Sgaboramfs_lookup_mntfs(am_node *new_mp, int *error_return) 341202719Sgabor{ 342202719Sgabor am_node *mp; 343202719Sgabor char *info; /* Mount info - where to get the file system */ 344202719Sgabor char **ivecs, **cur_ivec; /* Split version of info */ 345202719Sgabor int num_ivecs; 346202719Sgabor char *orig_def_opts; /* Original Automount options */ 347202719Sgabor char *def_opts; /* Automount options */ 348202719Sgabor int error = 0; /* Error so far */ 349202719Sgabor char path_name[MAXPATHLEN]; /* General path name buffer */ 350202719Sgabor char *pfname; /* Path for database lookup */ 351202719Sgabor mntfs *mf, **mf_array; 352202719Sgabor int count; 353202719Sgabor 354202719Sgabor dlog("in amfs_lookup_mntfs"); 355202719Sgabor 356202719Sgabor mp = new_mp->am_parent; 357202719Sgabor 358202719Sgabor /* 359202719Sgabor * If we get here then this is a reference to an, 360202719Sgabor * as yet, unknown name so we need to search the mount 361202719Sgabor * map for it. 362202719Sgabor */ 363202719Sgabor if (mp->am_pref) { 364202719Sgabor if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name)) 365202719Sgabor ereturn(ENAMETOOLONG); 366202719Sgabor xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name); 367202719Sgabor pfname = path_name; 368202719Sgabor } else { 369202719Sgabor pfname = new_mp->am_name; 370202719Sgabor } 371202719Sgabor 372202719Sgabor mf = mp->am_mnt; 373202719Sgabor 374202719Sgabor dlog("will search map info in %s to find %s", mf->mf_info, pfname); 375202719Sgabor /* 376202719Sgabor * Consult the oracle for some mount information. 377202719Sgabor * info is malloc'ed and belongs to this routine. 378202719Sgabor * It ends up being free'd in free_continuation(). 379202719Sgabor * 380202719Sgabor * Note that this may return -1 indicating that information 381202719Sgabor * is not yet available. 382202719Sgabor */ 383202719Sgabor error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); 384202719Sgabor if (error) { 385202719Sgabor if (error > 0) 386202719Sgabor plog(XLOG_MAP, "No map entry for %s", pfname); 387202719Sgabor else 388202719Sgabor plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 389202719Sgabor ereturn(error); 390202719Sgabor } 391202719Sgabor dlog("mount info is %s", info); 392202719Sgabor 393202719Sgabor /* 394202719Sgabor * Split info into an argument vector. 395202719Sgabor * The vector is malloc'ed and belongs to 396202719Sgabor * this routine. It is free'd further down. 397202719Sgabor * 398202719Sgabor * Note: the vector pointers point into info, so don't free it! 399202719Sgabor */ 400202719Sgabor ivecs = strsplit(info, ' ', '\"'); 401202719Sgabor 402202719Sgabor if (mf->mf_auto) 403202719Sgabor def_opts = mf->mf_auto; 404202719Sgabor else 405202719Sgabor def_opts = ""; 406202719Sgabor 407202719Sgabor orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts)); 408202719Sgabor def_opts = strdup(orig_def_opts); 409202719Sgabor 410202719Sgabor /* first build our defaults */ 411202719Sgabor num_ivecs = 0; 412202719Sgabor for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) { 413202719Sgabor if (**cur_ivec == '-') { 414202719Sgabor /* 415202719Sgabor * Pick up new defaults 416202719Sgabor */ 417202719Sgabor 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->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