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