Merge branch 'vendor/FILE'
[dragonfly.git] / contrib / amd / amd / amfs_auto.c
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  ****************************************************************************/
73 static int amfs_auto_bgmount(struct continuation * cp, int mpe);
74 static int amfs_auto_mount(am_node *mp);
75 static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
76 static void amfs_auto_umounted(am_node *mp);
77
78
79 /****************************************************************************
80  *** OPS STRUCTURES                                                       ***
81  ****************************************************************************/
82 am_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  */
107 char *
108 amfs_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  */
141 void
142 amfs_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  */
159 static int
160 amfs_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  */
227 int
228 amfs_auto_umount(am_node *mp)
229 {
230   return 0;
231 }
232
233
234 /*
235  * Unmount an automount node
236  */
237 static void
238 amfs_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  */
252 void
253 free_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  */
271 void
272 assign_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  */
307 void
308 amfs_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  */
377 void
378 amfs_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  */
413 int
414 try_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  */
506 static int
507 amfs_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  */
856 am_node *
857 amfs_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  */
1274 am_node *
1275 next_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  */
1302 int
1303 amfs_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 */
1425 static int
1426 amfs_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
1592 int
1593 amfs_auto_fmount(am_node *mp)
1594 {
1595   mntfs *mf = mp->am_mnt;
1596   return (*mf->mf_ops->fmount_fs) (mf);
1597 }
1598
1599
1600 int
1601 amfs_auto_fumount(am_node *mp)
1602 {
1603   mntfs *mf = mp->am_mnt;
1604   return (*mf->mf_ops->fumount_fs) (mf);
1605 }