amfs_generic.c revision 174295
1202719Sgabor/*
2202719Sgabor * Copyright (c) 1997-2006 Erez Zadok
3202719Sgabor * Copyright (c) 1990 Jan-Simon Pendry
4202719Sgabor * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5202719Sgabor * Copyright (c) 1990 The Regents of the University of California.
6202719Sgabor * All rights reserved.
7202719Sgabor *
8202719Sgabor * This code is derived from software contributed to Berkeley by
9202719Sgabor * Jan-Simon Pendry at Imperial College, London.
10202719Sgabor *
11202719Sgabor * Redistribution and use in source and binary forms, with or without
12202719Sgabor * modification, are permitted provided that the following conditions
13202719Sgabor * are met:
14202719Sgabor * 1. Redistributions of source code must retain the above copyright
15202719Sgabor *    notice, this list of conditions and the following disclaimer.
16202719Sgabor * 2. Redistributions in binary form must reproduce the above copyright
17202719Sgabor *    notice, this list of conditions and the following disclaimer in the
18202719Sgabor *    documentation and/or other materials provided with the distribution.
19202719Sgabor * 3. All advertising materials mentioning features or use of this software
20202719Sgabor *    must display the following acknowledgment:
21202719Sgabor *      This product includes software developed by the University of
22202719Sgabor *      California, Berkeley and its contributors.
23202719Sgabor * 4. Neither the name of the University nor the names of its contributors
24202719Sgabor *    may be used to endorse or promote products derived from this software
25202719Sgabor *    without specific prior written permission.
26202719Sgabor *
27202719Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28202719Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29202719Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30202719Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31202719Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32202719Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33202719Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34202719Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35202719Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36202719Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37202719Sgabor * SUCH DAMAGE.
38202719Sgabor *
39202719Sgabor *
40202719Sgabor * File: am-utils/amd/amfs_generic.c
41202719Sgabor *
42202719Sgabor */
43202719Sgabor
44202719Sgabor/*
45202719Sgabor * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
46202719Sgabor */
47202719Sgabor
48202719Sgabor#ifdef HAVE_CONFIG_H
49202719Sgabor# include <config.h>
50202719Sgabor#endif /* HAVE_CONFIG_H */
51202719Sgabor#include <am_defs.h>
52202719Sgabor#include <amd.h>
53202719Sgabor
54202719Sgabor
55202719Sgabor/****************************************************************************
56202719Sgabor *** MACROS                                                               ***
57202719Sgabor ****************************************************************************/
58202719Sgabor#define	IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
59202719Sgabor
60202719Sgabor
61202719Sgabor/****************************************************************************
62202719Sgabor *** STRUCTURES                                                           ***
63202719Sgabor ****************************************************************************/
64202719Sgabor/*
65202719Sgabor * Mounting a file system may take a significant period of time.  The
66202719Sgabor * problem is that if this is done in the main process thread then the
67202719Sgabor * entire automounter could be blocked, possibly hanging lots of processes
68202719Sgabor * on the system.  Instead we use a continuation scheme to allow mounts to
69202719Sgabor * be attempted in a sub-process.  When the sub-process exits we pick up the
70202719Sgabor * exit status (by convention a UN*X error number) and continue in a
71202719Sgabor * notifier.  The notifier gets handed a data structure and can then
72202719Sgabor * determine whether the mount was successful or not.  If not, it updates
73202719Sgabor * the data structure and tries again until there are no more ways to try
74202719Sgabor * the mount, or some other permanent error occurs.  In the mean time no RPC
75202719Sgabor * reply is sent, even after the mount is successful.  We rely on the RPC
76202719Sgabor * retry mechanism to resend the lookup request which can then be handled.
77202719Sgabor */
78202719Sgaborstruct continuation {
79202719Sgabor  am_node *mp;			/* Node we are trying to mount */
80202719Sgabor  int retry;			/* Try again? */
81202719Sgabor  time_t start;			/* Time we started this mount */
82202719Sgabor  int callout;			/* Callout identifier */
83202719Sgabor  mntfs **mf;			/* Current mntfs */
84202719Sgabor};
85202719Sgabor
86202719Sgabor
87202719Sgabor/****************************************************************************
88202719Sgabor *** FORWARD DEFINITIONS                                                  ***
89202719Sgabor ****************************************************************************/
90202719Sgaborstatic am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return);
91202719Sgaborstatic mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
92202719Sgabor				    char *def_opts, char *pfname);
93202719Sgaborstatic mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return);
94202719Sgaborstatic void amfs_cont(int rc, int term, opaque_t arg);
95202719Sgaborstatic void amfs_retry(int rc, int term, opaque_t arg);
96202719Sgaborstatic void free_continuation(struct continuation *cp);
97202719Sgaborstatic int amfs_bgmount(struct continuation *cp);
98202719Sgaborstatic char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts);
99202719Sgabor
100202719Sgabor
101202719Sgabor/****************************************************************************
102202719Sgabor *** FUNCTIONS                                                             ***
103202719Sgabor ****************************************************************************/
104202719Sgaborstatic am_node *
105202719Sgaboramfs_lookup_node(am_node *mp, char *fname, int *error_return)
106202719Sgabor{
107202719Sgabor  am_node *new_mp;
108202719Sgabor  int error = 0;		/* Error so far */
109202719Sgabor  int in_progress = 0;		/* # of (un)mount in progress */
110202719Sgabor  mntfs *mf;
111202719Sgabor  char *expanded_fname = 0;
112202719Sgabor
113202719Sgabor  dlog("in amfs_lookup_node");
114202719Sgabor
115202719Sgabor  /*
116202719Sgabor   * If the server is shutting down
117202719Sgabor   * then don't return information
118202719Sgabor   * about the mount point.
119202719Sgabor   */
120202719Sgabor  if (amd_state == Finishing) {
121202719Sgabor    if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) {
122202719Sgabor      dlog("%s mount ignored - going down", fname);
123202719Sgabor    } else {
124202719Sgabor      dlog("%s/%s mount ignored - going down", mp->am_path, fname);
125202719Sgabor    }
126202719Sgabor    ereturn(ENOENT);
127202719Sgabor  }
128202719Sgabor
129202719Sgabor  /*
130202719Sgabor   * Handle special case of "." and ".."
131202719Sgabor   */
132202719Sgabor  if (fname[0] == '.') {
133202719Sgabor    if (fname[1] == '\0')
134202719Sgabor      return mp;		/* "." is the current node */
135202719Sgabor    if (fname[1] == '.' && fname[2] == '\0') {
136202719Sgabor      if (mp->am_parent) {
137202719Sgabor	dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
138202719Sgabor	return mp->am_parent;	/* ".." is the parent node */
139202719Sgabor      }
140202719Sgabor      ereturn(ESTALE);
141202719Sgabor    }
142202719Sgabor  }
143202719Sgabor
144202719Sgabor  /*
145202719Sgabor   * Check for valid key name.
146202719Sgabor   * If it is invalid then pretend it doesn't exist.
147202719Sgabor   */
148202719Sgabor  if (!valid_key(fname)) {
149202719Sgabor    plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
150202719Sgabor    ereturn(ENOENT);
151202719Sgabor  }
152202719Sgabor
153202719Sgabor  /*
154202719Sgabor   * Expand key name.
155202719Sgabor   * expanded_fname is now a private copy.
156202719Sgabor   */
157202719Sgabor  expanded_fname = expand_selectors(fname);
158202719Sgabor
159202719Sgabor  /*
160202719Sgabor   * Search children of this node
161202719Sgabor   */
162202719Sgabor  for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) {
163202719Sgabor    if (FSTREQ(new_mp->am_name, expanded_fname)) {
164202719Sgabor      if (new_mp->am_error) {
165202719Sgabor	error = new_mp->am_error;
166202719Sgabor	continue;
167202719Sgabor      }
168202719Sgabor
169202719Sgabor      /*
170202719Sgabor       * If the error code is undefined then it must be
171202719Sgabor       * in progress.
172202719Sgabor       */
173202719Sgabor      mf = new_mp->am_mnt;
174202719Sgabor      if (mf->mf_error < 0)
175202719Sgabor	goto in_progrss;
176202719Sgabor
177202719Sgabor      /*
178202719Sgabor       * If there was a previous error with this node
179202719Sgabor       * then return that error code.
180202719Sgabor       */
181202719Sgabor      if (mf->mf_flags & MFF_ERROR) {
182202719Sgabor	error = mf->mf_error;
183202719Sgabor	continue;
184202719Sgabor      }
185202719Sgabor      if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
186202719Sgabor      in_progrss:
187202719Sgabor	/*
188202719Sgabor	 * If the fs is not mounted or it is unmounting then there
189202719Sgabor	 * is a background (un)mount in progress.  In this case
190202719Sgabor	 * we just drop the RPC request (return nil) and
191202719Sgabor	 * wait for a retry, by which time the (un)mount may
192202719Sgabor	 * have completed.
193202719Sgabor	 */
194202719Sgabor	dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
195202719Sgabor	     expanded_fname, mf->mf_mount,
196202719Sgabor	     (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags);
197202719Sgabor	in_progress++;
198202719Sgabor	if (mf->mf_flags & MFF_UNMOUNTING) {
199202719Sgabor	  dlog("will remount later");
200202719Sgabor	  new_mp->am_flags |= AMF_REMOUNT;
201202719Sgabor	}
202202719Sgabor	continue;
203202719Sgabor      }
204202719Sgabor
205202719Sgabor      /*
206202719Sgabor       * Otherwise we have a hit: return the current mount point.
207202719Sgabor       */
208202719Sgabor      dlog("matched %s in %s", expanded_fname, new_mp->am_path);
209202719Sgabor      XFREE(expanded_fname);
210202719Sgabor      return new_mp;
211202719Sgabor    }
212202719Sgabor  }
213202719Sgabor
214202719Sgabor  if (in_progress) {
215202719Sgabor    dlog("Waiting while %d mount(s) in progress", in_progress);
216202719Sgabor    XFREE(expanded_fname);
217202719Sgabor    ereturn(-1);
218202719Sgabor  }
219202719Sgabor
220202719Sgabor  /*
221202719Sgabor   * If an error occurred then return it.
222202719Sgabor   */
223202719Sgabor  if (error) {
224202719Sgabor    dlog("Returning error: %s", strerror(error));
225202719Sgabor    XFREE(expanded_fname);
226202719Sgabor    ereturn(error);
227202719Sgabor  }
228202719Sgabor
229202719Sgabor  /*
230202719Sgabor   * If the server is going down then just return,
231202719Sgabor   * don't try to mount any more file systems
232202719Sgabor   */
233202719Sgabor  if ((int) amd_state >= (int) Finishing) {
234202719Sgabor    dlog("not found - server going down anyway");
235202719Sgabor    ereturn(ENOENT);
236202719Sgabor  }
237202719Sgabor
238202719Sgabor  /*
239202719Sgabor   * Allocate a new map
240202719Sgabor   */
241202719Sgabor  new_mp = get_ap_child(mp, expanded_fname);
242202719Sgabor  XFREE(expanded_fname);
243202719Sgabor  if (new_mp == 0)
244202719Sgabor    ereturn(ENOSPC);
245202719Sgabor
246202719Sgabor  *error_return = -1;
247202719Sgabor  return new_mp;
248202719Sgabor}
249202719Sgabor
250202719Sgabor
251202719Sgabor
252202719Sgaborstatic mntfs *
253202719Sgaboramfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
254202719Sgabor		      char *def_opts, char *pfname)
255202719Sgabor{
256202719Sgabor  am_ops *p;
257202719Sgabor  am_opts *fs_opts;
258202719Sgabor  mntfs *new_mf;
259202719Sgabor  char *mp_dir = 0;
260202719Sgabor#ifdef HAVE_FS_AUTOFS
261202719Sgabor  int on_autofs = 1;
262202719Sgabor#endif /* HAVE_FS_AUTOFS */
263202719Sgabor
264202719Sgabor  /* match the operators */
265202719Sgabor  fs_opts = CALLOC(am_opts);
266202719Sgabor  p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path,
267202719Sgabor		pfname, mf->mf_info);
268202719Sgabor#ifdef HAVE_FS_AUTOFS
269202719Sgabor  /* XXX: this should be factored out into an autofs-specific function */
270202719Sgabor  if (new_mp->am_flags & AMF_AUTOFS) {
271202719Sgabor    /* ignore user-provided fs if we're using autofs */
272202719Sgabor    if (fs_opts->opt_sublink) {
273202719Sgabor      /*
274202719Sgabor       * For sublinks we need to use a hack with autofs:
275202719Sgabor       * mount the filesystem on the original opt_fs (which is NOT an
276202719Sgabor       * autofs mountpoint) and symlink (or lofs-mount) to it from
277202719Sgabor       * the autofs mountpoint.
278202719Sgabor       */
279202719Sgabor      on_autofs = 0;
280202719Sgabor      mp_dir = fs_opts->opt_fs;
281202719Sgabor    } else {
282202719Sgabor      if (p->autofs_fs_flags & FS_ON_AUTOFS) {
283202719Sgabor	mp_dir = new_mp->am_path;
284202719Sgabor      } else {
285202719Sgabor	mp_dir = fs_opts->opt_fs;
286202719Sgabor	on_autofs = 0;
287202719Sgabor      }
288202719Sgabor    }
289202719Sgabor  } else
290202719Sgabor#endif /* HAVE_FS_AUTOFS */
291202719Sgabor    mp_dir = fs_opts->opt_fs;
292202719Sgabor
293202719Sgabor  /*
294202719Sgabor   * Find or allocate a filesystem for this node.
295202719Sgabor   */
296202719Sgabor  new_mf = find_mntfs(p, fs_opts,
297202719Sgabor		      mp_dir,
298202719Sgabor		      fs_opts->fs_mtab,
299202719Sgabor		      def_opts,
300202719Sgabor		      fs_opts->opt_opts,
301202719Sgabor		      fs_opts->opt_remopts);
302202719Sgabor
303202719Sgabor  /*
304202719Sgabor   * See whether this is a real filesystem
305202719Sgabor   */
306202719Sgabor  p = new_mf->mf_ops;
307202719Sgabor  if (p == &amfs_error_ops) {
308202719Sgabor    plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
309202719Sgabor    free_mntfs(new_mf);
310202719Sgabor    return NULL;
311202719Sgabor  }
312202719Sgabor
313202719Sgabor  dlog("Got a hit with %s", p->fs_type);
314202719Sgabor
315202719Sgabor#ifdef HAVE_FS_AUTOFS
316202719Sgabor  if (new_mp->am_flags & AMF_AUTOFS && on_autofs) {
317202719Sgabor    new_mf->mf_flags |= MFF_ON_AUTOFS;
318202719Sgabor    new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags;
319202719Sgabor  }
320202719Sgabor  /*
321202719Sgabor   * A new filesystem is an autofs filesystems if:
322202719Sgabor   * 1. it claims it can be one (has the FS_AUTOFS flag)
323202719Sgabor   * 2. autofs is enabled system-wide
324202719Sgabor   * 3. either has an autofs parent,
325202719Sgabor   *    or it is explicitly requested to be autofs.
326202719Sgabor   */
327202719Sgabor  if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
328202719Sgabor      amd_use_autofs &&
329202719Sgabor      ((mf->mf_flags & MFF_IS_AUTOFS) ||
330202719Sgabor       (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type &&
331202719Sgabor	STREQ(new_mf->mf_fo->opt_mount_type, "autofs"))))
332202719Sgabor    new_mf->mf_flags |= MFF_IS_AUTOFS;
333202719Sgabor#endif /* HAVE_FS_AUTOFS */
334202719Sgabor
335202719Sgabor  return new_mf;
336202719Sgabor}
337202719Sgabor
338202719Sgabor
339202719Sgaborstatic mntfs **
340202719Sgaboramfs_lookup_mntfs(am_node *new_mp, int *error_return)
341202719Sgabor{
342202719Sgabor  am_node *mp;
343202719Sgabor  char *info;			/* Mount info - where to get the file system */
344202719Sgabor  char **ivecs, **cur_ivec;	/* Split version of info */
345202719Sgabor  int num_ivecs;
346202719Sgabor  char *orig_def_opts;          /* Original Automount options */
347202719Sgabor  char *def_opts;	       	/* Automount options */
348202719Sgabor  int error = 0;		/* Error so far */
349202719Sgabor  char path_name[MAXPATHLEN];	/* General path name buffer */
350202719Sgabor  char *pfname;			/* Path for database lookup */
351202719Sgabor  mntfs *mf, **mf_array;
352202719Sgabor  int count;
353202719Sgabor
354202719Sgabor  dlog("in amfs_lookup_mntfs");
355202719Sgabor
356202719Sgabor  mp = new_mp->am_parent;
357202719Sgabor
358202719Sgabor  /*
359202719Sgabor   * If we get here then this is a reference to an,
360202719Sgabor   * as yet, unknown name so we need to search the mount
361202719Sgabor   * map for it.
362202719Sgabor   */
363202719Sgabor  if (mp->am_pref) {
364202719Sgabor    if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
365202719Sgabor      ereturn(ENAMETOOLONG);
366202719Sgabor    xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name);
367202719Sgabor    pfname = path_name;
368202719Sgabor  } else {
369202719Sgabor    pfname = new_mp->am_name;
370202719Sgabor  }
371202719Sgabor
372202719Sgabor  mf = mp->am_mnt;
373202719Sgabor
374202719Sgabor  dlog("will search map info in %s to find %s", mf->mf_info, pfname);
375202719Sgabor  /*
376202719Sgabor   * Consult the oracle for some mount information.
377202719Sgabor   * info is malloc'ed and belongs to this routine.
378202719Sgabor   * It ends up being free'd in free_continuation().
379202719Sgabor   *
380202719Sgabor   * Note that this may return -1 indicating that information
381202719Sgabor   * is not yet available.
382202719Sgabor   */
383202719Sgabor  error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
384202719Sgabor  if (error) {
385202719Sgabor    if (error > 0)
386202719Sgabor      plog(XLOG_MAP, "No map entry for %s", pfname);
387202719Sgabor    else
388202719Sgabor      plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
389202719Sgabor    ereturn(error);
390202719Sgabor  }
391202719Sgabor  dlog("mount info is %s", info);
392202719Sgabor
393202719Sgabor  /*
394202719Sgabor   * Split info into an argument vector.
395202719Sgabor   * The vector is malloc'ed and belongs to
396202719Sgabor   * this routine.  It is free'd further down.
397202719Sgabor   *
398202719Sgabor   * Note: the vector pointers point into info, so don't free it!
399202719Sgabor   */
400202719Sgabor  ivecs = strsplit(info, ' ', '\"');
401202719Sgabor
402202719Sgabor  if (mf->mf_auto)
403202719Sgabor    def_opts = mf->mf_auto;
404202719Sgabor  else
405202719Sgabor    def_opts = "";
406202719Sgabor
407202719Sgabor  orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts));
408202719Sgabor  def_opts = strdup(orig_def_opts);
409202719Sgabor
410202719Sgabor  /* first build our defaults */
411202719Sgabor  num_ivecs = 0;
412202719Sgabor  for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
413202719Sgabor    if (**cur_ivec == '-') {
414202719Sgabor      /*
415202719Sgabor       * Pick up new defaults
416202719Sgabor       */
417202719Sgabor      char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1);
418      XFREE(def_opts);
419      def_opts = new_def_opts;
420      dlog("Setting def_opts to \"%s\"", def_opts);
421      continue;
422    } else
423      num_ivecs++;
424  }
425
426  mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
427
428  /* construct the array of struct mntfs for this mount point */
429  for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
430    mntfs *new_mf;
431
432    if (**cur_ivec == '-') {
433      XFREE(def_opts);
434      if ((*cur_ivec)[1] == '\0') {
435	/*
436	 * If we have a single dash '-' than we need to reset the
437	 * default options.
438	 */
439	def_opts = strdup(orig_def_opts);
440	dlog("Resetting the default options, a single dash '-' was found.");
441      } else {
442	/* append options to /default options */
443	def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1);
444	dlog("Resetting def_opts to \"%s\"", def_opts);
445      }
446      continue;
447    }
448
449    /*
450     * If a mntfs has already been found, and we find
451     * a cut then don't try any more locations.
452     *
453     * XXX: we do not know when the "/" was added as an equivalent for "||".
454     * It's undocumented, it might go away at any time. Caveat emptor.
455     */
456    if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
457      if (count > 0) {
458	dlog("Cut: not trying any more locations for %s", mp->am_path);
459	break;
460      }
461      continue;
462    }
463
464    new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname);
465    if (new_mf == NULL)
466      continue;
467    mf_array[count++] = new_mf;
468  }
469
470  /* We're done with ivecs */
471  XFREE(ivecs);
472  XFREE(info);
473  XFREE(orig_def_opts);
474  XFREE(def_opts);
475  if (count == 0) {			/* no match */
476    XFREE(mf_array);
477    ereturn(ENOENT);
478  }
479
480  return mf_array;
481}
482
483
484/*
485 * The continuation function.  This is called by
486 * the task notifier when a background mount attempt
487 * completes.
488 */
489static void
490amfs_cont(int rc, int term, opaque_t arg)
491{
492  struct continuation *cp = (struct continuation *) arg;
493  am_node *mp = cp->mp;
494  mntfs *mf = mp->am_mnt;
495
496  dlog("amfs_cont: '%s'", mp->am_path);
497
498  /*
499   * Definitely not trying to mount at the moment
500   */
501  mf->mf_flags &= ~MFF_MOUNTING;
502
503  /*
504   * While we are mounting - try to avoid race conditions
505   */
506  new_ttl(mp);
507
508  /*
509   * Wakeup anything waiting for this mount
510   */
511  wakeup(get_mntfs_wchan(mf));
512
513  /*
514   * Check for termination signal or exit status...
515   */
516  if (rc || term) {
517#ifdef HAVE_FS_AUTOFS
518    if (mf->mf_flags & MFF_IS_AUTOFS &&
519	!(mf->mf_flags & MFF_MOUNTED))
520      autofs_release_fh(mp);
521#endif /* HAVE_FS_AUTOFS */
522
523    if (term) {
524      /*
525       * Not sure what to do for an error code.
526       */
527      mf->mf_error = EIO;	/* XXX ? */
528      mf->mf_flags |= MFF_ERROR;
529      plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
530    } else {
531      /*
532       * Check for exit status...
533       */
534#ifdef __linux__
535      /*
536       * HACK ALERT!
537       *
538       * On Linux (and maybe not only) it's possible to run
539       * an amd which "knows" how to mount certain combinations
540       * of nfs_proto/nfs_version which the kernel doesn't grok.
541       * So if we got an EINVAL and we have a server that's not
542       * using NFSv2/UDP, try again with NFSv2/UDP.
543       *
544       * Too bad that there is no way to dynamically determine
545       * what combinations the _client_ supports, as opposed to
546       * what the _server_ supports...
547       */
548      if (rc == EINVAL &&
549	  mf->mf_server &&
550	  (mf->mf_server->fs_version != 2 ||
551	   !STREQ(mf->mf_server->fs_proto, "udp")))
552	mf->mf_flags |= MFF_NFS_SCALEDOWN;
553      else
554#endif /* __linux__ */
555      {
556	mf->mf_error = rc;
557	mf->mf_flags |= MFF_ERROR;
558	errno = rc;		/* XXX */
559	if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx"))
560	  plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
561      }
562    }
563
564    if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
565      /*
566       * If we get here then that attempt didn't work, so
567       * move the info vector pointer along by one and
568       * call the background mount routine again
569       */
570      amd_stats.d_merr++;
571      cp->mf++;
572    }
573    amfs_bgmount(cp);
574    if (mp->am_error > 0)
575      assign_error_mntfs(mp);
576  } else {
577    /*
578     * The mount worked.
579     */
580    dlog("Mounting %s returned success", cp->mp->am_path);
581    am_mounted(cp->mp);
582    free_continuation(cp);
583  }
584
585  reschedule_timeout_mp();
586}
587
588
589/*
590 * Retry a mount
591 */
592static void
593amfs_retry(int rc, int term, opaque_t arg)
594{
595  struct continuation *cp = (struct continuation *) arg;
596  am_node *mp = cp->mp;
597  int error = 0;
598
599  dlog("Commencing retry for mount of %s", mp->am_path);
600
601  new_ttl(mp);
602
603  if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
604    /*
605     * The entire mount has timed out.  Set the error code and skip past all
606     * the mntfs's so that amfs_bgmount will not have any more
607     * ways to try the mount, thus causing an error.
608     */
609    plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
610    error = ETIMEDOUT;
611    while (*cp->mf)
612      cp->mf++;
613    /* explicitly forbid further retries after timeout */
614    cp->retry = FALSE;
615  }
616  if (error || !IN_PROGRESS(cp))
617    error = amfs_bgmount(cp);
618
619  reschedule_timeout_mp();
620}
621
622
623/*
624 * Discard an old continuation
625 */
626static void
627free_continuation(struct continuation *cp)
628{
629  mntfs **mfp;
630
631  dlog("free_continuation");
632  if (cp->callout)
633    untimeout(cp->callout);
634  /*
635   * we must free the mntfs's in the list.
636   * so free all of them if there was an error,
637   * or free all but the used one, if the mount succeeded.
638   */
639  for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
640    free_mntfs(*mfp);
641  }
642  XFREE(cp->mp->am_mfarray);
643  cp->mp->am_mfarray = 0;
644  XFREE(cp);
645}
646
647
648/*
649 * Pick a file system to try mounting and
650 * do that in the background if necessary
651 *
652For each location:
653	discard previous mount location if required
654	fetch next mount location
655	if the filesystem failed to be mounted then
656		this_error = error from filesystem
657		goto failed
658	if the filesystem is mounting or unmounting then
659		goto retry;
660	if the fileserver is down then
661		this_error = EIO
662		continue;
663	if the filesystem is already mounted
664		break
665	fi
666
667	this_error = initialize mount point
668
669	if no error on this mount and mount is delayed then
670		this_error = -1
671	fi
672	if this_error < 0 then
673		retry = true
674	fi
675	if no error on this mount then
676		if mount in background then
677			run mount in background
678			return -1
679		else
680			this_error = mount in foreground
681		fi
682	fi
683	if an error occurred on this mount then
684		update stats
685		save error in mount point
686	fi
687endfor
688 */
689static int
690amfs_bgmount(struct continuation *cp)
691{
692  am_node *mp = cp->mp;
693  mntfs *mf;			/* Current mntfs */
694  int this_error = -1;		/* Per-mount error */
695  int hard_error = -1;		/* Cumulative per-node error */
696
697  if (mp->am_mnt)
698    free_mntfs(mp->am_mnt);
699
700  /*
701   * Try to mount each location.
702   * At the end:
703   * hard_error == 0 indicates something was mounted.
704   * hard_error > 0 indicates everything failed with a hard error
705   * hard_error < 0 indicates nothing could be mounted now
706   */
707  for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
708    am_ops *p;
709
710    mf = dup_mntfs(mp->am_mnt);
711    p = mf->mf_ops;
712
713    if (hard_error < 0)
714      hard_error = this_error;
715    this_error = 0;
716
717    if (mf->mf_error > 0) {
718      this_error = mf->mf_error;
719      goto failed;
720    }
721
722    if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
723      /*
724       * Still mounting - retry later
725       */
726      dlog("mount of \"%s\" already pending", mf->mf_info);
727      goto retry;
728    }
729
730    if (FSRV_ISDOWN(mf->mf_server)) {
731      /*
732       * Would just mount from the same place
733       * as a hung mount - so give up
734       */
735      dlog("%s is already hung - giving up", mf->mf_server->fs_host);
736      this_error = EIO;
737      goto failed;
738    }
739
740    if (mp->am_link) {
741      XFREE(mp->am_link);
742      mp->am_link = NULL;
743    }
744    if (mf->mf_fo && mf->mf_fo->opt_sublink)
745      mp->am_link = strdup(mf->mf_fo->opt_sublink);
746
747    /*
748     * Will usually need to play around with the mount nodes
749     * file attribute structure.  This must be done here.
750     * Try and get things initialized, even if the fileserver
751     * is not known to be up.  In the common case this will
752     * progress things faster.
753     */
754
755    /*
756     * Fill in attribute fields.
757     */
758    if (mf->mf_fsflags & FS_DIRECTORY)
759      mk_fattr(&mp->am_fattr, NFDIR);
760    else
761      mk_fattr(&mp->am_fattr, NFLNK);
762
763    if (mf->mf_flags & MFF_MOUNTED) {
764      dlog("duplicate mount of \"%s\" ...", mf->mf_info);
765      /*
766       * Skip initial processing of the mountpoint if already mounted.
767       * This could happen if we have multiple sublinks into the same f/s,
768       * or if we are restarting an already-mounted filesystem.
769       */
770      goto already_mounted;
771    }
772
773    if (mf->mf_fo && mf->mf_fo->fs_mtab) {
774      plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
775	   mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
776	   mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
777    }
778
779    if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
780      this_error = p->fs_init(mf);
781
782    if (this_error > 0)
783      goto failed;
784    if (this_error < 0)
785      goto retry;
786
787    if (mf->mf_fo && mf->mf_fo->opt_delay) {
788      /*
789       * If there is a delay timer on the mount
790       * then don't try to mount if the timer
791       * has not expired.
792       */
793      int i = atoi(mf->mf_fo->opt_delay);
794      time_t now = clocktime(NULL);
795      if (i > 0 && now < (cp->start + i)) {
796	dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
797	goto retry;
798      }
799    }
800
801    /*
802     * If the directory is not yet made and it needs to be made, then make it!
803     */
804    if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
805      plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
806      this_error = mkdirs(mf->mf_mount, 0555);
807      if (this_error) {
808	plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
809	goto failed;
810      }
811      mf->mf_flags |= MFF_MKMNT;
812    }
813
814#ifdef HAVE_FS_AUTOFS
815    if (mf->mf_flags & MFF_IS_AUTOFS)
816      if ((this_error = autofs_get_fh(mp)))
817	goto failed;
818#endif /* HAVE_FS_AUTOFS */
819
820  already_mounted:
821    mf->mf_flags |= MFF_MOUNTING;
822    if (mf->mf_fsflags & FS_MBACKGROUND) {
823      dlog("backgrounding mount of \"%s\"", mf->mf_mount);
824      if (cp->callout) {
825	untimeout(cp->callout);
826	cp->callout = 0;
827      }
828
829      /* actually run the task, backgrounding as necessary */
830      run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
831      return -1;
832    } else {
833      dlog("foreground mount of \"%s\" ...", mf->mf_mount);
834      this_error = mount_node((opaque_t) mp);
835    }
836
837    mf->mf_flags &= ~MFF_MOUNTING;
838    if (this_error > 0)
839      goto failed;
840    if (this_error == 0) {
841      am_mounted(mp);
842      break;					/* Success */
843    }
844
845  retry:
846    if (!cp->retry)
847      continue;
848    dlog("will retry ...\n");
849
850    /*
851     * Arrange that amfs_bgmount is called
852     * after anything else happens.
853     */
854    dlog("Arranging to retry mount of %s", mp->am_path);
855    sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
856    if (cp->callout)
857      untimeout(cp->callout);
858    cp->callout = timeout(RETRY_INTERVAL, wakeup,
859			  (opaque_t) get_mntfs_wchan(mf));
860
861    mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
862
863    /*
864     * Not done yet - so don't return anything
865     */
866    return -1;
867
868  failed:
869    amd_stats.d_merr++;
870    mf->mf_error = this_error;
871    mf->mf_flags |= MFF_ERROR;
872#ifdef HAVE_FS_AUTOFS
873    if (mp->am_autofs_fh)
874      autofs_release_fh(mp);
875#endif /* HAVE_FS_AUTOFS */
876    if (mf->mf_flags & MFF_MKMNT) {
877      rmdirs(mf->mf_mount);
878      mf->mf_flags &= ~MFF_MKMNT;
879    }
880    /*
881     * Wakeup anything waiting for this mount
882     */
883    wakeup(get_mntfs_wchan(mf));
884    free_mntfs(mf);
885    /* continue */
886  }
887
888  /*
889   * If we get here, then either the mount succeeded or
890   * there is no more mount information available.
891   */
892  if (this_error) {
893    mp->am_mnt = mf = new_mntfs();
894
895#ifdef HAVE_FS_AUTOFS
896    if (mp->am_flags & AMF_AUTOFS)
897      autofs_mount_failed(mp);
898    else
899#endif /* HAVE_FS_AUTOFS */
900      nfs_quick_reply(mp, this_error);
901
902    if (hard_error <= 0)
903      hard_error = this_error;
904    if (hard_error < 0)
905      hard_error = ETIMEDOUT;
906
907    /*
908     * Set a small(ish) timeout on an error node if
909     * the error was not a time out.
910     */
911    switch (hard_error) {
912    case ETIMEDOUT:
913    case EWOULDBLOCK:
914    case EIO:
915      mp->am_timeo = 17;
916      break;
917    default:
918      mp->am_timeo = 5;
919      break;
920    }
921    new_ttl(mp);
922  } else {
923    mf = mp->am_mnt;
924    /*
925     * Wakeup anything waiting for this mount
926     */
927    wakeup(get_mntfs_wchan(mf));
928    hard_error = 0;
929  }
930
931  /*
932   * Make sure that the error value in the mntfs has a
933   * reasonable value.
934   */
935  if (mf->mf_error < 0) {
936    mf->mf_error = hard_error;
937    if (hard_error)
938      mf->mf_flags |= MFF_ERROR;
939  }
940
941  /*
942   * In any case we don't need the continuation any more
943   */
944  free_continuation(cp);
945
946  return hard_error;
947}
948
949
950static char *
951amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
952{
953  char *dflts;
954  char *dfl;
955  char **rvec = NULL;
956  struct mnt_map *mm = (mnt_map *) mf->mf_private;
957
958  dlog("determining /defaults entry value");
959
960  /*
961   * Find out if amd.conf overrode any map-specific /defaults.
962   *
963   * HACK ALERT: there's no easy way to find out what the map mount point is
964   * at this point, so I am forced to initialize the mnt_map->cfm field here
965   * for the first time, upon the very first search for a /defaults entry in
966   * this map.  This initialization is much better done in mapc_create(),
967   * but it's impossible to do that there with the current code structure.
968   */
969  if (mm->cfm == NULL) {	/* then initialize it for first time */
970    mm->cfm = find_cf_map(mf->mf_mount);
971  }
972  if (mm->cfm && mm->cfm->cfm_defaults) {
973    dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
974    dflts = strdup(mm->cfm->cfm_defaults);
975  } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
976    dlog("/defaults gave %s", dflts);
977  } else {
978    return def_opts;		/* if nothing found */
979  }
980
981  /* trim leading '-' in case thee's one */
982  if (*dflts == '-')
983    dfl = dflts + 1;
984  else
985    dfl = dflts;
986
987  /*
988   * Chop the defaults up
989   */
990  rvec = strsplit(dfl, ' ', '\"');
991
992  if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
993    /*
994     * Pick whichever first entry matched the list of selectors.
995     * Strip the selectors from the string, and assign to dfl the
996     * rest of the string.
997     */
998    if (rvec) {
999      am_opts ap;
1000      am_ops *pt;
1001      char **sp = rvec;
1002      while (*sp) {		/* loop until you find something, if any */
1003	memset((char *) &ap, 0, sizeof(am_opts));
1004	/*
1005	 * This next routine cause many spurious "expansion of ... is"
1006	 * messages, which are ignored, b/c all we need out of this
1007	 * routine is to match selectors.  These spurious messages may
1008	 * be wrong, esp. if they try to expand ${key} b/c it will
1009	 * get expanded to "/defaults"
1010	 */
1011	pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1012		       mp->am_parent->am_mnt->mf_info);
1013	free_opts(&ap);	/* don't leak */
1014	if (pt == &amfs_error_ops) {
1015	  plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1016	} else {
1017	  dfl = strip_selectors(*sp, "/defaults");
1018	  plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1019	  break;
1020	}
1021	++sp;
1022      }
1023    }
1024  } else {			/* not selectors_in_defaults */
1025    /*
1026     * Extract first value
1027     */
1028    dfl = rvec[0];
1029  }
1030
1031  /*
1032   * If there were any values at all...
1033   */
1034  if (dfl) {
1035    /*
1036     * Log error if there were other values
1037     */
1038    if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1039      dlog("/defaults chopped into %s", dfl);
1040      plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1041    }
1042
1043    /*
1044     * Prepend to existing defaults if they exist,
1045     * otherwise just use these defaults.
1046     */
1047    if (*def_opts && *dfl) {
1048      size_t l = strlen(def_opts) + strlen(dfl) + 2;
1049      char *nopts = (char *) xmalloc(l);
1050      xsnprintf(nopts, l, "%s;%s", dfl, def_opts);
1051      XFREE(def_opts);
1052      def_opts = nopts;
1053    } else if (*dfl) {
1054      def_opts = strealloc(def_opts, dfl);
1055    }
1056  }
1057
1058  XFREE(dflts);
1059
1060  /* don't need info vector any more */
1061  if (rvec)
1062    XFREE(rvec);
1063
1064  return def_opts;
1065}
1066
1067
1068am_node *
1069amfs_generic_mount_child(am_node *new_mp, int *error_return)
1070{
1071  int error;
1072  struct continuation *cp;	/* Continuation structure if need to mount */
1073
1074  dlog("in amfs_generic_mount_child");
1075
1076  *error_return = error = 0;	/* Error so far */
1077
1078  /* we have an errorfs attached to the am_node, free it */
1079  free_mntfs(new_mp->am_mnt);
1080  new_mp->am_mnt = 0;
1081
1082  /*
1083   * Construct a continuation
1084   */
1085  cp = ALLOC(struct continuation);
1086  cp->callout = 0;
1087  cp->mp = new_mp;
1088  cp->retry = TRUE;
1089  cp->start = clocktime(NULL);
1090  cp->mf = new_mp->am_mfarray;
1091
1092  /*
1093   * Try and mount the file system.  If this succeeds immediately (possible
1094   * for a ufs file system) then return the attributes, otherwise just
1095   * return an error.
1096   */
1097  error = amfs_bgmount(cp);
1098  reschedule_timeout_mp();
1099  if (!error)
1100    return new_mp;
1101
1102  /*
1103   * Code for quick reply.  If current_transp is set, then it's the
1104   * transp that's been passed down from nfs_program_2() or from
1105   * autofs_program_[123]().
1106   * If new_mp->am_transp is not already set, set it by copying in
1107   * current_transp.  Once am_transp is set, nfs_quick_reply() and
1108   * autofs_mount_succeeded() can use it to send a reply to the
1109   * client that requested this mount.
1110   */
1111  if (current_transp && !new_mp->am_transp) {
1112    dlog("Saving RPC transport for %s", new_mp->am_path);
1113    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1114    *(new_mp->am_transp) = *current_transp;
1115  }
1116  if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1117    new_mp->am_error = error;
1118
1119  if (new_mp->am_error > 0)
1120    assign_error_mntfs(new_mp);
1121
1122  ereturn(error);
1123}
1124
1125
1126/*
1127 * Automount interface to RPC lookup routine
1128 * Find the corresponding entry and return
1129 * the file handle for it.
1130 */
1131am_node *
1132amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1133{
1134  am_node *new_mp;
1135  mntfs **mf_array;
1136  int mp_error;
1137
1138  dlog("in amfs_generic_lookup_child");
1139
1140  *error_return = 0;
1141  new_mp = amfs_lookup_node(mp, fname, error_return);
1142
1143  /* return if we got an error */
1144  if (!new_mp || *error_return > 0)
1145    return new_mp;
1146
1147  /* also return if it's already mounted and known to be up */
1148  if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server))
1149    return new_mp;
1150
1151  switch (op) {
1152  case VLOOK_DELETE:
1153    /*
1154     * If doing a delete then don't create again!
1155     */
1156    ereturn(ENOENT);
1157  case VLOOK_LOOKUP:
1158    return new_mp;
1159  }
1160
1161  /* save error_return */
1162  mp_error = *error_return;
1163
1164  mf_array = amfs_lookup_mntfs(new_mp, error_return);
1165  if (!mf_array) {
1166    new_mp->am_error = new_mp->am_mnt->mf_error = *error_return;
1167    free_map(new_mp);
1168    return NULL;
1169  }
1170
1171  /*
1172   * Already mounted but known to be down:
1173   * check if we have any alternatives to mount
1174   */
1175  if (mp_error == 0) {
1176    mntfs **mfp;
1177    for (mfp = mf_array; *mfp; mfp++)
1178      if (*mfp != new_mp->am_mnt)
1179	break;
1180    if (*mfp != NULL) {
1181      /*
1182       * we found an alternative, so try mounting again.
1183       */
1184      *error_return = -1;
1185    } else {
1186      for (mfp = mf_array; *mfp; mfp++)
1187	free_mntfs(*mfp);
1188      XFREE(mf_array);
1189      if (new_mp->am_flags & AMF_SOFTLOOKUP) {
1190	ereturn(EIO);
1191      } else {
1192	*error_return = 0;
1193	return new_mp;
1194      }
1195    }
1196  }
1197
1198  /* store the array inside the am_node */
1199  new_mp->am_mfarray = mf_array;
1200
1201  /*
1202   * Note: while it might seem like a good idea to prioritize
1203   * the list of mntfs's we got here, it probably isn't.
1204   * It would ignore the ordering of entries specified by the user,
1205   * which is counterintuitive and confusing.
1206   */
1207  return new_mp;
1208}
1209
1210
1211void
1212amfs_generic_mounted(mntfs *mf)
1213{
1214  amfs_mkcacheref(mf);
1215}
1216
1217
1218/*
1219 * Unmount an automount sub-node
1220 */
1221int
1222amfs_generic_umount(am_node *mp, mntfs *mf)
1223{
1224  int error = 0;
1225
1226#ifdef HAVE_FS_AUTOFS
1227  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1228  if (mf->mf_flags & MFF_IS_AUTOFS)
1229    error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
1230#endif /* HAVE_FS_AUTOFS */
1231
1232  return error;
1233}
1234
1235
1236char *
1237amfs_generic_match(am_opts *fo)
1238{
1239  char *p;
1240
1241  if (!fo->opt_rfs) {
1242    plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1243    return 0;
1244  }
1245  if (!fo->opt_fs) {
1246    plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1247    return 0;
1248  }
1249
1250  /*
1251   * Swap round fs:= and rfs:= options
1252   * ... historical (jsp)
1253   */
1254  p = fo->opt_rfs;
1255  fo->opt_rfs = fo->opt_fs;
1256  fo->opt_fs = p;
1257
1258  /*
1259   * mtab entry turns out to be the name of the mount map
1260   */
1261  return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
1262}
1263