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