amfs_auto.c revision 51292
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.4 1999/08/09 06:09:43 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 *
452 For 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
504 endfor
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    }
710
711    if (!this_error) {
712      if (p->fs_flags & FS_MBACKGROUND) {
713	mf->mf_flags |= MFF_MOUNTING;	/* XXX */
714#ifdef DEBUG
715	dlog("backgrounding mount of \"%s\"", mf->mf_mount);
716#endif /* DEBUG */
717	if (cp->callout) {
718	  untimeout(cp->callout);
719	  cp->callout = 0;
720	}
721	run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
722	mf->mf_flags |= MFF_MKMNT;	/* XXX */
723	if (mf_retry)
724	  free_mntfs(mf_retry);
725	return -1;
726      } else {
727#ifdef DEBUG
728	dlog("foreground mount of \"%s\" ...", mf->mf_info);
729#endif /* DEBUG */
730	this_error = try_mount((voidp) mp);
731	if (this_error < 0) {
732	  if (!mf_retry)
733	    mf_retry = dup_mntfs(mf);
734	  cp->retry = TRUE;
735	}
736      }
737    }
738
739    if (this_error >= 0) {
740      if (this_error > 0) {
741	amd_stats.d_merr++;
742	if (mf != mf_retry) {
743	  mf->mf_error = this_error;
744	  mf->mf_flags |= MFF_ERROR;
745	}
746      }
747
748      /*
749       * Wakeup anything waiting for this mount
750       */
751      wakeup((voidp) mf);
752    }
753  }
754
755  if (this_error && cp->retry) {
756    free_mntfs(mf);
757    mf = cp->mp->am_mnt = mf_retry;
758    /*
759     * Not retrying again (so far)
760     */
761    cp->retry = FALSE;
762    cp->tried = FALSE;
763    /*
764     * Start at the beginning.
765     * Rewind the location vector and
766     * reset the default options.
767     */
768    cp->ivec = cp->xivec;
769    cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
770    /*
771     * Arrange that amfs_auto_bgmount is called
772     * after anything else happens.
773     */
774#ifdef DEBUG
775    dlog("Arranging to retry mount of %s", cp->mp->am_path);
776#endif /* DEBUG */
777    sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
778    if (cp->callout)
779      untimeout(cp->callout);
780    cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
781
782    cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
783
784    /*
785     * Not done yet - so don't return anything
786     */
787    return -1;
788  }
789
790  if (hard_error < 0 || this_error == 0)
791    hard_error = this_error;
792
793  /*
794   * Discard handle on duff filesystem.
795   * This should never happen since it
796   * should be caught by the case above.
797   */
798  if (mf_retry) {
799    if (hard_error)
800      plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
801    free_mntfs(mf_retry);
802  }
803
804  /*
805   * If we get here, then either the mount succeeded or
806   * there is no more mount information available.
807   */
808  if (hard_error < 0 && mp_error)
809    hard_error = cp->mp->am_error = mp_error;
810  if (hard_error > 0) {
811    /*
812     * Set a small(ish) timeout on an error node if
813     * the error was not a time out.
814     */
815    switch (hard_error) {
816    case ETIMEDOUT:
817    case EWOULDBLOCK:
818      cp->mp->am_timeo = 17;
819      break;
820    default:
821      cp->mp->am_timeo = 5;
822      break;
823    }
824    new_ttl(cp->mp);
825  }
826
827  /*
828   * Make sure that the error value in the mntfs has a
829   * reasonable value.
830   */
831  if (mf->mf_error < 0) {
832    mf->mf_error = hard_error;
833    if (hard_error)
834      mf->mf_flags |= MFF_ERROR;
835  }
836
837  /*
838   * In any case we don't need the continuation any more
839   */
840  free_continuation(cp);
841
842  return hard_error;
843}
844
845
846/*
847 * Automount interface to RPC lookup routine
848 * Find the corresponding entry and return
849 * the file handle for it.
850 */
851am_node *
852amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
853{
854  am_node *ap, *new_mp, *ap_hung;
855  char *info;			/* Mount info - where to get the file system */
856  char **ivec, **xivec;		/* Split version of info */
857  char *auto_opts;		/* Automount options */
858  int error = 0;		/* Error so far */
859  char path_name[MAXPATHLEN];	/* General path name buffer */
860  char *pfname;			/* Path for database lookup */
861  struct continuation *cp;	/* Continuation structure if need to mount */
862  int in_progress = 0;		/* # of (un)mount in progress */
863  char *dflts;
864  mntfs *mf;
865
866#ifdef DEBUG
867  dlog("in amfs_auto_lookuppn");
868#endif /* DEBUG */
869
870  /*
871   * If the server is shutting down
872   * then don't return information
873   * about the mount point.
874   */
875  if (amd_state == Finishing) {
876#ifdef DEBUG
877    if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
878      dlog("%s mount ignored - going down", fname);
879    } else {
880      dlog("%s/%s mount ignored - going down", mp->am_path, fname);
881    }
882#endif /* DEBUG */
883    ereturn(ENOENT);
884  }
885
886  /*
887   * Handle special case of "." and ".."
888   */
889  if (fname[0] == '.') {
890    if (fname[1] == '\0')
891      return mp;		/* "." is the current node */
892    if (fname[1] == '.' && fname[2] == '\0') {
893      if (mp->am_parent) {
894#ifdef DEBUG
895	dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
896#endif /* DEBUG */
897	return mp->am_parent;	/* ".." is the parent node */
898      }
899      ereturn(ESTALE);
900    }
901  }
902
903  /*
904   * Check for valid key name.
905   * If it is invalid then pretend it doesn't exist.
906   */
907  if (!valid_key(fname)) {
908    plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
909    ereturn(ENOENT);
910  }
911
912  /*
913   * Expand key name.
914   * fname is now a private copy.
915   */
916  fname = expand_key(fname);
917
918  for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
919    /*
920     * Otherwise search children of this node
921     */
922    if (FSTREQ(ap->am_name, fname)) {
923      mf = ap->am_mnt;
924      if (ap->am_error) {
925	error = ap->am_error;
926	continue;
927      }
928      /*
929       * If the error code is undefined then it must be
930       * in progress.
931       */
932      if (mf->mf_error < 0)
933	goto in_progrss;
934
935      /*
936       * Check for a hung node
937       */
938      if (FSRV_ISDOWN(mf->mf_server)) {
939#ifdef DEBUG
940	dlog("server hung");
941#endif /* DEBUG */
942	error = ap->am_error;
943	ap_hung = ap;
944	continue;
945      }
946      /*
947       * If there was a previous error with this node
948       * then return that error code.
949       */
950      if (mf->mf_flags & MFF_ERROR) {
951	error = mf->mf_error;
952	continue;
953      }
954      if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
955      in_progrss:
956	/*
957	 * If the fs is not mounted or it is unmounting then there
958	 * is a background (un)mount in progress.  In this case
959	 * we just drop the RPC request (return nil) and
960	 * wait for a retry, by which time the (un)mount may
961	 * have completed.
962	 */
963#ifdef DEBUG
964	dlog("ignoring mount of %s in %s -- flags (%x) in progress",
965	     fname, mf->mf_mount, mf->mf_flags);
966#endif /* DEBUG */
967	in_progress++;
968	continue;
969      }
970
971      /*
972       * Otherwise we have a hit: return the current mount point.
973       */
974#ifdef DEBUG
975      dlog("matched %s in %s", fname, ap->am_path);
976#endif /* DEBUG */
977      XFREE(fname);
978      return ap;
979    }
980  }
981
982  if (in_progress) {
983#ifdef DEBUG
984    dlog("Waiting while %d mount(s) in progress", in_progress);
985#endif /* DEBUG */
986    XFREE(fname);
987    ereturn(-1);
988  }
989
990  /*
991   * If an error occurred then return it.
992   */
993  if (error) {
994#ifdef DEBUG
995    errno = error;		/* XXX */
996    dlog("Returning error: %m");
997#endif /* DEBUG */
998    XFREE(fname);
999    ereturn(error);
1000  }
1001
1002  /*
1003   * If doing a delete then don't create again!
1004   */
1005  switch (op) {
1006  case VLOOK_DELETE:
1007    ereturn(ENOENT);
1008
1009  case VLOOK_CREATE:
1010    break;
1011
1012  default:
1013    plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
1014    ereturn(EINVAL);
1015  }
1016
1017  /*
1018   * If the server is going down then just return,
1019   * don't try to mount any more file systems
1020   */
1021  if ((int) amd_state >= (int) Finishing) {
1022#ifdef DEBUG
1023    dlog("not found - server going down anyway");
1024#endif /* DEBUG */
1025    XFREE(fname);
1026    ereturn(ENOENT);
1027  }
1028
1029  /*
1030   * If we get there then this is a reference to an,
1031   * as yet, unknown name so we need to search the mount
1032   * map for it.
1033   */
1034  if (mp->am_pref) {
1035    sprintf(path_name, "%s%s", mp->am_pref, fname);
1036    pfname = path_name;
1037  } else {
1038    pfname = fname;
1039  }
1040
1041  mf = mp->am_mnt;
1042
1043#ifdef DEBUG
1044  dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1045#endif /* DEBUG */
1046  /*
1047   * Consult the oracle for some mount information.
1048   * info is malloc'ed and belongs to this routine.
1049   * It ends up being free'd in free_continuation().
1050   *
1051   * Note that this may return -1 indicating that information
1052   * is not yet available.
1053   */
1054  error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
1055  if (error) {
1056    if (error > 0)
1057      plog(XLOG_MAP, "No map entry for %s", pfname);
1058    else
1059      plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1060    XFREE(fname);
1061    ereturn(error);
1062  }
1063#ifdef DEBUG
1064  dlog("mount info is %s", info);
1065#endif /* DEBUG */
1066
1067  /*
1068   * Split info into an argument vector.
1069   * The vector is malloc'ed and belongs to
1070   * this routine.  It is free'd in free_continuation()
1071   */
1072  xivec = ivec = strsplit(info, ' ', '\"');
1073
1074  /*
1075   * Default error code...
1076   */
1077  if (ap_hung)
1078    error = EWOULDBLOCK;
1079  else
1080    error = ENOENT;
1081
1082  /*
1083   * Allocate a new map
1084   */
1085  new_mp = exported_ap_alloc();
1086  if (new_mp == 0) {
1087    XFREE(xivec);
1088    XFREE(info);
1089    XFREE(fname);
1090    ereturn(ENOSPC);
1091  }
1092  if (mf->mf_auto)
1093    auto_opts = mf->mf_auto;
1094  else
1095    auto_opts = "";
1096
1097  auto_opts = strdup(auto_opts);
1098
1099#ifdef DEBUG
1100  dlog("searching for /defaults entry");
1101#endif /* DEBUG */
1102  if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
1103    char *dfl;
1104    char **rvec;
1105#ifdef DEBUG
1106    dlog("/defaults gave %s", dflts);
1107#endif /* DEBUG */
1108    if (*dflts == '-')
1109      dfl = dflts + 1;
1110    else
1111      dfl = dflts;
1112
1113    /*
1114     * Chop the defaults up
1115     */
1116    rvec = strsplit(dfl, ' ', '\"');
1117
1118    if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
1119      /*
1120       * Pick whichever first entry matched the list of selectors.
1121       * Strip the selectors from the string, and assign to dfl the
1122       * rest of the string.
1123       */
1124      if (rvec) {
1125	am_opts ap;
1126	am_ops *pt;
1127	char **sp = rvec;
1128	while (*sp) {		/* loop until you find something, if any */
1129	  memset((char *) &ap, 0, sizeof(am_opts));
1130	  pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1131			 mp->am_parent->am_mnt->mf_info);
1132	  free_opts(&ap);	/* don't leak */
1133	  if (pt == &amfs_error_ops) {
1134	    plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
1135	  } else {
1136	    dfl = strip_selectors(*sp, "/defaults");
1137	    plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1138	    break;
1139	  }
1140	  ++sp;
1141	}
1142      }
1143    } else {			/* not enable_default_selectors */
1144      /*
1145       * Extract first value
1146       */
1147      dfl = rvec[0];
1148    }
1149
1150    /*
1151     * If there were any values at all...
1152     */
1153    if (dfl) {
1154      /*
1155       * Log error if there were other values
1156       */
1157      if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
1158# ifdef DEBUG
1159	dlog("/defaults chopped into %s", dfl);
1160# endif /* DEBUG */
1161	plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1162      }
1163
1164      /*
1165       * Prepend to existing defaults if they exist,
1166       * otherwise just use these defaults.
1167       */
1168      if (*auto_opts && *dfl) {
1169	char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1170	sprintf(nopts, "%s;%s", dfl, auto_opts);
1171	XFREE(auto_opts);
1172	auto_opts = nopts;
1173      } else if (*dfl) {
1174	auto_opts = strealloc(auto_opts, dfl);
1175      }
1176    }
1177    XFREE(dflts);
1178    /*
1179     * Don't need info vector any more
1180     */
1181    XFREE(rvec);
1182  }
1183
1184  /*
1185   * Fill it in
1186   */
1187  init_map(new_mp, fname);
1188
1189  /*
1190   * Put it in the table
1191   */
1192  insert_am(new_mp, mp);
1193
1194  /*
1195   * Fill in some other fields,
1196   * path and mount point.
1197   *
1198   * bugfix: do not prepend old am_path if direct map
1199   *         <wls@astro.umd.edu> William Sebok
1200   */
1201  new_mp->am_path = str3cat(new_mp->am_path,
1202			    mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1203			    *fname == '/' ? "" : "/", fname);
1204
1205#ifdef DEBUG
1206  dlog("setting path to %s", new_mp->am_path);
1207#endif /* DEBUG */
1208
1209  /*
1210   * Take private copy of pfname
1211   */
1212  pfname = strdup(pfname);
1213
1214  /*
1215   * Construct a continuation
1216   */
1217  cp = ALLOC(struct continuation);
1218  cp->callout = 0;
1219  cp->mp = new_mp;
1220  cp->xivec = xivec;
1221  cp->ivec = ivec;
1222  cp->info = info;
1223  cp->key = pfname;
1224  cp->auto_opts = auto_opts;
1225  cp->retry = FALSE;
1226  cp->tried = FALSE;
1227  cp->start = clocktime();
1228  cp->def_opts = strdup(auto_opts);
1229  memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1230
1231  /*
1232   * Try and mount the file system.  If this succeeds immediately (possible
1233   * for a ufs file system) then return the attributes, otherwise just
1234   * return an error.
1235   */
1236  error = amfs_auto_bgmount(cp, error);
1237  reschedule_timeout_mp();
1238  if (!error) {
1239    XFREE(fname);
1240    return new_mp;
1241  }
1242
1243  /*
1244   * Code for quick reply.  If nfs_program_2_transp is set, then
1245   * its the transp that's been passed down from nfs_program_2().
1246   * If new_mp->am_transp is not already set, set it by copying in
1247   * nfs_program_2_transp.  Once am_transp is set, quick_reply() can
1248   * use it to send a reply to the client that requested this mount.
1249   */
1250  if (nfs_program_2_transp && !new_mp->am_transp) {
1251    new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1252    *(new_mp->am_transp) = *nfs_program_2_transp;
1253  }
1254  if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1255    new_mp->am_error = error;
1256
1257  assign_error_mntfs(new_mp);
1258
1259  XFREE(fname);
1260
1261  ereturn(error);
1262}
1263
1264
1265/*
1266 * Locate next node in sibling list which is mounted
1267 * and is not an error node.
1268 */
1269am_node *
1270next_nonerror_node(am_node *xp)
1271{
1272  mntfs *mf;
1273
1274  /*
1275   * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1276   * Fixes a race condition when mounting direct automounts.
1277   * Also fixes a problem when doing a readdir on a directory
1278   * containing hung automounts.
1279   */
1280  while (xp &&
1281	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
1282	  mf->mf_error != 0 ||	/* There was a mntfs error */
1283	  xp->am_error != 0 ||	/* There was a mount error */
1284	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
1285	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
1286	 )
1287    xp = xp->am_osib;
1288
1289  return xp;
1290}
1291
1292
1293/*
1294 * This readdir function which call a special version of it that allows
1295 * browsing if browsable_dirs=yes was set on the map.
1296 */
1297int
1298amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
1299{
1300  u_int gen = *(u_int *) cookie;
1301  am_node *xp;
1302  mntent_t mnt;
1303
1304  dp->dl_eof = FALSE;		/* assume readdir not done */
1305
1306  /* check if map is browsable */
1307  if (mp->am_mnt && mp->am_mnt->mf_mopts) {
1308    mnt.mnt_opts = mp->am_mnt->mf_mopts;
1309    if (hasmntopt(&mnt, "fullybrowsable"))
1310      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
1311    if (hasmntopt(&mnt, "browsable"))
1312      return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
1313  }
1314
1315  if (gen == 0) {
1316    /*
1317     * In the default instance (which is used to start a search) we return
1318     * "." and "..".
1319     *
1320     * This assumes that the count is big enough to allow both "." and ".."
1321     * to be returned in a single packet.  If it isn't (which would be
1322     * fairly unbelievable) then tough.
1323     */
1324#ifdef DEBUG
1325    dlog("amfs_auto_readdir: default search");
1326#endif /* DEBUG */
1327    /*
1328     * Check for enough room.  This is extremely approximate but is more
1329     * than enough space.  Really need 2 times:
1330     *      4byte fileid
1331     *      4byte cookie
1332     *      4byte name length
1333     *      4byte name
1334     * plus the dirlist structure */
1335    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1336      return EINVAL;
1337
1338    xp = next_nonerror_node(mp->am_child);
1339    dp->dl_entries = ep;
1340
1341    /* construct "." */
1342    ep[0].ne_fileid = mp->am_gen;
1343    ep[0].ne_name = ".";
1344    ep[0].ne_nextentry = &ep[1];
1345    *(u_int *) ep[0].ne_cookie = 0;
1346
1347    /* construct ".." */
1348    if (mp->am_parent)
1349      ep[1].ne_fileid = mp->am_parent->am_gen;
1350    else
1351      ep[1].ne_fileid = mp->am_gen;
1352    ep[1].ne_name = "..";
1353    ep[1].ne_nextentry = 0;
1354    *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE);
1355
1356    if (!xp)
1357      dp->dl_eof = TRUE;	/* by default assume readdir done */
1358
1359    return 0;
1360  }
1361#ifdef DEBUG
1362  dlog("amfs_auto_readdir: real child");
1363#endif /* DEBUG */
1364
1365  if (gen == DOT_DOT_COOKIE) {
1366#ifdef DEBUG
1367    dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path);
1368#endif /* DEBUG */
1369    dp->dl_eof = TRUE;
1370    dp->dl_entries = 0;
1371    return 0;
1372  }
1373
1374  /* non-browsable directories code */
1375  xp = mp->am_child;
1376  while (xp && xp->am_gen != gen)
1377    xp = xp->am_osib;
1378
1379  if (xp) {
1380    int nbytes = count / 2;	/* conservative */
1381    int todo = MAX_READDIR_ENTRIES;
1382
1383    dp->dl_entries = ep;
1384    do {
1385      am_node *xp_next = next_nonerror_node(xp->am_osib);
1386
1387      if (xp_next) {
1388	*(u_int *) ep->ne_cookie = xp_next->am_gen;
1389      } else {
1390	*(u_int *) ep->ne_cookie = DOT_DOT_COOKIE;
1391	dp->dl_eof = TRUE;
1392      }
1393
1394      ep->ne_fileid = xp->am_gen;
1395      ep->ne_name = xp->am_name;
1396      nbytes -= sizeof(*ep) + 1;
1397      if (xp->am_name)
1398	nbytes -= strlen(xp->am_name);
1399
1400      xp = xp_next;
1401
1402      if (nbytes > 0 && !dp->dl_eof && todo > 1) {
1403	ep->ne_nextentry = ep + 1;
1404	ep++;
1405	--todo;
1406      } else {
1407	todo = 0;
1408      }
1409    } while (todo > 0);
1410
1411    ep->ne_nextentry = 0;
1412
1413    return 0;
1414  }
1415  return ESTALE;
1416}
1417
1418
1419/* This one is called only if map is browsable */
1420static int
1421amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
1422{
1423  u_int gen = *(u_int *) cookie;
1424  int chain_length, i;
1425  static nfsentry *te, *te_next;
1426#ifdef DEBUG_READDIR
1427  nfsentry *ne;
1428  static int j;
1429#endif /* DEBUG_READDIR */
1430
1431  dp->dl_eof = FALSE;		/* assume readdir not done */
1432
1433#ifdef DEBUG_READDIR
1434  plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
1435       gen, count);
1436#endif /* DEBUG_READDIR */
1437
1438  if (gen == 0) {
1439    /*
1440     * In the default instance (which is used to start a search) we return
1441     * "." and "..".
1442     *
1443     * This assumes that the count is big enough to allow both "." and ".."
1444     * to be returned in a single packet.  If it isn't (which would be
1445     * fairly unbelievable) then tough.
1446     */
1447#ifdef DEBUG
1448    dlog("amfs_auto_readdir_browsable: default search");
1449#endif /* DEBUG */
1450    /*
1451     * Check for enough room.  This is extremely approximate but is more
1452     * than enough space.  Really need 2 times:
1453     *      4byte fileid
1454     *      4byte cookie
1455     *      4byte name length
1456     *      4byte name
1457     * plus the dirlist structure */
1458    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1459      return EINVAL;
1460
1461    /*
1462     * compute # of entries to send in this chain.
1463     * heuristics: 128 bytes per entry.
1464     * This is too much probably, but it seems to work better because
1465     * of the re-entrant nature of nfs_readdir, and esp. on systems
1466     * like OpenBSD 2.2.
1467     */
1468    chain_length = count / 128;
1469
1470    /* reset static state counters */
1471    te = te_next = NULL;
1472
1473    dp->dl_entries = ep;
1474
1475    /* construct "." */
1476    ep[0].ne_fileid = mp->am_gen;
1477    ep[0].ne_name = ".";
1478    ep[0].ne_nextentry = &ep[1];
1479    *(u_int *) ep[0].ne_cookie = 0;
1480
1481    /* construct ".." */
1482    if (mp->am_parent)
1483      ep[1].ne_fileid = mp->am_parent->am_gen;
1484    else
1485      ep[1].ne_fileid = mp->am_gen;
1486    ep[1].ne_name = "..";
1487    ep[1].ne_nextentry = 0;
1488    *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE;
1489
1490    /*
1491     * If map is browsable, call a function make_entry_chain() to construct
1492     * a linked list of unmounted keys, and return it.  Then link the chain
1493     * to the regular list.  Get the chain only once, but return
1494     * chunks of it each time.
1495     */
1496    te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
1497    if (!te)
1498      return 0;
1499#ifdef DEBUG_READDIR
1500    for (j=0,ne=te; ne; ne=ne->ne_nextentry)
1501      plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
1502#endif /* DEBUG_READDIR */
1503
1504    /* return only "chain_length" entries */
1505    te_next = te;
1506    for (i=1; i<chain_length; ++i) {
1507      te_next = te_next->ne_nextentry;
1508      if (!te_next)
1509	break;
1510    }
1511    if (te_next) {
1512      nfsentry *te_saved = te_next->ne_nextentry;
1513      te_next->ne_nextentry = NULL; /* terminate "te" chain */
1514      te_next = te_saved;	/* save rest of "te" for next iteration */
1515      dp->dl_eof = FALSE;	/* tell readdir there's more */
1516    } else {
1517      dp->dl_eof = TRUE;	/* tell readdir that's it */
1518    }
1519    ep[1].ne_nextentry = te;	/* append this chunk of "te" chain */
1520#ifdef DEBUG_READDIR
1521    for (j=0,ne=te; ne; ne=ne->ne_nextentry)
1522      plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
1523    for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
1524      plog(XLOG_INFO, "gen2+ key %4d \"%s\" fi=%d ck=%d",
1525	   j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
1526    plog(XLOG_INFO, "EOF is %d", dp->dl_eof);
1527#endif /* DEBUG_READDIR */
1528    return 0;
1529  } /* end of "if (gen == 0)" statement */
1530
1531#ifdef DEBUG
1532  dlog("amfs_auto_readdir_browsable: real child");
1533#endif /* DEBUG */
1534
1535  if (gen == DOT_DOT_COOKIE) {
1536#ifdef DEBUG
1537    dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path);
1538#endif /* DEBUG */
1539    dp->dl_eof = TRUE;
1540    dp->dl_entries = 0;
1541    return 0;
1542  }
1543
1544  /*
1545   * If browsable directories, then continue serving readdir() with another
1546   * chunk of entries, starting from where we left off (when gen was equal
1547   * to 0).  Once again, assume last chunk served to readdir.
1548   */
1549  dp->dl_eof = TRUE;
1550  dp->dl_entries = ep;
1551
1552  te = te_next;			/* reset 'te' from last saved te_next */
1553  if (!te) {			/* another indicator of end of readdir */
1554    dp->dl_entries = 0;
1555    return 0;
1556  }
1557  /*
1558   * compute # of entries to send in this chain.
1559   * heuristics: 128 bytes per entry.
1560   */
1561  chain_length = count / 128;
1562
1563  /* return only "chain_length" entries */
1564  for (i=1; i<chain_length; ++i) {
1565    te_next = te_next->ne_nextentry;
1566    if (!te_next)
1567      break;
1568  }
1569  if (te_next) {
1570    nfsentry *te_saved = te_next->ne_nextentry;
1571    te_next->ne_nextentry = NULL; /* terminate "te" chain */
1572    te_next = te_saved;		/* save rest of "te" for next iteration */
1573    dp->dl_eof = FALSE;		/* tell readdir there's more */
1574  }
1575  ep = te;			/* send next chunk of "te" chain */
1576  dp->dl_entries = ep;
1577#ifdef DEBUG_READDIR
1578  plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
1579       (int) dp->dl_entries, (int) te_next, dp->dl_eof);
1580  for (ne=te; ne; ne=ne->ne_nextentry)
1581    plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
1582#endif /* DEBUG_READDIR */
1583  return 0;
1584}
1585
1586
1587int
1588amfs_auto_fmount(am_node *mp)
1589{
1590  mntfs *mf = mp->am_mnt;
1591  return (*mf->mf_ops->fmount_fs) (mf);
1592}
1593
1594
1595int
1596amfs_auto_fumount(am_node *mp)
1597{
1598  mntfs *mf = mp->am_mnt;
1599  return (*mf->mf_ops->fumount_fs) (mf);
1600}
1601