1174294Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
3174294Sobrien * Copyright (c) 1990 Jan-Simon Pendry
4174294Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5174294Sobrien * Copyright (c) 1990 The Regents of the University of California.
6174294Sobrien * All rights reserved.
7174294Sobrien *
8174294Sobrien * This code is derived from software contributed to Berkeley by
9174294Sobrien * Jan-Simon Pendry at Imperial College, London.
10174294Sobrien *
11174294Sobrien * Redistribution and use in source and binary forms, with or without
12174294Sobrien * modification, are permitted provided that the following conditions
13174294Sobrien * are met:
14174294Sobrien * 1. Redistributions of source code must retain the above copyright
15174294Sobrien *    notice, this list of conditions and the following disclaimer.
16174294Sobrien * 2. Redistributions in binary form must reproduce the above copyright
17174294Sobrien *    notice, this list of conditions and the following disclaimer in the
18174294Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
20174294Sobrien *    may be used to endorse or promote products derived from this software
21174294Sobrien *    without specific prior written permission.
22174294Sobrien *
23174294Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24174294Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25174294Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26174294Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27174294Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28174294Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29174294Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30174294Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31174294Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32174294Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33174294Sobrien * SUCH DAMAGE.
34174294Sobrien *
35174294Sobrien *
36174294Sobrien * File: am-utils/amd/amfs_generic.c
37174294Sobrien *
38174294Sobrien */
39174294Sobrien
40174294Sobrien/*
41174294Sobrien * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
42174294Sobrien */
43174294Sobrien
44174294Sobrien#ifdef HAVE_CONFIG_H
45174294Sobrien# include <config.h>
46174294Sobrien#endif /* HAVE_CONFIG_H */
47174294Sobrien#include <am_defs.h>
48174294Sobrien#include <amd.h>
49174294Sobrien
50174294Sobrien
51174294Sobrien/****************************************************************************
52174294Sobrien *** MACROS                                                               ***
53174294Sobrien ****************************************************************************/
54310490Scy#define	IN_PROGRESS(cp) ((cp)->mp->am_al->al_mnt->mf_flags & MFF_MOUNTING)
55174294Sobrien
56174294Sobrien
57174294Sobrien/****************************************************************************
58174294Sobrien *** STRUCTURES                                                           ***
59174294Sobrien ****************************************************************************/
60174294Sobrien/*
61174294Sobrien * Mounting a file system may take a significant period of time.  The
62174294Sobrien * problem is that if this is done in the main process thread then the
63174294Sobrien * entire automounter could be blocked, possibly hanging lots of processes
64174294Sobrien * on the system.  Instead we use a continuation scheme to allow mounts to
65174294Sobrien * be attempted in a sub-process.  When the sub-process exits we pick up the
66174294Sobrien * exit status (by convention a UN*X error number) and continue in a
67174294Sobrien * notifier.  The notifier gets handed a data structure and can then
68174294Sobrien * determine whether the mount was successful or not.  If not, it updates
69174294Sobrien * the data structure and tries again until there are no more ways to try
70174294Sobrien * the mount, or some other permanent error occurs.  In the mean time no RPC
71174294Sobrien * reply is sent, even after the mount is successful.  We rely on the RPC
72174294Sobrien * retry mechanism to resend the lookup request which can then be handled.
73174294Sobrien */
74174294Sobrienstruct continuation {
75174294Sobrien  am_node *mp;			/* Node we are trying to mount */
76174294Sobrien  int retry;			/* Try again? */
77174294Sobrien  time_t start;			/* Time we started this mount */
78174294Sobrien  int callout;			/* Callout identifier */
79310490Scy  am_loc **al;			/* Current location */
80174294Sobrien};
81174294Sobrien
82174294Sobrien
83174294Sobrien/****************************************************************************
84174294Sobrien *** FORWARD DEFINITIONS                                                  ***
85174294Sobrien ****************************************************************************/
86174294Sobrienstatic am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return);
87310490Scystatic am_loc *amfs_lookup_one_location(am_node *new_mp, mntfs *mf, char *ivec,
88174294Sobrien				    char *def_opts, char *pfname);
89310490Scystatic am_loc **amfs_lookup_loc(am_node *new_mp, int *error_return);
90174294Sobrienstatic void amfs_cont(int rc, int term, opaque_t arg);
91174294Sobrienstatic void amfs_retry(int rc, int term, opaque_t arg);
92174294Sobrienstatic void free_continuation(struct continuation *cp);
93174294Sobrienstatic int amfs_bgmount(struct continuation *cp);
94174294Sobrienstatic char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts);
95174294Sobrien
96174294Sobrien
97174294Sobrien/****************************************************************************
98174294Sobrien *** FUNCTIONS                                                             ***
99174294Sobrien ****************************************************************************/
100174294Sobrienstatic am_node *
101174294Sobrienamfs_lookup_node(am_node *mp, char *fname, int *error_return)
102174294Sobrien{
103174294Sobrien  am_node *new_mp;
104174294Sobrien  int error = 0;		/* Error so far */
105174294Sobrien  int in_progress = 0;		/* # of (un)mount in progress */
106174294Sobrien  mntfs *mf;
107310490Scy  char *expanded_fname = NULL;
108174294Sobrien
109174294Sobrien  dlog("in amfs_lookup_node");
110174294Sobrien
111174294Sobrien  /*
112174294Sobrien   * If the server is shutting down
113174294Sobrien   * then don't return information
114174294Sobrien   * about the mount point.
115174294Sobrien   */
116174294Sobrien  if (amd_state == Finishing) {
117310490Scy    if (mp->am_al == NULL || mp->am_al->al_mnt == NULL || mp->am_al->al_mnt->mf_fsflags & FS_DIRECT) {
118174294Sobrien      dlog("%s mount ignored - going down", fname);
119174294Sobrien    } else {
120174294Sobrien      dlog("%s/%s mount ignored - going down", mp->am_path, fname);
121174294Sobrien    }
122174294Sobrien    ereturn(ENOENT);
123174294Sobrien  }
124174294Sobrien
125174294Sobrien  /*
126174294Sobrien   * Handle special case of "." and ".."
127174294Sobrien   */
128174294Sobrien  if (fname[0] == '.') {
129174294Sobrien    if (fname[1] == '\0')
130174294Sobrien      return mp;		/* "." is the current node */
131174294Sobrien    if (fname[1] == '.' && fname[2] == '\0') {
132174294Sobrien      if (mp->am_parent) {
133174294Sobrien	dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
134174294Sobrien	return mp->am_parent;	/* ".." is the parent node */
135174294Sobrien      }
136174294Sobrien      ereturn(ESTALE);
137174294Sobrien    }
138174294Sobrien  }
139174294Sobrien
140174294Sobrien  /*
141174294Sobrien   * Check for valid key name.
142174294Sobrien   * If it is invalid then pretend it doesn't exist.
143174294Sobrien   */
144174294Sobrien  if (!valid_key(fname)) {
145174294Sobrien    plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
146174294Sobrien    ereturn(ENOENT);
147174294Sobrien  }
148174294Sobrien
149174294Sobrien  /*
150174294Sobrien   * Expand key name.
151174294Sobrien   * expanded_fname is now a private copy.
152174294Sobrien   */
153174294Sobrien  expanded_fname = expand_selectors(fname);
154174294Sobrien
155174294Sobrien  /*
156174294Sobrien   * Search children of this node
157174294Sobrien   */
158174294Sobrien  for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) {
159174294Sobrien    if (FSTREQ(new_mp->am_name, expanded_fname)) {
160174294Sobrien      if (new_mp->am_error) {
161174294Sobrien	error = new_mp->am_error;
162174294Sobrien	continue;
163174294Sobrien      }
164174294Sobrien
165174294Sobrien      /*
166174294Sobrien       * If the error code is undefined then it must be
167174294Sobrien       * in progress.
168174294Sobrien       */
169310490Scy      mf = new_mp->am_al->al_mnt;
170174294Sobrien      if (mf->mf_error < 0)
171174294Sobrien	goto in_progrss;
172174294Sobrien
173174294Sobrien      /*
174174294Sobrien       * If there was a previous error with this node
175174294Sobrien       * then return that error code.
176174294Sobrien       */
177174294Sobrien      if (mf->mf_flags & MFF_ERROR) {
178174294Sobrien	error = mf->mf_error;
179174294Sobrien	continue;
180174294Sobrien      }
181174294Sobrien      if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
182174294Sobrien      in_progrss:
183174294Sobrien	/*
184174294Sobrien	 * If the fs is not mounted or it is unmounting then there
185174294Sobrien	 * is a background (un)mount in progress.  In this case
186174294Sobrien	 * we just drop the RPC request (return nil) and
187174294Sobrien	 * wait for a retry, by which time the (un)mount may
188174294Sobrien	 * have completed.
189174294Sobrien	 */
190174294Sobrien	dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
191174294Sobrien	     expanded_fname, mf->mf_mount,
192174294Sobrien	     (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags);
193174294Sobrien	in_progress++;
194174294Sobrien	if (mf->mf_flags & MFF_UNMOUNTING) {
195174294Sobrien	  dlog("will remount later");
196174294Sobrien	  new_mp->am_flags |= AMF_REMOUNT;
197174294Sobrien	}
198174294Sobrien	continue;
199174294Sobrien      }
200174294Sobrien
201174294Sobrien      /*
202174294Sobrien       * Otherwise we have a hit: return the current mount point.
203174294Sobrien       */
204174294Sobrien      dlog("matched %s in %s", expanded_fname, new_mp->am_path);
205174294Sobrien      XFREE(expanded_fname);
206174294Sobrien      return new_mp;
207174294Sobrien    }
208174294Sobrien  }
209174294Sobrien
210174294Sobrien  if (in_progress) {
211174294Sobrien    dlog("Waiting while %d mount(s) in progress", in_progress);
212174294Sobrien    XFREE(expanded_fname);
213174294Sobrien    ereturn(-1);
214174294Sobrien  }
215174294Sobrien
216174294Sobrien  /*
217174294Sobrien   * If an error occurred then return it.
218174294Sobrien   */
219174294Sobrien  if (error) {
220174294Sobrien    dlog("Returning error: %s", strerror(error));
221174294Sobrien    XFREE(expanded_fname);
222174294Sobrien    ereturn(error);
223174294Sobrien  }
224174294Sobrien
225174294Sobrien  /*
226174294Sobrien   * If the server is going down then just return,
227174294Sobrien   * don't try to mount any more file systems
228174294Sobrien   */
229174294Sobrien  if ((int) amd_state >= (int) Finishing) {
230174294Sobrien    dlog("not found - server going down anyway");
231174294Sobrien    ereturn(ENOENT);
232174294Sobrien  }
233174294Sobrien
234174294Sobrien  /*
235174294Sobrien   * Allocate a new map
236174294Sobrien   */
237174294Sobrien  new_mp = get_ap_child(mp, expanded_fname);
238174294Sobrien  XFREE(expanded_fname);
239310490Scy  if (new_mp == NULL)
240174294Sobrien    ereturn(ENOSPC);
241174294Sobrien
242174294Sobrien  *error_return = -1;
243174294Sobrien  return new_mp;
244174294Sobrien}
245174294Sobrien
246174294Sobrien
247174294Sobrien
248310490Scystatic am_loc *
249310490Scyamfs_lookup_one_location(am_node *new_mp, mntfs *mf, char *ivec,
250310490Scy			char *def_opts, char *pfname)
251174294Sobrien{
252174294Sobrien  am_ops *p;
253174294Sobrien  am_opts *fs_opts;
254310490Scy  am_loc *new_al;
255174294Sobrien  mntfs *new_mf;
256310490Scy  char *mp_dir = NULL;
257174294Sobrien#ifdef HAVE_FS_AUTOFS
258174294Sobrien  int on_autofs = 1;
259174294Sobrien#endif /* HAVE_FS_AUTOFS */
260174294Sobrien
261174294Sobrien  /* match the operators */
262310490Scy  /*
263310490Scy   * although we alloc the fs_opts here, the pointer is 'owned' by the am_loc and will
264310490Scy   * be free'd on destruction of the am_loc. If we don't allocate a loc, then we need
265310490Scy   * to free this.
266310490Scy   */
267174294Sobrien  fs_opts = CALLOC(am_opts);
268174294Sobrien  p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path,
269174294Sobrien		pfname, mf->mf_info);
270174294Sobrien#ifdef HAVE_FS_AUTOFS
271174294Sobrien  /* XXX: this should be factored out into an autofs-specific function */
272174294Sobrien  if (new_mp->am_flags & AMF_AUTOFS) {
273174294Sobrien    /* ignore user-provided fs if we're using autofs */
274310490Scy    if (fs_opts->opt_sublink && fs_opts->opt_sublink[0]) {
275174294Sobrien      /*
276174294Sobrien       * For sublinks we need to use a hack with autofs:
277174294Sobrien       * mount the filesystem on the original opt_fs (which is NOT an
278174294Sobrien       * autofs mountpoint) and symlink (or lofs-mount) to it from
279174294Sobrien       * the autofs mountpoint.
280174294Sobrien       */
281174294Sobrien      on_autofs = 0;
282174294Sobrien      mp_dir = fs_opts->opt_fs;
283174294Sobrien    } else {
284174294Sobrien      if (p->autofs_fs_flags & FS_ON_AUTOFS) {
285174294Sobrien	mp_dir = new_mp->am_path;
286174294Sobrien      } else {
287174294Sobrien	mp_dir = fs_opts->opt_fs;
288174294Sobrien	on_autofs = 0;
289174294Sobrien      }
290174294Sobrien    }
291174294Sobrien  } else
292174294Sobrien#endif /* HAVE_FS_AUTOFS */
293174294Sobrien    mp_dir = fs_opts->opt_fs;
294174294Sobrien
295174294Sobrien  /*
296174294Sobrien   * Find or allocate a filesystem for this node.
297310490Scy   * we search for a matching backend share, since
298310490Scy   * we will construct our own al_loc to handle
299310490Scy   * any customisations for this usage.
300174294Sobrien   */
301174294Sobrien  new_mf = find_mntfs(p, fs_opts,
302174294Sobrien		      mp_dir,
303174294Sobrien		      fs_opts->fs_mtab,
304174294Sobrien		      def_opts,
305174294Sobrien		      fs_opts->opt_opts,
306174294Sobrien		      fs_opts->opt_remopts);
307174294Sobrien
308310490Scy
309174294Sobrien  /*
310174294Sobrien   * See whether this is a real filesystem
311174294Sobrien   */
312174294Sobrien  p = new_mf->mf_ops;
313174294Sobrien  if (p == &amfs_error_ops) {
314174294Sobrien    plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
315174294Sobrien    free_mntfs(new_mf);
316310490Scy    free_opts(fs_opts);
317310490Scy    XFREE(fs_opts);
318174294Sobrien    return NULL;
319174294Sobrien  }
320174294Sobrien
321174294Sobrien  dlog("Got a hit with %s", p->fs_type);
322310490Scy  new_al = new_loc();
323310490Scy  free_mntfs(new_al->al_mnt);
324310490Scy  new_al->al_mnt = new_mf;
325310490Scy  new_al->al_fo = fs_opts; /* now the loc is in charge of free'ing this mem */
326174294Sobrien
327174294Sobrien#ifdef HAVE_FS_AUTOFS
328174294Sobrien  if (new_mp->am_flags & AMF_AUTOFS && on_autofs) {
329174294Sobrien    new_mf->mf_flags |= MFF_ON_AUTOFS;
330174294Sobrien    new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags;
331174294Sobrien  }
332174294Sobrien  /*
333174294Sobrien   * A new filesystem is an autofs filesystems if:
334174294Sobrien   * 1. it claims it can be one (has the FS_AUTOFS flag)
335174294Sobrien   * 2. autofs is enabled system-wide
336174294Sobrien   * 3. either has an autofs parent,
337174294Sobrien   *    or it is explicitly requested to be autofs.
338174294Sobrien   */
339174294Sobrien  if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
340174294Sobrien      amd_use_autofs &&
341174294Sobrien      ((mf->mf_flags & MFF_IS_AUTOFS) ||
342174294Sobrien       (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type &&
343174294Sobrien	STREQ(new_mf->mf_fo->opt_mount_type, "autofs"))))
344174294Sobrien    new_mf->mf_flags |= MFF_IS_AUTOFS;
345174294Sobrien#endif /* HAVE_FS_AUTOFS */
346174294Sobrien
347310490Scy  return new_al;
348174294Sobrien}
349174294Sobrien
350174294Sobrien
351310490Scystatic am_loc **
352310490Scyamfs_lookup_loc(am_node *new_mp, int *error_return)
353174294Sobrien{
354174294Sobrien  am_node *mp;
355174294Sobrien  char *info;			/* Mount info - where to get the file system */
356174294Sobrien  char **ivecs, **cur_ivec;	/* Split version of info */
357174294Sobrien  int num_ivecs;
358174294Sobrien  char *orig_def_opts;          /* Original Automount options */
359174294Sobrien  char *def_opts;	       	/* Automount options */
360174294Sobrien  int error = 0;		/* Error so far */
361174294Sobrien  char path_name[MAXPATHLEN];	/* General path name buffer */
362174294Sobrien  char *pfname;			/* Path for database lookup */
363310490Scy  mntfs* mf;			/* The mntfs for the map of our parent */
364310490Scy  am_loc **al_array;		/* the generated list of locations */
365174294Sobrien  int count;
366174294Sobrien
367310490Scy  dlog("in amfs_lookup_loc");
368174294Sobrien
369174294Sobrien  mp = new_mp->am_parent;
370174294Sobrien
371174294Sobrien  /*
372174294Sobrien   * If we get here then this is a reference to an,
373174294Sobrien   * as yet, unknown name so we need to search the mount
374174294Sobrien   * map for it.
375174294Sobrien   */
376174294Sobrien  if (mp->am_pref) {
377174294Sobrien    if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
378174294Sobrien      ereturn(ENAMETOOLONG);
379174294Sobrien    xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name);
380174294Sobrien    pfname = path_name;
381174294Sobrien  } else {
382174294Sobrien    pfname = new_mp->am_name;
383174294Sobrien  }
384174294Sobrien
385310490Scy  mf = mp->am_al->al_mnt;
386174294Sobrien
387174294Sobrien  dlog("will search map info in %s to find %s", mf->mf_info, pfname);
388174294Sobrien  /*
389174294Sobrien   * Consult the oracle for some mount information.
390174294Sobrien   * info is malloc'ed and belongs to this routine.
391174294Sobrien   * It ends up being free'd in free_continuation().
392174294Sobrien   *
393174294Sobrien   * Note that this may return -1 indicating that information
394174294Sobrien   * is not yet available.
395174294Sobrien   */
396174294Sobrien  error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
397174294Sobrien  if (error) {
398174294Sobrien    if (error > 0)
399174294Sobrien      plog(XLOG_MAP, "No map entry for %s", pfname);
400174294Sobrien    else
401174294Sobrien      plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
402174294Sobrien    ereturn(error);
403174294Sobrien  }
404174294Sobrien  dlog("mount info is %s", info);
405174294Sobrien
406174294Sobrien  /*
407174294Sobrien   * Split info into an argument vector.
408174294Sobrien   * The vector is malloc'ed and belongs to
409174294Sobrien   * this routine.  It is free'd further down.
410174294Sobrien   *
411174294Sobrien   * Note: the vector pointers point into info, so don't free it!
412174294Sobrien   */
413174294Sobrien  ivecs = strsplit(info, ' ', '\"');
414174294Sobrien
415174294Sobrien  if (mf->mf_auto)
416174294Sobrien    def_opts = mf->mf_auto;
417174294Sobrien  else
418174294Sobrien    def_opts = "";
419174294Sobrien
420310490Scy  orig_def_opts = amfs_parse_defaults(mp, mf, xstrdup(def_opts));
421310490Scy  def_opts = xstrdup(orig_def_opts);
422174294Sobrien
423174294Sobrien  /* first build our defaults */
424174294Sobrien  num_ivecs = 0;
425174294Sobrien  for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
426174294Sobrien    if (**cur_ivec == '-') {
427174294Sobrien      /*
428174294Sobrien       * Pick up new defaults
429174294Sobrien       */
430174294Sobrien      char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1);
431174294Sobrien      XFREE(def_opts);
432174294Sobrien      def_opts = new_def_opts;
433174294Sobrien      dlog("Setting def_opts to \"%s\"", def_opts);
434174294Sobrien      continue;
435174294Sobrien    } else
436174294Sobrien      num_ivecs++;
437174294Sobrien  }
438174294Sobrien
439310490Scy  al_array = calloc(num_ivecs + 1, sizeof(am_loc *));
440174294Sobrien
441310490Scy  /* construct the array of struct locations for this key */
442174294Sobrien  for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
443310490Scy    am_loc *new_al;
444174294Sobrien
445174294Sobrien    if (**cur_ivec == '-') {
446174294Sobrien      XFREE(def_opts);
447174294Sobrien      if ((*cur_ivec)[1] == '\0') {
448174294Sobrien	/*
449174294Sobrien	 * If we have a single dash '-' than we need to reset the
450174294Sobrien	 * default options.
451174294Sobrien	 */
452310490Scy	def_opts = xstrdup(orig_def_opts);
453174294Sobrien	dlog("Resetting the default options, a single dash '-' was found.");
454174294Sobrien      } else {
455174294Sobrien	/* append options to /default options */
456310490Scy	def_opts = str3cat((char *) NULL, orig_def_opts, ";", *cur_ivec + 1);
457174294Sobrien	dlog("Resetting def_opts to \"%s\"", def_opts);
458174294Sobrien      }
459174294Sobrien      continue;
460174294Sobrien    }
461174294Sobrien
462174294Sobrien    /*
463310490Scy     * If a loc has already been found, and we find
464174294Sobrien     * a cut then don't try any more locations.
465174294Sobrien     *
466174294Sobrien     * XXX: we do not know when the "/" was added as an equivalent for "||".
467174294Sobrien     * It's undocumented, it might go away at any time. Caveat emptor.
468174294Sobrien     */
469174294Sobrien    if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
470174294Sobrien      if (count > 0) {
471310490Scy	dlog("Cut: not trying any more locations for %s", pfname);
472174294Sobrien	break;
473174294Sobrien      }
474174294Sobrien      continue;
475174294Sobrien    }
476174294Sobrien
477310490Scy    new_al = amfs_lookup_one_location(new_mp, mf, *cur_ivec, def_opts, pfname);
478310490Scy    if (new_al == NULL)
479174294Sobrien      continue;
480310490Scy    al_array[count++] = new_al;
481174294Sobrien  }
482174294Sobrien
483174294Sobrien  /* We're done with ivecs */
484174294Sobrien  XFREE(ivecs);
485174294Sobrien  XFREE(info);
486174294Sobrien  XFREE(orig_def_opts);
487174294Sobrien  XFREE(def_opts);
488174294Sobrien  if (count == 0) {			/* no match */
489310490Scy    XFREE(al_array);
490174294Sobrien    ereturn(ENOENT);
491174294Sobrien  }
492174294Sobrien
493310490Scy  return al_array;
494174294Sobrien}
495174294Sobrien
496174294Sobrien
497174294Sobrien/*
498174294Sobrien * The continuation function.  This is called by
499174294Sobrien * the task notifier when a background mount attempt
500174294Sobrien * completes.
501174294Sobrien */
502174294Sobrienstatic void
503174294Sobrienamfs_cont(int rc, int term, opaque_t arg)
504174294Sobrien{
505174294Sobrien  struct continuation *cp = (struct continuation *) arg;
506174294Sobrien  am_node *mp = cp->mp;
507310490Scy  mntfs *mf = mp->am_al->al_mnt;
508174294Sobrien
509174294Sobrien  dlog("amfs_cont: '%s'", mp->am_path);
510174294Sobrien
511174294Sobrien  /*
512174294Sobrien   * Definitely not trying to mount at the moment
513174294Sobrien   */
514174294Sobrien  mf->mf_flags &= ~MFF_MOUNTING;
515174294Sobrien
516174294Sobrien  /*
517174294Sobrien   * While we are mounting - try to avoid race conditions
518174294Sobrien   */
519174294Sobrien  new_ttl(mp);
520174294Sobrien
521174294Sobrien  /*
522174294Sobrien   * Wakeup anything waiting for this mount
523174294Sobrien   */
524174294Sobrien  wakeup(get_mntfs_wchan(mf));
525174294Sobrien
526174294Sobrien  /*
527174294Sobrien   * Check for termination signal or exit status...
528174294Sobrien   */
529174294Sobrien  if (rc || term) {
530174294Sobrien#ifdef HAVE_FS_AUTOFS
531174294Sobrien    if (mf->mf_flags & MFF_IS_AUTOFS &&
532174294Sobrien	!(mf->mf_flags & MFF_MOUNTED))
533174294Sobrien      autofs_release_fh(mp);
534174294Sobrien#endif /* HAVE_FS_AUTOFS */
535174294Sobrien
536174294Sobrien    if (term) {
537174294Sobrien      /*
538174294Sobrien       * Not sure what to do for an error code.
539174294Sobrien       */
540174294Sobrien      mf->mf_error = EIO;	/* XXX ? */
541174294Sobrien      mf->mf_flags |= MFF_ERROR;
542174294Sobrien      plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
543174294Sobrien    } else {
544174294Sobrien      /*
545174294Sobrien       * Check for exit status...
546174294Sobrien       */
547174294Sobrien#ifdef __linux__
548174294Sobrien      /*
549174294Sobrien       * HACK ALERT!
550174294Sobrien       *
551174294Sobrien       * On Linux (and maybe not only) it's possible to run
552174294Sobrien       * an amd which "knows" how to mount certain combinations
553174294Sobrien       * of nfs_proto/nfs_version which the kernel doesn't grok.
554174294Sobrien       * So if we got an EINVAL and we have a server that's not
555174294Sobrien       * using NFSv2/UDP, try again with NFSv2/UDP.
556174294Sobrien       *
557174294Sobrien       * Too bad that there is no way to dynamically determine
558174294Sobrien       * what combinations the _client_ supports, as opposed to
559174294Sobrien       * what the _server_ supports...
560174294Sobrien       */
561174294Sobrien      if (rc == EINVAL &&
562174294Sobrien	  mf->mf_server &&
563174294Sobrien	  (mf->mf_server->fs_version != 2 ||
564174294Sobrien	   !STREQ(mf->mf_server->fs_proto, "udp")))
565174294Sobrien	mf->mf_flags |= MFF_NFS_SCALEDOWN;
566174294Sobrien      else
567174294Sobrien#endif /* __linux__ */
568174294Sobrien      {
569174294Sobrien	mf->mf_error = rc;
570174294Sobrien	mf->mf_flags |= MFF_ERROR;
571174294Sobrien	errno = rc;		/* XXX */
572310490Scy	if (!STREQ(mp->am_al->al_mnt->mf_ops->fs_type, "linkx"))
573174294Sobrien	  plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
574174294Sobrien      }
575174294Sobrien    }
576174294Sobrien
577174294Sobrien    if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
578174294Sobrien      /*
579174294Sobrien       * If we get here then that attempt didn't work, so
580174294Sobrien       * move the info vector pointer along by one and
581174294Sobrien       * call the background mount routine again
582174294Sobrien       */
583174294Sobrien      amd_stats.d_merr++;
584310490Scy      cp->al++;
585174294Sobrien    }
586174294Sobrien    amfs_bgmount(cp);
587174294Sobrien    if (mp->am_error > 0)
588174294Sobrien      assign_error_mntfs(mp);
589174294Sobrien  } else {
590174294Sobrien    /*
591174294Sobrien     * The mount worked.
592174294Sobrien     */
593174294Sobrien    dlog("Mounting %s returned success", cp->mp->am_path);
594174294Sobrien    am_mounted(cp->mp);
595174294Sobrien    free_continuation(cp);
596174294Sobrien  }
597174294Sobrien
598174294Sobrien  reschedule_timeout_mp();
599174294Sobrien}
600174294Sobrien
601174294Sobrien
602174294Sobrien/*
603174294Sobrien * Retry a mount
604174294Sobrien */
605174294Sobrienstatic void
606174294Sobrienamfs_retry(int rc, int term, opaque_t arg)
607174294Sobrien{
608174294Sobrien  struct continuation *cp = (struct continuation *) arg;
609174294Sobrien  am_node *mp = cp->mp;
610174294Sobrien  int error = 0;
611174294Sobrien
612174294Sobrien  dlog("Commencing retry for mount of %s", mp->am_path);
613174294Sobrien
614174294Sobrien  new_ttl(mp);
615174294Sobrien
616174294Sobrien  if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
617174294Sobrien    /*
618174294Sobrien     * The entire mount has timed out.  Set the error code and skip past all
619174294Sobrien     * the mntfs's so that amfs_bgmount will not have any more
620174294Sobrien     * ways to try the mount, thus causing an error.
621174294Sobrien     */
622174294Sobrien    plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
623174294Sobrien    error = ETIMEDOUT;
624310490Scy    while (*cp->al)
625310490Scy      cp->al++;
626174294Sobrien    /* explicitly forbid further retries after timeout */
627174294Sobrien    cp->retry = FALSE;
628174294Sobrien  }
629174294Sobrien  if (error || !IN_PROGRESS(cp))
630174294Sobrien    error = amfs_bgmount(cp);
631310490Scy  else
632310490Scy    /* Normally it's amfs_bgmount() which frees the continuation. However, if
633310490Scy     * the mount is already in progress and we're in amfs_retry() for another
634310490Scy     * node we don't try mounting the filesystem once again. Still, we have
635310490Scy     * to free the continuation as we won't get called again and thus would
636310490Scy     * leak the continuation structure and our am_loc references.
637310490Scy     */
638310490Scy    free_continuation(cp);
639174294Sobrien
640174294Sobrien  reschedule_timeout_mp();
641174294Sobrien}
642174294Sobrien
643174294Sobrien
644174294Sobrien/*
645174294Sobrien * Discard an old continuation
646174294Sobrien */
647174294Sobrienstatic void
648174294Sobrienfree_continuation(struct continuation *cp)
649174294Sobrien{
650310490Scy  am_loc **alp;
651174294Sobrien
652174294Sobrien  dlog("free_continuation");
653174294Sobrien  if (cp->callout)
654174294Sobrien    untimeout(cp->callout);
655174294Sobrien  /*
656174294Sobrien   * we must free the mntfs's in the list.
657174294Sobrien   * so free all of them if there was an error,
658174294Sobrien   */
659310490Scy  for (alp = cp->mp->am_alarray; *alp; alp++) {
660310490Scy    free_loc(*alp);
661174294Sobrien  }
662310490Scy  XFREE(cp->mp->am_alarray);
663310490Scy  cp->mp->am_alarray = 0;
664174294Sobrien  XFREE(cp);
665174294Sobrien}
666174294Sobrien
667174294Sobrien
668174294Sobrien/*
669174294Sobrien * Pick a file system to try mounting and
670174294Sobrien * do that in the background if necessary
671174294Sobrien *
672174294SobrienFor each location:
673174294Sobrien	discard previous mount location if required
674174294Sobrien	fetch next mount location
675174294Sobrien	if the filesystem failed to be mounted then
676174294Sobrien		this_error = error from filesystem
677174294Sobrien		goto failed
678174294Sobrien	if the filesystem is mounting or unmounting then
679174294Sobrien		goto retry;
680174294Sobrien	if the fileserver is down then
681174294Sobrien		this_error = EIO
682174294Sobrien		continue;
683174294Sobrien	if the filesystem is already mounted
684174294Sobrien		break
685174294Sobrien	fi
686174294Sobrien
687174294Sobrien	this_error = initialize mount point
688174294Sobrien
689174294Sobrien	if no error on this mount and mount is delayed then
690174294Sobrien		this_error = -1
691174294Sobrien	fi
692174294Sobrien	if this_error < 0 then
693174294Sobrien		retry = true
694174294Sobrien	fi
695174294Sobrien	if no error on this mount then
696174294Sobrien		if mount in background then
697174294Sobrien			run mount in background
698174294Sobrien			return -1
699174294Sobrien		else
700174294Sobrien			this_error = mount in foreground
701174294Sobrien		fi
702174294Sobrien	fi
703174294Sobrien	if an error occurred on this mount then
704174294Sobrien		update stats
705174294Sobrien		save error in mount point
706174294Sobrien	fi
707174294Sobrienendfor
708174294Sobrien */
709174294Sobrienstatic int
710174294Sobrienamfs_bgmount(struct continuation *cp)
711174294Sobrien{
712174294Sobrien  am_node *mp = cp->mp;
713310490Scy  am_loc *loc;
714310490Scy  mntfs *mf;
715174294Sobrien  int this_error = -1;		/* Per-mount error */
716174294Sobrien  int hard_error = -1;		/* Cumulative per-node error */
717174294Sobrien
718310490Scy  if (mp->am_al)
719310490Scy    free_loc(mp->am_al);
720174294Sobrien
721174294Sobrien  /*
722174294Sobrien   * Try to mount each location.
723174294Sobrien   * At the end:
724174294Sobrien   * hard_error == 0 indicates something was mounted.
725174294Sobrien   * hard_error > 0 indicates everything failed with a hard error
726174294Sobrien   * hard_error < 0 indicates nothing could be mounted now
727174294Sobrien   */
728310490Scy  for (mp->am_al = *cp->al; *cp->al; cp->al++, mp->am_al = *cp->al) {
729174294Sobrien    am_ops *p;
730174294Sobrien
731310490Scy    loc = dup_loc(mp->am_al);
732310490Scy    mf = loc->al_mnt;
733174294Sobrien    p = mf->mf_ops;
734174294Sobrien
735174294Sobrien    if (hard_error < 0)
736174294Sobrien      hard_error = this_error;
737174294Sobrien    this_error = 0;
738174294Sobrien
739174294Sobrien    if (mf->mf_error > 0) {
740174294Sobrien      this_error = mf->mf_error;
741174294Sobrien      goto failed;
742174294Sobrien    }
743174294Sobrien
744174294Sobrien    if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
745174294Sobrien      /*
746174294Sobrien       * Still mounting - retry later
747174294Sobrien       */
748174294Sobrien      dlog("mount of \"%s\" already pending", mf->mf_info);
749174294Sobrien      goto retry;
750174294Sobrien    }
751174294Sobrien
752174294Sobrien    if (FSRV_ISDOWN(mf->mf_server)) {
753174294Sobrien      /*
754174294Sobrien       * Would just mount from the same place
755174294Sobrien       * as a hung mount - so give up
756174294Sobrien       */
757174294Sobrien      dlog("%s is already hung - giving up", mf->mf_server->fs_host);
758174294Sobrien      this_error = EIO;
759174294Sobrien      goto failed;
760174294Sobrien    }
761174294Sobrien
762310490Scy    XFREE(mp->am_link);
763310490Scy    mp->am_link = NULL;
764174294Sobrien
765310490Scy    if (loc->al_fo && loc->al_fo->opt_sublink && loc->al_fo->opt_sublink[0])
766310490Scy      mp->am_link = xstrdup(loc->al_fo->opt_sublink);
767310490Scy
768174294Sobrien    /*
769174294Sobrien     * Will usually need to play around with the mount nodes
770174294Sobrien     * file attribute structure.  This must be done here.
771174294Sobrien     * Try and get things initialized, even if the fileserver
772174294Sobrien     * is not known to be up.  In the common case this will
773174294Sobrien     * progress things faster.
774174294Sobrien     */
775174294Sobrien
776174294Sobrien    /*
777174294Sobrien     * Fill in attribute fields.
778174294Sobrien     */
779174294Sobrien    if (mf->mf_fsflags & FS_DIRECTORY)
780174294Sobrien      mk_fattr(&mp->am_fattr, NFDIR);
781174294Sobrien    else
782174294Sobrien      mk_fattr(&mp->am_fattr, NFLNK);
783174294Sobrien
784174294Sobrien    if (mf->mf_flags & MFF_MOUNTED) {
785174294Sobrien      dlog("duplicate mount of \"%s\" ...", mf->mf_info);
786174294Sobrien      /*
787174294Sobrien       * Skip initial processing of the mountpoint if already mounted.
788174294Sobrien       * This could happen if we have multiple sublinks into the same f/s,
789174294Sobrien       * or if we are restarting an already-mounted filesystem.
790174294Sobrien       */
791174294Sobrien      goto already_mounted;
792174294Sobrien    }
793174294Sobrien
794174294Sobrien    if (mf->mf_fo && mf->mf_fo->fs_mtab) {
795174294Sobrien      plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
796174294Sobrien	   mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
797174294Sobrien	   mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
798174294Sobrien    }
799174294Sobrien
800174294Sobrien    if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
801174294Sobrien      this_error = p->fs_init(mf);
802174294Sobrien
803174294Sobrien    if (this_error > 0)
804174294Sobrien      goto failed;
805174294Sobrien    if (this_error < 0)
806174294Sobrien      goto retry;
807174294Sobrien
808310490Scy    if (loc->al_fo && loc->al_fo->opt_delay) {
809174294Sobrien      /*
810310490Scy       * If there is a delay timer on the location
811174294Sobrien       * then don't try to mount if the timer
812174294Sobrien       * has not expired.
813174294Sobrien       */
814310490Scy      int i = atoi(loc->al_fo->opt_delay);
815174294Sobrien      time_t now = clocktime(NULL);
816174294Sobrien      if (i > 0 && now < (cp->start + i)) {
817174294Sobrien	dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
818174294Sobrien	goto retry;
819174294Sobrien      }
820174294Sobrien    }
821174294Sobrien
822174294Sobrien    /*
823174294Sobrien     * If the directory is not yet made and it needs to be made, then make it!
824174294Sobrien     */
825174294Sobrien    if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
826174294Sobrien      plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
827174294Sobrien      this_error = mkdirs(mf->mf_mount, 0555);
828174294Sobrien      if (this_error) {
829174294Sobrien	plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
830174294Sobrien	goto failed;
831174294Sobrien      }
832174294Sobrien      mf->mf_flags |= MFF_MKMNT;
833174294Sobrien    }
834174294Sobrien
835174294Sobrien#ifdef HAVE_FS_AUTOFS
836174294Sobrien    if (mf->mf_flags & MFF_IS_AUTOFS)
837174294Sobrien      if ((this_error = autofs_get_fh(mp)))
838174294Sobrien	goto failed;
839174294Sobrien#endif /* HAVE_FS_AUTOFS */
840174294Sobrien
841174294Sobrien  already_mounted:
842174294Sobrien    mf->mf_flags |= MFF_MOUNTING;
843174294Sobrien    if (mf->mf_fsflags & FS_MBACKGROUND) {
844174294Sobrien      dlog("backgrounding mount of \"%s\"", mf->mf_mount);
845174294Sobrien      if (cp->callout) {
846174294Sobrien	untimeout(cp->callout);
847174294Sobrien	cp->callout = 0;
848174294Sobrien      }
849174294Sobrien
850174294Sobrien      /* actually run the task, backgrounding as necessary */
851174294Sobrien      run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
852174294Sobrien      return -1;
853174294Sobrien    } else {
854174294Sobrien      dlog("foreground mount of \"%s\" ...", mf->mf_mount);
855174294Sobrien      this_error = mount_node((opaque_t) mp);
856174294Sobrien    }
857174294Sobrien
858174294Sobrien    mf->mf_flags &= ~MFF_MOUNTING;
859174294Sobrien    if (this_error > 0)
860174294Sobrien      goto failed;
861174294Sobrien    if (this_error == 0) {
862174294Sobrien      am_mounted(mp);
863174294Sobrien      break;					/* Success */
864174294Sobrien    }
865174294Sobrien
866174294Sobrien  retry:
867174294Sobrien    if (!cp->retry)
868174294Sobrien      continue;
869174294Sobrien    dlog("will retry ...\n");
870174294Sobrien
871174294Sobrien    /*
872174294Sobrien     * Arrange that amfs_bgmount is called
873174294Sobrien     * after anything else happens.
874174294Sobrien     */
875174294Sobrien    dlog("Arranging to retry mount of %s", mp->am_path);
876174294Sobrien    sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
877174294Sobrien    if (cp->callout)
878174294Sobrien      untimeout(cp->callout);
879174294Sobrien    cp->callout = timeout(RETRY_INTERVAL, wakeup,
880174294Sobrien			  (opaque_t) get_mntfs_wchan(mf));
881174294Sobrien
882174294Sobrien    mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
883174294Sobrien
884174294Sobrien    /*
885174294Sobrien     * Not done yet - so don't return anything
886174294Sobrien     */
887174294Sobrien    return -1;
888174294Sobrien
889174294Sobrien  failed:
890310490Scy    if (!FSRV_ISDOWN(mf->mf_server)) {
891310490Scy      /* mark the mount as failed unless the server is down */
892310490Scy      amd_stats.d_merr++;
893310490Scy      mf->mf_error = this_error;
894310490Scy      mf->mf_flags |= MFF_ERROR;
895174294Sobrien#ifdef HAVE_FS_AUTOFS
896310490Scy      if (mp->am_autofs_fh)
897310490Scy	autofs_release_fh(mp);
898174294Sobrien#endif /* HAVE_FS_AUTOFS */
899310490Scy      if (mf->mf_flags & MFF_MKMNT) {
900310490Scy	rmdirs(mf->mf_mount);
901310490Scy	mf->mf_flags &= ~MFF_MKMNT;
902310490Scy      }
903174294Sobrien    }
904174294Sobrien    /*
905174294Sobrien     * Wakeup anything waiting for this mount
906174294Sobrien     */
907174294Sobrien    wakeup(get_mntfs_wchan(mf));
908310490Scy    free_loc(loc);
909174294Sobrien    /* continue */
910174294Sobrien  }
911174294Sobrien
912174294Sobrien  /*
913174294Sobrien   * If we get here, then either the mount succeeded or
914174294Sobrien   * there is no more mount information available.
915174294Sobrien   */
916174294Sobrien  if (this_error) {
917310490Scy    if (mp->am_al)
918310490Scy      free_loc(mp->am_al);
919310490Scy    mp->am_al = loc = new_loc();
920310490Scy    mf = loc->al_mnt;
921174294Sobrien
922174294Sobrien#ifdef HAVE_FS_AUTOFS
923174294Sobrien    if (mp->am_flags & AMF_AUTOFS)
924174294Sobrien      autofs_mount_failed(mp);
925174294Sobrien    else
926174294Sobrien#endif /* HAVE_FS_AUTOFS */
927174294Sobrien      nfs_quick_reply(mp, this_error);
928174294Sobrien
929174294Sobrien    if (hard_error <= 0)
930174294Sobrien      hard_error = this_error;
931174294Sobrien    if (hard_error < 0)
932174294Sobrien      hard_error = ETIMEDOUT;
933174294Sobrien
934174294Sobrien    /*
935174294Sobrien     * Set a small(ish) timeout on an error node if
936174294Sobrien     * the error was not a time out.
937174294Sobrien     */
938174294Sobrien    switch (hard_error) {
939174294Sobrien    case ETIMEDOUT:
940174294Sobrien    case EWOULDBLOCK:
941174294Sobrien    case EIO:
942174294Sobrien      mp->am_timeo = 17;
943174294Sobrien      break;
944174294Sobrien    default:
945174294Sobrien      mp->am_timeo = 5;
946174294Sobrien      break;
947174294Sobrien    }
948174294Sobrien    new_ttl(mp);
949174294Sobrien  } else {
950310490Scy    mf = loc->al_mnt;
951174294Sobrien    /*
952174294Sobrien     * Wakeup anything waiting for this mount
953174294Sobrien     */
954174294Sobrien    wakeup(get_mntfs_wchan(mf));
955174294Sobrien    hard_error = 0;
956174294Sobrien  }
957174294Sobrien
958174294Sobrien  /*
959174294Sobrien   * Make sure that the error value in the mntfs has a
960174294Sobrien   * reasonable value.
961174294Sobrien   */
962174294Sobrien  if (mf->mf_error < 0) {
963174294Sobrien    mf->mf_error = hard_error;
964174294Sobrien    if (hard_error)
965174294Sobrien      mf->mf_flags |= MFF_ERROR;
966174294Sobrien  }
967174294Sobrien
968174294Sobrien  /*
969174294Sobrien   * In any case we don't need the continuation any more
970174294Sobrien   */
971174294Sobrien  free_continuation(cp);
972174294Sobrien
973174294Sobrien  return hard_error;
974174294Sobrien}
975174294Sobrien
976174294Sobrien
977174294Sobrienstatic char *
978174294Sobrienamfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
979174294Sobrien{
980174294Sobrien  char *dflts;
981174294Sobrien  char *dfl;
982174294Sobrien  char **rvec = NULL;
983174294Sobrien  struct mnt_map *mm = (mnt_map *) mf->mf_private;
984174294Sobrien
985174294Sobrien  dlog("determining /defaults entry value");
986174294Sobrien
987174294Sobrien  /*
988174294Sobrien   * Find out if amd.conf overrode any map-specific /defaults.
989174294Sobrien   */
990174294Sobrien  if (mm->cfm && mm->cfm->cfm_defaults) {
991174294Sobrien    dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
992310490Scy    dflts = xstrdup(mm->cfm->cfm_defaults);
993174294Sobrien  } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
994174294Sobrien    dlog("/defaults gave %s", dflts);
995174294Sobrien  } else {
996174294Sobrien    return def_opts;		/* if nothing found */
997174294Sobrien  }
998174294Sobrien
999174294Sobrien  /* trim leading '-' in case thee's one */
1000174294Sobrien  if (*dflts == '-')
1001174294Sobrien    dfl = dflts + 1;
1002174294Sobrien  else
1003174294Sobrien    dfl = dflts;
1004174294Sobrien
1005174294Sobrien  /*
1006174294Sobrien   * Chop the defaults up
1007174294Sobrien   */
1008174294Sobrien  rvec = strsplit(dfl, ' ', '\"');
1009174294Sobrien
1010174294Sobrien  if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
1011174294Sobrien    /*
1012174294Sobrien     * Pick whichever first entry matched the list of selectors.
1013174294Sobrien     * Strip the selectors from the string, and assign to dfl the
1014174294Sobrien     * rest of the string.
1015174294Sobrien     */
1016174294Sobrien    if (rvec) {
1017174294Sobrien      am_opts ap;
1018174294Sobrien      am_ops *pt;
1019174294Sobrien      char **sp = rvec;
1020174294Sobrien      while (*sp) {		/* loop until you find something, if any */
1021174294Sobrien	memset((char *) &ap, 0, sizeof(am_opts));
1022174294Sobrien	/*
1023174294Sobrien	 * This next routine cause many spurious "expansion of ... is"
1024174294Sobrien	 * messages, which are ignored, b/c all we need out of this
1025174294Sobrien	 * routine is to match selectors.  These spurious messages may
1026174294Sobrien	 * be wrong, esp. if they try to expand ${key} b/c it will
1027174294Sobrien	 * get expanded to "/defaults"
1028174294Sobrien	 */
1029174294Sobrien	pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1030310490Scy		       mp->am_parent->am_al->al_mnt->mf_info);
1031174294Sobrien	free_opts(&ap);	/* don't leak */
1032174294Sobrien	if (pt == &amfs_error_ops) {
1033174294Sobrien	  plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1034174294Sobrien	} else {
1035174294Sobrien	  dfl = strip_selectors(*sp, "/defaults");
1036174294Sobrien	  plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1037174294Sobrien	  break;
1038174294Sobrien	}
1039174294Sobrien	++sp;
1040174294Sobrien      }
1041174294Sobrien    }
1042174294Sobrien  } else {			/* not selectors_in_defaults */
1043174294Sobrien    /*
1044174294Sobrien     * Extract first value
1045174294Sobrien     */
1046174294Sobrien    dfl = rvec[0];
1047174294Sobrien  }
1048174294Sobrien
1049174294Sobrien  /*
1050174294Sobrien   * If there were any values at all...
1051174294Sobrien   */
1052174294Sobrien  if (dfl) {
1053174294Sobrien    /*
1054174294Sobrien     * Log error if there were other values
1055174294Sobrien     */
1056174294Sobrien    if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1057174294Sobrien      dlog("/defaults chopped into %s", dfl);
1058174294Sobrien      plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1059174294Sobrien    }
1060174294Sobrien
1061174294Sobrien    /*
1062174294Sobrien     * Prepend to existing defaults if they exist,
1063174294Sobrien     * otherwise just use these defaults.
1064174294Sobrien     */
1065174294Sobrien    if (*def_opts && *dfl) {
1066174294Sobrien      size_t l = strlen(def_opts) + strlen(dfl) + 2;
1067174294Sobrien      char *nopts = (char *) xmalloc(l);
1068174294Sobrien      xsnprintf(nopts, l, "%s;%s", dfl, def_opts);
1069174294Sobrien      XFREE(def_opts);
1070174294Sobrien      def_opts = nopts;
1071174294Sobrien    } else if (*dfl) {
1072174294Sobrien      def_opts = strealloc(def_opts, dfl);
1073174294Sobrien    }
1074174294Sobrien  }
1075174294Sobrien
1076174294Sobrien  XFREE(dflts);
1077174294Sobrien
1078174294Sobrien  /* don't need info vector any more */
1079174294Sobrien  if (rvec)
1080174294Sobrien    XFREE(rvec);
1081174294Sobrien
1082174294Sobrien  return def_opts;
1083174294Sobrien}
1084174294Sobrien
1085174294Sobrien
1086174294Sobrienam_node *
1087174294Sobrienamfs_generic_mount_child(am_node *new_mp, int *error_return)
1088174294Sobrien{
1089174294Sobrien  int error;
1090174294Sobrien  struct continuation *cp;	/* Continuation structure if need to mount */
1091174294Sobrien
1092174294Sobrien  dlog("in amfs_generic_mount_child");
1093174294Sobrien
1094174294Sobrien  *error_return = error = 0;	/* Error so far */
1095174294Sobrien
1096174294Sobrien  /* we have an errorfs attached to the am_node, free it */
1097310490Scy  if (new_mp->am_al)
1098310490Scy    free_loc(new_mp->am_al);
1099310490Scy  new_mp->am_al = NULL;
1100174294Sobrien
1101174294Sobrien  /*
1102174294Sobrien   * Construct a continuation
1103174294Sobrien   */
1104174294Sobrien  cp = ALLOC(struct continuation);
1105174294Sobrien  cp->callout = 0;
1106174294Sobrien  cp->mp = new_mp;
1107174294Sobrien  cp->retry = TRUE;
1108174294Sobrien  cp->start = clocktime(NULL);
1109310490Scy  cp->al = new_mp->am_alarray;
1110174294Sobrien
1111174294Sobrien  /*
1112174294Sobrien   * Try and mount the file system.  If this succeeds immediately (possible
1113174294Sobrien   * for a ufs file system) then return the attributes, otherwise just
1114174294Sobrien   * return an error.
1115174294Sobrien   */
1116174294Sobrien  error = amfs_bgmount(cp);
1117174294Sobrien  reschedule_timeout_mp();
1118174294Sobrien  if (!error)
1119174294Sobrien    return new_mp;
1120174294Sobrien
1121174294Sobrien  /*
1122174294Sobrien   * Code for quick reply.  If current_transp is set, then it's the
1123310490Scy   * transp that's been passed down from nfs_dispatcher() or from
1124174294Sobrien   * autofs_program_[123]().
1125174294Sobrien   * If new_mp->am_transp is not already set, set it by copying in
1126174294Sobrien   * current_transp.  Once am_transp is set, nfs_quick_reply() and
1127174294Sobrien   * autofs_mount_succeeded() can use it to send a reply to the
1128174294Sobrien   * client that requested this mount.
1129174294Sobrien   */
1130174294Sobrien  if (current_transp && !new_mp->am_transp) {
1131174294Sobrien    dlog("Saving RPC transport for %s", new_mp->am_path);
1132174294Sobrien    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1133174294Sobrien    *(new_mp->am_transp) = *current_transp;
1134174294Sobrien  }
1135310490Scy  if (error && new_mp->am_al && new_mp->am_al->al_mnt &&
1136310490Scy      (new_mp->am_al->al_mnt->mf_ops == &amfs_error_ops))
1137174294Sobrien    new_mp->am_error = error;
1138174294Sobrien
1139174294Sobrien  if (new_mp->am_error > 0)
1140174294Sobrien    assign_error_mntfs(new_mp);
1141174294Sobrien
1142174294Sobrien  ereturn(error);
1143174294Sobrien}
1144174294Sobrien
1145174294Sobrien
1146174294Sobrien/*
1147174294Sobrien * Automount interface to RPC lookup routine
1148174294Sobrien * Find the corresponding entry and return
1149174294Sobrien * the file handle for it.
1150174294Sobrien */
1151174294Sobrienam_node *
1152174294Sobrienamfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1153174294Sobrien{
1154174294Sobrien  am_node *new_mp;
1155310490Scy  am_loc **al_array;
1156174294Sobrien  int mp_error;
1157174294Sobrien
1158174294Sobrien  dlog("in amfs_generic_lookup_child");
1159174294Sobrien
1160174294Sobrien  *error_return = 0;
1161174294Sobrien  new_mp = amfs_lookup_node(mp, fname, error_return);
1162174294Sobrien
1163174294Sobrien  /* return if we got an error */
1164174294Sobrien  if (!new_mp || *error_return > 0)
1165174294Sobrien    return new_mp;
1166174294Sobrien
1167174294Sobrien  /* also return if it's already mounted and known to be up */
1168310490Scy  if (*error_return == 0 && FSRV_ISUP(new_mp->am_al->al_mnt->mf_server))
1169174294Sobrien    return new_mp;
1170174294Sobrien
1171174294Sobrien  switch (op) {
1172174294Sobrien  case VLOOK_DELETE:
1173174294Sobrien    /*
1174174294Sobrien     * If doing a delete then don't create again!
1175174294Sobrien     */
1176174294Sobrien    ereturn(ENOENT);
1177174294Sobrien  case VLOOK_LOOKUP:
1178174294Sobrien    return new_mp;
1179174294Sobrien  }
1180174294Sobrien
1181174294Sobrien  /* save error_return */
1182174294Sobrien  mp_error = *error_return;
1183174294Sobrien
1184310490Scy  al_array = amfs_lookup_loc(new_mp, error_return);
1185310490Scy  if (!al_array) {
1186310490Scy    new_mp->am_error = new_mp->am_al->al_mnt->mf_error = *error_return;
1187174294Sobrien    free_map(new_mp);
1188174294Sobrien    return NULL;
1189174294Sobrien  }
1190174294Sobrien
1191174294Sobrien  /* store the array inside the am_node */
1192310490Scy  new_mp->am_alarray = al_array;
1193174294Sobrien
1194174294Sobrien  /*
1195174294Sobrien   * Note: while it might seem like a good idea to prioritize
1196174294Sobrien   * the list of mntfs's we got here, it probably isn't.
1197174294Sobrien   * It would ignore the ordering of entries specified by the user,
1198174294Sobrien   * which is counterintuitive and confusing.
1199174294Sobrien   */
1200174294Sobrien  return new_mp;
1201174294Sobrien}
1202174294Sobrien
1203174294Sobrien
1204174294Sobrienvoid
1205174294Sobrienamfs_generic_mounted(mntfs *mf)
1206174294Sobrien{
1207174294Sobrien  amfs_mkcacheref(mf);
1208174294Sobrien}
1209174294Sobrien
1210174294Sobrien
1211174294Sobrien/*
1212174294Sobrien * Unmount an automount sub-node
1213174294Sobrien */
1214174294Sobrienint
1215174294Sobrienamfs_generic_umount(am_node *mp, mntfs *mf)
1216174294Sobrien{
1217174294Sobrien  int error = 0;
1218174294Sobrien
1219174294Sobrien#ifdef HAVE_FS_AUTOFS
1220174294Sobrien  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1221174294Sobrien  if (mf->mf_flags & MFF_IS_AUTOFS)
1222174294Sobrien    error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
1223174294Sobrien#endif /* HAVE_FS_AUTOFS */
1224174294Sobrien
1225174294Sobrien  return error;
1226174294Sobrien}
1227174294Sobrien
1228174294Sobrien
1229174294Sobrienchar *
1230174294Sobrienamfs_generic_match(am_opts *fo)
1231174294Sobrien{
1232174294Sobrien  char *p;
1233174294Sobrien
1234174294Sobrien  if (!fo->opt_rfs) {
1235174294Sobrien    plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1236174294Sobrien    return 0;
1237174294Sobrien  }
1238174294Sobrien  if (!fo->opt_fs) {
1239174294Sobrien    plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1240174294Sobrien    return 0;
1241174294Sobrien  }
1242174294Sobrien
1243174294Sobrien  /*
1244174294Sobrien   * Swap round fs:= and rfs:= options
1245174294Sobrien   * ... historical (jsp)
1246174294Sobrien   */
1247174294Sobrien  p = fo->opt_rfs;
1248174294Sobrien  fo->opt_rfs = fo->opt_fs;
1249174294Sobrien  fo->opt_fs = p;
1250174294Sobrien
1251174294Sobrien  /*
1252174294Sobrien   * mtab entry turns out to be the name of the mount map
1253174294Sobrien   */
1254310490Scy  return xstrdup(fo->opt_rfs ? fo->opt_rfs : ".");
1255174294Sobrien}
1256