amfs_auto.c revision 38494
1/*
2 * Copyright (c) 1997-1998 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *      This product includes software developed by the University of
22 *      California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *      %W% (Berkeley) %G%
40 *
41 * $Id: amfs_auto.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
42 *
43 */
44
45/*
46 * Automount file system
47 */
48
49#ifdef HAVE_CONFIG_H
50# include <config.h>
51#endif /* HAVE_CONFIG_H */
52#include <am_defs.h>
53#include <amd.h>
54
55/****************************************************************************
56 *** MACROS                                                               ***
57 ****************************************************************************/
58#define	IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
59
60/* DEVELOPERS: turn this on for special debugging of readdir code */
61#undef DEBUG_READDIR
62
63/****************************************************************************
64 *** STRUCTURES                                                           ***
65 ****************************************************************************/
66
67
68
69/****************************************************************************
70 *** FORWARD DEFINITIONS                                                  ***
71 ****************************************************************************/
72static int amfs_auto_bgmount(struct continuation * cp, int mpe);
73static int amfs_auto_mount(am_node *mp);
74static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
75static void amfs_auto_umounted(am_node *mp);
76
77
78/****************************************************************************
79 *** OPS STRUCTURES                                                       ***
80 ****************************************************************************/
81am_ops amfs_auto_ops =
82{
83  "auto",
84  amfs_auto_match,
85  0,				/* amfs_auto_init */
86  amfs_auto_mount,
87  0,
88  amfs_auto_umount,
89  0,
90  amfs_auto_lookuppn,
91  amfs_auto_readdir,
92  0,				/* amfs_auto_readlink */
93  0,				/* amfs_auto_mounted */
94  amfs_auto_umounted,
95  find_amfs_auto_srvr,
96  FS_AMQINFO | FS_DIRECTORY
97};
98
99
100/****************************************************************************
101 *** FUNCTIONS                                                             ***
102 ****************************************************************************/
103/*
104 * AMFS_AUTO needs nothing in particular.
105 */
106char *
107amfs_auto_match(am_opts *fo)
108{
109  char *p = fo->opt_rfs;
110
111  if (!fo->opt_rfs) {
112    plog(XLOG_USER, "auto: no mount point named (rfs:=)");
113    return 0;
114  }
115  if (!fo->opt_fs) {
116    plog(XLOG_USER, "auto: no map named (fs:=)");
117    return 0;
118  }
119
120  /*
121   * Swap round fs:= and rfs:= options
122   * ... historical (jsp)
123   */
124  fo->opt_rfs = fo->opt_fs;
125  fo->opt_fs = p;
126
127  /*
128   * mtab entry turns out to be the name of the mount map
129   */
130  return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
131}
132
133
134
135
136/*
137 * Build a new map cache for this node, or re-use
138 * an existing cache for the same map.
139 */
140void
141amfs_auto_mkcacheref(mntfs *mf)
142{
143  char *cache;
144
145  if (mf->mf_fo && mf->mf_fo->opt_cache)
146    cache = mf->mf_fo->opt_cache;
147  else
148    cache = "none";
149  mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
150				     mf->mf_fo->opt_maptype);
151  mf->mf_prfree = mapc_free;
152}
153
154
155/*
156 * Mount a sub-mount
157 */
158static int
159amfs_auto_mount(am_node *mp)
160{
161  mntfs *mf = mp->am_mnt;
162
163  /*
164   * Pseudo-directories are used to provide some structure
165   * to the automounted directories instead
166   * of putting them all in the top-level automount directory.
167   *
168   * Here, just increment the parent's link count.
169   */
170  mp->am_parent->am_fattr.na_nlink++;
171
172  /*
173   * Info field of . means use parent's info field.
174   * Historical - not documented.
175   */
176  if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
177    mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
178
179  /*
180   * Compute prefix:
181   *
182   * If there is an option prefix then use that else
183   * If the parent had a prefix then use that with name
184   *      of this node appended else
185   * Use the name of this node.
186   *
187   * That means if you want no prefix you must say so
188   * in the map.
189   */
190  if (mf->mf_fo->opt_pref) {
191    /* allow pref:=null to set a real null prefix */
192    if (STREQ(mf->mf_fo->opt_pref, "null")) {
193      mp->am_pref = "";
194    } else {
195      /*
196       * the prefix specified as an option
197       */
198      mp->am_pref = strdup(mf->mf_fo->opt_pref);
199    }
200  } else {
201    /*
202     * else the parent's prefix
203     * followed by the name
204     * followed by /
205     */
206    char *ppref = mp->am_parent->am_pref;
207    if (ppref == 0)
208      ppref = "";
209    mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
210  }
211
212  /*
213   * Attach a map cache
214   */
215  amfs_auto_mkcacheref(mf);
216
217  return 0;
218}
219
220
221
222
223/*
224 * Unmount an automount sub-node
225 */
226int
227amfs_auto_umount(am_node *mp)
228{
229  return 0;
230}
231
232
233/*
234 * Unmount an automount node
235 */
236static void
237amfs_auto_umounted(am_node *mp)
238{
239  /*
240   * If this is a pseudo-directory then just adjust the link count
241   * in the parent, otherwise call the generic unmount routine
242   */
243  if (mp->am_parent && mp->am_parent->am_parent)
244    --mp->am_parent->am_fattr.na_nlink;
245}
246
247
248/*
249 * Discard an old continuation
250 */
251void
252free_continuation(struct continuation *cp)
253{
254  if (cp->callout)
255    untimeout(cp->callout);
256  XFREE(cp->key);
257  XFREE(cp->xivec);
258  XFREE(cp->info);
259  XFREE(cp->auto_opts);
260  XFREE(cp->def_opts);
261  free_opts(&cp->fs_opts);
262  XFREE(cp);
263}
264
265
266/*
267 * Discard the underlying mount point and replace
268 * with a reference to an error filesystem.
269 */
270void
271assign_error_mntfs(am_node *mp)
272{
273  if (mp->am_error > 0) {
274    /*
275     * Save the old error code
276     */
277    int error = mp->am_error;
278    if (error <= 0)
279      error = mp->am_mnt->mf_error;
280    /*
281     * Discard the old filesystem
282     */
283    free_mntfs(mp->am_mnt);
284    /*
285     * Allocate a new error reference
286     */
287    mp->am_mnt = new_mntfs();
288    /*
289     * Put back the error code
290     */
291    mp->am_mnt->mf_error = error;
292    mp->am_mnt->mf_flags |= MFF_ERROR;
293    /*
294     * Zero the error in the mount point
295     */
296    mp->am_error = 0;
297  }
298}
299
300
301/*
302 * The continuation function.  This is called by
303 * the task notifier when a background mount attempt
304 * completes.
305 */
306void
307amfs_auto_cont(int rc, int term, voidp closure)
308{
309  struct continuation *cp = (struct continuation *) closure;
310  mntfs *mf = cp->mp->am_mnt;
311
312  /*
313   * Definitely not trying to mount at the moment
314   */
315  mf->mf_flags &= ~MFF_MOUNTING;
316
317  /*
318   * While we are mounting - try to avoid race conditions
319   */
320  new_ttl(cp->mp);
321
322  /*
323   * Wakeup anything waiting for this mount
324   */
325  wakeup((voidp) mf);
326
327  /*
328   * Check for termination signal or exit status...
329   */
330  if (rc || term) {
331    am_node *xmp;
332
333    if (term) {
334      /*
335       * Not sure what to do for an error code.
336       */
337      mf->mf_error = EIO;	/* XXX ? */
338      mf->mf_flags |= MFF_ERROR;
339      plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
340    } else {
341      /*
342       * Check for exit status...
343       */
344      mf->mf_error = rc;
345      mf->mf_flags |= MFF_ERROR;
346      errno = rc;		/* XXX */
347      if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
348	plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
349    }
350
351    /*
352     * If we get here then that attempt didn't work, so
353     * move the info vector pointer along by one and
354     * call the background mount routine again
355     */
356    amd_stats.d_merr++;
357    cp->ivec++;
358    xmp = cp->mp;
359    (void) amfs_auto_bgmount(cp, 0);
360    assign_error_mntfs(xmp);
361  } else {
362    /*
363     * The mount worked.
364     */
365    am_mounted(cp->mp);
366    free_continuation(cp);
367  }
368
369  reschedule_timeout_mp();
370}
371
372
373/*
374 * Retry a mount
375 */
376void
377amfs_auto_retry(int rc, int term, voidp closure)
378{
379  struct continuation *cp = (struct continuation *) closure;
380  int error = 0;
381
382#ifdef DEBUG
383  dlog("Commencing retry for mount of %s", cp->mp->am_path);
384#endif /* DEBUG */
385
386  new_ttl(cp->mp);
387
388  if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
389    /*
390     * The entire mount has timed out.  Set the error code and skip past all
391     * the info vectors so that amfs_auto_bgmount will not have any more
392     * ways to try the mount, so causing an error.
393     */
394    plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
395    error = ETIMEDOUT;
396    while (*cp->ivec)
397      cp->ivec++;
398    /* explicitly forbid further retries after timeout */
399    cp->retry = FALSE;
400  }
401  if (error || !IN_PROGRESS(cp)) {
402    (void) amfs_auto_bgmount(cp, error);
403  }
404  reschedule_timeout_mp();
405}
406
407
408/*
409 * Try to mount a file system.  Can be called
410 * directly or in a sub-process by run_task.
411 */
412int
413try_mount(voidp mvp)
414{
415  int error = 0;
416  am_node *mp = (am_node *) mvp;
417  mntfs *mf = mp->am_mnt;
418
419  /*
420   * If the directory is not yet made and it needs to be made, then make it!
421   * This may be run in a background process in which case the flag setting
422   * won't be noticed later - but it is set anyway just after run_task is
423   * called.  It should probably go away totally...
424   */
425  if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
426    error = mkdirs(mf->mf_mount, 0555);
427    if (!error)
428      mf->mf_flags |= MFF_MKMNT;
429  }
430
431  /*
432   * Mount it!
433   */
434  error = mount_node(mp);
435
436#ifdef DEBUG
437  if (error > 0) {
438    errno = error;
439    dlog("amfs_auto call to mount_node failed: %m");
440  }
441#endif /* DEBUG */
442
443  return error;
444}
445
446
447/*
448 * Pick a file system to try mounting and
449 * do that in the background if necessary
450 *
451 For each location:
452 if it is new -defaults then
453 extract and process
454 continue;
455 fi
456 if it is a cut then
457 if a location has been tried then
458 break;
459 fi
460 continue;
461 fi
462 parse mount location
463 discard previous mount location if required
464 find matching mounted filesystem
465 if not applicable then
466 this_error = No such file or directory
467 continue
468 fi
469 if the filesystem failed to be mounted then
470 this_error = error from filesystem
471 elif the filesystem is mounting or unmounting then
472 this_error = -1
473 elif the fileserver is down then
474 this_error = -1
475 elif the filesystem is already mounted
476 this_error = 0
477 break
478 fi
479 if no error on this mount then
480 this_error = initialise mount point
481 fi
482 if no error on this mount and mount is delayed then
483 this_error = -1
484 fi
485 if this_error < 0 then
486 retry = true
487 fi
488 if no error on this mount then
489 make mount point if required
490 fi
491 if no error on this mount then
492 if mount in background then
493 run mount in background
494 return -1
495 else
496 this_error = mount in foreground
497 fi
498 fi
499 if an error occured on this mount then
500 update stats
501 save error in mount point
502 fi
503 endfor
504 */
505static int
506amfs_auto_bgmount(struct continuation * cp, int mpe)
507{
508  mntfs *mf = cp->mp->am_mnt;	/* Current mntfs */
509  mntfs *mf_retry = 0;		/* First mntfs which needed retrying */
510  int this_error = -1;		/* Per-mount error */
511  int hard_error = -1;
512  int mp_error = mpe;
513
514  /*
515   * Try to mount each location.
516   * At the end:
517   * hard_error == 0 indicates something was mounted.
518   * hard_error > 0 indicates everything failed with a hard error
519   * hard_error < 0 indicates nothing could be mounted now
520   */
521  for (; this_error && *cp->ivec; cp->ivec++) {
522    am_ops *p;
523    am_node *mp = cp->mp;
524    char *link_dir;
525    int dont_retry;
526
527    if (hard_error < 0)
528      hard_error = this_error;
529
530    this_error = -1;
531
532    if (**cp->ivec == '-') {
533      /*
534       * Pick up new defaults
535       */
536      if (cp->auto_opts && *cp->auto_opts)
537	cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
538      else
539	cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
540#ifdef DEBUG
541      dlog("Setting def_opts to \"%s\"", cp->def_opts);
542#endif /* DEBUG */
543      continue;
544    }
545    /*
546     * If a mount has been attempted, and we find
547     * a cut then don't try any more locations.
548     */
549    if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
550      if (cp->tried) {
551#ifdef DEBUG
552	dlog("Cut: not trying any more locations for %s",
553	     mp->am_path);
554#endif /* DEBUG */
555	break;
556      }
557      continue;
558    }
559
560    /* match the operators */
561    p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
562
563    /*
564     * Find a mounted filesystem for this node.
565     */
566    mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
567				    cp->fs_opts.opt_fs,
568				    cp->fs_opts.fs_mtab,
569				    cp->auto_opts,
570				    cp->fs_opts.opt_opts,
571				    cp->fs_opts.opt_remopts);
572
573    p = mf->mf_ops;
574#ifdef DEBUG
575    dlog("Got a hit with %s", p->fs_type);
576#endif /* DEBUG */
577
578    /*
579     * Note whether this is a real mount attempt
580     */
581    if (p == &amfs_error_ops) {
582      plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
583      if (this_error <= 0)
584	this_error = ENOENT;
585      continue;
586    } else {
587      if (cp->fs_opts.fs_mtab) {
588	plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
589	     cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
590      }
591      cp->tried = TRUE;
592    }
593
594    this_error = 0;
595    dont_retry = FALSE;
596
597    if (mp->am_link) {
598      XFREE(mp->am_link);
599      mp->am_link = 0;
600    }
601    link_dir = mf->mf_fo->opt_sublink;
602
603    if (link_dir && *link_dir) {
604      if (*link_dir == '/') {
605	mp->am_link = strdup(link_dir);
606      } else {
607	/*
608	 * try getting fs option from continuation, not mountpoint!
609	 * Don't try logging the string from mf, since it may be bad!
610	 */
611	if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
612	  plog(XLOG_ERROR, "use %s instead of 0x%x",
613	       cp->fs_opts.opt_fs, mf->mf_fo->opt_fs);
614
615	mp->am_link = str3cat((char *) 0,
616			      cp->fs_opts.opt_fs, "/", link_dir);
617
618	normalize_slash(mp->am_link);
619      }
620    }
621
622    if (mf->mf_error > 0) {
623      this_error = mf->mf_error;
624    } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
625      /*
626       * Still mounting - retry later
627       */
628#ifdef DEBUG
629      dlog("Duplicate pending mount fstype %s", p->fs_type);
630#endif /* DEBUG */
631      this_error = -1;
632    } else if (FSRV_ISDOWN(mf->mf_server)) {
633      /*
634       * Would just mount from the same place
635       * as a hung mount - so give up
636       */
637#ifdef DEBUG
638      dlog("%s is already hung - giving up", mf->mf_mount);
639#endif /* DEBUG */
640      mp_error = EWOULDBLOCK;
641      dont_retry = TRUE;
642      this_error = -1;
643    } else if (mf->mf_flags & MFF_MOUNTED) {
644#ifdef DEBUG
645      dlog("duplicate mount of \"%s\" ...", mf->mf_info);
646#endif /* DEBUG */
647
648      /*
649       * Just call mounted()
650       */
651      am_mounted(mp);
652
653      this_error = 0;
654      break;
655    }
656
657    /*
658     * Will usually need to play around with the mount nodes
659     * file attribute structure.  This must be done here.
660     * Try and get things initialised, even if the fileserver
661     * is not known to be up.  In the common case this will
662     * progress things faster.
663     */
664    if (!this_error) {
665      /*
666       * Fill in attribute fields.
667       */
668      if (mf->mf_ops->fs_flags & FS_DIRECTORY)
669	mk_fattr(mp, NFDIR);
670      else
671	mk_fattr(mp, NFLNK);
672
673      mp->am_fattr.na_fileid = mp->am_gen;
674
675      if (p->fs_init)
676	this_error = (*p->fs_init) (mf);
677    }
678
679    /*
680     * Make sure the fileserver is UP before doing any more work
681     */
682    if (!FSRV_ISUP(mf->mf_server)) {
683#ifdef DEBUG
684      dlog("waiting for server %s to become available", mf->mf_server->fs_host);
685#endif /* DEBUG */
686      this_error = -1;
687    }
688
689    if (!this_error && mf->mf_fo->opt_delay) {
690      /*
691       * If there is a delay timer on the mount
692       * then don't try to mount if the timer
693       * has not expired.
694       */
695      int i = atoi(mf->mf_fo->opt_delay);
696      if (i > 0 && clocktime() < (cp->start + i)) {
697#ifdef DEBUG
698	dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
699#endif /* DEBUG */
700	this_error = -1;
701      }
702    }
703
704    if (this_error < 0 && !dont_retry) {
705      if (!mf_retry)
706	mf_retry = dup_mntfs(mf);
707      cp->retry = TRUE;
708    }
709
710    if (!this_error) {
711      if (p->fs_flags & FS_MBACKGROUND) {
712	mf->mf_flags |= MFF_MOUNTING;	/* XXX */
713#ifdef DEBUG
714	dlog("backgrounding mount of \"%s\"", mf->mf_mount);
715#endif /* DEBUG */
716	if (cp->callout) {
717	  untimeout(cp->callout);
718	  cp->callout = 0;
719	}
720	run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
721	mf->mf_flags |= MFF_MKMNT;	/* XXX */
722	if (mf_retry)
723	  free_mntfs(mf_retry);
724	return -1;
725      } else {
726#ifdef DEBUG
727	dlog("foreground mount of \"%s\" ...", mf->mf_info);
728#endif /* DEBUG */
729	this_error = try_mount((voidp) mp);
730	if (this_error < 0) {
731	  if (!mf_retry)
732	    mf_retry = dup_mntfs(mf);
733	  cp->retry = TRUE;
734	}
735      }
736    }
737
738    if (this_error >= 0) {
739      if (this_error > 0) {
740	amd_stats.d_merr++;
741	if (mf != mf_retry) {
742	  mf->mf_error = this_error;
743	  mf->mf_flags |= MFF_ERROR;
744	}
745      }
746
747      /*
748       * Wakeup anything waiting for this mount
749       */
750      wakeup((voidp) mf);
751    }
752  }
753
754  if (this_error && cp->retry) {
755    free_mntfs(mf);
756    mf = cp->mp->am_mnt = mf_retry;
757    /*
758     * Not retrying again (so far)
759     */
760    cp->retry = FALSE;
761    cp->tried = FALSE;
762    /*
763     * Start at the beginning.
764     * Rewind the location vector and
765     * reset the default options.
766     */
767    cp->ivec = cp->xivec;
768    cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
769    /*
770     * Arrange that amfs_auto_bgmount is called
771     * after anything else happens.
772     */
773#ifdef DEBUG
774    dlog("Arranging to retry mount of %s", cp->mp->am_path);
775#endif /* DEBUG */
776    sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
777    if (cp->callout)
778      untimeout(cp->callout);
779    cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
780
781    cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
782
783    /*
784     * Not done yet - so don't return anything
785     */
786    return -1;
787  }
788
789  if (hard_error < 0 || this_error == 0)
790    hard_error = this_error;
791
792  /*
793   * Discard handle on duff filesystem.
794   * This should never happen since it
795   * should be caught by the case above.
796   */
797  if (mf_retry) {
798    if (hard_error)
799      plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
800    free_mntfs(mf_retry);
801  }
802
803  /*
804   * If we get here, then either the mount succeeded or
805   * there is no more mount information available.
806   */
807  if (hard_error < 0 && mp_error)
808    hard_error = cp->mp->am_error = mp_error;
809  if (hard_error > 0) {
810    /*
811     * Set a small(ish) timeout on an error node if
812     * the error was not a time out.
813     */
814    switch (hard_error) {
815    case ETIMEDOUT:
816    case EWOULDBLOCK:
817      cp->mp->am_timeo = 17;
818      break;
819    default:
820      cp->mp->am_timeo = 5;
821      break;
822    }
823    new_ttl(cp->mp);
824  }
825
826  /*
827   * Make sure that the error value in the mntfs has a
828   * reasonable value.
829   */
830  if (mf->mf_error < 0) {
831    mf->mf_error = hard_error;
832    if (hard_error)
833      mf->mf_flags |= MFF_ERROR;
834  }
835
836  /*
837   * In any case we don't need the continuation any more
838   */
839  free_continuation(cp);
840
841  return hard_error;
842}
843
844
845/*
846 * Automount interface to RPC lookup routine
847 * Find the corresponding entry and return
848 * the file handle for it.
849 */
850am_node *
851amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
852{
853  am_node *ap, *new_mp, *ap_hung;
854  char *info;			/* Mount info - where to get the file system */
855  char **ivec, **xivec;		/* Split version of info */
856  char *auto_opts;		/* Automount options */
857  int error = 0;		/* Error so far */
858  char path_name[MAXPATHLEN];	/* General path name buffer */
859  char *pfname;			/* Path for database lookup */
860  struct continuation *cp;	/* Continuation structure if need to mount */
861  int in_progress = 0;		/* # of (un)mount in progress */
862  char *dflts;
863  mntfs *mf;
864
865#ifdef DEBUG
866  dlog("in amfs_auto_lookuppn");
867#endif /* DEBUG */
868
869  /*
870   * If the server is shutting down
871   * then don't return information
872   * about the mount point.
873   */
874  if (amd_state == Finishing) {
875#ifdef DEBUG
876    if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
877      dlog("%s mount ignored - going down", fname);
878    } else {
879      dlog("%s/%s mount ignored - going down", mp->am_path, fname);
880    }
881#endif /* DEBUG */
882    ereturn(ENOENT);
883  }
884
885  /*
886   * Handle special case of "." and ".."
887   */
888  if (fname[0] == '.') {
889    if (fname[1] == '\0')
890      return mp;		/* "." is the current node */
891    if (fname[1] == '.' && fname[2] == '\0') {
892      if (mp->am_parent) {
893#ifdef DEBUG
894	dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
895#endif /* DEBUG */
896	return mp->am_parent;	/* ".." is the parent node */
897      }
898      ereturn(ESTALE);
899    }
900  }
901
902  /*
903   * Check for valid key name.
904   * If it is invalid then pretend it doesn't exist.
905   */
906  if (!valid_key(fname)) {
907    plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
908    ereturn(ENOENT);
909  }
910
911  /*
912   * Expand key name.
913   * fname is now a private copy.
914   */
915  fname = expand_key(fname);
916
917  for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
918    /*
919     * Otherwise search children of this node
920     */
921    if (FSTREQ(ap->am_name, fname)) {
922      mf = ap->am_mnt;
923      if (ap->am_error) {
924	error = ap->am_error;
925	continue;
926      }
927      /*
928       * If the error code is undefined then it must be
929       * in progress.
930       */
931      if (mf->mf_error < 0)
932	goto in_progrss;
933
934      /*
935       * Check for a hung node
936       */
937      if (FSRV_ISDOWN(mf->mf_server)) {
938#ifdef DEBUG
939	dlog("server hung");
940#endif /* DEBUG */
941	error = ap->am_error;
942	ap_hung = ap;
943	continue;
944      }
945      /*
946       * If there was a previous error with this node
947       * then return that error code.
948       */
949      if (mf->mf_flags & MFF_ERROR) {
950	error = mf->mf_error;
951	continue;
952      }
953      if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
954      in_progrss:
955	/*
956	 * If the fs is not mounted or it is unmounting then there
957	 * is a background (un)mount in progress.  In this case
958	 * we just drop the RPC request (return nil) and
959	 * wait for a retry, by which time the (un)mount may
960	 * have completed.
961	 */
962#ifdef DEBUG
963	dlog("ignoring mount of %s in %s -- flags (%x) in progress",
964	     fname, mf->mf_mount, mf->mf_flags);
965#endif /* DEBUG */
966	in_progress++;
967	continue;
968      }
969
970      /*
971       * Otherwise we have a hit: return the current mount point.
972       */
973#ifdef DEBUG
974      dlog("matched %s in %s", fname, ap->am_path);
975#endif /* DEBUG */
976      XFREE(fname);
977      return ap;
978    }
979  }
980
981  if (in_progress) {
982#ifdef DEBUG
983    dlog("Waiting while %d mount(s) in progress", in_progress);
984#endif /* DEBUG */
985    XFREE(fname);
986    ereturn(-1);
987  }
988
989  /*
990   * If an error occured then return it.
991   */
992  if (error) {
993#ifdef DEBUG
994    errno = error;		/* XXX */
995    dlog("Returning error: %m", error);
996#endif /* DEBUG */
997    XFREE(fname);
998    ereturn(error);
999  }
1000
1001  /*
1002   * If doing a delete then don't create again!
1003   */
1004  switch (op) {
1005  case VLOOK_DELETE:
1006    ereturn(ENOENT);
1007
1008  case VLOOK_CREATE:
1009    break;
1010
1011  default:
1012    plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
1013    ereturn(EINVAL);
1014  }
1015
1016  /*
1017   * If the server is going down then just return,
1018   * don't try to mount any more file systems
1019   */
1020  if ((int) amd_state >= (int) Finishing) {
1021#ifdef DEBUG
1022    dlog("not found - server going down anyway");
1023#endif /* DEBUG */
1024    XFREE(fname);
1025    ereturn(ENOENT);
1026  }
1027
1028  /*
1029   * If we get there then this is a reference to an,
1030   * as yet, unknown name so we need to search the mount
1031   * map for it.
1032   */
1033  if (mp->am_pref) {
1034    sprintf(path_name, "%s%s", mp->am_pref, fname);
1035    pfname = path_name;
1036  } else {
1037    pfname = fname;
1038  }
1039
1040  mf = mp->am_mnt;
1041
1042#ifdef DEBUG
1043  dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1044#endif /* DEBUG */
1045  /*
1046   * Consult the oracle for some mount information.
1047   * info is malloc'ed and belongs to this routine.
1048   * It ends up being free'd in free_continuation().
1049   *
1050   * Note that this may return -1 indicating that information
1051   * is not yet available.
1052   */
1053  error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
1054  if (error) {
1055    if (error > 0)
1056      plog(XLOG_MAP, "No map entry for %s", pfname);
1057    else
1058      plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1059    XFREE(fname);
1060    ereturn(error);
1061  }
1062#ifdef DEBUG
1063  dlog("mount info is %s", info);
1064#endif /* DEBUG */
1065
1066  /*
1067   * Split info into an argument vector.
1068   * The vector is malloc'ed and belongs to
1069   * this routine.  It is free'd in free_continuation()
1070   */
1071  xivec = ivec = strsplit(info, ' ', '\"');
1072
1073  /*
1074   * Default error code...
1075   */
1076  if (ap_hung)
1077    error = EWOULDBLOCK;
1078  else
1079    error = ENOENT;
1080
1081  /*
1082   * Allocate a new map
1083   */
1084  new_mp = exported_ap_alloc();
1085  if (new_mp == 0) {
1086    XFREE(xivec);
1087    XFREE(info);
1088    XFREE(fname);
1089    ereturn(ENOSPC);
1090  }
1091  if (mf->mf_auto)
1092    auto_opts = mf->mf_auto;
1093  else
1094    auto_opts = "";
1095
1096  auto_opts = strdup(auto_opts);
1097
1098#ifdef DEBUG
1099  dlog("searching for /defaults entry");
1100#endif /* DEBUG */
1101  if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
1102    char *dfl;
1103    char **rvec;
1104#ifdef DEBUG
1105    dlog("/defaults gave %s", dflts);
1106#endif /* DEBUG */
1107    if (*dflts == '-')
1108      dfl = dflts + 1;
1109    else
1110      dfl = dflts;
1111
1112    /*
1113     * Chop the defaults up
1114     */
1115    rvec = strsplit(dfl, ' ', '\"');
1116
1117    if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
1118      /*
1119       * Pick whichever first entry matched the list of selectors.
1120       * Strip the selectors from the string, and assign to dfl the
1121       * rest of the string.
1122       */
1123      if (rvec) {
1124	am_opts ap;
1125	am_ops *pt;
1126	char **sp = rvec;
1127	while (*sp) {		/* loop until you find something, if any */
1128	  memset((char *) &ap, 0, sizeof(am_opts));
1129	  pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1130			 mp->am_parent->am_mnt->mf_info);
1131	  if (pt == &amfs_error_ops) {
1132	    plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
1133	  } else {
1134	    dfl = strip_selectors(*sp, "/defaults");
1135	    plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1136	    break;
1137	  }
1138	  ++sp;
1139	}
1140      }
1141    } else {			/* not enable_default_selectors */
1142      /*
1143       * Extract first value
1144       */
1145      dfl = rvec[0];
1146    }
1147
1148    /*
1149     * If there were any values at all...
1150     */
1151    if (dfl) {
1152      /*
1153       * Log error if there were other values
1154       */
1155      if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
1156# ifdef DEBUG
1157	dlog("/defaults chopped into %s", dfl);
1158# endif /* DEBUG */
1159	plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1160      }
1161
1162      /*
1163       * Prepend to existing defaults if they exist,
1164       * otherwise just use these defaults.
1165       */
1166      if (*auto_opts && *dfl) {
1167	char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1168	sprintf(nopts, "%s;%s", dfl, auto_opts);
1169	XFREE(auto_opts);
1170	auto_opts = nopts;
1171      } else if (*dfl) {
1172	auto_opts = strealloc(auto_opts, dfl);
1173      }
1174    }
1175    XFREE(dflts);
1176    /*
1177     * Don't need info vector any more
1178     */
1179    XFREE(rvec);
1180  }
1181
1182  /*
1183   * Fill it in
1184   */
1185  init_map(new_mp, fname);
1186
1187  /*
1188   * Put it in the table
1189   */
1190  insert_am(new_mp, mp);
1191
1192  /*
1193   * Fill in some other fields,
1194   * path and mount point.
1195   *
1196   * bugfix: do not prepend old am_path if direct map
1197   *         <wls@astro.umd.edu> William Sebok
1198   */
1199  new_mp->am_path = str3cat(new_mp->am_path,
1200			    mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1201			    *fname == '/' ? "" : "/", fname);
1202
1203#ifdef DEBUG
1204  dlog("setting path to %s", new_mp->am_path);
1205#endif /* DEBUG */
1206
1207  /*
1208   * Take private copy of pfname
1209   */
1210  pfname = strdup(pfname);
1211
1212  /*
1213   * Construct a continuation
1214   */
1215  cp = ALLOC(struct continuation);
1216  cp->callout = 0;
1217  cp->mp = new_mp;
1218  cp->xivec = xivec;
1219  cp->ivec = ivec;
1220  cp->info = info;
1221  cp->key = pfname;
1222  cp->auto_opts = auto_opts;
1223  cp->retry = FALSE;
1224  cp->tried = FALSE;
1225  cp->start = clocktime();
1226  cp->def_opts = strdup(auto_opts);
1227  memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1228
1229  /*
1230   * Try and mount the file system.  If this succeeds immediately (possible
1231   * for a ufs file system) then return the attributes, otherwise just
1232   * return an error.
1233   */
1234  error = amfs_auto_bgmount(cp, error);
1235  reschedule_timeout_mp();
1236  if (!error) {
1237    XFREE(fname);
1238    return new_mp;
1239  }
1240
1241  /*
1242   * Code for quick reply.  If nfs_program_2_transp is set, then
1243   * its the transp that's been passed down from nfs_program_2().
1244   * If new_mp->am_transp is not already set, set it by copying in
1245   * nfs_program_2_transp.  Once am_transp is set, quick_reply() can
1246   * use it to send a reply to the client that requested this mount.
1247   */
1248  if (nfs_program_2_transp && !new_mp->am_transp) {
1249    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1250    *(new_mp->am_transp) = *nfs_program_2_transp;
1251  }
1252  if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1253    new_mp->am_error = error;
1254
1255  assign_error_mntfs(new_mp);
1256
1257  XFREE(fname);
1258
1259  ereturn(error);
1260}
1261
1262
1263/*
1264 * Locate next node in sibling list which is mounted
1265 * and is not an error node.
1266 */
1267am_node *
1268next_nonerror_node(am_node *xp)
1269{
1270  mntfs *mf;
1271
1272  /*
1273   * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1274   * Fixes a race condition when mounting direct automounts.
1275   * Also fixes a problem when doing a readdir on a directory
1276   * containing hung automounts.
1277   */
1278  while (xp &&
1279	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
1280	  mf->mf_error != 0 ||	/* There was a mntfs error */
1281	  xp->am_error != 0 ||	/* There was a mount error */
1282	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
1283	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
1284	 )
1285    xp = xp->am_osib;
1286
1287  return xp;
1288}
1289
1290
1291/*
1292 * This readdir function which call a special version of it that allows
1293 * browsing if browsable_dirs=yes was set on the map.
1294 */
1295int
1296amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
1297{
1298  u_int gen = *(u_int *) cookie;
1299  am_node *xp;
1300  mntent_t mnt;
1301
1302  dp->dl_eof = FALSE;		/* assume readdir not done */
1303
1304  /* check if map is browsable */
1305  if (mp->am_mnt && mp->am_mnt->mf_mopts) {
1306    mnt.mnt_opts = mp->am_mnt->mf_mopts;
1307    if (hasmntopt(&mnt, "fullybrowsable"))
1308      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
1309    if (hasmntopt(&mnt, "browsable"))
1310      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
1311  }
1312
1313  if (gen == 0) {
1314    /*
1315     * In the default instance (which is used to start a search) we return
1316     * "." and "..".
1317     *
1318     * This assumes that the count is big enough to allow both "." and ".."
1319     * to be returned in a single packet.  If it isn't (which would be
1320     * fairly unbelievable) then tough.
1321     */
1322#ifdef DEBUG
1323    dlog("default search");
1324#endif /* DEBUG */
1325    /*
1326     * Check for enough room.  This is extremely approximate but is more
1327     * than enough space.  Really need 2 times:
1328     *      4byte fileid
1329     *      4byte cookie
1330     *      4byte name length
1331     *      4byte name
1332     * plus the dirlist structure */
1333    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1334      return EINVAL;
1335
1336    xp = next_nonerror_node(mp->am_child);
1337    dp->dl_entries = ep;
1338
1339    /* construct "." */
1340    ep[0].ne_fileid = mp->am_gen;
1341    ep[0].ne_name = ".";
1342    ep[0].ne_nextentry = &ep[1];
1343    *(u_int *) ep[0].ne_cookie = 0;
1344
1345    /* construct ".." */
1346    if (mp->am_parent)
1347      ep[1].ne_fileid = mp->am_parent->am_gen;
1348    else
1349      ep[1].ne_fileid = mp->am_gen;
1350    ep[1].ne_name = "..";
1351    ep[1].ne_nextentry = 0;
1352    *(u_int *) ep[1].ne_cookie =
1353      xp ? xp->am_gen : ~(u_int) 0;
1354
1355    if (!xp)
1356      dp->dl_eof = TRUE;	/* by default assume readdir done */
1357
1358    return 0;
1359  }
1360#ifdef DEBUG
1361  dlog("real child");
1362#endif /* DEBUG */
1363
1364  if (gen == ~(u_int) 0) {
1365#ifdef DEBUG
1366    dlog("End of readdir in %s", mp->am_path);
1367#endif /* DEBUG */
1368    dp->dl_eof = TRUE;
1369    dp->dl_entries = 0;
1370    return 0;
1371  }
1372
1373  /* non-browsable directories code */
1374  xp = mp->am_child;
1375  while (xp && xp->am_gen != gen)
1376    xp = xp->am_osib;
1377
1378  if (xp) {
1379    int nbytes = count / 2;	/* conservative */
1380    int todo = MAX_READDIR_ENTRIES;
1381    dp->dl_entries = ep;
1382    do {
1383      am_node *xp_next = next_nonerror_node(xp->am_osib);
1384
1385      if (xp_next) {
1386	*(u_int *) ep->ne_cookie = xp_next->am_gen;
1387      } else {
1388	*(u_int *) ep->ne_cookie = ~(u_int) 0;
1389	dp->dl_eof = TRUE;
1390      }
1391
1392      ep->ne_fileid = xp->am_gen;
1393      ep->ne_name = xp->am_name;
1394      nbytes -= sizeof(*ep) + 1;
1395      if (xp->am_name)
1396	nbytes -= strlen(xp->am_name);
1397
1398      xp = xp_next;
1399
1400      if (nbytes > 0 && !dp->dl_eof && todo > 1) {
1401	ep->ne_nextentry = ep + 1;
1402	ep++;
1403	--todo;
1404      } else {
1405	todo = 0;
1406      }
1407    } while (todo > 0);
1408
1409    ep->ne_nextentry = 0;
1410
1411    return 0;
1412  }
1413  return ESTALE;
1414}
1415
1416
1417/* This one is called only if map is browsable */
1418static int
1419amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
1420{
1421  u_int gen = *(u_int *) cookie;
1422  int chain_length, i;
1423  static nfsentry *te, *te_next;
1424#ifdef DEBUG_READDIR
1425  nfsentry *ne;
1426  static int j;
1427#endif /* DEBUG_READDIR */
1428
1429  dp->dl_eof = FALSE;		/* assume readdir not done */
1430
1431#ifdef DEBUG_READDIR
1432  plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
1433       gen, count);
1434#endif /* DEBUG_READDIR */
1435
1436  if (gen == 0) {
1437    /*
1438     * In the default instance (which is used to start a search) we return
1439     * "." and "..".
1440     *
1441     * This assumes that the count is big enough to allow both "." and ".."
1442     * to be returned in a single packet.  If it isn't (which would be
1443     * fairly unbelievable) then tough.
1444     */
1445#ifdef DEBUG
1446    dlog("default search");
1447#endif /* DEBUG */
1448    /*
1449     * Check for enough room.  This is extremely approximate but is more
1450     * than enough space.  Really need 2 times:
1451     *      4byte fileid
1452     *      4byte cookie
1453     *      4byte name length
1454     *      4byte name
1455     * plus the dirlist structure */
1456    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1457      return EINVAL;
1458
1459    /*
1460     * compute # of entries to send in this chain.
1461     * heuristics: 128 bytes per entry.
1462     * This is too much probably, but it seems to work better because
1463     * of the re-entrant nature of nfs_readdir, and esp. on systems
1464     * like OpenBSD 2.2.
1465     */
1466    chain_length = count / 128;
1467
1468    /* reset static state counters */
1469    te = te_next = NULL;
1470
1471    dp->dl_entries = ep;
1472
1473    /* construct "." */
1474    ep[0].ne_fileid = mp->am_gen;
1475    ep[0].ne_name = ".";
1476    ep[0].ne_nextentry = &ep[1];
1477    *(u_int *) ep[0].ne_cookie = 0;
1478
1479    /* construct ".." */
1480    if (mp->am_parent)
1481      ep[1].ne_fileid = mp->am_parent->am_gen;
1482    else
1483      ep[1].ne_fileid = mp->am_gen;
1484    ep[1].ne_name = "..";
1485    ep[1].ne_nextentry = 0;
1486    *(u_int *) ep[1].ne_cookie = ~(u_int) 0;
1487
1488    /*
1489     * If map is browsable, call a function make_entry_chain() to construct
1490     * a linked list of unmounted keys, and return it.  Then link the chain
1491     * to the regular list.  Get the chain only once, but return
1492     * chunks of it each time.
1493     */
1494    te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
1495    if (!te)
1496      return 0;
1497#ifdef DEBUG_READDIR
1498    j = 0;
1499    for (ne=te; ne; ne=ne->ne_nextentry)
1500      plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
1501#endif /* DEBUG_READDIR */
1502
1503    /* return only "chain_length" entries */
1504    te_next = te;
1505    for (i=1; i<chain_length; ++i) {
1506      te_next = te_next->ne_nextentry;
1507      if (!te_next)
1508	break;
1509    }
1510    if (te_next) {
1511      nfsentry *te_saved = te_next->ne_nextentry;
1512      te_next->ne_nextentry = NULL; /* terminate "te" chain */
1513      te_next = te_saved;	/* save rest of "te" for next interation */
1514      dp->dl_eof = FALSE;	/* tell readdir there's more */
1515    } else {
1516      dp->dl_eof = TRUE;	/* tell readdir that's it */
1517    }
1518    ep[1].ne_nextentry = te;	/* append this chunk of "te" chain */
1519#ifdef DEBUG_READDIR
1520    for (ne=te; ne; ne=ne->ne_nextentry)
1521      plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
1522#endif /* DEBUG_READDIR */
1523    return 0;
1524  } /* end of "if (gen == 0)" statement */
1525
1526#ifdef DEBUG
1527  dlog("real child");
1528#endif /* DEBUG */
1529
1530  if (gen == ~(u_int) 0) {
1531#ifdef DEBUG
1532    dlog("End of readdir in %s", mp->am_path);
1533#endif /* DEBUG */
1534    dp->dl_eof = TRUE;
1535    dp->dl_entries = 0;
1536    return 0;
1537  }
1538
1539  /*
1540   * If browsable directories, then continue serving readdir() with another
1541   * chunk of entries, starting from where we left off (when gen was equal
1542   * to 0).  Once again, assume last chunk served to readdir.
1543   */
1544  dp->dl_eof = TRUE;
1545  dp->dl_entries = ep;
1546
1547  te = te_next;			/* reset 'te' from last saved te_next */
1548  if (!te) {			/* another indicator of end of readdir */
1549    dp->dl_entries = 0;
1550    return 0;
1551  }
1552  /*
1553   * compute # of entries to send in this chain.
1554   * heuristics: 128 bytes per entry.
1555   */
1556  chain_length = count / 128;
1557
1558  /* return only "chain_length" entries */
1559  for (i=1; i<chain_length; ++i) {
1560    te_next = te_next->ne_nextentry;
1561    if (!te_next)
1562      break;
1563  }
1564  if (te_next) {
1565    nfsentry *te_saved = te_next->ne_nextentry;
1566    te_next->ne_nextentry = NULL; /* terminate "te" chain */
1567    te_next = te_saved;		/* save rest of "te" for next interation */
1568    dp->dl_eof = FALSE;		/* tell readdir there's more */
1569  }
1570  ep = te;			/* send next chunk of "te" chain */
1571  dp->dl_entries = ep;
1572#ifdef DEBUG_READDIR
1573  plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
1574       dp->dl_entries, te_next, dp->dl_eof);
1575  for (ne=te; ne; ne=ne->ne_nextentry)
1576    plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
1577#endif /* DEBUG_READDIR */
1578  return 0;
1579}
1580
1581
1582int
1583amfs_auto_fmount(am_node *mp)
1584{
1585  mntfs *mf = mp->am_mnt;
1586  return (*mf->mf_ops->fmount_fs) (mf);
1587}
1588
1589
1590int
1591amfs_auto_fumount(am_node *mp)
1592{
1593  mntfs *mf = mp->am_mnt;
1594  return (*mf->mf_ops->fumount_fs) (mf);
1595}
1596