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