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