amfs_auto.c revision 42629
138494Sobrien/*
238494Sobrien * Copyright (c) 1997-1998 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 *
4142629Sobrien * $Id: amfs_auto.c,v 1.1.1.1 1998/11/05 02:04:46 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
6038494Sobrien/* DEVELOPERS: turn this on for special debugging of readdir code */
6138494Sobrien#undef DEBUG_READDIR
6238494Sobrien
6338494Sobrien/****************************************************************************
6438494Sobrien *** STRUCTURES                                                           ***
6538494Sobrien ****************************************************************************/
6638494Sobrien
6738494Sobrien
6838494Sobrien
6938494Sobrien/****************************************************************************
7038494Sobrien *** FORWARD DEFINITIONS                                                  ***
7138494Sobrien ****************************************************************************/
7238494Sobrienstatic int amfs_auto_bgmount(struct continuation * cp, int mpe);
7338494Sobrienstatic int amfs_auto_mount(am_node *mp);
7438494Sobrienstatic int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
7538494Sobrienstatic void amfs_auto_umounted(am_node *mp);
7638494Sobrien
7738494Sobrien
7838494Sobrien/****************************************************************************
7938494Sobrien *** OPS STRUCTURES                                                       ***
8038494Sobrien ****************************************************************************/
8138494Sobrienam_ops amfs_auto_ops =
8238494Sobrien{
8338494Sobrien  "auto",
8438494Sobrien  amfs_auto_match,
8538494Sobrien  0,				/* amfs_auto_init */
8638494Sobrien  amfs_auto_mount,
8738494Sobrien  0,
8838494Sobrien  amfs_auto_umount,
8938494Sobrien  0,
9038494Sobrien  amfs_auto_lookuppn,
9138494Sobrien  amfs_auto_readdir,
9238494Sobrien  0,				/* amfs_auto_readlink */
9338494Sobrien  0,				/* amfs_auto_mounted */
9438494Sobrien  amfs_auto_umounted,
9538494Sobrien  find_amfs_auto_srvr,
9638494Sobrien  FS_AMQINFO | FS_DIRECTORY
9738494Sobrien};
9838494Sobrien
9938494Sobrien
10038494Sobrien/****************************************************************************
10138494Sobrien *** FUNCTIONS                                                             ***
10238494Sobrien ****************************************************************************/
10338494Sobrien/*
10438494Sobrien * AMFS_AUTO needs nothing in particular.
10538494Sobrien */
10638494Sobrienchar *
10738494Sobrienamfs_auto_match(am_opts *fo)
10838494Sobrien{
10938494Sobrien  char *p = fo->opt_rfs;
11038494Sobrien
11138494Sobrien  if (!fo->opt_rfs) {
11238494Sobrien    plog(XLOG_USER, "auto: no mount point named (rfs:=)");
11338494Sobrien    return 0;
11438494Sobrien  }
11538494Sobrien  if (!fo->opt_fs) {
11638494Sobrien    plog(XLOG_USER, "auto: no map named (fs:=)");
11738494Sobrien    return 0;
11838494Sobrien  }
11938494Sobrien
12038494Sobrien  /*
12138494Sobrien   * Swap round fs:= and rfs:= options
12238494Sobrien   * ... historical (jsp)
12338494Sobrien   */
12438494Sobrien  fo->opt_rfs = fo->opt_fs;
12538494Sobrien  fo->opt_fs = p;
12638494Sobrien
12738494Sobrien  /*
12838494Sobrien   * mtab entry turns out to be the name of the mount map
12938494Sobrien   */
13038494Sobrien  return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
13138494Sobrien}
13238494Sobrien
13338494Sobrien
13438494Sobrien
13538494Sobrien
13638494Sobrien/*
13738494Sobrien * Build a new map cache for this node, or re-use
13838494Sobrien * an existing cache for the same map.
13938494Sobrien */
14038494Sobrienvoid
14138494Sobrienamfs_auto_mkcacheref(mntfs *mf)
14238494Sobrien{
14338494Sobrien  char *cache;
14438494Sobrien
14538494Sobrien  if (mf->mf_fo && mf->mf_fo->opt_cache)
14638494Sobrien    cache = mf->mf_fo->opt_cache;
14738494Sobrien  else
14838494Sobrien    cache = "none";
14938494Sobrien  mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
15038494Sobrien				     mf->mf_fo->opt_maptype);
15138494Sobrien  mf->mf_prfree = mapc_free;
15238494Sobrien}
15338494Sobrien
15438494Sobrien
15538494Sobrien/*
15638494Sobrien * Mount a sub-mount
15738494Sobrien */
15838494Sobrienstatic int
15938494Sobrienamfs_auto_mount(am_node *mp)
16038494Sobrien{
16138494Sobrien  mntfs *mf = mp->am_mnt;
16238494Sobrien
16338494Sobrien  /*
16438494Sobrien   * Pseudo-directories are used to provide some structure
16538494Sobrien   * to the automounted directories instead
16638494Sobrien   * of putting them all in the top-level automount directory.
16738494Sobrien   *
16838494Sobrien   * Here, just increment the parent's link count.
16938494Sobrien   */
17038494Sobrien  mp->am_parent->am_fattr.na_nlink++;
17138494Sobrien
17238494Sobrien  /*
17338494Sobrien   * Info field of . means use parent's info field.
17438494Sobrien   * Historical - not documented.
17538494Sobrien   */
17638494Sobrien  if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
17738494Sobrien    mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
17838494Sobrien
17938494Sobrien  /*
18038494Sobrien   * Compute prefix:
18138494Sobrien   *
18238494Sobrien   * If there is an option prefix then use that else
18338494Sobrien   * If the parent had a prefix then use that with name
18438494Sobrien   *      of this node appended else
18538494Sobrien   * Use the name of this node.
18638494Sobrien   *
18738494Sobrien   * That means if you want no prefix you must say so
18838494Sobrien   * in the map.
18938494Sobrien   */
19038494Sobrien  if (mf->mf_fo->opt_pref) {
19138494Sobrien    /* allow pref:=null to set a real null prefix */
19238494Sobrien    if (STREQ(mf->mf_fo->opt_pref, "null")) {
19338494Sobrien      mp->am_pref = "";
19438494Sobrien    } else {
19538494Sobrien      /*
19638494Sobrien       * the prefix specified as an option
19738494Sobrien       */
19838494Sobrien      mp->am_pref = strdup(mf->mf_fo->opt_pref);
19938494Sobrien    }
20038494Sobrien  } else {
20138494Sobrien    /*
20238494Sobrien     * else the parent's prefix
20338494Sobrien     * followed by the name
20438494Sobrien     * followed by /
20538494Sobrien     */
20638494Sobrien    char *ppref = mp->am_parent->am_pref;
20738494Sobrien    if (ppref == 0)
20838494Sobrien      ppref = "";
20938494Sobrien    mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
21038494Sobrien  }
21138494Sobrien
21238494Sobrien  /*
21338494Sobrien   * Attach a map cache
21438494Sobrien   */
21538494Sobrien  amfs_auto_mkcacheref(mf);
21638494Sobrien
21738494Sobrien  return 0;
21838494Sobrien}
21938494Sobrien
22038494Sobrien
22138494Sobrien
22238494Sobrien
22338494Sobrien/*
22438494Sobrien * Unmount an automount sub-node
22538494Sobrien */
22638494Sobrienint
22738494Sobrienamfs_auto_umount(am_node *mp)
22838494Sobrien{
22938494Sobrien  return 0;
23038494Sobrien}
23138494Sobrien
23238494Sobrien
23338494Sobrien/*
23438494Sobrien * Unmount an automount node
23538494Sobrien */
23638494Sobrienstatic void
23738494Sobrienamfs_auto_umounted(am_node *mp)
23838494Sobrien{
23938494Sobrien  /*
24038494Sobrien   * If this is a pseudo-directory then just adjust the link count
24138494Sobrien   * in the parent, otherwise call the generic unmount routine
24238494Sobrien   */
24338494Sobrien  if (mp->am_parent && mp->am_parent->am_parent)
24438494Sobrien    --mp->am_parent->am_fattr.na_nlink;
24538494Sobrien}
24638494Sobrien
24738494Sobrien
24838494Sobrien/*
24938494Sobrien * Discard an old continuation
25038494Sobrien */
25138494Sobrienvoid
25238494Sobrienfree_continuation(struct continuation *cp)
25338494Sobrien{
25438494Sobrien  if (cp->callout)
25538494Sobrien    untimeout(cp->callout);
25638494Sobrien  XFREE(cp->key);
25738494Sobrien  XFREE(cp->xivec);
25838494Sobrien  XFREE(cp->info);
25938494Sobrien  XFREE(cp->auto_opts);
26038494Sobrien  XFREE(cp->def_opts);
26138494Sobrien  free_opts(&cp->fs_opts);
26238494Sobrien  XFREE(cp);
26338494Sobrien}
26438494Sobrien
26538494Sobrien
26638494Sobrien/*
26738494Sobrien * Discard the underlying mount point and replace
26838494Sobrien * with a reference to an error filesystem.
26938494Sobrien */
27038494Sobrienvoid
27138494Sobrienassign_error_mntfs(am_node *mp)
27238494Sobrien{
27338494Sobrien  if (mp->am_error > 0) {
27438494Sobrien    /*
27538494Sobrien     * Save the old error code
27638494Sobrien     */
27738494Sobrien    int error = mp->am_error;
27838494Sobrien    if (error <= 0)
27938494Sobrien      error = mp->am_mnt->mf_error;
28038494Sobrien    /*
28138494Sobrien     * Discard the old filesystem
28238494Sobrien     */
28338494Sobrien    free_mntfs(mp->am_mnt);
28438494Sobrien    /*
28538494Sobrien     * Allocate a new error reference
28638494Sobrien     */
28738494Sobrien    mp->am_mnt = new_mntfs();
28838494Sobrien    /*
28938494Sobrien     * Put back the error code
29038494Sobrien     */
29138494Sobrien    mp->am_mnt->mf_error = error;
29238494Sobrien    mp->am_mnt->mf_flags |= MFF_ERROR;
29338494Sobrien    /*
29438494Sobrien     * Zero the error in the mount point
29538494Sobrien     */
29638494Sobrien    mp->am_error = 0;
29738494Sobrien  }
29838494Sobrien}
29938494Sobrien
30038494Sobrien
30138494Sobrien/*
30238494Sobrien * The continuation function.  This is called by
30338494Sobrien * the task notifier when a background mount attempt
30438494Sobrien * completes.
30538494Sobrien */
30638494Sobrienvoid
30738494Sobrienamfs_auto_cont(int rc, int term, voidp closure)
30838494Sobrien{
30938494Sobrien  struct continuation *cp = (struct continuation *) closure;
31038494Sobrien  mntfs *mf = cp->mp->am_mnt;
31138494Sobrien
31238494Sobrien  /*
31338494Sobrien   * Definitely not trying to mount at the moment
31438494Sobrien   */
31538494Sobrien  mf->mf_flags &= ~MFF_MOUNTING;
31638494Sobrien
31738494Sobrien  /*
31838494Sobrien   * While we are mounting - try to avoid race conditions
31938494Sobrien   */
32038494Sobrien  new_ttl(cp->mp);
32138494Sobrien
32238494Sobrien  /*
32338494Sobrien   * Wakeup anything waiting for this mount
32438494Sobrien   */
32538494Sobrien  wakeup((voidp) mf);
32638494Sobrien
32738494Sobrien  /*
32838494Sobrien   * Check for termination signal or exit status...
32938494Sobrien   */
33038494Sobrien  if (rc || term) {
33138494Sobrien    am_node *xmp;
33238494Sobrien
33338494Sobrien    if (term) {
33438494Sobrien      /*
33538494Sobrien       * Not sure what to do for an error code.
33638494Sobrien       */
33738494Sobrien      mf->mf_error = EIO;	/* XXX ? */
33838494Sobrien      mf->mf_flags |= MFF_ERROR;
33938494Sobrien      plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
34038494Sobrien    } else {
34138494Sobrien      /*
34238494Sobrien       * Check for exit status...
34338494Sobrien       */
34438494Sobrien      mf->mf_error = rc;
34538494Sobrien      mf->mf_flags |= MFF_ERROR;
34638494Sobrien      errno = rc;		/* XXX */
34738494Sobrien      if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
34838494Sobrien	plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
34938494Sobrien    }
35038494Sobrien
35138494Sobrien    /*
35238494Sobrien     * If we get here then that attempt didn't work, so
35338494Sobrien     * move the info vector pointer along by one and
35438494Sobrien     * call the background mount routine again
35538494Sobrien     */
35638494Sobrien    amd_stats.d_merr++;
35738494Sobrien    cp->ivec++;
35838494Sobrien    xmp = cp->mp;
35938494Sobrien    (void) amfs_auto_bgmount(cp, 0);
36038494Sobrien    assign_error_mntfs(xmp);
36138494Sobrien  } else {
36238494Sobrien    /*
36338494Sobrien     * The mount worked.
36438494Sobrien     */
36538494Sobrien    am_mounted(cp->mp);
36638494Sobrien    free_continuation(cp);
36738494Sobrien  }
36838494Sobrien
36938494Sobrien  reschedule_timeout_mp();
37038494Sobrien}
37138494Sobrien
37238494Sobrien
37338494Sobrien/*
37438494Sobrien * Retry a mount
37538494Sobrien */
37638494Sobrienvoid
37738494Sobrienamfs_auto_retry(int rc, int term, voidp closure)
37838494Sobrien{
37938494Sobrien  struct continuation *cp = (struct continuation *) closure;
38038494Sobrien  int error = 0;
38138494Sobrien
38238494Sobrien#ifdef DEBUG
38338494Sobrien  dlog("Commencing retry for mount of %s", cp->mp->am_path);
38438494Sobrien#endif /* DEBUG */
38538494Sobrien
38638494Sobrien  new_ttl(cp->mp);
38738494Sobrien
38838494Sobrien  if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
38938494Sobrien    /*
39038494Sobrien     * The entire mount has timed out.  Set the error code and skip past all
39138494Sobrien     * the info vectors so that amfs_auto_bgmount will not have any more
39238494Sobrien     * ways to try the mount, so causing an error.
39338494Sobrien     */
39438494Sobrien    plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
39538494Sobrien    error = ETIMEDOUT;
39638494Sobrien    while (*cp->ivec)
39738494Sobrien      cp->ivec++;
39838494Sobrien    /* explicitly forbid further retries after timeout */
39938494Sobrien    cp->retry = FALSE;
40038494Sobrien  }
40138494Sobrien  if (error || !IN_PROGRESS(cp)) {
40238494Sobrien    (void) amfs_auto_bgmount(cp, error);
40338494Sobrien  }
40438494Sobrien  reschedule_timeout_mp();
40538494Sobrien}
40638494Sobrien
40738494Sobrien
40838494Sobrien/*
40938494Sobrien * Try to mount a file system.  Can be called
41038494Sobrien * directly or in a sub-process by run_task.
41138494Sobrien */
41238494Sobrienint
41338494Sobrientry_mount(voidp mvp)
41438494Sobrien{
41538494Sobrien  int error = 0;
41638494Sobrien  am_node *mp = (am_node *) mvp;
41738494Sobrien  mntfs *mf = mp->am_mnt;
41838494Sobrien
41938494Sobrien  /*
42038494Sobrien   * If the directory is not yet made and it needs to be made, then make it!
42138494Sobrien   * This may be run in a background process in which case the flag setting
42238494Sobrien   * won't be noticed later - but it is set anyway just after run_task is
42338494Sobrien   * called.  It should probably go away totally...
42438494Sobrien   */
42538494Sobrien  if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
42638494Sobrien    error = mkdirs(mf->mf_mount, 0555);
42738494Sobrien    if (!error)
42838494Sobrien      mf->mf_flags |= MFF_MKMNT;
42938494Sobrien  }
43038494Sobrien
43138494Sobrien  /*
43238494Sobrien   * Mount it!
43338494Sobrien   */
43438494Sobrien  error = mount_node(mp);
43538494Sobrien
43638494Sobrien#ifdef DEBUG
43738494Sobrien  if (error > 0) {
43838494Sobrien    errno = error;
43938494Sobrien    dlog("amfs_auto call to mount_node failed: %m");
44038494Sobrien  }
44138494Sobrien#endif /* DEBUG */
44238494Sobrien
44338494Sobrien  return error;
44438494Sobrien}
44538494Sobrien
44638494Sobrien
44738494Sobrien/*
44838494Sobrien * Pick a file system to try mounting and
44938494Sobrien * do that in the background if necessary
45038494Sobrien *
45138494Sobrien For each location:
45238494Sobrien if it is new -defaults then
45338494Sobrien extract and process
45438494Sobrien continue;
45538494Sobrien fi
45638494Sobrien if it is a cut then
45738494Sobrien if a location has been tried then
45838494Sobrien break;
45938494Sobrien fi
46038494Sobrien continue;
46138494Sobrien fi
46238494Sobrien parse mount location
46338494Sobrien discard previous mount location if required
46438494Sobrien find matching mounted filesystem
46538494Sobrien if not applicable then
46638494Sobrien this_error = No such file or directory
46738494Sobrien continue
46838494Sobrien fi
46938494Sobrien if the filesystem failed to be mounted then
47038494Sobrien this_error = error from filesystem
47138494Sobrien elif the filesystem is mounting or unmounting then
47238494Sobrien this_error = -1
47338494Sobrien elif the fileserver is down then
47438494Sobrien this_error = -1
47538494Sobrien elif the filesystem is already mounted
47638494Sobrien this_error = 0
47738494Sobrien break
47838494Sobrien fi
47938494Sobrien if no error on this mount then
48042629Sobrien this_error = initialize mount point
48138494Sobrien fi
48238494Sobrien if no error on this mount and mount is delayed then
48338494Sobrien this_error = -1
48438494Sobrien fi
48538494Sobrien if this_error < 0 then
48638494Sobrien retry = true
48738494Sobrien fi
48838494Sobrien if no error on this mount then
48938494Sobrien make mount point if required
49038494Sobrien fi
49138494Sobrien if no error on this mount then
49238494Sobrien if mount in background then
49338494Sobrien run mount in background
49438494Sobrien return -1
49538494Sobrien else
49638494Sobrien this_error = mount in foreground
49738494Sobrien fi
49838494Sobrien fi
49942629Sobrien if an error occurred on this mount then
50038494Sobrien update stats
50138494Sobrien save error in mount point
50238494Sobrien fi
50338494Sobrien endfor
50438494Sobrien */
50538494Sobrienstatic int
50638494Sobrienamfs_auto_bgmount(struct continuation * cp, int mpe)
50738494Sobrien{
50838494Sobrien  mntfs *mf = cp->mp->am_mnt;	/* Current mntfs */
50938494Sobrien  mntfs *mf_retry = 0;		/* First mntfs which needed retrying */
51038494Sobrien  int this_error = -1;		/* Per-mount error */
51138494Sobrien  int hard_error = -1;
51238494Sobrien  int mp_error = mpe;
51338494Sobrien
51438494Sobrien  /*
51538494Sobrien   * Try to mount each location.
51638494Sobrien   * At the end:
51738494Sobrien   * hard_error == 0 indicates something was mounted.
51838494Sobrien   * hard_error > 0 indicates everything failed with a hard error
51938494Sobrien   * hard_error < 0 indicates nothing could be mounted now
52038494Sobrien   */
52138494Sobrien  for (; this_error && *cp->ivec; cp->ivec++) {
52238494Sobrien    am_ops *p;
52338494Sobrien    am_node *mp = cp->mp;
52438494Sobrien    char *link_dir;
52538494Sobrien    int dont_retry;
52638494Sobrien
52738494Sobrien    if (hard_error < 0)
52838494Sobrien      hard_error = this_error;
52938494Sobrien
53038494Sobrien    this_error = -1;
53138494Sobrien
53238494Sobrien    if (**cp->ivec == '-') {
53338494Sobrien      /*
53438494Sobrien       * Pick up new defaults
53538494Sobrien       */
53638494Sobrien      if (cp->auto_opts && *cp->auto_opts)
53738494Sobrien	cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
53838494Sobrien      else
53938494Sobrien	cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
54038494Sobrien#ifdef DEBUG
54138494Sobrien      dlog("Setting def_opts to \"%s\"", cp->def_opts);
54238494Sobrien#endif /* DEBUG */
54338494Sobrien      continue;
54438494Sobrien    }
54538494Sobrien    /*
54638494Sobrien     * If a mount has been attempted, and we find
54738494Sobrien     * a cut then don't try any more locations.
54838494Sobrien     */
54938494Sobrien    if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
55038494Sobrien      if (cp->tried) {
55138494Sobrien#ifdef DEBUG
55238494Sobrien	dlog("Cut: not trying any more locations for %s",
55338494Sobrien	     mp->am_path);
55438494Sobrien#endif /* DEBUG */
55538494Sobrien	break;
55638494Sobrien      }
55738494Sobrien      continue;
55838494Sobrien    }
55938494Sobrien
56038494Sobrien    /* match the operators */
56138494Sobrien    p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
56238494Sobrien
56338494Sobrien    /*
56438494Sobrien     * Find a mounted filesystem for this node.
56538494Sobrien     */
56638494Sobrien    mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
56738494Sobrien				    cp->fs_opts.opt_fs,
56838494Sobrien				    cp->fs_opts.fs_mtab,
56938494Sobrien				    cp->auto_opts,
57038494Sobrien				    cp->fs_opts.opt_opts,
57138494Sobrien				    cp->fs_opts.opt_remopts);
57238494Sobrien
57338494Sobrien    p = mf->mf_ops;
57438494Sobrien#ifdef DEBUG
57538494Sobrien    dlog("Got a hit with %s", p->fs_type);
57638494Sobrien#endif /* DEBUG */
57738494Sobrien
57838494Sobrien    /*
57938494Sobrien     * Note whether this is a real mount attempt
58038494Sobrien     */
58138494Sobrien    if (p == &amfs_error_ops) {
58238494Sobrien      plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
58338494Sobrien      if (this_error <= 0)
58438494Sobrien	this_error = ENOENT;
58538494Sobrien      continue;
58638494Sobrien    } else {
58738494Sobrien      if (cp->fs_opts.fs_mtab) {
58838494Sobrien	plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
58938494Sobrien	     cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
59038494Sobrien      }
59138494Sobrien      cp->tried = TRUE;
59238494Sobrien    }
59338494Sobrien
59438494Sobrien    this_error = 0;
59538494Sobrien    dont_retry = FALSE;
59638494Sobrien
59738494Sobrien    if (mp->am_link) {
59838494Sobrien      XFREE(mp->am_link);
59938494Sobrien      mp->am_link = 0;
60038494Sobrien    }
60138494Sobrien    link_dir = mf->mf_fo->opt_sublink;
60238494Sobrien
60338494Sobrien    if (link_dir && *link_dir) {
60438494Sobrien      if (*link_dir == '/') {
60538494Sobrien	mp->am_link = strdup(link_dir);
60638494Sobrien      } else {
60738494Sobrien	/*
60838494Sobrien	 * try getting fs option from continuation, not mountpoint!
60938494Sobrien	 * Don't try logging the string from mf, since it may be bad!
61038494Sobrien	 */
61138494Sobrien	if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
61238494Sobrien	  plog(XLOG_ERROR, "use %s instead of 0x%x",
61338494Sobrien	       cp->fs_opts.opt_fs, mf->mf_fo->opt_fs);
61438494Sobrien
61538494Sobrien	mp->am_link = str3cat((char *) 0,
61638494Sobrien			      cp->fs_opts.opt_fs, "/", link_dir);
61738494Sobrien
61838494Sobrien	normalize_slash(mp->am_link);
61938494Sobrien      }
62038494Sobrien    }
62138494Sobrien
62238494Sobrien    if (mf->mf_error > 0) {
62338494Sobrien      this_error = mf->mf_error;
62438494Sobrien    } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
62538494Sobrien      /*
62638494Sobrien       * Still mounting - retry later
62738494Sobrien       */
62838494Sobrien#ifdef DEBUG
62938494Sobrien      dlog("Duplicate pending mount fstype %s", p->fs_type);
63038494Sobrien#endif /* DEBUG */
63138494Sobrien      this_error = -1;
63238494Sobrien    } else if (FSRV_ISDOWN(mf->mf_server)) {
63338494Sobrien      /*
63438494Sobrien       * Would just mount from the same place
63538494Sobrien       * as a hung mount - so give up
63638494Sobrien       */
63738494Sobrien#ifdef DEBUG
63838494Sobrien      dlog("%s is already hung - giving up", mf->mf_mount);
63938494Sobrien#endif /* DEBUG */
64038494Sobrien      mp_error = EWOULDBLOCK;
64138494Sobrien      dont_retry = TRUE;
64238494Sobrien      this_error = -1;
64338494Sobrien    } else if (mf->mf_flags & MFF_MOUNTED) {
64438494Sobrien#ifdef DEBUG
64538494Sobrien      dlog("duplicate mount of \"%s\" ...", mf->mf_info);
64638494Sobrien#endif /* DEBUG */
64738494Sobrien
64838494Sobrien      /*
64938494Sobrien       * Just call mounted()
65038494Sobrien       */
65138494Sobrien      am_mounted(mp);
65238494Sobrien
65338494Sobrien      this_error = 0;
65438494Sobrien      break;
65538494Sobrien    }
65638494Sobrien
65738494Sobrien    /*
65838494Sobrien     * Will usually need to play around with the mount nodes
65938494Sobrien     * file attribute structure.  This must be done here.
66042629Sobrien     * Try and get things initialized, even if the fileserver
66138494Sobrien     * is not known to be up.  In the common case this will
66238494Sobrien     * progress things faster.
66338494Sobrien     */
66438494Sobrien    if (!this_error) {
66538494Sobrien      /*
66638494Sobrien       * Fill in attribute fields.
66738494Sobrien       */
66838494Sobrien      if (mf->mf_ops->fs_flags & FS_DIRECTORY)
66938494Sobrien	mk_fattr(mp, NFDIR);
67038494Sobrien      else
67138494Sobrien	mk_fattr(mp, NFLNK);
67238494Sobrien
67338494Sobrien      mp->am_fattr.na_fileid = mp->am_gen;
67438494Sobrien
67538494Sobrien      if (p->fs_init)
67638494Sobrien	this_error = (*p->fs_init) (mf);
67738494Sobrien    }
67838494Sobrien
67938494Sobrien    /*
68038494Sobrien     * Make sure the fileserver is UP before doing any more work
68138494Sobrien     */
68238494Sobrien    if (!FSRV_ISUP(mf->mf_server)) {
68338494Sobrien#ifdef DEBUG
68438494Sobrien      dlog("waiting for server %s to become available", mf->mf_server->fs_host);
68538494Sobrien#endif /* DEBUG */
68638494Sobrien      this_error = -1;
68738494Sobrien    }
68838494Sobrien
68938494Sobrien    if (!this_error && mf->mf_fo->opt_delay) {
69038494Sobrien      /*
69138494Sobrien       * If there is a delay timer on the mount
69238494Sobrien       * then don't try to mount if the timer
69338494Sobrien       * has not expired.
69438494Sobrien       */
69538494Sobrien      int i = atoi(mf->mf_fo->opt_delay);
69638494Sobrien      if (i > 0 && clocktime() < (cp->start + i)) {
69738494Sobrien#ifdef DEBUG
69838494Sobrien	dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
69938494Sobrien#endif /* DEBUG */
70038494Sobrien	this_error = -1;
70138494Sobrien      }
70238494Sobrien    }
70338494Sobrien
70438494Sobrien    if (this_error < 0 && !dont_retry) {
70538494Sobrien      if (!mf_retry)
70638494Sobrien	mf_retry = dup_mntfs(mf);
70738494Sobrien      cp->retry = TRUE;
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	}
72038494Sobrien	run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
72138494Sobrien	mf->mf_flags |= MFF_MKMNT;	/* XXX */
72238494Sobrien	if (mf_retry)
72338494Sobrien	  free_mntfs(mf_retry);
72438494Sobrien	return -1;
72538494Sobrien      } else {
72638494Sobrien#ifdef DEBUG
72738494Sobrien	dlog("foreground mount of \"%s\" ...", mf->mf_info);
72838494Sobrien#endif /* DEBUG */
72938494Sobrien	this_error = try_mount((voidp) mp);
73038494Sobrien	if (this_error < 0) {
73138494Sobrien	  if (!mf_retry)
73238494Sobrien	    mf_retry = dup_mntfs(mf);
73338494Sobrien	  cp->retry = TRUE;
73438494Sobrien	}
73538494Sobrien      }
73638494Sobrien    }
73738494Sobrien
73838494Sobrien    if (this_error >= 0) {
73938494Sobrien      if (this_error > 0) {
74038494Sobrien	amd_stats.d_merr++;
74138494Sobrien	if (mf != mf_retry) {
74238494Sobrien	  mf->mf_error = this_error;
74338494Sobrien	  mf->mf_flags |= MFF_ERROR;
74438494Sobrien	}
74538494Sobrien      }
74638494Sobrien
74738494Sobrien      /*
74838494Sobrien       * Wakeup anything waiting for this mount
74938494Sobrien       */
75038494Sobrien      wakeup((voidp) mf);
75138494Sobrien    }
75238494Sobrien  }
75338494Sobrien
75438494Sobrien  if (this_error && cp->retry) {
75538494Sobrien    free_mntfs(mf);
75638494Sobrien    mf = cp->mp->am_mnt = mf_retry;
75738494Sobrien    /*
75838494Sobrien     * Not retrying again (so far)
75938494Sobrien     */
76038494Sobrien    cp->retry = FALSE;
76138494Sobrien    cp->tried = FALSE;
76238494Sobrien    /*
76338494Sobrien     * Start at the beginning.
76438494Sobrien     * Rewind the location vector and
76538494Sobrien     * reset the default options.
76638494Sobrien     */
76738494Sobrien    cp->ivec = cp->xivec;
76838494Sobrien    cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
76938494Sobrien    /*
77038494Sobrien     * Arrange that amfs_auto_bgmount is called
77138494Sobrien     * after anything else happens.
77238494Sobrien     */
77338494Sobrien#ifdef DEBUG
77438494Sobrien    dlog("Arranging to retry mount of %s", cp->mp->am_path);
77538494Sobrien#endif /* DEBUG */
77638494Sobrien    sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
77738494Sobrien    if (cp->callout)
77838494Sobrien      untimeout(cp->callout);
77938494Sobrien    cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
78038494Sobrien
78138494Sobrien    cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
78238494Sobrien
78338494Sobrien    /*
78438494Sobrien     * Not done yet - so don't return anything
78538494Sobrien     */
78638494Sobrien    return -1;
78738494Sobrien  }
78838494Sobrien
78938494Sobrien  if (hard_error < 0 || this_error == 0)
79038494Sobrien    hard_error = this_error;
79138494Sobrien
79238494Sobrien  /*
79338494Sobrien   * Discard handle on duff filesystem.
79438494Sobrien   * This should never happen since it
79538494Sobrien   * should be caught by the case above.
79638494Sobrien   */
79738494Sobrien  if (mf_retry) {
79838494Sobrien    if (hard_error)
79938494Sobrien      plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
80038494Sobrien    free_mntfs(mf_retry);
80138494Sobrien  }
80238494Sobrien
80338494Sobrien  /*
80438494Sobrien   * If we get here, then either the mount succeeded or
80538494Sobrien   * there is no more mount information available.
80638494Sobrien   */
80738494Sobrien  if (hard_error < 0 && mp_error)
80838494Sobrien    hard_error = cp->mp->am_error = mp_error;
80938494Sobrien  if (hard_error > 0) {
81038494Sobrien    /*
81138494Sobrien     * Set a small(ish) timeout on an error node if
81238494Sobrien     * the error was not a time out.
81338494Sobrien     */
81438494Sobrien    switch (hard_error) {
81538494Sobrien    case ETIMEDOUT:
81638494Sobrien    case EWOULDBLOCK:
81738494Sobrien      cp->mp->am_timeo = 17;
81838494Sobrien      break;
81938494Sobrien    default:
82038494Sobrien      cp->mp->am_timeo = 5;
82138494Sobrien      break;
82238494Sobrien    }
82338494Sobrien    new_ttl(cp->mp);
82438494Sobrien  }
82538494Sobrien
82638494Sobrien  /*
82738494Sobrien   * Make sure that the error value in the mntfs has a
82838494Sobrien   * reasonable value.
82938494Sobrien   */
83038494Sobrien  if (mf->mf_error < 0) {
83138494Sobrien    mf->mf_error = hard_error;
83238494Sobrien    if (hard_error)
83338494Sobrien      mf->mf_flags |= MFF_ERROR;
83438494Sobrien  }
83538494Sobrien
83638494Sobrien  /*
83738494Sobrien   * In any case we don't need the continuation any more
83838494Sobrien   */
83938494Sobrien  free_continuation(cp);
84038494Sobrien
84138494Sobrien  return hard_error;
84238494Sobrien}
84338494Sobrien
84438494Sobrien
84538494Sobrien/*
84638494Sobrien * Automount interface to RPC lookup routine
84738494Sobrien * Find the corresponding entry and return
84838494Sobrien * the file handle for it.
84938494Sobrien */
85038494Sobrienam_node *
85138494Sobrienamfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
85238494Sobrien{
85338494Sobrien  am_node *ap, *new_mp, *ap_hung;
85438494Sobrien  char *info;			/* Mount info - where to get the file system */
85538494Sobrien  char **ivec, **xivec;		/* Split version of info */
85638494Sobrien  char *auto_opts;		/* Automount options */
85738494Sobrien  int error = 0;		/* Error so far */
85838494Sobrien  char path_name[MAXPATHLEN];	/* General path name buffer */
85938494Sobrien  char *pfname;			/* Path for database lookup */
86038494Sobrien  struct continuation *cp;	/* Continuation structure if need to mount */
86138494Sobrien  int in_progress = 0;		/* # of (un)mount in progress */
86238494Sobrien  char *dflts;
86338494Sobrien  mntfs *mf;
86438494Sobrien
86538494Sobrien#ifdef DEBUG
86638494Sobrien  dlog("in amfs_auto_lookuppn");
86738494Sobrien#endif /* DEBUG */
86838494Sobrien
86938494Sobrien  /*
87038494Sobrien   * If the server is shutting down
87138494Sobrien   * then don't return information
87238494Sobrien   * about the mount point.
87338494Sobrien   */
87438494Sobrien  if (amd_state == Finishing) {
87538494Sobrien#ifdef DEBUG
87638494Sobrien    if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
87738494Sobrien      dlog("%s mount ignored - going down", fname);
87838494Sobrien    } else {
87938494Sobrien      dlog("%s/%s mount ignored - going down", mp->am_path, fname);
88038494Sobrien    }
88138494Sobrien#endif /* DEBUG */
88238494Sobrien    ereturn(ENOENT);
88338494Sobrien  }
88438494Sobrien
88538494Sobrien  /*
88638494Sobrien   * Handle special case of "." and ".."
88738494Sobrien   */
88838494Sobrien  if (fname[0] == '.') {
88938494Sobrien    if (fname[1] == '\0')
89038494Sobrien      return mp;		/* "." is the current node */
89138494Sobrien    if (fname[1] == '.' && fname[2] == '\0') {
89238494Sobrien      if (mp->am_parent) {
89338494Sobrien#ifdef DEBUG
89438494Sobrien	dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
89538494Sobrien#endif /* DEBUG */
89638494Sobrien	return mp->am_parent;	/* ".." is the parent node */
89738494Sobrien      }
89838494Sobrien      ereturn(ESTALE);
89938494Sobrien    }
90038494Sobrien  }
90138494Sobrien
90238494Sobrien  /*
90338494Sobrien   * Check for valid key name.
90438494Sobrien   * If it is invalid then pretend it doesn't exist.
90538494Sobrien   */
90638494Sobrien  if (!valid_key(fname)) {
90738494Sobrien    plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
90838494Sobrien    ereturn(ENOENT);
90938494Sobrien  }
91038494Sobrien
91138494Sobrien  /*
91238494Sobrien   * Expand key name.
91338494Sobrien   * fname is now a private copy.
91438494Sobrien   */
91538494Sobrien  fname = expand_key(fname);
91638494Sobrien
91738494Sobrien  for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
91838494Sobrien    /*
91938494Sobrien     * Otherwise search children of this node
92038494Sobrien     */
92138494Sobrien    if (FSTREQ(ap->am_name, fname)) {
92238494Sobrien      mf = ap->am_mnt;
92338494Sobrien      if (ap->am_error) {
92438494Sobrien	error = ap->am_error;
92538494Sobrien	continue;
92638494Sobrien      }
92738494Sobrien      /*
92838494Sobrien       * If the error code is undefined then it must be
92938494Sobrien       * in progress.
93038494Sobrien       */
93138494Sobrien      if (mf->mf_error < 0)
93238494Sobrien	goto in_progrss;
93338494Sobrien
93438494Sobrien      /*
93538494Sobrien       * Check for a hung node
93638494Sobrien       */
93738494Sobrien      if (FSRV_ISDOWN(mf->mf_server)) {
93838494Sobrien#ifdef DEBUG
93938494Sobrien	dlog("server hung");
94038494Sobrien#endif /* DEBUG */
94138494Sobrien	error = ap->am_error;
94238494Sobrien	ap_hung = ap;
94338494Sobrien	continue;
94438494Sobrien      }
94538494Sobrien      /*
94638494Sobrien       * If there was a previous error with this node
94738494Sobrien       * then return that error code.
94838494Sobrien       */
94938494Sobrien      if (mf->mf_flags & MFF_ERROR) {
95038494Sobrien	error = mf->mf_error;
95138494Sobrien	continue;
95238494Sobrien      }
95338494Sobrien      if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
95438494Sobrien      in_progrss:
95538494Sobrien	/*
95638494Sobrien	 * If the fs is not mounted or it is unmounting then there
95738494Sobrien	 * is a background (un)mount in progress.  In this case
95838494Sobrien	 * we just drop the RPC request (return nil) and
95938494Sobrien	 * wait for a retry, by which time the (un)mount may
96038494Sobrien	 * have completed.
96138494Sobrien	 */
96238494Sobrien#ifdef DEBUG
96338494Sobrien	dlog("ignoring mount of %s in %s -- flags (%x) in progress",
96438494Sobrien	     fname, mf->mf_mount, mf->mf_flags);
96538494Sobrien#endif /* DEBUG */
96638494Sobrien	in_progress++;
96738494Sobrien	continue;
96838494Sobrien      }
96938494Sobrien
97038494Sobrien      /*
97138494Sobrien       * Otherwise we have a hit: return the current mount point.
97238494Sobrien       */
97338494Sobrien#ifdef DEBUG
97438494Sobrien      dlog("matched %s in %s", fname, ap->am_path);
97538494Sobrien#endif /* DEBUG */
97638494Sobrien      XFREE(fname);
97738494Sobrien      return ap;
97838494Sobrien    }
97938494Sobrien  }
98038494Sobrien
98138494Sobrien  if (in_progress) {
98238494Sobrien#ifdef DEBUG
98338494Sobrien    dlog("Waiting while %d mount(s) in progress", in_progress);
98438494Sobrien#endif /* DEBUG */
98538494Sobrien    XFREE(fname);
98638494Sobrien    ereturn(-1);
98738494Sobrien  }
98838494Sobrien
98938494Sobrien  /*
99042629Sobrien   * If an error occurred then return it.
99138494Sobrien   */
99238494Sobrien  if (error) {
99338494Sobrien#ifdef DEBUG
99438494Sobrien    errno = error;		/* XXX */
99538494Sobrien    dlog("Returning error: %m", error);
99638494Sobrien#endif /* DEBUG */
99738494Sobrien    XFREE(fname);
99838494Sobrien    ereturn(error);
99938494Sobrien  }
100038494Sobrien
100138494Sobrien  /*
100238494Sobrien   * If doing a delete then don't create again!
100338494Sobrien   */
100438494Sobrien  switch (op) {
100538494Sobrien  case VLOOK_DELETE:
100638494Sobrien    ereturn(ENOENT);
100738494Sobrien
100838494Sobrien  case VLOOK_CREATE:
100938494Sobrien    break;
101038494Sobrien
101138494Sobrien  default:
101238494Sobrien    plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
101338494Sobrien    ereturn(EINVAL);
101438494Sobrien  }
101538494Sobrien
101638494Sobrien  /*
101738494Sobrien   * If the server is going down then just return,
101838494Sobrien   * don't try to mount any more file systems
101938494Sobrien   */
102038494Sobrien  if ((int) amd_state >= (int) Finishing) {
102138494Sobrien#ifdef DEBUG
102238494Sobrien    dlog("not found - server going down anyway");
102338494Sobrien#endif /* DEBUG */
102438494Sobrien    XFREE(fname);
102538494Sobrien    ereturn(ENOENT);
102638494Sobrien  }
102738494Sobrien
102838494Sobrien  /*
102938494Sobrien   * If we get there then this is a reference to an,
103038494Sobrien   * as yet, unknown name so we need to search the mount
103138494Sobrien   * map for it.
103238494Sobrien   */
103338494Sobrien  if (mp->am_pref) {
103438494Sobrien    sprintf(path_name, "%s%s", mp->am_pref, fname);
103538494Sobrien    pfname = path_name;
103638494Sobrien  } else {
103738494Sobrien    pfname = fname;
103838494Sobrien  }
103938494Sobrien
104038494Sobrien  mf = mp->am_mnt;
104138494Sobrien
104238494Sobrien#ifdef DEBUG
104338494Sobrien  dlog("will search map info in %s to find %s", mf->mf_info, pfname);
104438494Sobrien#endif /* DEBUG */
104538494Sobrien  /*
104638494Sobrien   * Consult the oracle for some mount information.
104738494Sobrien   * info is malloc'ed and belongs to this routine.
104838494Sobrien   * It ends up being free'd in free_continuation().
104938494Sobrien   *
105038494Sobrien   * Note that this may return -1 indicating that information
105138494Sobrien   * is not yet available.
105238494Sobrien   */
105338494Sobrien  error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
105438494Sobrien  if (error) {
105538494Sobrien    if (error > 0)
105638494Sobrien      plog(XLOG_MAP, "No map entry for %s", pfname);
105738494Sobrien    else
105838494Sobrien      plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
105938494Sobrien    XFREE(fname);
106038494Sobrien    ereturn(error);
106138494Sobrien  }
106238494Sobrien#ifdef DEBUG
106338494Sobrien  dlog("mount info is %s", info);
106438494Sobrien#endif /* DEBUG */
106538494Sobrien
106638494Sobrien  /*
106738494Sobrien   * Split info into an argument vector.
106838494Sobrien   * The vector is malloc'ed and belongs to
106938494Sobrien   * this routine.  It is free'd in free_continuation()
107038494Sobrien   */
107138494Sobrien  xivec = ivec = strsplit(info, ' ', '\"');
107238494Sobrien
107338494Sobrien  /*
107438494Sobrien   * Default error code...
107538494Sobrien   */
107638494Sobrien  if (ap_hung)
107738494Sobrien    error = EWOULDBLOCK;
107838494Sobrien  else
107938494Sobrien    error = ENOENT;
108038494Sobrien
108138494Sobrien  /*
108238494Sobrien   * Allocate a new map
108338494Sobrien   */
108438494Sobrien  new_mp = exported_ap_alloc();
108538494Sobrien  if (new_mp == 0) {
108638494Sobrien    XFREE(xivec);
108738494Sobrien    XFREE(info);
108838494Sobrien    XFREE(fname);
108938494Sobrien    ereturn(ENOSPC);
109038494Sobrien  }
109138494Sobrien  if (mf->mf_auto)
109238494Sobrien    auto_opts = mf->mf_auto;
109338494Sobrien  else
109438494Sobrien    auto_opts = "";
109538494Sobrien
109638494Sobrien  auto_opts = strdup(auto_opts);
109738494Sobrien
109838494Sobrien#ifdef DEBUG
109938494Sobrien  dlog("searching for /defaults entry");
110038494Sobrien#endif /* DEBUG */
110138494Sobrien  if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
110238494Sobrien    char *dfl;
110338494Sobrien    char **rvec;
110438494Sobrien#ifdef DEBUG
110538494Sobrien    dlog("/defaults gave %s", dflts);
110638494Sobrien#endif /* DEBUG */
110738494Sobrien    if (*dflts == '-')
110838494Sobrien      dfl = dflts + 1;
110938494Sobrien    else
111038494Sobrien      dfl = dflts;
111138494Sobrien
111238494Sobrien    /*
111338494Sobrien     * Chop the defaults up
111438494Sobrien     */
111538494Sobrien    rvec = strsplit(dfl, ' ', '\"');
111638494Sobrien
111738494Sobrien    if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
111838494Sobrien      /*
111938494Sobrien       * Pick whichever first entry matched the list of selectors.
112038494Sobrien       * Strip the selectors from the string, and assign to dfl the
112138494Sobrien       * rest of the string.
112238494Sobrien       */
112338494Sobrien      if (rvec) {
112438494Sobrien	am_opts ap;
112538494Sobrien	am_ops *pt;
112638494Sobrien	char **sp = rvec;
112738494Sobrien	while (*sp) {		/* loop until you find something, if any */
112838494Sobrien	  memset((char *) &ap, 0, sizeof(am_opts));
112938494Sobrien	  pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
113038494Sobrien			 mp->am_parent->am_mnt->mf_info);
113141142Sobrien	  free_opts(&ap);	/* don't leak */
113238494Sobrien	  if (pt == &amfs_error_ops) {
113338494Sobrien	    plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
113438494Sobrien	  } else {
113538494Sobrien	    dfl = strip_selectors(*sp, "/defaults");
113638494Sobrien	    plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
113738494Sobrien	    break;
113838494Sobrien	  }
113938494Sobrien	  ++sp;
114038494Sobrien	}
114138494Sobrien      }
114238494Sobrien    } else {			/* not enable_default_selectors */
114338494Sobrien      /*
114438494Sobrien       * Extract first value
114538494Sobrien       */
114638494Sobrien      dfl = rvec[0];
114738494Sobrien    }
114838494Sobrien
114938494Sobrien    /*
115038494Sobrien     * If there were any values at all...
115138494Sobrien     */
115238494Sobrien    if (dfl) {
115338494Sobrien      /*
115438494Sobrien       * Log error if there were other values
115538494Sobrien       */
115638494Sobrien      if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
115738494Sobrien# ifdef DEBUG
115838494Sobrien	dlog("/defaults chopped into %s", dfl);
115938494Sobrien# endif /* DEBUG */
116038494Sobrien	plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
116138494Sobrien      }
116238494Sobrien
116338494Sobrien      /*
116438494Sobrien       * Prepend to existing defaults if they exist,
116538494Sobrien       * otherwise just use these defaults.
116638494Sobrien       */
116738494Sobrien      if (*auto_opts && *dfl) {
116838494Sobrien	char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
116938494Sobrien	sprintf(nopts, "%s;%s", dfl, auto_opts);
117038494Sobrien	XFREE(auto_opts);
117138494Sobrien	auto_opts = nopts;
117238494Sobrien      } else if (*dfl) {
117338494Sobrien	auto_opts = strealloc(auto_opts, dfl);
117438494Sobrien      }
117538494Sobrien    }
117638494Sobrien    XFREE(dflts);
117738494Sobrien    /*
117838494Sobrien     * Don't need info vector any more
117938494Sobrien     */
118038494Sobrien    XFREE(rvec);
118138494Sobrien  }
118238494Sobrien
118338494Sobrien  /*
118438494Sobrien   * Fill it in
118538494Sobrien   */
118638494Sobrien  init_map(new_mp, fname);
118738494Sobrien
118838494Sobrien  /*
118938494Sobrien   * Put it in the table
119038494Sobrien   */
119138494Sobrien  insert_am(new_mp, mp);
119238494Sobrien
119338494Sobrien  /*
119438494Sobrien   * Fill in some other fields,
119538494Sobrien   * path and mount point.
119638494Sobrien   *
119738494Sobrien   * bugfix: do not prepend old am_path if direct map
119838494Sobrien   *         <wls@astro.umd.edu> William Sebok
119938494Sobrien   */
120038494Sobrien  new_mp->am_path = str3cat(new_mp->am_path,
120138494Sobrien			    mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
120238494Sobrien			    *fname == '/' ? "" : "/", fname);
120338494Sobrien
120438494Sobrien#ifdef DEBUG
120538494Sobrien  dlog("setting path to %s", new_mp->am_path);
120638494Sobrien#endif /* DEBUG */
120738494Sobrien
120838494Sobrien  /*
120938494Sobrien   * Take private copy of pfname
121038494Sobrien   */
121138494Sobrien  pfname = strdup(pfname);
121238494Sobrien
121338494Sobrien  /*
121438494Sobrien   * Construct a continuation
121538494Sobrien   */
121638494Sobrien  cp = ALLOC(struct continuation);
121738494Sobrien  cp->callout = 0;
121838494Sobrien  cp->mp = new_mp;
121938494Sobrien  cp->xivec = xivec;
122038494Sobrien  cp->ivec = ivec;
122138494Sobrien  cp->info = info;
122238494Sobrien  cp->key = pfname;
122338494Sobrien  cp->auto_opts = auto_opts;
122438494Sobrien  cp->retry = FALSE;
122538494Sobrien  cp->tried = FALSE;
122638494Sobrien  cp->start = clocktime();
122738494Sobrien  cp->def_opts = strdup(auto_opts);
122838494Sobrien  memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
122938494Sobrien
123038494Sobrien  /*
123138494Sobrien   * Try and mount the file system.  If this succeeds immediately (possible
123238494Sobrien   * for a ufs file system) then return the attributes, otherwise just
123338494Sobrien   * return an error.
123438494Sobrien   */
123538494Sobrien  error = amfs_auto_bgmount(cp, error);
123638494Sobrien  reschedule_timeout_mp();
123738494Sobrien  if (!error) {
123838494Sobrien    XFREE(fname);
123938494Sobrien    return new_mp;
124038494Sobrien  }
124138494Sobrien
124238494Sobrien  /*
124338494Sobrien   * Code for quick reply.  If nfs_program_2_transp is set, then
124438494Sobrien   * its the transp that's been passed down from nfs_program_2().
124538494Sobrien   * If new_mp->am_transp is not already set, set it by copying in
124638494Sobrien   * nfs_program_2_transp.  Once am_transp is set, quick_reply() can
124738494Sobrien   * use it to send a reply to the client that requested this mount.
124838494Sobrien   */
124938494Sobrien  if (nfs_program_2_transp && !new_mp->am_transp) {
125038494Sobrien    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
125138494Sobrien    *(new_mp->am_transp) = *nfs_program_2_transp;
125238494Sobrien  }
125338494Sobrien  if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
125438494Sobrien    new_mp->am_error = error;
125538494Sobrien
125638494Sobrien  assign_error_mntfs(new_mp);
125738494Sobrien
125838494Sobrien  XFREE(fname);
125938494Sobrien
126038494Sobrien  ereturn(error);
126138494Sobrien}
126238494Sobrien
126338494Sobrien
126438494Sobrien/*
126538494Sobrien * Locate next node in sibling list which is mounted
126638494Sobrien * and is not an error node.
126738494Sobrien */
126838494Sobrienam_node *
126938494Sobriennext_nonerror_node(am_node *xp)
127038494Sobrien{
127138494Sobrien  mntfs *mf;
127238494Sobrien
127338494Sobrien  /*
127438494Sobrien   * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
127538494Sobrien   * Fixes a race condition when mounting direct automounts.
127638494Sobrien   * Also fixes a problem when doing a readdir on a directory
127738494Sobrien   * containing hung automounts.
127838494Sobrien   */
127938494Sobrien  while (xp &&
128038494Sobrien	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
128138494Sobrien	  mf->mf_error != 0 ||	/* There was a mntfs error */
128238494Sobrien	  xp->am_error != 0 ||	/* There was a mount error */
128338494Sobrien	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
128438494Sobrien	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
128538494Sobrien	 )
128638494Sobrien    xp = xp->am_osib;
128738494Sobrien
128838494Sobrien  return xp;
128938494Sobrien}
129038494Sobrien
129138494Sobrien
129238494Sobrien/*
129338494Sobrien * This readdir function which call a special version of it that allows
129438494Sobrien * browsing if browsable_dirs=yes was set on the map.
129538494Sobrien */
129638494Sobrienint
129738494Sobrienamfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
129838494Sobrien{
129938494Sobrien  u_int gen = *(u_int *) cookie;
130038494Sobrien  am_node *xp;
130138494Sobrien  mntent_t mnt;
130238494Sobrien
130338494Sobrien  dp->dl_eof = FALSE;		/* assume readdir not done */
130438494Sobrien
130538494Sobrien  /* check if map is browsable */
130638494Sobrien  if (mp->am_mnt && mp->am_mnt->mf_mopts) {
130738494Sobrien    mnt.mnt_opts = mp->am_mnt->mf_mopts;
130838494Sobrien    if (hasmntopt(&mnt, "fullybrowsable"))
130938494Sobrien      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
131038494Sobrien    if (hasmntopt(&mnt, "browsable"))
131138494Sobrien      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
131238494Sobrien  }
131338494Sobrien
131438494Sobrien  if (gen == 0) {
131538494Sobrien    /*
131638494Sobrien     * In the default instance (which is used to start a search) we return
131738494Sobrien     * "." and "..".
131838494Sobrien     *
131938494Sobrien     * This assumes that the count is big enough to allow both "." and ".."
132038494Sobrien     * to be returned in a single packet.  If it isn't (which would be
132138494Sobrien     * fairly unbelievable) then tough.
132238494Sobrien     */
132338494Sobrien#ifdef DEBUG
132438494Sobrien    dlog("default search");
132538494Sobrien#endif /* DEBUG */
132638494Sobrien    /*
132738494Sobrien     * Check for enough room.  This is extremely approximate but is more
132838494Sobrien     * than enough space.  Really need 2 times:
132938494Sobrien     *      4byte fileid
133038494Sobrien     *      4byte cookie
133138494Sobrien     *      4byte name length
133238494Sobrien     *      4byte name
133338494Sobrien     * plus the dirlist structure */
133438494Sobrien    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
133538494Sobrien      return EINVAL;
133638494Sobrien
133738494Sobrien    xp = next_nonerror_node(mp->am_child);
133838494Sobrien    dp->dl_entries = ep;
133938494Sobrien
134038494Sobrien    /* construct "." */
134138494Sobrien    ep[0].ne_fileid = mp->am_gen;
134238494Sobrien    ep[0].ne_name = ".";
134338494Sobrien    ep[0].ne_nextentry = &ep[1];
134438494Sobrien    *(u_int *) ep[0].ne_cookie = 0;
134538494Sobrien
134638494Sobrien    /* construct ".." */
134738494Sobrien    if (mp->am_parent)
134838494Sobrien      ep[1].ne_fileid = mp->am_parent->am_gen;
134938494Sobrien    else
135038494Sobrien      ep[1].ne_fileid = mp->am_gen;
135138494Sobrien    ep[1].ne_name = "..";
135238494Sobrien    ep[1].ne_nextentry = 0;
135338494Sobrien    *(u_int *) ep[1].ne_cookie =
135438494Sobrien      xp ? xp->am_gen : ~(u_int) 0;
135538494Sobrien
135638494Sobrien    if (!xp)
135738494Sobrien      dp->dl_eof = TRUE;	/* by default assume readdir done */
135838494Sobrien
135938494Sobrien    return 0;
136038494Sobrien  }
136138494Sobrien#ifdef DEBUG
136238494Sobrien  dlog("real child");
136338494Sobrien#endif /* DEBUG */
136438494Sobrien
136538494Sobrien  if (gen == ~(u_int) 0) {
136638494Sobrien#ifdef DEBUG
136738494Sobrien    dlog("End of readdir in %s", mp->am_path);
136838494Sobrien#endif /* DEBUG */
136938494Sobrien    dp->dl_eof = TRUE;
137038494Sobrien    dp->dl_entries = 0;
137138494Sobrien    return 0;
137238494Sobrien  }
137338494Sobrien
137438494Sobrien  /* non-browsable directories code */
137538494Sobrien  xp = mp->am_child;
137638494Sobrien  while (xp && xp->am_gen != gen)
137738494Sobrien    xp = xp->am_osib;
137838494Sobrien
137938494Sobrien  if (xp) {
138038494Sobrien    int nbytes = count / 2;	/* conservative */
138138494Sobrien    int todo = MAX_READDIR_ENTRIES;
138238494Sobrien    dp->dl_entries = ep;
138338494Sobrien    do {
138438494Sobrien      am_node *xp_next = next_nonerror_node(xp->am_osib);
138538494Sobrien
138638494Sobrien      if (xp_next) {
138738494Sobrien	*(u_int *) ep->ne_cookie = xp_next->am_gen;
138838494Sobrien      } else {
138938494Sobrien	*(u_int *) ep->ne_cookie = ~(u_int) 0;
139038494Sobrien	dp->dl_eof = TRUE;
139138494Sobrien      }
139238494Sobrien
139338494Sobrien      ep->ne_fileid = xp->am_gen;
139438494Sobrien      ep->ne_name = xp->am_name;
139538494Sobrien      nbytes -= sizeof(*ep) + 1;
139638494Sobrien      if (xp->am_name)
139738494Sobrien	nbytes -= strlen(xp->am_name);
139838494Sobrien
139938494Sobrien      xp = xp_next;
140038494Sobrien
140138494Sobrien      if (nbytes > 0 && !dp->dl_eof && todo > 1) {
140238494Sobrien	ep->ne_nextentry = ep + 1;
140338494Sobrien	ep++;
140438494Sobrien	--todo;
140538494Sobrien      } else {
140638494Sobrien	todo = 0;
140738494Sobrien      }
140838494Sobrien    } while (todo > 0);
140938494Sobrien
141038494Sobrien    ep->ne_nextentry = 0;
141138494Sobrien
141238494Sobrien    return 0;
141338494Sobrien  }
141438494Sobrien  return ESTALE;
141538494Sobrien}
141638494Sobrien
141738494Sobrien
141838494Sobrien/* This one is called only if map is browsable */
141938494Sobrienstatic int
142038494Sobrienamfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
142138494Sobrien{
142238494Sobrien  u_int gen = *(u_int *) cookie;
142338494Sobrien  int chain_length, i;
142438494Sobrien  static nfsentry *te, *te_next;
142538494Sobrien#ifdef DEBUG_READDIR
142638494Sobrien  nfsentry *ne;
142738494Sobrien  static int j;
142838494Sobrien#endif /* DEBUG_READDIR */
142938494Sobrien
143038494Sobrien  dp->dl_eof = FALSE;		/* assume readdir not done */
143138494Sobrien
143238494Sobrien#ifdef DEBUG_READDIR
143338494Sobrien  plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
143438494Sobrien       gen, count);
143538494Sobrien#endif /* DEBUG_READDIR */
143638494Sobrien
143738494Sobrien  if (gen == 0) {
143838494Sobrien    /*
143938494Sobrien     * In the default instance (which is used to start a search) we return
144038494Sobrien     * "." and "..".
144138494Sobrien     *
144238494Sobrien     * This assumes that the count is big enough to allow both "." and ".."
144338494Sobrien     * to be returned in a single packet.  If it isn't (which would be
144438494Sobrien     * fairly unbelievable) then tough.
144538494Sobrien     */
144638494Sobrien#ifdef DEBUG
144738494Sobrien    dlog("default search");
144838494Sobrien#endif /* DEBUG */
144938494Sobrien    /*
145038494Sobrien     * Check for enough room.  This is extremely approximate but is more
145138494Sobrien     * than enough space.  Really need 2 times:
145238494Sobrien     *      4byte fileid
145338494Sobrien     *      4byte cookie
145438494Sobrien     *      4byte name length
145538494Sobrien     *      4byte name
145638494Sobrien     * plus the dirlist structure */
145738494Sobrien    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
145838494Sobrien      return EINVAL;
145938494Sobrien
146038494Sobrien    /*
146138494Sobrien     * compute # of entries to send in this chain.
146238494Sobrien     * heuristics: 128 bytes per entry.
146338494Sobrien     * This is too much probably, but it seems to work better because
146438494Sobrien     * of the re-entrant nature of nfs_readdir, and esp. on systems
146538494Sobrien     * like OpenBSD 2.2.
146638494Sobrien     */
146738494Sobrien    chain_length = count / 128;
146838494Sobrien
146938494Sobrien    /* reset static state counters */
147038494Sobrien    te = te_next = NULL;
147138494Sobrien
147238494Sobrien    dp->dl_entries = ep;
147338494Sobrien
147438494Sobrien    /* construct "." */
147538494Sobrien    ep[0].ne_fileid = mp->am_gen;
147638494Sobrien    ep[0].ne_name = ".";
147738494Sobrien    ep[0].ne_nextentry = &ep[1];
147838494Sobrien    *(u_int *) ep[0].ne_cookie = 0;
147938494Sobrien
148038494Sobrien    /* construct ".." */
148138494Sobrien    if (mp->am_parent)
148238494Sobrien      ep[1].ne_fileid = mp->am_parent->am_gen;
148338494Sobrien    else
148438494Sobrien      ep[1].ne_fileid = mp->am_gen;
148538494Sobrien    ep[1].ne_name = "..";
148638494Sobrien    ep[1].ne_nextentry = 0;
148738494Sobrien    *(u_int *) ep[1].ne_cookie = ~(u_int) 0;
148838494Sobrien
148938494Sobrien    /*
149038494Sobrien     * If map is browsable, call a function make_entry_chain() to construct
149138494Sobrien     * a linked list of unmounted keys, and return it.  Then link the chain
149238494Sobrien     * to the regular list.  Get the chain only once, but return
149338494Sobrien     * chunks of it each time.
149438494Sobrien     */
149538494Sobrien    te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
149638494Sobrien    if (!te)
149738494Sobrien      return 0;
149838494Sobrien#ifdef DEBUG_READDIR
149938494Sobrien    j = 0;
150038494Sobrien    for (ne=te; ne; ne=ne->ne_nextentry)
150138494Sobrien      plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
150238494Sobrien#endif /* DEBUG_READDIR */
150338494Sobrien
150438494Sobrien    /* return only "chain_length" entries */
150538494Sobrien    te_next = te;
150638494Sobrien    for (i=1; i<chain_length; ++i) {
150738494Sobrien      te_next = te_next->ne_nextentry;
150838494Sobrien      if (!te_next)
150938494Sobrien	break;
151038494Sobrien    }
151138494Sobrien    if (te_next) {
151238494Sobrien      nfsentry *te_saved = te_next->ne_nextentry;
151338494Sobrien      te_next->ne_nextentry = NULL; /* terminate "te" chain */
151442629Sobrien      te_next = te_saved;	/* save rest of "te" for next iteration */
151538494Sobrien      dp->dl_eof = FALSE;	/* tell readdir there's more */
151638494Sobrien    } else {
151738494Sobrien      dp->dl_eof = TRUE;	/* tell readdir that's it */
151838494Sobrien    }
151938494Sobrien    ep[1].ne_nextentry = te;	/* append this chunk of "te" chain */
152038494Sobrien#ifdef DEBUG_READDIR
152138494Sobrien    for (ne=te; ne; ne=ne->ne_nextentry)
152238494Sobrien      plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
152338494Sobrien#endif /* DEBUG_READDIR */
152438494Sobrien    return 0;
152538494Sobrien  } /* end of "if (gen == 0)" statement */
152638494Sobrien
152738494Sobrien#ifdef DEBUG
152838494Sobrien  dlog("real child");
152938494Sobrien#endif /* DEBUG */
153038494Sobrien
153138494Sobrien  if (gen == ~(u_int) 0) {
153238494Sobrien#ifdef DEBUG
153338494Sobrien    dlog("End of readdir in %s", mp->am_path);
153438494Sobrien#endif /* DEBUG */
153538494Sobrien    dp->dl_eof = TRUE;
153638494Sobrien    dp->dl_entries = 0;
153738494Sobrien    return 0;
153838494Sobrien  }
153938494Sobrien
154038494Sobrien  /*
154138494Sobrien   * If browsable directories, then continue serving readdir() with another
154238494Sobrien   * chunk of entries, starting from where we left off (when gen was equal
154338494Sobrien   * to 0).  Once again, assume last chunk served to readdir.
154438494Sobrien   */
154538494Sobrien  dp->dl_eof = TRUE;
154638494Sobrien  dp->dl_entries = ep;
154738494Sobrien
154838494Sobrien  te = te_next;			/* reset 'te' from last saved te_next */
154938494Sobrien  if (!te) {			/* another indicator of end of readdir */
155038494Sobrien    dp->dl_entries = 0;
155138494Sobrien    return 0;
155238494Sobrien  }
155338494Sobrien  /*
155438494Sobrien   * compute # of entries to send in this chain.
155538494Sobrien   * heuristics: 128 bytes per entry.
155638494Sobrien   */
155738494Sobrien  chain_length = count / 128;
155838494Sobrien
155938494Sobrien  /* return only "chain_length" entries */
156038494Sobrien  for (i=1; i<chain_length; ++i) {
156138494Sobrien    te_next = te_next->ne_nextentry;
156238494Sobrien    if (!te_next)
156338494Sobrien      break;
156438494Sobrien  }
156538494Sobrien  if (te_next) {
156638494Sobrien    nfsentry *te_saved = te_next->ne_nextentry;
156738494Sobrien    te_next->ne_nextentry = NULL; /* terminate "te" chain */
156842629Sobrien    te_next = te_saved;		/* save rest of "te" for next iteration */
156938494Sobrien    dp->dl_eof = FALSE;		/* tell readdir there's more */
157038494Sobrien  }
157138494Sobrien  ep = te;			/* send next chunk of "te" chain */
157238494Sobrien  dp->dl_entries = ep;
157338494Sobrien#ifdef DEBUG_READDIR
157438494Sobrien  plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
157538494Sobrien       dp->dl_entries, te_next, dp->dl_eof);
157638494Sobrien  for (ne=te; ne; ne=ne->ne_nextentry)
157738494Sobrien    plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
157838494Sobrien#endif /* DEBUG_READDIR */
157938494Sobrien  return 0;
158038494Sobrien}
158138494Sobrien
158238494Sobrien
158338494Sobrienint
158438494Sobrienamfs_auto_fmount(am_node *mp)
158538494Sobrien{
158638494Sobrien  mntfs *mf = mp->am_mnt;
158738494Sobrien  return (*mf->mf_ops->fmount_fs) (mf);
158838494Sobrien}
158938494Sobrien
159038494Sobrien
159138494Sobrienint
159238494Sobrienamfs_auto_fumount(am_node *mp)
159338494Sobrien{
159438494Sobrien  mntfs *mf = mp->am_mnt;
159538494Sobrien  return (*mf->mf_ops->fumount_fs) (mf);
159638494Sobrien}
1597