amfs_auto.c revision 119679
138494Sobrien/*
2119679Smbr * Copyright (c) 1997-2003 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *      %W% (Berkeley) %G%
4038494Sobrien *
41119679Smbr * $Id: amfs_auto.c,v 1.9.2.11 2003/07/18 04:50:18 ezk Exp $
4238494Sobrien *
4338494Sobrien */
4438494Sobrien
4538494Sobrien/*
4638494Sobrien * Automount file system
4738494Sobrien */
4838494Sobrien
4938494Sobrien#ifdef HAVE_CONFIG_H
5038494Sobrien# include <config.h>
5138494Sobrien#endif /* HAVE_CONFIG_H */
5238494Sobrien#include <am_defs.h>
5338494Sobrien#include <amd.h>
5438494Sobrien
5538494Sobrien/****************************************************************************
5638494Sobrien *** MACROS                                                               ***
5738494Sobrien ****************************************************************************/
5838494Sobrien#define	IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
5938494Sobrien
6082794Sobrien#define DOT_DOT_COOKIE	(u_int) 1
6138494Sobrien
6238494Sobrien/****************************************************************************
6338494Sobrien *** STRUCTURES                                                           ***
6438494Sobrien ****************************************************************************/
6538494Sobrien
6638494Sobrien
6738494Sobrien/****************************************************************************
6838494Sobrien *** FORWARD DEFINITIONS                                                  ***
6938494Sobrien ****************************************************************************/
7082794Sobrienstatic int amfs_auto_bgmount(struct continuation *cp, int mpe);
7138494Sobrienstatic int amfs_auto_mount(am_node *mp);
7238494Sobrienstatic int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
7338494Sobrienstatic void amfs_auto_umounted(am_node *mp);
7438494Sobrien
7538494Sobrien
7638494Sobrien/****************************************************************************
7738494Sobrien *** OPS STRUCTURES                                                       ***
7838494Sobrien ****************************************************************************/
7938494Sobrienam_ops amfs_auto_ops =
8038494Sobrien{
8138494Sobrien  "auto",
8238494Sobrien  amfs_auto_match,
8338494Sobrien  0,				/* amfs_auto_init */
8438494Sobrien  amfs_auto_mount,
8538494Sobrien  0,
8638494Sobrien  amfs_auto_umount,
8738494Sobrien  0,
8838494Sobrien  amfs_auto_lookuppn,
8938494Sobrien  amfs_auto_readdir,
9038494Sobrien  0,				/* amfs_auto_readlink */
9138494Sobrien  0,				/* amfs_auto_mounted */
9238494Sobrien  amfs_auto_umounted,
9338494Sobrien  find_amfs_auto_srvr,
9438494Sobrien  FS_AMQINFO | FS_DIRECTORY
9538494Sobrien};
9638494Sobrien
9738494Sobrien
9838494Sobrien/****************************************************************************
9938494Sobrien *** FUNCTIONS                                                             ***
10038494Sobrien ****************************************************************************/
10138494Sobrien/*
10238494Sobrien * AMFS_AUTO needs nothing in particular.
10338494Sobrien */
10438494Sobrienchar *
10538494Sobrienamfs_auto_match(am_opts *fo)
10638494Sobrien{
10738494Sobrien  char *p = fo->opt_rfs;
10838494Sobrien
10938494Sobrien  if (!fo->opt_rfs) {
11038494Sobrien    plog(XLOG_USER, "auto: no mount point named (rfs:=)");
11138494Sobrien    return 0;
11238494Sobrien  }
11338494Sobrien  if (!fo->opt_fs) {
11438494Sobrien    plog(XLOG_USER, "auto: no map named (fs:=)");
11538494Sobrien    return 0;
11638494Sobrien  }
11738494Sobrien
11838494Sobrien  /*
11938494Sobrien   * Swap round fs:= and rfs:= options
12038494Sobrien   * ... historical (jsp)
12138494Sobrien   */
12238494Sobrien  fo->opt_rfs = fo->opt_fs;
12338494Sobrien  fo->opt_fs = p;
12438494Sobrien
12538494Sobrien  /*
12638494Sobrien   * mtab entry turns out to be the name of the mount map
12738494Sobrien   */
12838494Sobrien  return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
12938494Sobrien}
13038494Sobrien
13138494Sobrien
13238494Sobrien
13338494Sobrien
13438494Sobrien/*
13538494Sobrien * Build a new map cache for this node, or re-use
13638494Sobrien * an existing cache for the same map.
13738494Sobrien */
13838494Sobrienvoid
13938494Sobrienamfs_auto_mkcacheref(mntfs *mf)
14038494Sobrien{
14138494Sobrien  char *cache;
14238494Sobrien
14338494Sobrien  if (mf->mf_fo && mf->mf_fo->opt_cache)
14438494Sobrien    cache = mf->mf_fo->opt_cache;
14538494Sobrien  else
14638494Sobrien    cache = "none";
14738494Sobrien  mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
14838494Sobrien				     mf->mf_fo->opt_maptype);
14938494Sobrien  mf->mf_prfree = mapc_free;
15038494Sobrien}
15138494Sobrien
15238494Sobrien
15338494Sobrien/*
15438494Sobrien * Mount a sub-mount
15538494Sobrien */
15638494Sobrienstatic int
15738494Sobrienamfs_auto_mount(am_node *mp)
15838494Sobrien{
15938494Sobrien  mntfs *mf = mp->am_mnt;
16038494Sobrien
16138494Sobrien  /*
16238494Sobrien   * Pseudo-directories are used to provide some structure
16338494Sobrien   * to the automounted directories instead
16438494Sobrien   * of putting them all in the top-level automount directory.
16538494Sobrien   *
16638494Sobrien   * Here, just increment the parent's link count.
16738494Sobrien   */
16838494Sobrien  mp->am_parent->am_fattr.na_nlink++;
16938494Sobrien
17038494Sobrien  /*
17138494Sobrien   * Info field of . means use parent's info field.
17238494Sobrien   * Historical - not documented.
17338494Sobrien   */
17438494Sobrien  if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
17538494Sobrien    mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
17638494Sobrien
17738494Sobrien  /*
17838494Sobrien   * Compute prefix:
17938494Sobrien   *
18038494Sobrien   * If there is an option prefix then use that else
18138494Sobrien   * If the parent had a prefix then use that with name
18238494Sobrien   *      of this node appended else
18338494Sobrien   * Use the name of this node.
18438494Sobrien   *
18538494Sobrien   * That means if you want no prefix you must say so
18638494Sobrien   * in the map.
18738494Sobrien   */
18838494Sobrien  if (mf->mf_fo->opt_pref) {
18938494Sobrien    /* allow pref:=null to set a real null prefix */
19038494Sobrien    if (STREQ(mf->mf_fo->opt_pref, "null")) {
191119679Smbr      mp->am_pref = strdup("");
19238494Sobrien    } else {
19338494Sobrien      /*
19438494Sobrien       * the prefix specified as an option
19538494Sobrien       */
19638494Sobrien      mp->am_pref = strdup(mf->mf_fo->opt_pref);
19738494Sobrien    }
19838494Sobrien  } else {
19938494Sobrien    /*
20038494Sobrien     * else the parent's prefix
20138494Sobrien     * followed by the name
20238494Sobrien     * followed by /
20338494Sobrien     */
20438494Sobrien    char *ppref = mp->am_parent->am_pref;
20538494Sobrien    if (ppref == 0)
20638494Sobrien      ppref = "";
20738494Sobrien    mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
20838494Sobrien  }
20938494Sobrien
21038494Sobrien  /*
21138494Sobrien   * Attach a map cache
21238494Sobrien   */
21338494Sobrien  amfs_auto_mkcacheref(mf);
21438494Sobrien
21538494Sobrien  return 0;
21638494Sobrien}
21738494Sobrien
21838494Sobrien
21938494Sobrien
22038494Sobrien
22138494Sobrien/*
22238494Sobrien * Unmount an automount sub-node
22338494Sobrien */
22438494Sobrienint
22538494Sobrienamfs_auto_umount(am_node *mp)
22638494Sobrien{
22738494Sobrien  return 0;
22838494Sobrien}
22938494Sobrien
23038494Sobrien
23138494Sobrien/*
23238494Sobrien * Unmount an automount node
23338494Sobrien */
23438494Sobrienstatic void
23538494Sobrienamfs_auto_umounted(am_node *mp)
23638494Sobrien{
23738494Sobrien  /*
23838494Sobrien   * If this is a pseudo-directory then just adjust the link count
23938494Sobrien   * in the parent, otherwise call the generic unmount routine
24038494Sobrien   */
24138494Sobrien  if (mp->am_parent && mp->am_parent->am_parent)
24238494Sobrien    --mp->am_parent->am_fattr.na_nlink;
24338494Sobrien}
24438494Sobrien
24538494Sobrien
24638494Sobrien/*
24738494Sobrien * Discard an old continuation
24838494Sobrien */
24938494Sobrienvoid
25038494Sobrienfree_continuation(struct continuation *cp)
25138494Sobrien{
25238494Sobrien  if (cp->callout)
25338494Sobrien    untimeout(cp->callout);
25438494Sobrien  XFREE(cp->key);
25538494Sobrien  XFREE(cp->xivec);
25638494Sobrien  XFREE(cp->info);
25738494Sobrien  XFREE(cp->auto_opts);
25838494Sobrien  XFREE(cp->def_opts);
25938494Sobrien  free_opts(&cp->fs_opts);
26038494Sobrien  XFREE(cp);
26138494Sobrien}
26238494Sobrien
26338494Sobrien
26438494Sobrien/*
26538494Sobrien * Discard the underlying mount point and replace
26638494Sobrien * with a reference to an error filesystem.
26738494Sobrien */
26838494Sobrienvoid
26938494Sobrienassign_error_mntfs(am_node *mp)
27038494Sobrien{
27138494Sobrien  if (mp->am_error > 0) {
27238494Sobrien    /*
27338494Sobrien     * Save the old error code
27438494Sobrien     */
27538494Sobrien    int error = mp->am_error;
27638494Sobrien    if (error <= 0)
27738494Sobrien      error = mp->am_mnt->mf_error;
27838494Sobrien    /*
27938494Sobrien     * Discard the old filesystem
28038494Sobrien     */
28138494Sobrien    free_mntfs(mp->am_mnt);
28238494Sobrien    /*
28338494Sobrien     * Allocate a new error reference
28438494Sobrien     */
28538494Sobrien    mp->am_mnt = new_mntfs();
28638494Sobrien    /*
28738494Sobrien     * Put back the error code
28838494Sobrien     */
28938494Sobrien    mp->am_mnt->mf_error = error;
29038494Sobrien    mp->am_mnt->mf_flags |= MFF_ERROR;
29138494Sobrien    /*
29238494Sobrien     * Zero the error in the mount point
29338494Sobrien     */
29438494Sobrien    mp->am_error = 0;
29538494Sobrien  }
29638494Sobrien}
29738494Sobrien
29838494Sobrien
29938494Sobrien/*
30038494Sobrien * The continuation function.  This is called by
30138494Sobrien * the task notifier when a background mount attempt
30238494Sobrien * completes.
30338494Sobrien */
30438494Sobrienvoid
30538494Sobrienamfs_auto_cont(int rc, int term, voidp closure)
30638494Sobrien{
30738494Sobrien  struct continuation *cp = (struct continuation *) closure;
30838494Sobrien  mntfs *mf = cp->mp->am_mnt;
30938494Sobrien
31038494Sobrien  /*
31138494Sobrien   * Definitely not trying to mount at the moment
31238494Sobrien   */
31338494Sobrien  mf->mf_flags &= ~MFF_MOUNTING;
31438494Sobrien
31538494Sobrien  /*
31638494Sobrien   * While we are mounting - try to avoid race conditions
31738494Sobrien   */
31838494Sobrien  new_ttl(cp->mp);
31938494Sobrien
32038494Sobrien  /*
32138494Sobrien   * Wakeup anything waiting for this mount
32238494Sobrien   */
32338494Sobrien  wakeup((voidp) mf);
32438494Sobrien
32538494Sobrien  /*
32638494Sobrien   * Check for termination signal or exit status...
32738494Sobrien   */
32838494Sobrien  if (rc || term) {
32938494Sobrien    am_node *xmp;
33038494Sobrien
33138494Sobrien    if (term) {
33238494Sobrien      /*
33338494Sobrien       * Not sure what to do for an error code.
33438494Sobrien       */
33538494Sobrien      mf->mf_error = EIO;	/* XXX ? */
33638494Sobrien      mf->mf_flags |= MFF_ERROR;
33738494Sobrien      plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
33838494Sobrien    } else {
33938494Sobrien      /*
34038494Sobrien       * Check for exit status...
34138494Sobrien       */
34238494Sobrien      mf->mf_error = rc;
34338494Sobrien      mf->mf_flags |= MFF_ERROR;
34438494Sobrien      errno = rc;		/* XXX */
34538494Sobrien      if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
34638494Sobrien	plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
34738494Sobrien    }
34838494Sobrien
34938494Sobrien    /*
35038494Sobrien     * If we get here then that attempt didn't work, so
35138494Sobrien     * move the info vector pointer along by one and
35238494Sobrien     * call the background mount routine again
35338494Sobrien     */
35438494Sobrien    amd_stats.d_merr++;
35538494Sobrien    cp->ivec++;
35638494Sobrien    xmp = cp->mp;
35738494Sobrien    (void) amfs_auto_bgmount(cp, 0);
35838494Sobrien    assign_error_mntfs(xmp);
35938494Sobrien  } else {
36038494Sobrien    /*
36138494Sobrien     * The mount worked.
36238494Sobrien     */
36338494Sobrien    am_mounted(cp->mp);
36438494Sobrien    free_continuation(cp);
36538494Sobrien  }
36638494Sobrien
36738494Sobrien  reschedule_timeout_mp();
36838494Sobrien}
36938494Sobrien
37038494Sobrien
37138494Sobrien/*
37238494Sobrien * Retry a mount
37338494Sobrien */
37438494Sobrienvoid
37538494Sobrienamfs_auto_retry(int rc, int term, voidp closure)
37638494Sobrien{
37738494Sobrien  struct continuation *cp = (struct continuation *) closure;
37838494Sobrien  int error = 0;
37938494Sobrien
38038494Sobrien#ifdef DEBUG
38138494Sobrien  dlog("Commencing retry for mount of %s", cp->mp->am_path);
38238494Sobrien#endif /* DEBUG */
38338494Sobrien
38438494Sobrien  new_ttl(cp->mp);
38538494Sobrien
38638494Sobrien  if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
38738494Sobrien    /*
38838494Sobrien     * The entire mount has timed out.  Set the error code and skip past all
38938494Sobrien     * the info vectors so that amfs_auto_bgmount will not have any more
39038494Sobrien     * ways to try the mount, so causing an error.
39138494Sobrien     */
39238494Sobrien    plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
39338494Sobrien    error = ETIMEDOUT;
39438494Sobrien    while (*cp->ivec)
39538494Sobrien      cp->ivec++;
39638494Sobrien    /* explicitly forbid further retries after timeout */
39738494Sobrien    cp->retry = FALSE;
39838494Sobrien  }
39938494Sobrien  if (error || !IN_PROGRESS(cp)) {
40038494Sobrien    (void) amfs_auto_bgmount(cp, error);
40138494Sobrien  }
40238494Sobrien  reschedule_timeout_mp();
40338494Sobrien}
40438494Sobrien
40538494Sobrien
40638494Sobrien/*
40738494Sobrien * Try to mount a file system.  Can be called
40838494Sobrien * directly or in a sub-process by run_task.
40938494Sobrien */
41038494Sobrienint
41138494Sobrientry_mount(voidp mvp)
41238494Sobrien{
41338494Sobrien  int error = 0;
41438494Sobrien  am_node *mp = (am_node *) mvp;
41538494Sobrien  mntfs *mf = mp->am_mnt;
41638494Sobrien
41738494Sobrien  /*
41838494Sobrien   * If the directory is not yet made and it needs to be made, then make it!
41938494Sobrien   * This may be run in a background process in which case the flag setting
42038494Sobrien   * won't be noticed later - but it is set anyway just after run_task is
42138494Sobrien   * called.  It should probably go away totally...
42238494Sobrien   */
42338494Sobrien  if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
42438494Sobrien    error = mkdirs(mf->mf_mount, 0555);
42538494Sobrien    if (!error)
42638494Sobrien      mf->mf_flags |= MFF_MKMNT;
42738494Sobrien  }
42838494Sobrien
42938494Sobrien  /*
43038494Sobrien   * Mount it!
43138494Sobrien   */
43238494Sobrien  error = mount_node(mp);
43338494Sobrien
43438494Sobrien#ifdef DEBUG
43538494Sobrien  if (error > 0) {
43638494Sobrien    errno = error;
437119679Smbr    dlog("amfs_auto: call to mount_node(%s) failed: %m", mp->am_path);
43838494Sobrien  }
43938494Sobrien#endif /* DEBUG */
44038494Sobrien
44138494Sobrien  return error;
44238494Sobrien}
44338494Sobrien
44438494Sobrien
44538494Sobrien/*
44638494Sobrien * Pick a file system to try mounting and
44738494Sobrien * do that in the background if necessary
44838494Sobrien *
44952894SobrienFor each location:
45052894Sobrien	if it is new -defaults then
45152894Sobrien		extract and process
45252894Sobrien		continue;
45352894Sobrien	fi
45452894Sobrien	if it is a cut then
45552894Sobrien		if a location has been tried then
45652894Sobrien			break;
45752894Sobrien		fi
45852894Sobrien		continue;
45952894Sobrien	fi
46052894Sobrien	parse mount location
46152894Sobrien	discard previous mount location if required
46252894Sobrien	find matching mounted filesystem
46352894Sobrien	if not applicable then
46452894Sobrien		this_error = No such file or directory
46552894Sobrien		continue
46652894Sobrien	fi
46752894Sobrien	if the filesystem failed to be mounted then
46852894Sobrien		this_error = error from filesystem
46952894Sobrien	elif the filesystem is mounting or unmounting then
47052894Sobrien		this_error = -1
47152894Sobrien	elif the fileserver is down then
47252894Sobrien		this_error = -1
47352894Sobrien	elif the filesystem is already mounted
47452894Sobrien		this_error = 0
47552894Sobrien		break
47652894Sobrien	fi
47752894Sobrien	if no error on this mount then
47852894Sobrien		this_error = initialize mount point
47952894Sobrien	fi
48052894Sobrien	if no error on this mount and mount is delayed then
48152894Sobrien		this_error = -1
48252894Sobrien	fi
48352894Sobrien	if this_error < 0 then
48452894Sobrien		retry = true
48552894Sobrien	fi
48652894Sobrien	if no error on this mount then
48752894Sobrien		make mount point if required
48852894Sobrien	fi
48952894Sobrien	if no error on this mount then
49052894Sobrien		if mount in background then
49152894Sobrien			run mount in background
49252894Sobrien			return -1
49352894Sobrien		else
49452894Sobrien			this_error = mount in foreground
49552894Sobrien		fi
49652894Sobrien	fi
49752894Sobrien	if an error occurred on this mount then
49852894Sobrien		update stats
49952894Sobrien		save error in mount point
50052894Sobrien	fi
50152894Sobrienendfor
50238494Sobrien */
50338494Sobrienstatic int
50482794Sobrienamfs_auto_bgmount(struct continuation *cp, int mpe)
50538494Sobrien{
50638494Sobrien  mntfs *mf = cp->mp->am_mnt;	/* Current mntfs */
50738494Sobrien  mntfs *mf_retry = 0;		/* First mntfs which needed retrying */
50838494Sobrien  int this_error = -1;		/* Per-mount error */
50938494Sobrien  int hard_error = -1;
51038494Sobrien  int mp_error = mpe;
51138494Sobrien
51238494Sobrien  /*
51338494Sobrien   * Try to mount each location.
51438494Sobrien   * At the end:
51538494Sobrien   * hard_error == 0 indicates something was mounted.
51638494Sobrien   * hard_error > 0 indicates everything failed with a hard error
51738494Sobrien   * hard_error < 0 indicates nothing could be mounted now
51838494Sobrien   */
51938494Sobrien  for (; this_error && *cp->ivec; cp->ivec++) {
52038494Sobrien    am_ops *p;
52138494Sobrien    am_node *mp = cp->mp;
52238494Sobrien    char *link_dir;
52338494Sobrien    int dont_retry;
52438494Sobrien
52538494Sobrien    if (hard_error < 0)
52638494Sobrien      hard_error = this_error;
52738494Sobrien
52838494Sobrien    this_error = -1;
52938494Sobrien
53038494Sobrien    if (**cp->ivec == '-') {
53138494Sobrien      /*
53238494Sobrien       * Pick up new defaults
53338494Sobrien       */
53438494Sobrien      if (cp->auto_opts && *cp->auto_opts)
53538494Sobrien	cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
53638494Sobrien      else
53738494Sobrien	cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
53838494Sobrien#ifdef DEBUG
53938494Sobrien      dlog("Setting def_opts to \"%s\"", cp->def_opts);
54038494Sobrien#endif /* DEBUG */
54138494Sobrien      continue;
54238494Sobrien    }
54338494Sobrien    /*
54438494Sobrien     * If a mount has been attempted, and we find
54538494Sobrien     * a cut then don't try any more locations.
54638494Sobrien     */
54738494Sobrien    if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
54838494Sobrien      if (cp->tried) {
54938494Sobrien#ifdef DEBUG
55038494Sobrien	dlog("Cut: not trying any more locations for %s",
55138494Sobrien	     mp->am_path);
55238494Sobrien#endif /* DEBUG */
55338494Sobrien	break;
55438494Sobrien      }
55538494Sobrien      continue;
55638494Sobrien    }
55738494Sobrien
55838494Sobrien    /* match the operators */
55938494Sobrien    p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
56038494Sobrien
56138494Sobrien    /*
56238494Sobrien     * Find a mounted filesystem for this node.
56338494Sobrien     */
56438494Sobrien    mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
56538494Sobrien				    cp->fs_opts.opt_fs,
56638494Sobrien				    cp->fs_opts.fs_mtab,
56738494Sobrien				    cp->auto_opts,
56838494Sobrien				    cp->fs_opts.opt_opts,
56938494Sobrien				    cp->fs_opts.opt_remopts);
57038494Sobrien
57138494Sobrien    p = mf->mf_ops;
57238494Sobrien#ifdef DEBUG
57338494Sobrien    dlog("Got a hit with %s", p->fs_type);
57438494Sobrien#endif /* DEBUG */
57538494Sobrien
57638494Sobrien    /*
57738494Sobrien     * Note whether this is a real mount attempt
57838494Sobrien     */
57938494Sobrien    if (p == &amfs_error_ops) {
58082794Sobrien      plog(XLOG_MAP, "Map entry %s for %s did not match", *cp->ivec, mp->am_path);
58138494Sobrien      if (this_error <= 0)
58238494Sobrien	this_error = ENOENT;
58338494Sobrien      continue;
58438494Sobrien    } else {
58538494Sobrien      if (cp->fs_opts.fs_mtab) {
58638494Sobrien	plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
58738494Sobrien	     cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
58838494Sobrien      }
58938494Sobrien      cp->tried = TRUE;
59038494Sobrien    }
59138494Sobrien
59238494Sobrien    this_error = 0;
59338494Sobrien    dont_retry = FALSE;
59438494Sobrien
59538494Sobrien    if (mp->am_link) {
59638494Sobrien      XFREE(mp->am_link);
59738494Sobrien      mp->am_link = 0;
59838494Sobrien    }
59938494Sobrien    link_dir = mf->mf_fo->opt_sublink;
60038494Sobrien
60138494Sobrien    if (link_dir && *link_dir) {
60238494Sobrien      if (*link_dir == '/') {
60338494Sobrien	mp->am_link = strdup(link_dir);
60438494Sobrien      } else {
60538494Sobrien	/*
60682794Sobrien	 * Try getting fs option from continuation, not mountpoint!
60738494Sobrien	 * Don't try logging the string from mf, since it may be bad!
60838494Sobrien	 */
60938494Sobrien	if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
61051292Sobrien	  plog(XLOG_ERROR, "use %s instead of 0x%lx",
61151292Sobrien	       cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs);
61238494Sobrien
61338494Sobrien	mp->am_link = str3cat((char *) 0,
61438494Sobrien			      cp->fs_opts.opt_fs, "/", link_dir);
61538494Sobrien
61638494Sobrien	normalize_slash(mp->am_link);
61738494Sobrien      }
61838494Sobrien    }
61938494Sobrien
62038494Sobrien    if (mf->mf_error > 0) {
62138494Sobrien      this_error = mf->mf_error;
62238494Sobrien    } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
62338494Sobrien      /*
62438494Sobrien       * Still mounting - retry later
62538494Sobrien       */
62638494Sobrien#ifdef DEBUG
62738494Sobrien      dlog("Duplicate pending mount fstype %s", p->fs_type);
62838494Sobrien#endif /* DEBUG */
62938494Sobrien      this_error = -1;
63038494Sobrien    } else if (FSRV_ISDOWN(mf->mf_server)) {
63138494Sobrien      /*
63238494Sobrien       * Would just mount from the same place
63338494Sobrien       * as a hung mount - so give up
63438494Sobrien       */
63538494Sobrien#ifdef DEBUG
63638494Sobrien      dlog("%s is already hung - giving up", mf->mf_mount);
63738494Sobrien#endif /* DEBUG */
63838494Sobrien      mp_error = EWOULDBLOCK;
63938494Sobrien      dont_retry = TRUE;
64038494Sobrien      this_error = -1;
64138494Sobrien    } else if (mf->mf_flags & MFF_MOUNTED) {
64238494Sobrien#ifdef DEBUG
64338494Sobrien      dlog("duplicate mount of \"%s\" ...", mf->mf_info);
64438494Sobrien#endif /* DEBUG */
64538494Sobrien
64638494Sobrien      /*
64738494Sobrien       * Just call mounted()
64838494Sobrien       */
64938494Sobrien      am_mounted(mp);
65038494Sobrien
65138494Sobrien      this_error = 0;
65238494Sobrien      break;
65338494Sobrien    }
65438494Sobrien
65538494Sobrien    /*
65638494Sobrien     * Will usually need to play around with the mount nodes
65738494Sobrien     * file attribute structure.  This must be done here.
65842629Sobrien     * Try and get things initialized, even if the fileserver
65938494Sobrien     * is not known to be up.  In the common case this will
66038494Sobrien     * progress things faster.
66138494Sobrien     */
66238494Sobrien    if (!this_error) {
66338494Sobrien      /*
66438494Sobrien       * Fill in attribute fields.
66538494Sobrien       */
66638494Sobrien      if (mf->mf_ops->fs_flags & FS_DIRECTORY)
66738494Sobrien	mk_fattr(mp, NFDIR);
66838494Sobrien      else
66938494Sobrien	mk_fattr(mp, NFLNK);
67038494Sobrien
67138494Sobrien      if (p->fs_init)
67238494Sobrien	this_error = (*p->fs_init) (mf);
67338494Sobrien    }
67438494Sobrien
67538494Sobrien    /*
67638494Sobrien     * Make sure the fileserver is UP before doing any more work
67738494Sobrien     */
67838494Sobrien    if (!FSRV_ISUP(mf->mf_server)) {
67938494Sobrien#ifdef DEBUG
68038494Sobrien      dlog("waiting for server %s to become available", mf->mf_server->fs_host);
68138494Sobrien#endif /* DEBUG */
68238494Sobrien      this_error = -1;
68338494Sobrien    }
68438494Sobrien
68538494Sobrien    if (!this_error && mf->mf_fo->opt_delay) {
68638494Sobrien      /*
68738494Sobrien       * If there is a delay timer on the mount
68838494Sobrien       * then don't try to mount if the timer
68938494Sobrien       * has not expired.
69038494Sobrien       */
69138494Sobrien      int i = atoi(mf->mf_fo->opt_delay);
69238494Sobrien      if (i > 0 && clocktime() < (cp->start + i)) {
69338494Sobrien#ifdef DEBUG
69451292Sobrien	dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start));
69538494Sobrien#endif /* DEBUG */
69638494Sobrien	this_error = -1;
69738494Sobrien      }
69838494Sobrien    }
69938494Sobrien
70038494Sobrien    if (this_error < 0 && !dont_retry) {
70138494Sobrien      if (!mf_retry)
70238494Sobrien	mf_retry = dup_mntfs(mf);
70338494Sobrien      cp->retry = TRUE;
70452894Sobrien#ifdef DEBUG
70552894Sobrien      dlog("will retry ...\n");
70652894Sobrien#endif /* DEBUG */
70752894Sobrien      break;
70838494Sobrien    }
70938494Sobrien
71038494Sobrien    if (!this_error) {
71138494Sobrien      if (p->fs_flags & FS_MBACKGROUND) {
71238494Sobrien	mf->mf_flags |= MFF_MOUNTING;	/* XXX */
71338494Sobrien#ifdef DEBUG
71438494Sobrien	dlog("backgrounding mount of \"%s\"", mf->mf_mount);
71538494Sobrien#endif /* DEBUG */
71638494Sobrien	if (cp->callout) {
71738494Sobrien	  untimeout(cp->callout);
71838494Sobrien	  cp->callout = 0;
71938494Sobrien	}
72082794Sobrien
72182794Sobrien	/* actually run the task, backgrounding as necessary */
72238494Sobrien	run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
72382794Sobrien
72438494Sobrien	mf->mf_flags |= MFF_MKMNT;	/* XXX */
72538494Sobrien	if (mf_retry)
72638494Sobrien	  free_mntfs(mf_retry);
72738494Sobrien	return -1;
72838494Sobrien      } else {
72938494Sobrien#ifdef DEBUG
73038494Sobrien	dlog("foreground mount of \"%s\" ...", mf->mf_info);
73138494Sobrien#endif /* DEBUG */
73238494Sobrien	this_error = try_mount((voidp) mp);
73338494Sobrien	if (this_error < 0) {
73438494Sobrien	  if (!mf_retry)
73538494Sobrien	    mf_retry = dup_mntfs(mf);
73638494Sobrien	  cp->retry = TRUE;
73738494Sobrien	}
73838494Sobrien      }
73938494Sobrien    }
74038494Sobrien
74138494Sobrien    if (this_error >= 0) {
74238494Sobrien      if (this_error > 0) {
74338494Sobrien	amd_stats.d_merr++;
74438494Sobrien	if (mf != mf_retry) {
74538494Sobrien	  mf->mf_error = this_error;
74638494Sobrien	  mf->mf_flags |= MFF_ERROR;
74738494Sobrien	}
74838494Sobrien      }
74938494Sobrien
75038494Sobrien      /*
75138494Sobrien       * Wakeup anything waiting for this mount
75238494Sobrien       */
75338494Sobrien      wakeup((voidp) mf);
75438494Sobrien    }
75538494Sobrien  }
75638494Sobrien
75738494Sobrien  if (this_error && cp->retry) {
75838494Sobrien    free_mntfs(mf);
75938494Sobrien    mf = cp->mp->am_mnt = mf_retry;
76038494Sobrien    /*
76138494Sobrien     * Not retrying again (so far)
76238494Sobrien     */
76338494Sobrien    cp->retry = FALSE;
76438494Sobrien    cp->tried = FALSE;
76538494Sobrien    /*
76638494Sobrien     * Start at the beginning.
76738494Sobrien     * Rewind the location vector and
76838494Sobrien     * reset the default options.
76938494Sobrien     */
77052894Sobrien#ifdef DEBUG
77152894Sobrien    dlog("(skipping rewind)\n");
77252894Sobrien#endif /* DEBUG */
77338494Sobrien    /*
77438494Sobrien     * Arrange that amfs_auto_bgmount is called
77538494Sobrien     * after anything else happens.
77638494Sobrien     */
77738494Sobrien#ifdef DEBUG
77838494Sobrien    dlog("Arranging to retry mount of %s", cp->mp->am_path);
77938494Sobrien#endif /* DEBUG */
78038494Sobrien    sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
78138494Sobrien    if (cp->callout)
78238494Sobrien      untimeout(cp->callout);
78338494Sobrien    cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
78438494Sobrien
78538494Sobrien    cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
78638494Sobrien
78738494Sobrien    /*
78838494Sobrien     * Not done yet - so don't return anything
78938494Sobrien     */
79038494Sobrien    return -1;
79138494Sobrien  }
79238494Sobrien
79338494Sobrien  if (hard_error < 0 || this_error == 0)
79438494Sobrien    hard_error = this_error;
79538494Sobrien
79638494Sobrien  /*
79738494Sobrien   * Discard handle on duff filesystem.
79838494Sobrien   * This should never happen since it
79938494Sobrien   * should be caught by the case above.
80038494Sobrien   */
80138494Sobrien  if (mf_retry) {
80238494Sobrien    if (hard_error)
80338494Sobrien      plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
80438494Sobrien    free_mntfs(mf_retry);
80538494Sobrien  }
80638494Sobrien
80738494Sobrien  /*
80838494Sobrien   * If we get here, then either the mount succeeded or
80938494Sobrien   * there is no more mount information available.
81038494Sobrien   */
81138494Sobrien  if (hard_error < 0 && mp_error)
81238494Sobrien    hard_error = cp->mp->am_error = mp_error;
81338494Sobrien  if (hard_error > 0) {
81438494Sobrien    /*
81538494Sobrien     * Set a small(ish) timeout on an error node if
81638494Sobrien     * the error was not a time out.
81738494Sobrien     */
81838494Sobrien    switch (hard_error) {
81938494Sobrien    case ETIMEDOUT:
82038494Sobrien    case EWOULDBLOCK:
82138494Sobrien      cp->mp->am_timeo = 17;
82238494Sobrien      break;
82338494Sobrien    default:
82438494Sobrien      cp->mp->am_timeo = 5;
82538494Sobrien      break;
82638494Sobrien    }
82738494Sobrien    new_ttl(cp->mp);
82838494Sobrien  }
82938494Sobrien
83038494Sobrien  /*
83138494Sobrien   * Make sure that the error value in the mntfs has a
83238494Sobrien   * reasonable value.
83338494Sobrien   */
83438494Sobrien  if (mf->mf_error < 0) {
83538494Sobrien    mf->mf_error = hard_error;
83638494Sobrien    if (hard_error)
83738494Sobrien      mf->mf_flags |= MFF_ERROR;
83838494Sobrien  }
83938494Sobrien
84038494Sobrien  /*
84138494Sobrien   * In any case we don't need the continuation any more
84238494Sobrien   */
84338494Sobrien  free_continuation(cp);
84438494Sobrien
84538494Sobrien  return hard_error;
84638494Sobrien}
84738494Sobrien
84838494Sobrien
84938494Sobrien/*
85038494Sobrien * Automount interface to RPC lookup routine
85138494Sobrien * Find the corresponding entry and return
85238494Sobrien * the file handle for it.
85338494Sobrien */
85438494Sobrienam_node *
85538494Sobrienamfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
85638494Sobrien{
85738494Sobrien  am_node *ap, *new_mp, *ap_hung;
85838494Sobrien  char *info;			/* Mount info - where to get the file system */
85938494Sobrien  char **ivec, **xivec;		/* Split version of info */
86038494Sobrien  char *auto_opts;		/* Automount options */
86138494Sobrien  int error = 0;		/* Error so far */
86238494Sobrien  char path_name[MAXPATHLEN];	/* General path name buffer */
86338494Sobrien  char *pfname;			/* Path for database lookup */
86438494Sobrien  struct continuation *cp;	/* Continuation structure if need to mount */
86538494Sobrien  int in_progress = 0;		/* # of (un)mount in progress */
86638494Sobrien  char *dflts;
86738494Sobrien  mntfs *mf;
86838494Sobrien
86938494Sobrien#ifdef DEBUG
87038494Sobrien  dlog("in amfs_auto_lookuppn");
87138494Sobrien#endif /* DEBUG */
87238494Sobrien
87338494Sobrien  /*
87438494Sobrien   * If the server is shutting down
87538494Sobrien   * then don't return information
87638494Sobrien   * about the mount point.
87738494Sobrien   */
87838494Sobrien  if (amd_state == Finishing) {
87938494Sobrien#ifdef DEBUG
88038494Sobrien    if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
88138494Sobrien      dlog("%s mount ignored - going down", fname);
88238494Sobrien    } else {
88338494Sobrien      dlog("%s/%s mount ignored - going down", mp->am_path, fname);
88438494Sobrien    }
88538494Sobrien#endif /* DEBUG */
88638494Sobrien    ereturn(ENOENT);
88738494Sobrien  }
88838494Sobrien
88938494Sobrien  /*
89038494Sobrien   * Handle special case of "." and ".."
89138494Sobrien   */
89238494Sobrien  if (fname[0] == '.') {
89338494Sobrien    if (fname[1] == '\0')
89438494Sobrien      return mp;		/* "." is the current node */
89538494Sobrien    if (fname[1] == '.' && fname[2] == '\0') {
89638494Sobrien      if (mp->am_parent) {
89738494Sobrien#ifdef DEBUG
89838494Sobrien	dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
89938494Sobrien#endif /* DEBUG */
90038494Sobrien	return mp->am_parent;	/* ".." is the parent node */
90138494Sobrien      }
90238494Sobrien      ereturn(ESTALE);
90338494Sobrien    }
90438494Sobrien  }
90538494Sobrien
90638494Sobrien  /*
90738494Sobrien   * Check for valid key name.
90838494Sobrien   * If it is invalid then pretend it doesn't exist.
90938494Sobrien   */
91038494Sobrien  if (!valid_key(fname)) {
91138494Sobrien    plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
91238494Sobrien    ereturn(ENOENT);
91338494Sobrien  }
91438494Sobrien
91538494Sobrien  /*
91638494Sobrien   * Expand key name.
91738494Sobrien   * fname is now a private copy.
91838494Sobrien   */
91938494Sobrien  fname = expand_key(fname);
92038494Sobrien
92138494Sobrien  for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
92238494Sobrien    /*
92338494Sobrien     * Otherwise search children of this node
92438494Sobrien     */
92538494Sobrien    if (FSTREQ(ap->am_name, fname)) {
92638494Sobrien      mf = ap->am_mnt;
92738494Sobrien      if (ap->am_error) {
92838494Sobrien	error = ap->am_error;
92938494Sobrien	continue;
93038494Sobrien      }
93138494Sobrien      /*
93238494Sobrien       * If the error code is undefined then it must be
93338494Sobrien       * in progress.
93438494Sobrien       */
93538494Sobrien      if (mf->mf_error < 0)
93638494Sobrien	goto in_progrss;
93738494Sobrien
93838494Sobrien      /*
93938494Sobrien       * Check for a hung node
94038494Sobrien       */
94138494Sobrien      if (FSRV_ISDOWN(mf->mf_server)) {
94238494Sobrien#ifdef DEBUG
94338494Sobrien	dlog("server hung");
94438494Sobrien#endif /* DEBUG */
94538494Sobrien	error = ap->am_error;
94638494Sobrien	ap_hung = ap;
94738494Sobrien	continue;
94838494Sobrien      }
94938494Sobrien      /*
95038494Sobrien       * If there was a previous error with this node
95138494Sobrien       * then return that error code.
95238494Sobrien       */
95338494Sobrien      if (mf->mf_flags & MFF_ERROR) {
95438494Sobrien	error = mf->mf_error;
95538494Sobrien	continue;
95638494Sobrien      }
95738494Sobrien      if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
95838494Sobrien      in_progrss:
95938494Sobrien	/*
96038494Sobrien	 * If the fs is not mounted or it is unmounting then there
96138494Sobrien	 * is a background (un)mount in progress.  In this case
96238494Sobrien	 * we just drop the RPC request (return nil) and
96338494Sobrien	 * wait for a retry, by which time the (un)mount may
96438494Sobrien	 * have completed.
96538494Sobrien	 */
96638494Sobrien#ifdef DEBUG
96738494Sobrien	dlog("ignoring mount of %s in %s -- flags (%x) in progress",
96838494Sobrien	     fname, mf->mf_mount, mf->mf_flags);
96938494Sobrien#endif /* DEBUG */
97038494Sobrien	in_progress++;
97138494Sobrien	continue;
97238494Sobrien      }
97338494Sobrien
97438494Sobrien      /*
97538494Sobrien       * Otherwise we have a hit: return the current mount point.
97638494Sobrien       */
97738494Sobrien#ifdef DEBUG
97838494Sobrien      dlog("matched %s in %s", fname, ap->am_path);
97938494Sobrien#endif /* DEBUG */
98038494Sobrien      XFREE(fname);
98138494Sobrien      return ap;
98238494Sobrien    }
98338494Sobrien  }
98438494Sobrien
98538494Sobrien  if (in_progress) {
98638494Sobrien#ifdef DEBUG
98738494Sobrien    dlog("Waiting while %d mount(s) in progress", in_progress);
98838494Sobrien#endif /* DEBUG */
98938494Sobrien    XFREE(fname);
99038494Sobrien    ereturn(-1);
99138494Sobrien  }
99238494Sobrien
99338494Sobrien  /*
99442629Sobrien   * If an error occurred then return it.
99538494Sobrien   */
99638494Sobrien  if (error) {
99738494Sobrien#ifdef DEBUG
99838494Sobrien    errno = error;		/* XXX */
99951292Sobrien    dlog("Returning error: %m");
100038494Sobrien#endif /* DEBUG */
100138494Sobrien    XFREE(fname);
100238494Sobrien    ereturn(error);
100338494Sobrien  }
100438494Sobrien
100538494Sobrien  /*
100638494Sobrien   * If doing a delete then don't create again!
100738494Sobrien   */
100838494Sobrien  switch (op) {
100938494Sobrien  case VLOOK_DELETE:
101038494Sobrien    ereturn(ENOENT);
101138494Sobrien
101238494Sobrien  case VLOOK_CREATE:
101338494Sobrien    break;
101438494Sobrien
101538494Sobrien  default:
101638494Sobrien    plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
101738494Sobrien    ereturn(EINVAL);
101838494Sobrien  }
101938494Sobrien
102038494Sobrien  /*
102138494Sobrien   * If the server is going down then just return,
102238494Sobrien   * don't try to mount any more file systems
102338494Sobrien   */
102438494Sobrien  if ((int) amd_state >= (int) Finishing) {
102538494Sobrien#ifdef DEBUG
102638494Sobrien    dlog("not found - server going down anyway");
102738494Sobrien#endif /* DEBUG */
102838494Sobrien    XFREE(fname);
102938494Sobrien    ereturn(ENOENT);
103038494Sobrien  }
103138494Sobrien
103238494Sobrien  /*
103338494Sobrien   * If we get there then this is a reference to an,
103438494Sobrien   * as yet, unknown name so we need to search the mount
103538494Sobrien   * map for it.
103638494Sobrien   */
103738494Sobrien  if (mp->am_pref) {
103838494Sobrien    sprintf(path_name, "%s%s", mp->am_pref, fname);
103938494Sobrien    pfname = path_name;
104038494Sobrien  } else {
104138494Sobrien    pfname = fname;
104238494Sobrien  }
104338494Sobrien
104438494Sobrien  mf = mp->am_mnt;
104538494Sobrien
104638494Sobrien#ifdef DEBUG
104738494Sobrien  dlog("will search map info in %s to find %s", mf->mf_info, pfname);
104838494Sobrien#endif /* DEBUG */
104938494Sobrien  /*
105038494Sobrien   * Consult the oracle for some mount information.
105138494Sobrien   * info is malloc'ed and belongs to this routine.
105238494Sobrien   * It ends up being free'd in free_continuation().
105338494Sobrien   *
105438494Sobrien   * Note that this may return -1 indicating that information
105538494Sobrien   * is not yet available.
105638494Sobrien   */
105738494Sobrien  error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
105838494Sobrien  if (error) {
105938494Sobrien    if (error > 0)
106038494Sobrien      plog(XLOG_MAP, "No map entry for %s", pfname);
106138494Sobrien    else
106238494Sobrien      plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
106338494Sobrien    XFREE(fname);
106438494Sobrien    ereturn(error);
106538494Sobrien  }
106638494Sobrien#ifdef DEBUG
106738494Sobrien  dlog("mount info is %s", info);
106838494Sobrien#endif /* DEBUG */
106938494Sobrien
107038494Sobrien  /*
107138494Sobrien   * Split info into an argument vector.
107238494Sobrien   * The vector is malloc'ed and belongs to
107338494Sobrien   * this routine.  It is free'd in free_continuation()
107438494Sobrien   */
107538494Sobrien  xivec = ivec = strsplit(info, ' ', '\"');
107638494Sobrien
107738494Sobrien  /*
107838494Sobrien   * Default error code...
107938494Sobrien   */
108038494Sobrien  if (ap_hung)
108138494Sobrien    error = EWOULDBLOCK;
108238494Sobrien  else
108338494Sobrien    error = ENOENT;
108438494Sobrien
108538494Sobrien  /*
108638494Sobrien   * Allocate a new map
108738494Sobrien   */
108838494Sobrien  new_mp = exported_ap_alloc();
108938494Sobrien  if (new_mp == 0) {
109038494Sobrien    XFREE(xivec);
109138494Sobrien    XFREE(info);
109238494Sobrien    XFREE(fname);
109338494Sobrien    ereturn(ENOSPC);
109438494Sobrien  }
109538494Sobrien  if (mf->mf_auto)
109638494Sobrien    auto_opts = mf->mf_auto;
109738494Sobrien  else
109838494Sobrien    auto_opts = "";
109938494Sobrien
110038494Sobrien  auto_opts = strdup(auto_opts);
110138494Sobrien
110238494Sobrien#ifdef DEBUG
110338494Sobrien  dlog("searching for /defaults entry");
110438494Sobrien#endif /* DEBUG */
110538494Sobrien  if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
110638494Sobrien    char *dfl;
110738494Sobrien    char **rvec;
110838494Sobrien#ifdef DEBUG
110938494Sobrien    dlog("/defaults gave %s", dflts);
111038494Sobrien#endif /* DEBUG */
111138494Sobrien    if (*dflts == '-')
111238494Sobrien      dfl = dflts + 1;
111338494Sobrien    else
111438494Sobrien      dfl = dflts;
111538494Sobrien
111638494Sobrien    /*
111738494Sobrien     * Chop the defaults up
111838494Sobrien     */
111938494Sobrien    rvec = strsplit(dfl, ' ', '\"');
112038494Sobrien
112182794Sobrien    if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
112238494Sobrien      /*
112338494Sobrien       * Pick whichever first entry matched the list of selectors.
112438494Sobrien       * Strip the selectors from the string, and assign to dfl the
112538494Sobrien       * rest of the string.
112638494Sobrien       */
112738494Sobrien      if (rvec) {
112838494Sobrien	am_opts ap;
112938494Sobrien	am_ops *pt;
113038494Sobrien	char **sp = rvec;
113138494Sobrien	while (*sp) {		/* loop until you find something, if any */
113238494Sobrien	  memset((char *) &ap, 0, sizeof(am_opts));
113382794Sobrien	  /*
113482794Sobrien	   * This next routine cause many spurious "expansion of ... is"
113582794Sobrien	   * messages, which are ignored, b/c all we need out of this
113682794Sobrien	   * routine is to match selectors.  These spurious messages may
113782794Sobrien	   * be wrong, esp. if they try to expand ${key} b/c it will
113882794Sobrien	   * get expanded to "/defaults"
113982794Sobrien	   */
114038494Sobrien	  pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
114138494Sobrien			 mp->am_parent->am_mnt->mf_info);
114241142Sobrien	  free_opts(&ap);	/* don't leak */
114338494Sobrien	  if (pt == &amfs_error_ops) {
114482794Sobrien	    plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
114538494Sobrien	  } else {
114638494Sobrien	    dfl = strip_selectors(*sp, "/defaults");
114738494Sobrien	    plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
114838494Sobrien	    break;
114938494Sobrien	  }
115038494Sobrien	  ++sp;
115138494Sobrien	}
115238494Sobrien      }
115338494Sobrien    } else {			/* not enable_default_selectors */
115438494Sobrien      /*
115538494Sobrien       * Extract first value
115638494Sobrien       */
115738494Sobrien      dfl = rvec[0];
115838494Sobrien    }
115938494Sobrien
116038494Sobrien    /*
116138494Sobrien     * If there were any values at all...
116238494Sobrien     */
116338494Sobrien    if (dfl) {
116438494Sobrien      /*
116538494Sobrien       * Log error if there were other values
116638494Sobrien       */
116782794Sobrien      if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
116838494Sobrien# ifdef DEBUG
116938494Sobrien	dlog("/defaults chopped into %s", dfl);
117038494Sobrien# endif /* DEBUG */
117138494Sobrien	plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
117238494Sobrien      }
117338494Sobrien
117438494Sobrien      /*
117538494Sobrien       * Prepend to existing defaults if they exist,
117638494Sobrien       * otherwise just use these defaults.
117738494Sobrien       */
117838494Sobrien      if (*auto_opts && *dfl) {
117938494Sobrien	char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
118038494Sobrien	sprintf(nopts, "%s;%s", dfl, auto_opts);
118138494Sobrien	XFREE(auto_opts);
118238494Sobrien	auto_opts = nopts;
118338494Sobrien      } else if (*dfl) {
118438494Sobrien	auto_opts = strealloc(auto_opts, dfl);
118538494Sobrien      }
118638494Sobrien    }
118738494Sobrien    XFREE(dflts);
118838494Sobrien    /*
118938494Sobrien     * Don't need info vector any more
119038494Sobrien     */
119138494Sobrien    XFREE(rvec);
119238494Sobrien  }
119338494Sobrien
119438494Sobrien  /*
119538494Sobrien   * Fill it in
119638494Sobrien   */
119738494Sobrien  init_map(new_mp, fname);
119838494Sobrien
119938494Sobrien  /*
120038494Sobrien   * Put it in the table
120138494Sobrien   */
120238494Sobrien  insert_am(new_mp, mp);
120338494Sobrien
120438494Sobrien  /*
120538494Sobrien   * Fill in some other fields,
120638494Sobrien   * path and mount point.
120738494Sobrien   *
120838494Sobrien   * bugfix: do not prepend old am_path if direct map
120938494Sobrien   *         <wls@astro.umd.edu> William Sebok
121038494Sobrien   */
121138494Sobrien  new_mp->am_path = str3cat(new_mp->am_path,
121238494Sobrien			    mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
121338494Sobrien			    *fname == '/' ? "" : "/", fname);
121438494Sobrien
121538494Sobrien#ifdef DEBUG
121638494Sobrien  dlog("setting path to %s", new_mp->am_path);
121738494Sobrien#endif /* DEBUG */
121838494Sobrien
121938494Sobrien  /*
122038494Sobrien   * Take private copy of pfname
122138494Sobrien   */
122238494Sobrien  pfname = strdup(pfname);
122338494Sobrien
122438494Sobrien  /*
122538494Sobrien   * Construct a continuation
122638494Sobrien   */
122738494Sobrien  cp = ALLOC(struct continuation);
122838494Sobrien  cp->callout = 0;
122938494Sobrien  cp->mp = new_mp;
123038494Sobrien  cp->xivec = xivec;
123138494Sobrien  cp->ivec = ivec;
123238494Sobrien  cp->info = info;
123338494Sobrien  cp->key = pfname;
123438494Sobrien  cp->auto_opts = auto_opts;
123538494Sobrien  cp->retry = FALSE;
123638494Sobrien  cp->tried = FALSE;
123738494Sobrien  cp->start = clocktime();
123838494Sobrien  cp->def_opts = strdup(auto_opts);
123938494Sobrien  memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
124038494Sobrien
124138494Sobrien  /*
124238494Sobrien   * Try and mount the file system.  If this succeeds immediately (possible
124338494Sobrien   * for a ufs file system) then return the attributes, otherwise just
124438494Sobrien   * return an error.
124538494Sobrien   */
124638494Sobrien  error = amfs_auto_bgmount(cp, error);
124738494Sobrien  reschedule_timeout_mp();
124838494Sobrien  if (!error) {
124938494Sobrien    XFREE(fname);
125038494Sobrien    return new_mp;
125138494Sobrien  }
125238494Sobrien
125338494Sobrien  /*
125438494Sobrien   * Code for quick reply.  If nfs_program_2_transp is set, then
125538494Sobrien   * its the transp that's been passed down from nfs_program_2().
125638494Sobrien   * If new_mp->am_transp is not already set, set it by copying in
125738494Sobrien   * nfs_program_2_transp.  Once am_transp is set, quick_reply() can
125838494Sobrien   * use it to send a reply to the client that requested this mount.
125938494Sobrien   */
126038494Sobrien  if (nfs_program_2_transp && !new_mp->am_transp) {
126138494Sobrien    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
126238494Sobrien    *(new_mp->am_transp) = *nfs_program_2_transp;
126338494Sobrien  }
126438494Sobrien  if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
126538494Sobrien    new_mp->am_error = error;
126638494Sobrien
126738494Sobrien  assign_error_mntfs(new_mp);
126838494Sobrien
126938494Sobrien  XFREE(fname);
127038494Sobrien
127138494Sobrien  ereturn(error);
127238494Sobrien}
127338494Sobrien
127438494Sobrien
127538494Sobrien/*
127638494Sobrien * Locate next node in sibling list which is mounted
127738494Sobrien * and is not an error node.
127838494Sobrien */
127938494Sobrienam_node *
128038494Sobriennext_nonerror_node(am_node *xp)
128138494Sobrien{
128238494Sobrien  mntfs *mf;
128338494Sobrien
128438494Sobrien  /*
128538494Sobrien   * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
128638494Sobrien   * Fixes a race condition when mounting direct automounts.
128738494Sobrien   * Also fixes a problem when doing a readdir on a directory
128838494Sobrien   * containing hung automounts.
128938494Sobrien   */
129038494Sobrien  while (xp &&
129138494Sobrien	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
129238494Sobrien	  mf->mf_error != 0 ||	/* There was a mntfs error */
129338494Sobrien	  xp->am_error != 0 ||	/* There was a mount error */
129438494Sobrien	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
129538494Sobrien	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
129638494Sobrien	 )
129738494Sobrien    xp = xp->am_osib;
129838494Sobrien
129938494Sobrien  return xp;
130038494Sobrien}
130138494Sobrien
130238494Sobrien
130338494Sobrien/*
130438494Sobrien * This readdir function which call a special version of it that allows
130538494Sobrien * browsing if browsable_dirs=yes was set on the map.
130638494Sobrien */
130738494Sobrienint
130838494Sobrienamfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
130938494Sobrien{
131038494Sobrien  u_int gen = *(u_int *) cookie;
131138494Sobrien  am_node *xp;
131238494Sobrien  mntent_t mnt;
131382794Sobrien#ifdef DEBUG
131482794Sobrien  nfsentry *ne;
131582794Sobrien  static int j;
131682794Sobrien#endif /* DEBUG */
131738494Sobrien
131838494Sobrien  dp->dl_eof = FALSE;		/* assume readdir not done */
131938494Sobrien
132038494Sobrien  /* check if map is browsable */
132138494Sobrien  if (mp->am_mnt && mp->am_mnt->mf_mopts) {
132238494Sobrien    mnt.mnt_opts = mp->am_mnt->mf_mopts;
132338494Sobrien    if (hasmntopt(&mnt, "fullybrowsable"))
132438494Sobrien      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
132538494Sobrien    if (hasmntopt(&mnt, "browsable"))
132638494Sobrien      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
132738494Sobrien  }
132838494Sobrien
132982794Sobrien  /* when gen is 0, we start reading from the beginning of the directory */
133038494Sobrien  if (gen == 0) {
133138494Sobrien    /*
133238494Sobrien     * In the default instance (which is used to start a search) we return
133338494Sobrien     * "." and "..".
133438494Sobrien     *
133538494Sobrien     * This assumes that the count is big enough to allow both "." and ".."
133638494Sobrien     * to be returned in a single packet.  If it isn't (which would be
133738494Sobrien     * fairly unbelievable) then tough.
133838494Sobrien     */
133938494Sobrien#ifdef DEBUG
134051292Sobrien    dlog("amfs_auto_readdir: default search");
134138494Sobrien#endif /* DEBUG */
134238494Sobrien    /*
134338494Sobrien     * Check for enough room.  This is extremely approximate but is more
134438494Sobrien     * than enough space.  Really need 2 times:
134538494Sobrien     *      4byte fileid
134638494Sobrien     *      4byte cookie
134738494Sobrien     *      4byte name length
134838494Sobrien     *      4byte name
134938494Sobrien     * plus the dirlist structure */
135038494Sobrien    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
135138494Sobrien      return EINVAL;
135238494Sobrien
135338494Sobrien    xp = next_nonerror_node(mp->am_child);
135438494Sobrien    dp->dl_entries = ep;
135538494Sobrien
135638494Sobrien    /* construct "." */
135738494Sobrien    ep[0].ne_fileid = mp->am_gen;
135838494Sobrien    ep[0].ne_name = ".";
135938494Sobrien    ep[0].ne_nextentry = &ep[1];
136038494Sobrien    *(u_int *) ep[0].ne_cookie = 0;
136138494Sobrien
136238494Sobrien    /* construct ".." */
136338494Sobrien    if (mp->am_parent)
136438494Sobrien      ep[1].ne_fileid = mp->am_parent->am_gen;
136538494Sobrien    else
136638494Sobrien      ep[1].ne_fileid = mp->am_gen;
136738494Sobrien    ep[1].ne_name = "..";
136838494Sobrien    ep[1].ne_nextentry = 0;
136951292Sobrien    *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE);
137038494Sobrien
137138494Sobrien    if (!xp)
137238494Sobrien      dp->dl_eof = TRUE;	/* by default assume readdir done */
137338494Sobrien
137482794Sobrien#ifdef DEBUG
137582794Sobrien    amuDebug(D_READDIR)
137682794Sobrien      for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
137782794Sobrien	plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d",
137882794Sobrien	     j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
137982794Sobrien#endif /* DEBUG */
138038494Sobrien    return 0;
138138494Sobrien  }
138238494Sobrien#ifdef DEBUG
138351292Sobrien  dlog("amfs_auto_readdir: real child");
138438494Sobrien#endif /* DEBUG */
138538494Sobrien
138651292Sobrien  if (gen == DOT_DOT_COOKIE) {
138738494Sobrien#ifdef DEBUG
138851292Sobrien    dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path);
138938494Sobrien#endif /* DEBUG */
139038494Sobrien    dp->dl_eof = TRUE;
139138494Sobrien    dp->dl_entries = 0;
139282794Sobrien#ifdef DEBUG
139382794Sobrien    amuDebug(D_READDIR)
139482794Sobrien      plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
139582794Sobrien#endif /* DEBUG */
139638494Sobrien    return 0;
139738494Sobrien  }
139838494Sobrien
139938494Sobrien  /* non-browsable directories code */
140038494Sobrien  xp = mp->am_child;
140138494Sobrien  while (xp && xp->am_gen != gen)
140238494Sobrien    xp = xp->am_osib;
140338494Sobrien
140438494Sobrien  if (xp) {
140538494Sobrien    int nbytes = count / 2;	/* conservative */
140638494Sobrien    int todo = MAX_READDIR_ENTRIES;
140751292Sobrien
140838494Sobrien    dp->dl_entries = ep;
140938494Sobrien    do {
141038494Sobrien      am_node *xp_next = next_nonerror_node(xp->am_osib);
141138494Sobrien
141238494Sobrien      if (xp_next) {
141338494Sobrien	*(u_int *) ep->ne_cookie = xp_next->am_gen;
141438494Sobrien      } else {
141551292Sobrien	*(u_int *) ep->ne_cookie = DOT_DOT_COOKIE;
141638494Sobrien	dp->dl_eof = TRUE;
141738494Sobrien      }
141838494Sobrien
141938494Sobrien      ep->ne_fileid = xp->am_gen;
142038494Sobrien      ep->ne_name = xp->am_name;
142138494Sobrien      nbytes -= sizeof(*ep) + 1;
142238494Sobrien      if (xp->am_name)
142338494Sobrien	nbytes -= strlen(xp->am_name);
142438494Sobrien
142538494Sobrien      xp = xp_next;
142638494Sobrien
142738494Sobrien      if (nbytes > 0 && !dp->dl_eof && todo > 1) {
142838494Sobrien	ep->ne_nextentry = ep + 1;
142938494Sobrien	ep++;
143038494Sobrien	--todo;
143138494Sobrien      } else {
143238494Sobrien	todo = 0;
143338494Sobrien      }
143438494Sobrien    } while (todo > 0);
143538494Sobrien
143638494Sobrien    ep->ne_nextentry = 0;
143738494Sobrien
143882794Sobrien#ifdef DEBUG
143982794Sobrien    amuDebug(D_READDIR)
144082794Sobrien      for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
144182794Sobrien	plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d",
144282794Sobrien	     j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
144382794Sobrien#endif /* DEBUG */
144438494Sobrien    return 0;
144538494Sobrien  }
144638494Sobrien  return ESTALE;
144738494Sobrien}
144838494Sobrien
144938494Sobrien
145038494Sobrien/* This one is called only if map is browsable */
145138494Sobrienstatic int
145238494Sobrienamfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
145338494Sobrien{
145438494Sobrien  u_int gen = *(u_int *) cookie;
145538494Sobrien  int chain_length, i;
145638494Sobrien  static nfsentry *te, *te_next;
145782794Sobrien#ifdef DEBUG
145838494Sobrien  nfsentry *ne;
145938494Sobrien  static int j;
146082794Sobrien#endif /* DEBUG */
146138494Sobrien
146238494Sobrien  dp->dl_eof = FALSE;		/* assume readdir not done */
146338494Sobrien
146482794Sobrien#ifdef DEBUG
146582794Sobrien  amuDebug(D_READDIR)
146682794Sobrien    plog(XLOG_DEBUG, "amfs_auto_readdir_browsable gen=%u, count=%d",
146782794Sobrien	 gen, count);
146882794Sobrien#endif /* DEBUG */
146938494Sobrien
147038494Sobrien  if (gen == 0) {
147138494Sobrien    /*
147238494Sobrien     * In the default instance (which is used to start a search) we return
147338494Sobrien     * "." and "..".
147438494Sobrien     *
147538494Sobrien     * This assumes that the count is big enough to allow both "." and ".."
147638494Sobrien     * to be returned in a single packet.  If it isn't (which would be
147738494Sobrien     * fairly unbelievable) then tough.
147838494Sobrien     */
147938494Sobrien#ifdef DEBUG
148051292Sobrien    dlog("amfs_auto_readdir_browsable: default search");
148138494Sobrien#endif /* DEBUG */
148238494Sobrien    /*
148338494Sobrien     * Check for enough room.  This is extremely approximate but is more
148438494Sobrien     * than enough space.  Really need 2 times:
148538494Sobrien     *      4byte fileid
148638494Sobrien     *      4byte cookie
148738494Sobrien     *      4byte name length
148838494Sobrien     *      4byte name
148938494Sobrien     * plus the dirlist structure */
149038494Sobrien    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
149138494Sobrien      return EINVAL;
149238494Sobrien
149338494Sobrien    /*
149438494Sobrien     * compute # of entries to send in this chain.
149538494Sobrien     * heuristics: 128 bytes per entry.
149638494Sobrien     * This is too much probably, but it seems to work better because
149738494Sobrien     * of the re-entrant nature of nfs_readdir, and esp. on systems
149838494Sobrien     * like OpenBSD 2.2.
149938494Sobrien     */
150038494Sobrien    chain_length = count / 128;
150138494Sobrien
150238494Sobrien    /* reset static state counters */
150338494Sobrien    te = te_next = NULL;
150438494Sobrien
150538494Sobrien    dp->dl_entries = ep;
150638494Sobrien
150738494Sobrien    /* construct "." */
150838494Sobrien    ep[0].ne_fileid = mp->am_gen;
150938494Sobrien    ep[0].ne_name = ".";
151038494Sobrien    ep[0].ne_nextentry = &ep[1];
151138494Sobrien    *(u_int *) ep[0].ne_cookie = 0;
151238494Sobrien
151338494Sobrien    /* construct ".." */
151438494Sobrien    if (mp->am_parent)
151538494Sobrien      ep[1].ne_fileid = mp->am_parent->am_gen;
151638494Sobrien    else
151738494Sobrien      ep[1].ne_fileid = mp->am_gen;
151882794Sobrien
151938494Sobrien    ep[1].ne_name = "..";
152038494Sobrien    ep[1].ne_nextentry = 0;
152151292Sobrien    *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE;
152238494Sobrien
152338494Sobrien    /*
152438494Sobrien     * If map is browsable, call a function make_entry_chain() to construct
152538494Sobrien     * a linked list of unmounted keys, and return it.  Then link the chain
152638494Sobrien     * to the regular list.  Get the chain only once, but return
152738494Sobrien     * chunks of it each time.
152838494Sobrien     */
152938494Sobrien    te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
153038494Sobrien    if (!te)
153138494Sobrien      return 0;
153282794Sobrien#ifdef DEBUG
153382794Sobrien    amuDebug(D_READDIR)
153482794Sobrien      for (j=0,ne=te; ne; ne=ne->ne_nextentry)
153582794Sobrien	plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
153682794Sobrien#endif /* DEBUG */
153738494Sobrien
153838494Sobrien    /* return only "chain_length" entries */
153938494Sobrien    te_next = te;
154038494Sobrien    for (i=1; i<chain_length; ++i) {
154138494Sobrien      te_next = te_next->ne_nextentry;
154238494Sobrien      if (!te_next)
154338494Sobrien	break;
154438494Sobrien    }
154538494Sobrien    if (te_next) {
154638494Sobrien      nfsentry *te_saved = te_next->ne_nextentry;
154738494Sobrien      te_next->ne_nextentry = NULL; /* terminate "te" chain */
154842629Sobrien      te_next = te_saved;	/* save rest of "te" for next iteration */
154938494Sobrien      dp->dl_eof = FALSE;	/* tell readdir there's more */
155038494Sobrien    } else {
155138494Sobrien      dp->dl_eof = TRUE;	/* tell readdir that's it */
155238494Sobrien    }
155338494Sobrien    ep[1].ne_nextentry = te;	/* append this chunk of "te" chain */
155482794Sobrien#ifdef DEBUG
155582794Sobrien    amuDebug(D_READDIR) {
155682794Sobrien      for (j=0,ne=te; ne; ne=ne->ne_nextentry)
155782794Sobrien	plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name);
155882794Sobrien      for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
155982794Sobrien	plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d",
156082794Sobrien	     j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
156182794Sobrien      plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof);
156282794Sobrien    }
156338494Sobrien#endif /* DEBUG_READDIR */
156438494Sobrien    return 0;
156538494Sobrien  } /* end of "if (gen == 0)" statement */
156638494Sobrien
156738494Sobrien#ifdef DEBUG
156851292Sobrien  dlog("amfs_auto_readdir_browsable: real child");
156938494Sobrien#endif /* DEBUG */
157038494Sobrien
157151292Sobrien  if (gen == DOT_DOT_COOKIE) {
157238494Sobrien#ifdef DEBUG
157351292Sobrien    dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path);
157438494Sobrien#endif /* DEBUG */
157538494Sobrien    dp->dl_eof = TRUE;
157638494Sobrien    dp->dl_entries = 0;
157738494Sobrien    return 0;
157838494Sobrien  }
157938494Sobrien
158038494Sobrien  /*
158138494Sobrien   * If browsable directories, then continue serving readdir() with another
158238494Sobrien   * chunk of entries, starting from where we left off (when gen was equal
158338494Sobrien   * to 0).  Once again, assume last chunk served to readdir.
158438494Sobrien   */
158538494Sobrien  dp->dl_eof = TRUE;
158638494Sobrien  dp->dl_entries = ep;
158738494Sobrien
158838494Sobrien  te = te_next;			/* reset 'te' from last saved te_next */
158938494Sobrien  if (!te) {			/* another indicator of end of readdir */
159038494Sobrien    dp->dl_entries = 0;
159138494Sobrien    return 0;
159238494Sobrien  }
159338494Sobrien  /*
159438494Sobrien   * compute # of entries to send in this chain.
159538494Sobrien   * heuristics: 128 bytes per entry.
159638494Sobrien   */
159738494Sobrien  chain_length = count / 128;
159838494Sobrien
159938494Sobrien  /* return only "chain_length" entries */
160038494Sobrien  for (i=1; i<chain_length; ++i) {
160138494Sobrien    te_next = te_next->ne_nextentry;
160238494Sobrien    if (!te_next)
160338494Sobrien      break;
160438494Sobrien  }
160538494Sobrien  if (te_next) {
160638494Sobrien    nfsentry *te_saved = te_next->ne_nextentry;
160738494Sobrien    te_next->ne_nextentry = NULL; /* terminate "te" chain */
160842629Sobrien    te_next = te_saved;		/* save rest of "te" for next iteration */
160938494Sobrien    dp->dl_eof = FALSE;		/* tell readdir there's more */
161038494Sobrien  }
161138494Sobrien  ep = te;			/* send next chunk of "te" chain */
161238494Sobrien  dp->dl_entries = ep;
161382794Sobrien#ifdef DEBUG
161482794Sobrien  amuDebug(D_READDIR) {
161582794Sobrien    plog(XLOG_DEBUG, "dl_entries=0x%lx, te_next=0x%lx, dl_eof=%d",
161682794Sobrien	 (long) dp->dl_entries, (long) te_next, dp->dl_eof);
161782794Sobrien    for (ne=te; ne; ne=ne->ne_nextentry)
161882794Sobrien      plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name);
161982794Sobrien  }
162082794Sobrien#endif /* DEBUG */
162138494Sobrien  return 0;
162238494Sobrien}
162338494Sobrien
162438494Sobrien
162538494Sobrienint
162638494Sobrienamfs_auto_fmount(am_node *mp)
162738494Sobrien{
162838494Sobrien  mntfs *mf = mp->am_mnt;
162938494Sobrien  return (*mf->mf_ops->fmount_fs) (mf);
163038494Sobrien}
163138494Sobrien
163238494Sobrien
163338494Sobrienint
163438494Sobrienamfs_auto_fumount(am_node *mp)
163538494Sobrien{
163638494Sobrien  mntfs *mf = mp->am_mnt;
163738494Sobrien  return (*mf->mf_ops->fumount_fs) (mf);
163838494Sobrien}
1639