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