amfs_auto.c revision 41142
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	  free_opts(&ap);	/* don't leak */
1132	  if (pt == &amfs_error_ops) {
1133	    plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
1134	  } else {
1135	    dfl = strip_selectors(*sp, "/defaults");
1136	    plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1137	    break;
1138	  }
1139	  ++sp;
1140	}
1141      }
1142    } else {			/* not enable_default_selectors */
1143      /*
1144       * Extract first value
1145       */
1146      dfl = rvec[0];
1147    }
1148
1149    /*
1150     * If there were any values at all...
1151     */
1152    if (dfl) {
1153      /*
1154       * Log error if there were other values
1155       */
1156      if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
1157# ifdef DEBUG
1158	dlog("/defaults chopped into %s", dfl);
1159# endif /* DEBUG */
1160	plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1161      }
1162
1163      /*
1164       * Prepend to existing defaults if they exist,
1165       * otherwise just use these defaults.
1166       */
1167      if (*auto_opts && *dfl) {
1168	char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1169	sprintf(nopts, "%s;%s", dfl, auto_opts);
1170	XFREE(auto_opts);
1171	auto_opts = nopts;
1172      } else if (*dfl) {
1173	auto_opts = strealloc(auto_opts, dfl);
1174      }
1175    }
1176    XFREE(dflts);
1177    /*
1178     * Don't need info vector any more
1179     */
1180    XFREE(rvec);
1181  }
1182
1183  /*
1184   * Fill it in
1185   */
1186  init_map(new_mp, fname);
1187
1188  /*
1189   * Put it in the table
1190   */
1191  insert_am(new_mp, mp);
1192
1193  /*
1194   * Fill in some other fields,
1195   * path and mount point.
1196   *
1197   * bugfix: do not prepend old am_path if direct map
1198   *         <wls@astro.umd.edu> William Sebok
1199   */
1200  new_mp->am_path = str3cat(new_mp->am_path,
1201			    mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1202			    *fname == '/' ? "" : "/", fname);
1203
1204#ifdef DEBUG
1205  dlog("setting path to %s", new_mp->am_path);
1206#endif /* DEBUG */
1207
1208  /*
1209   * Take private copy of pfname
1210   */
1211  pfname = strdup(pfname);
1212
1213  /*
1214   * Construct a continuation
1215   */
1216  cp = ALLOC(struct continuation);
1217  cp->callout = 0;
1218  cp->mp = new_mp;
1219  cp->xivec = xivec;
1220  cp->ivec = ivec;
1221  cp->info = info;
1222  cp->key = pfname;
1223  cp->auto_opts = auto_opts;
1224  cp->retry = FALSE;
1225  cp->tried = FALSE;
1226  cp->start = clocktime();
1227  cp->def_opts = strdup(auto_opts);
1228  memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1229
1230  /*
1231   * Try and mount the file system.  If this succeeds immediately (possible
1232   * for a ufs file system) then return the attributes, otherwise just
1233   * return an error.
1234   */
1235  error = amfs_auto_bgmount(cp, error);
1236  reschedule_timeout_mp();
1237  if (!error) {
1238    XFREE(fname);
1239    return new_mp;
1240  }
1241
1242  /*
1243   * Code for quick reply.  If nfs_program_2_transp is set, then
1244   * its the transp that's been passed down from nfs_program_2().
1245   * If new_mp->am_transp is not already set, set it by copying in
1246   * nfs_program_2_transp.  Once am_transp is set, quick_reply() can
1247   * use it to send a reply to the client that requested this mount.
1248   */
1249  if (nfs_program_2_transp && !new_mp->am_transp) {
1250    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1251    *(new_mp->am_transp) = *nfs_program_2_transp;
1252  }
1253  if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1254    new_mp->am_error = error;
1255
1256  assign_error_mntfs(new_mp);
1257
1258  XFREE(fname);
1259
1260  ereturn(error);
1261}
1262
1263
1264/*
1265 * Locate next node in sibling list which is mounted
1266 * and is not an error node.
1267 */
1268am_node *
1269next_nonerror_node(am_node *xp)
1270{
1271  mntfs *mf;
1272
1273  /*
1274   * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1275   * Fixes a race condition when mounting direct automounts.
1276   * Also fixes a problem when doing a readdir on a directory
1277   * containing hung automounts.
1278   */
1279  while (xp &&
1280	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
1281	  mf->mf_error != 0 ||	/* There was a mntfs error */
1282	  xp->am_error != 0 ||	/* There was a mount error */
1283	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
1284	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
1285	 )
1286    xp = xp->am_osib;
1287
1288  return xp;
1289}
1290
1291
1292/*
1293 * This readdir function which call a special version of it that allows
1294 * browsing if browsable_dirs=yes was set on the map.
1295 */
1296int
1297amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
1298{
1299  u_int gen = *(u_int *) cookie;
1300  am_node *xp;
1301  mntent_t mnt;
1302
1303  dp->dl_eof = FALSE;		/* assume readdir not done */
1304
1305  /* check if map is browsable */
1306  if (mp->am_mnt && mp->am_mnt->mf_mopts) {
1307    mnt.mnt_opts = mp->am_mnt->mf_mopts;
1308    if (hasmntopt(&mnt, "fullybrowsable"))
1309      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
1310    if (hasmntopt(&mnt, "browsable"))
1311      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
1312  }
1313
1314  if (gen == 0) {
1315    /*
1316     * In the default instance (which is used to start a search) we return
1317     * "." and "..".
1318     *
1319     * This assumes that the count is big enough to allow both "." and ".."
1320     * to be returned in a single packet.  If it isn't (which would be
1321     * fairly unbelievable) then tough.
1322     */
1323#ifdef DEBUG
1324    dlog("default search");
1325#endif /* DEBUG */
1326    /*
1327     * Check for enough room.  This is extremely approximate but is more
1328     * than enough space.  Really need 2 times:
1329     *      4byte fileid
1330     *      4byte cookie
1331     *      4byte name length
1332     *      4byte name
1333     * plus the dirlist structure */
1334    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1335      return EINVAL;
1336
1337    xp = next_nonerror_node(mp->am_child);
1338    dp->dl_entries = ep;
1339
1340    /* construct "." */
1341    ep[0].ne_fileid = mp->am_gen;
1342    ep[0].ne_name = ".";
1343    ep[0].ne_nextentry = &ep[1];
1344    *(u_int *) ep[0].ne_cookie = 0;
1345
1346    /* construct ".." */
1347    if (mp->am_parent)
1348      ep[1].ne_fileid = mp->am_parent->am_gen;
1349    else
1350      ep[1].ne_fileid = mp->am_gen;
1351    ep[1].ne_name = "..";
1352    ep[1].ne_nextentry = 0;
1353    *(u_int *) ep[1].ne_cookie =
1354      xp ? xp->am_gen : ~(u_int) 0;
1355
1356    if (!xp)
1357      dp->dl_eof = TRUE;	/* by default assume readdir done */
1358
1359    return 0;
1360  }
1361#ifdef DEBUG
1362  dlog("real child");
1363#endif /* DEBUG */
1364
1365  if (gen == ~(u_int) 0) {
1366#ifdef DEBUG
1367    dlog("End of readdir in %s", mp->am_path);
1368#endif /* DEBUG */
1369    dp->dl_eof = TRUE;
1370    dp->dl_entries = 0;
1371    return 0;
1372  }
1373
1374  /* non-browsable directories code */
1375  xp = mp->am_child;
1376  while (xp && xp->am_gen != gen)
1377    xp = xp->am_osib;
1378
1379  if (xp) {
1380    int nbytes = count / 2;	/* conservative */
1381    int todo = MAX_READDIR_ENTRIES;
1382    dp->dl_entries = ep;
1383    do {
1384      am_node *xp_next = next_nonerror_node(xp->am_osib);
1385
1386      if (xp_next) {
1387	*(u_int *) ep->ne_cookie = xp_next->am_gen;
1388      } else {
1389	*(u_int *) ep->ne_cookie = ~(u_int) 0;
1390	dp->dl_eof = TRUE;
1391      }
1392
1393      ep->ne_fileid = xp->am_gen;
1394      ep->ne_name = xp->am_name;
1395      nbytes -= sizeof(*ep) + 1;
1396      if (xp->am_name)
1397	nbytes -= strlen(xp->am_name);
1398
1399      xp = xp_next;
1400
1401      if (nbytes > 0 && !dp->dl_eof && todo > 1) {
1402	ep->ne_nextentry = ep + 1;
1403	ep++;
1404	--todo;
1405      } else {
1406	todo = 0;
1407      }
1408    } while (todo > 0);
1409
1410    ep->ne_nextentry = 0;
1411
1412    return 0;
1413  }
1414  return ESTALE;
1415}
1416
1417
1418/* This one is called only if map is browsable */
1419static int
1420amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
1421{
1422  u_int gen = *(u_int *) cookie;
1423  int chain_length, i;
1424  static nfsentry *te, *te_next;
1425#ifdef DEBUG_READDIR
1426  nfsentry *ne;
1427  static int j;
1428#endif /* DEBUG_READDIR */
1429
1430  dp->dl_eof = FALSE;		/* assume readdir not done */
1431
1432#ifdef DEBUG_READDIR
1433  plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
1434       gen, count);
1435#endif /* DEBUG_READDIR */
1436
1437  if (gen == 0) {
1438    /*
1439     * In the default instance (which is used to start a search) we return
1440     * "." and "..".
1441     *
1442     * This assumes that the count is big enough to allow both "." and ".."
1443     * to be returned in a single packet.  If it isn't (which would be
1444     * fairly unbelievable) then tough.
1445     */
1446#ifdef DEBUG
1447    dlog("default search");
1448#endif /* DEBUG */
1449    /*
1450     * Check for enough room.  This is extremely approximate but is more
1451     * than enough space.  Really need 2 times:
1452     *      4byte fileid
1453     *      4byte cookie
1454     *      4byte name length
1455     *      4byte name
1456     * plus the dirlist structure */
1457    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1458      return EINVAL;
1459
1460    /*
1461     * compute # of entries to send in this chain.
1462     * heuristics: 128 bytes per entry.
1463     * This is too much probably, but it seems to work better because
1464     * of the re-entrant nature of nfs_readdir, and esp. on systems
1465     * like OpenBSD 2.2.
1466     */
1467    chain_length = count / 128;
1468
1469    /* reset static state counters */
1470    te = te_next = NULL;
1471
1472    dp->dl_entries = ep;
1473
1474    /* construct "." */
1475    ep[0].ne_fileid = mp->am_gen;
1476    ep[0].ne_name = ".";
1477    ep[0].ne_nextentry = &ep[1];
1478    *(u_int *) ep[0].ne_cookie = 0;
1479
1480    /* construct ".." */
1481    if (mp->am_parent)
1482      ep[1].ne_fileid = mp->am_parent->am_gen;
1483    else
1484      ep[1].ne_fileid = mp->am_gen;
1485    ep[1].ne_name = "..";
1486    ep[1].ne_nextentry = 0;
1487    *(u_int *) ep[1].ne_cookie = ~(u_int) 0;
1488
1489    /*
1490     * If map is browsable, call a function make_entry_chain() to construct
1491     * a linked list of unmounted keys, and return it.  Then link the chain
1492     * to the regular list.  Get the chain only once, but return
1493     * chunks of it each time.
1494     */
1495    te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
1496    if (!te)
1497      return 0;
1498#ifdef DEBUG_READDIR
1499    j = 0;
1500    for (ne=te; ne; ne=ne->ne_nextentry)
1501      plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
1502#endif /* DEBUG_READDIR */
1503
1504    /* return only "chain_length" entries */
1505    te_next = te;
1506    for (i=1; i<chain_length; ++i) {
1507      te_next = te_next->ne_nextentry;
1508      if (!te_next)
1509	break;
1510    }
1511    if (te_next) {
1512      nfsentry *te_saved = te_next->ne_nextentry;
1513      te_next->ne_nextentry = NULL; /* terminate "te" chain */
1514      te_next = te_saved;	/* save rest of "te" for next interation */
1515      dp->dl_eof = FALSE;	/* tell readdir there's more */
1516    } else {
1517      dp->dl_eof = TRUE;	/* tell readdir that's it */
1518    }
1519    ep[1].ne_nextentry = te;	/* append this chunk of "te" chain */
1520#ifdef DEBUG_READDIR
1521    for (ne=te; ne; ne=ne->ne_nextentry)
1522      plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
1523#endif /* DEBUG_READDIR */
1524    return 0;
1525  } /* end of "if (gen == 0)" statement */
1526
1527#ifdef DEBUG
1528  dlog("real child");
1529#endif /* DEBUG */
1530
1531  if (gen == ~(u_int) 0) {
1532#ifdef DEBUG
1533    dlog("End of readdir in %s", mp->am_path);
1534#endif /* DEBUG */
1535    dp->dl_eof = TRUE;
1536    dp->dl_entries = 0;
1537    return 0;
1538  }
1539
1540  /*
1541   * If browsable directories, then continue serving readdir() with another
1542   * chunk of entries, starting from where we left off (when gen was equal
1543   * to 0).  Once again, assume last chunk served to readdir.
1544   */
1545  dp->dl_eof = TRUE;
1546  dp->dl_entries = ep;
1547
1548  te = te_next;			/* reset 'te' from last saved te_next */
1549  if (!te) {			/* another indicator of end of readdir */
1550    dp->dl_entries = 0;
1551    return 0;
1552  }
1553  /*
1554   * compute # of entries to send in this chain.
1555   * heuristics: 128 bytes per entry.
1556   */
1557  chain_length = count / 128;
1558
1559  /* return only "chain_length" entries */
1560  for (i=1; i<chain_length; ++i) {
1561    te_next = te_next->ne_nextentry;
1562    if (!te_next)
1563      break;
1564  }
1565  if (te_next) {
1566    nfsentry *te_saved = te_next->ne_nextentry;
1567    te_next->ne_nextentry = NULL; /* terminate "te" chain */
1568    te_next = te_saved;		/* save rest of "te" for next interation */
1569    dp->dl_eof = FALSE;		/* tell readdir there's more */
1570  }
1571  ep = te;			/* send next chunk of "te" chain */
1572  dp->dl_entries = ep;
1573#ifdef DEBUG_READDIR
1574  plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
1575       dp->dl_entries, te_next, dp->dl_eof);
1576  for (ne=te; ne; ne=ne->ne_nextentry)
1577    plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
1578#endif /* DEBUG_READDIR */
1579  return 0;
1580}
1581
1582
1583int
1584amfs_auto_fmount(am_node *mp)
1585{
1586  mntfs *mf = mp->am_mnt;
1587  return (*mf->mf_ops->fmount_fs) (mf);
1588}
1589
1590
1591int
1592amfs_auto_fumount(am_node *mp)
1593{
1594  mntfs *mf = mp->am_mnt;
1595  return (*mf->mf_ops->fumount_fs) (mf);
1596}
1597