]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/amd/amfs_generic.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / amd / amfs_generic.c
1 /*
2  * Copyright (c) 1997-2006 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  *
40  * File: am-utils/amd/amfs_generic.c
41  *
42  */
43
44 /*
45  * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
46  */
47
48 #ifdef HAVE_CONFIG_H
49 # include <config.h>
50 #endif /* HAVE_CONFIG_H */
51 #include <am_defs.h>
52 #include <amd.h>
53
54
55 /****************************************************************************
56  *** MACROS                                                               ***
57  ****************************************************************************/
58 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
59
60
61 /****************************************************************************
62  *** STRUCTURES                                                           ***
63  ****************************************************************************/
64 /*
65  * Mounting a file system may take a significant period of time.  The
66  * problem is that if this is done in the main process thread then the
67  * entire automounter could be blocked, possibly hanging lots of processes
68  * on the system.  Instead we use a continuation scheme to allow mounts to
69  * be attempted in a sub-process.  When the sub-process exits we pick up the
70  * exit status (by convention a UN*X error number) and continue in a
71  * notifier.  The notifier gets handed a data structure and can then
72  * determine whether the mount was successful or not.  If not, it updates
73  * the data structure and tries again until there are no more ways to try
74  * the mount, or some other permanent error occurs.  In the mean time no RPC
75  * reply is sent, even after the mount is successful.  We rely on the RPC
76  * retry mechanism to resend the lookup request which can then be handled.
77  */
78 struct continuation {
79   am_node *mp;                  /* Node we are trying to mount */
80   int retry;                    /* Try again? */
81   time_t start;                 /* Time we started this mount */
82   int callout;                  /* Callout identifier */
83   mntfs **mf;                   /* Current mntfs */
84 };
85
86
87 /****************************************************************************
88  *** FORWARD DEFINITIONS                                                  ***
89  ****************************************************************************/
90 static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return);
91 static mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
92                                     char *def_opts, char *pfname);
93 static mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return);
94 static void amfs_cont(int rc, int term, opaque_t arg);
95 static void amfs_retry(int rc, int term, opaque_t arg);
96 static void free_continuation(struct continuation *cp);
97 static int amfs_bgmount(struct continuation *cp);
98 static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts);
99
100
101 /****************************************************************************
102  *** FUNCTIONS                                                             ***
103  ****************************************************************************/
104 static am_node *
105 amfs_lookup_node(am_node *mp, char *fname, int *error_return)
106 {
107   am_node *new_mp;
108   int error = 0;                /* Error so far */
109   int in_progress = 0;          /* # of (un)mount in progress */
110   mntfs *mf;
111   char *expanded_fname = 0;
112
113   dlog("in amfs_lookup_node");
114
115   /*
116    * If the server is shutting down
117    * then don't return information
118    * about the mount point.
119    */
120   if (amd_state == Finishing) {
121     if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) {
122       dlog("%s mount ignored - going down", fname);
123     } else {
124       dlog("%s/%s mount ignored - going down", mp->am_path, fname);
125     }
126     ereturn(ENOENT);
127   }
128
129   /*
130    * Handle special case of "." and ".."
131    */
132   if (fname[0] == '.') {
133     if (fname[1] == '\0')
134       return mp;                /* "." is the current node */
135     if (fname[1] == '.' && fname[2] == '\0') {
136       if (mp->am_parent) {
137         dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
138         return mp->am_parent;   /* ".." is the parent node */
139       }
140       ereturn(ESTALE);
141     }
142   }
143
144   /*
145    * Check for valid key name.
146    * If it is invalid then pretend it doesn't exist.
147    */
148   if (!valid_key(fname)) {
149     plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
150     ereturn(ENOENT);
151   }
152
153   /*
154    * Expand key name.
155    * expanded_fname is now a private copy.
156    */
157   expanded_fname = expand_selectors(fname);
158
159   /*
160    * Search children of this node
161    */
162   for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) {
163     if (FSTREQ(new_mp->am_name, expanded_fname)) {
164       if (new_mp->am_error) {
165         error = new_mp->am_error;
166         continue;
167       }
168
169       /*
170        * If the error code is undefined then it must be
171        * in progress.
172        */
173       mf = new_mp->am_mnt;
174       if (mf->mf_error < 0)
175         goto in_progrss;
176
177       /*
178        * If there was a previous error with this node
179        * then return that error code.
180        */
181       if (mf->mf_flags & MFF_ERROR) {
182         error = mf->mf_error;
183         continue;
184       }
185       if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
186       in_progrss:
187         /*
188          * If the fs is not mounted or it is unmounting then there
189          * is a background (un)mount in progress.  In this case
190          * we just drop the RPC request (return nil) and
191          * wait for a retry, by which time the (un)mount may
192          * have completed.
193          */
194         dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
195              expanded_fname, mf->mf_mount,
196              (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags);
197         in_progress++;
198         if (mf->mf_flags & MFF_UNMOUNTING) {
199           dlog("will remount later");
200           new_mp->am_flags |= AMF_REMOUNT;
201         }
202         continue;
203       }
204
205       /*
206        * Otherwise we have a hit: return the current mount point.
207        */
208       dlog("matched %s in %s", expanded_fname, new_mp->am_path);
209       XFREE(expanded_fname);
210       return new_mp;
211     }
212   }
213
214   if (in_progress) {
215     dlog("Waiting while %d mount(s) in progress", in_progress);
216     XFREE(expanded_fname);
217     ereturn(-1);
218   }
219
220   /*
221    * If an error occurred then return it.
222    */
223   if (error) {
224     dlog("Returning error: %s", strerror(error));
225     XFREE(expanded_fname);
226     ereturn(error);
227   }
228
229   /*
230    * If the server is going down then just return,
231    * don't try to mount any more file systems
232    */
233   if ((int) amd_state >= (int) Finishing) {
234     dlog("not found - server going down anyway");
235     ereturn(ENOENT);
236   }
237
238   /*
239    * Allocate a new map
240    */
241   new_mp = get_ap_child(mp, expanded_fname);
242   XFREE(expanded_fname);
243   if (new_mp == 0)
244     ereturn(ENOSPC);
245
246   *error_return = -1;
247   return new_mp;
248 }
249
250
251
252 static mntfs *
253 amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
254                       char *def_opts, char *pfname)
255 {
256   am_ops *p;
257   am_opts *fs_opts;
258   mntfs *new_mf;
259   char *mp_dir = 0;
260 #ifdef HAVE_FS_AUTOFS
261   int on_autofs = 1;
262 #endif /* HAVE_FS_AUTOFS */
263
264   /* match the operators */
265   fs_opts = CALLOC(am_opts);
266   p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path,
267                 pfname, mf->mf_info);
268 #ifdef HAVE_FS_AUTOFS
269   /* XXX: this should be factored out into an autofs-specific function */
270   if (new_mp->am_flags & AMF_AUTOFS) {
271     /* ignore user-provided fs if we're using autofs */
272     if (fs_opts->opt_sublink) {
273       /*
274        * For sublinks we need to use a hack with autofs:
275        * mount the filesystem on the original opt_fs (which is NOT an
276        * autofs mountpoint) and symlink (or lofs-mount) to it from
277        * the autofs mountpoint.
278        */
279       on_autofs = 0;
280       mp_dir = fs_opts->opt_fs;
281     } else {
282       if (p->autofs_fs_flags & FS_ON_AUTOFS) {
283         mp_dir = new_mp->am_path;
284       } else {
285         mp_dir = fs_opts->opt_fs;
286         on_autofs = 0;
287       }
288     }
289   } else
290 #endif /* HAVE_FS_AUTOFS */
291     mp_dir = fs_opts->opt_fs;
292
293   /*
294    * Find or allocate a filesystem for this node.
295    */
296   new_mf = find_mntfs(p, fs_opts,
297                       mp_dir,
298                       fs_opts->fs_mtab,
299                       def_opts,
300                       fs_opts->opt_opts,
301                       fs_opts->opt_remopts);
302
303   /*
304    * See whether this is a real filesystem
305    */
306   p = new_mf->mf_ops;
307   if (p == &amfs_error_ops) {
308     plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
309     free_mntfs(new_mf);
310     return NULL;
311   }
312
313   dlog("Got a hit with %s", p->fs_type);
314
315 #ifdef HAVE_FS_AUTOFS
316   if (new_mp->am_flags & AMF_AUTOFS && on_autofs) {
317     new_mf->mf_flags |= MFF_ON_AUTOFS;
318     new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags;
319   }
320   /*
321    * A new filesystem is an autofs filesystems if:
322    * 1. it claims it can be one (has the FS_AUTOFS flag)
323    * 2. autofs is enabled system-wide
324    * 3. either has an autofs parent,
325    *    or it is explicitly requested to be autofs.
326    */
327   if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
328       amd_use_autofs &&
329       ((mf->mf_flags & MFF_IS_AUTOFS) ||
330        (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type &&
331         STREQ(new_mf->mf_fo->opt_mount_type, "autofs"))))
332     new_mf->mf_flags |= MFF_IS_AUTOFS;
333 #endif /* HAVE_FS_AUTOFS */
334
335   return new_mf;
336 }
337
338
339 static mntfs **
340 amfs_lookup_mntfs(am_node *new_mp, int *error_return)
341 {
342   am_node *mp;
343   char *info;                   /* Mount info - where to get the file system */
344   char **ivecs, **cur_ivec;     /* Split version of info */
345   int num_ivecs;
346   char *orig_def_opts;          /* Original Automount options */
347   char *def_opts;               /* Automount options */
348   int error = 0;                /* Error so far */
349   char path_name[MAXPATHLEN];   /* General path name buffer */
350   char *pfname;                 /* Path for database lookup */
351   mntfs *mf, **mf_array;
352   int count;
353
354   dlog("in amfs_lookup_mntfs");
355
356   mp = new_mp->am_parent;
357
358   /*
359    * If we get here then this is a reference to an,
360    * as yet, unknown name so we need to search the mount
361    * map for it.
362    */
363   if (mp->am_pref) {
364     if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
365       ereturn(ENAMETOOLONG);
366     xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name);
367     pfname = path_name;
368   } else {
369     pfname = new_mp->am_name;
370   }
371
372   mf = mp->am_mnt;
373
374   dlog("will search map info in %s to find %s", mf->mf_info, pfname);
375   /*
376    * Consult the oracle for some mount information.
377    * info is malloc'ed and belongs to this routine.
378    * It ends up being free'd in free_continuation().
379    *
380    * Note that this may return -1 indicating that information
381    * is not yet available.
382    */
383   error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
384   if (error) {
385     if (error > 0)
386       plog(XLOG_MAP, "No map entry for %s", pfname);
387     else
388       plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
389     ereturn(error);
390   }
391   dlog("mount info is %s", info);
392
393   /*
394    * Split info into an argument vector.
395    * The vector is malloc'ed and belongs to
396    * this routine.  It is free'd further down.
397    *
398    * Note: the vector pointers point into info, so don't free it!
399    */
400   ivecs = strsplit(info, ' ', '\"');
401
402   if (mf->mf_auto)
403     def_opts = mf->mf_auto;
404   else
405     def_opts = "";
406
407   orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts));
408   def_opts = strdup(orig_def_opts);
409
410   /* first build our defaults */
411   num_ivecs = 0;
412   for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
413     if (**cur_ivec == '-') {
414       /*
415        * Pick up new defaults
416        */
417       char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1);
418       XFREE(def_opts);
419       def_opts = new_def_opts;
420       dlog("Setting def_opts to \"%s\"", def_opts);
421       continue;
422     } else
423       num_ivecs++;
424   }
425
426   mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
427
428   /* construct the array of struct mntfs for this mount point */
429   for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
430     mntfs *new_mf;
431
432     if (**cur_ivec == '-') {
433       XFREE(def_opts);
434       if ((*cur_ivec)[1] == '\0') {
435         /*
436          * If we have a single dash '-' than we need to reset the
437          * default options.
438          */
439         def_opts = strdup(orig_def_opts);
440         dlog("Resetting the default options, a single dash '-' was found.");
441       } else {
442         /* append options to /default options */
443         def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1);
444         dlog("Resetting def_opts to \"%s\"", def_opts);
445       }
446       continue;
447     }
448
449     /*
450      * If a mntfs has already been found, and we find
451      * a cut then don't try any more locations.
452      *
453      * XXX: we do not know when the "/" was added as an equivalent for "||".
454      * It's undocumented, it might go away at any time. Caveat emptor.
455      */
456     if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
457       if (count > 0) {
458         dlog("Cut: not trying any more locations for %s", mp->am_path);
459         break;
460       }
461       continue;
462     }
463
464     new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname);
465     if (new_mf == NULL)
466       continue;
467     mf_array[count++] = new_mf;
468   }
469
470   /* We're done with ivecs */
471   XFREE(ivecs);
472   XFREE(info);
473   XFREE(orig_def_opts);
474   XFREE(def_opts);
475   if (count == 0) {                     /* no match */
476     XFREE(mf_array);
477     ereturn(ENOENT);
478   }
479
480   return mf_array;
481 }
482
483
484 /*
485  * The continuation function.  This is called by
486  * the task notifier when a background mount attempt
487  * completes.
488  */
489 static void
490 amfs_cont(int rc, int term, opaque_t arg)
491 {
492   struct continuation *cp = (struct continuation *) arg;
493   am_node *mp = cp->mp;
494   mntfs *mf = mp->am_mnt;
495
496   dlog("amfs_cont: '%s'", mp->am_path);
497
498   /*
499    * Definitely not trying to mount at the moment
500    */
501   mf->mf_flags &= ~MFF_MOUNTING;
502
503   /*
504    * While we are mounting - try to avoid race conditions
505    */
506   new_ttl(mp);
507
508   /*
509    * Wakeup anything waiting for this mount
510    */
511   wakeup(get_mntfs_wchan(mf));
512
513   /*
514    * Check for termination signal or exit status...
515    */
516   if (rc || term) {
517 #ifdef HAVE_FS_AUTOFS
518     if (mf->mf_flags & MFF_IS_AUTOFS &&
519         !(mf->mf_flags & MFF_MOUNTED))
520       autofs_release_fh(mp);
521 #endif /* HAVE_FS_AUTOFS */
522
523     if (term) {
524       /*
525        * Not sure what to do for an error code.
526        */
527       mf->mf_error = EIO;       /* XXX ? */
528       mf->mf_flags |= MFF_ERROR;
529       plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
530     } else {
531       /*
532        * Check for exit status...
533        */
534 #ifdef __linux__
535       /*
536        * HACK ALERT!
537        *
538        * On Linux (and maybe not only) it's possible to run
539        * an amd which "knows" how to mount certain combinations
540        * of nfs_proto/nfs_version which the kernel doesn't grok.
541        * So if we got an EINVAL and we have a server that's not
542        * using NFSv2/UDP, try again with NFSv2/UDP.
543        *
544        * Too bad that there is no way to dynamically determine
545        * what combinations the _client_ supports, as opposed to
546        * what the _server_ supports...
547        */
548       if (rc == EINVAL &&
549           mf->mf_server &&
550           (mf->mf_server->fs_version != 2 ||
551            !STREQ(mf->mf_server->fs_proto, "udp")))
552         mf->mf_flags |= MFF_NFS_SCALEDOWN;
553       else
554 #endif /* __linux__ */
555       {
556         mf->mf_error = rc;
557         mf->mf_flags |= MFF_ERROR;
558         errno = rc;             /* XXX */
559         if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx"))
560           plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
561       }
562     }
563
564     if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
565       /*
566        * If we get here then that attempt didn't work, so
567        * move the info vector pointer along by one and
568        * call the background mount routine again
569        */
570       amd_stats.d_merr++;
571       cp->mf++;
572     }
573     amfs_bgmount(cp);
574     if (mp->am_error > 0)
575       assign_error_mntfs(mp);
576   } else {
577     /*
578      * The mount worked.
579      */
580     dlog("Mounting %s returned success", cp->mp->am_path);
581     am_mounted(cp->mp);
582     free_continuation(cp);
583   }
584
585   reschedule_timeout_mp();
586 }
587
588
589 /*
590  * Retry a mount
591  */
592 static void
593 amfs_retry(int rc, int term, opaque_t arg)
594 {
595   struct continuation *cp = (struct continuation *) arg;
596   am_node *mp = cp->mp;
597   int error = 0;
598
599   dlog("Commencing retry for mount of %s", mp->am_path);
600
601   new_ttl(mp);
602
603   if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
604     /*
605      * The entire mount has timed out.  Set the error code and skip past all
606      * the mntfs's so that amfs_bgmount will not have any more
607      * ways to try the mount, thus causing an error.
608      */
609     plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
610     error = ETIMEDOUT;
611     while (*cp->mf)
612       cp->mf++;
613     /* explicitly forbid further retries after timeout */
614     cp->retry = FALSE;
615   }
616   if (error || !IN_PROGRESS(cp))
617     error = amfs_bgmount(cp);
618
619   reschedule_timeout_mp();
620 }
621
622
623 /*
624  * Discard an old continuation
625  */
626 static void
627 free_continuation(struct continuation *cp)
628 {
629   mntfs **mfp;
630
631   dlog("free_continuation");
632   if (cp->callout)
633     untimeout(cp->callout);
634   /*
635    * we must free the mntfs's in the list.
636    * so free all of them if there was an error,
637    * or free all but the used one, if the mount succeeded.
638    */
639   for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
640     free_mntfs(*mfp);
641   }
642   XFREE(cp->mp->am_mfarray);
643   cp->mp->am_mfarray = 0;
644   XFREE(cp);
645 }
646
647
648 /*
649  * Pick a file system to try mounting and
650  * do that in the background if necessary
651  *
652 For each location:
653         discard previous mount location if required
654         fetch next mount location
655         if the filesystem failed to be mounted then
656                 this_error = error from filesystem
657                 goto failed
658         if the filesystem is mounting or unmounting then
659                 goto retry;
660         if the fileserver is down then
661                 this_error = EIO
662                 continue;
663         if the filesystem is already mounted
664                 break
665         fi
666
667         this_error = initialize mount point
668
669         if no error on this mount and mount is delayed then
670                 this_error = -1
671         fi
672         if this_error < 0 then
673                 retry = true
674         fi
675         if no error on this mount then
676                 if mount in background then
677                         run mount in background
678                         return -1
679                 else
680                         this_error = mount in foreground
681                 fi
682         fi
683         if an error occurred on this mount then
684                 update stats
685                 save error in mount point
686         fi
687 endfor
688  */
689 static int
690 amfs_bgmount(struct continuation *cp)
691 {
692   am_node *mp = cp->mp;
693   mntfs *mf;                    /* Current mntfs */
694   int this_error = -1;          /* Per-mount error */
695   int hard_error = -1;          /* Cumulative per-node error */
696
697   if (mp->am_mnt)
698     free_mntfs(mp->am_mnt);
699
700   /*
701    * Try to mount each location.
702    * At the end:
703    * hard_error == 0 indicates something was mounted.
704    * hard_error > 0 indicates everything failed with a hard error
705    * hard_error < 0 indicates nothing could be mounted now
706    */
707   for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
708     am_ops *p;
709
710     mf = dup_mntfs(mp->am_mnt);
711     p = mf->mf_ops;
712
713     if (hard_error < 0)
714       hard_error = this_error;
715     this_error = 0;
716
717     if (mf->mf_error > 0) {
718       this_error = mf->mf_error;
719       goto failed;
720     }
721
722     if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
723       /*
724        * Still mounting - retry later
725        */
726       dlog("mount of \"%s\" already pending", mf->mf_info);
727       goto retry;
728     }
729
730     if (FSRV_ISDOWN(mf->mf_server)) {
731       /*
732        * Would just mount from the same place
733        * as a hung mount - so give up
734        */
735       dlog("%s is already hung - giving up", mf->mf_server->fs_host);
736       this_error = EIO;
737       goto failed;
738     }
739
740     if (mp->am_link) {
741       XFREE(mp->am_link);
742       mp->am_link = NULL;
743     }
744     if (mf->mf_fo && mf->mf_fo->opt_sublink)
745       mp->am_link = strdup(mf->mf_fo->opt_sublink);
746
747     /*
748      * Will usually need to play around with the mount nodes
749      * file attribute structure.  This must be done here.
750      * Try and get things initialized, even if the fileserver
751      * is not known to be up.  In the common case this will
752      * progress things faster.
753      */
754
755     /*
756      * Fill in attribute fields.
757      */
758     if (mf->mf_fsflags & FS_DIRECTORY)
759       mk_fattr(&mp->am_fattr, NFDIR);
760     else
761       mk_fattr(&mp->am_fattr, NFLNK);
762
763     if (mf->mf_flags & MFF_MOUNTED) {
764       dlog("duplicate mount of \"%s\" ...", mf->mf_info);
765       /*
766        * Skip initial processing of the mountpoint if already mounted.
767        * This could happen if we have multiple sublinks into the same f/s,
768        * or if we are restarting an already-mounted filesystem.
769        */
770       goto already_mounted;
771     }
772
773     if (mf->mf_fo && mf->mf_fo->fs_mtab) {
774       plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
775            mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
776            mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
777     }
778
779     if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
780       this_error = p->fs_init(mf);
781
782     if (this_error > 0)
783       goto failed;
784     if (this_error < 0)
785       goto retry;
786
787     if (mf->mf_fo && mf->mf_fo->opt_delay) {
788       /*
789        * If there is a delay timer on the mount
790        * then don't try to mount if the timer
791        * has not expired.
792        */
793       int i = atoi(mf->mf_fo->opt_delay);
794       time_t now = clocktime(NULL);
795       if (i > 0 && now < (cp->start + i)) {
796         dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
797         goto retry;
798       }
799     }
800
801     /*
802      * If the directory is not yet made and it needs to be made, then make it!
803      */
804     if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
805       plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
806       this_error = mkdirs(mf->mf_mount, 0555);
807       if (this_error) {
808         plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
809         goto failed;
810       }
811       mf->mf_flags |= MFF_MKMNT;
812     }
813
814 #ifdef HAVE_FS_AUTOFS
815     if (mf->mf_flags & MFF_IS_AUTOFS)
816       if ((this_error = autofs_get_fh(mp)))
817         goto failed;
818 #endif /* HAVE_FS_AUTOFS */
819
820   already_mounted:
821     mf->mf_flags |= MFF_MOUNTING;
822     if (mf->mf_fsflags & FS_MBACKGROUND) {
823       dlog("backgrounding mount of \"%s\"", mf->mf_mount);
824       if (cp->callout) {
825         untimeout(cp->callout);
826         cp->callout = 0;
827       }
828
829       /* actually run the task, backgrounding as necessary */
830       run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
831       return -1;
832     } else {
833       dlog("foreground mount of \"%s\" ...", mf->mf_mount);
834       this_error = mount_node((opaque_t) mp);
835     }
836
837     mf->mf_flags &= ~MFF_MOUNTING;
838     if (this_error > 0)
839       goto failed;
840     if (this_error == 0) {
841       am_mounted(mp);
842       break;                                    /* Success */
843     }
844
845   retry:
846     if (!cp->retry)
847       continue;
848     dlog("will retry ...\n");
849
850     /*
851      * Arrange that amfs_bgmount is called
852      * after anything else happens.
853      */
854     dlog("Arranging to retry mount of %s", mp->am_path);
855     sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
856     if (cp->callout)
857       untimeout(cp->callout);
858     cp->callout = timeout(RETRY_INTERVAL, wakeup,
859                           (opaque_t) get_mntfs_wchan(mf));
860
861     mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
862
863     /*
864      * Not done yet - so don't return anything
865      */
866     return -1;
867
868   failed:
869     amd_stats.d_merr++;
870     mf->mf_error = this_error;
871     mf->mf_flags |= MFF_ERROR;
872 #ifdef HAVE_FS_AUTOFS
873     if (mp->am_autofs_fh)
874       autofs_release_fh(mp);
875 #endif /* HAVE_FS_AUTOFS */
876     if (mf->mf_flags & MFF_MKMNT) {
877       rmdirs(mf->mf_mount);
878       mf->mf_flags &= ~MFF_MKMNT;
879     }
880     /*
881      * Wakeup anything waiting for this mount
882      */
883     wakeup(get_mntfs_wchan(mf));
884     free_mntfs(mf);
885     /* continue */
886   }
887
888   /*
889    * If we get here, then either the mount succeeded or
890    * there is no more mount information available.
891    */
892   if (this_error) {
893     mp->am_mnt = mf = new_mntfs();
894
895 #ifdef HAVE_FS_AUTOFS
896     if (mp->am_flags & AMF_AUTOFS)
897       autofs_mount_failed(mp);
898     else
899 #endif /* HAVE_FS_AUTOFS */
900       nfs_quick_reply(mp, this_error);
901
902     if (hard_error <= 0)
903       hard_error = this_error;
904     if (hard_error < 0)
905       hard_error = ETIMEDOUT;
906
907     /*
908      * Set a small(ish) timeout on an error node if
909      * the error was not a time out.
910      */
911     switch (hard_error) {
912     case ETIMEDOUT:
913     case EWOULDBLOCK:
914     case EIO:
915       mp->am_timeo = 17;
916       break;
917     default:
918       mp->am_timeo = 5;
919       break;
920     }
921     new_ttl(mp);
922   } else {
923     mf = mp->am_mnt;
924     /*
925      * Wakeup anything waiting for this mount
926      */
927     wakeup(get_mntfs_wchan(mf));
928     hard_error = 0;
929   }
930
931   /*
932    * Make sure that the error value in the mntfs has a
933    * reasonable value.
934    */
935   if (mf->mf_error < 0) {
936     mf->mf_error = hard_error;
937     if (hard_error)
938       mf->mf_flags |= MFF_ERROR;
939   }
940
941   /*
942    * In any case we don't need the continuation any more
943    */
944   free_continuation(cp);
945
946   return hard_error;
947 }
948
949
950 static char *
951 amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
952 {
953   char *dflts;
954   char *dfl;
955   char **rvec = NULL;
956   struct mnt_map *mm = (mnt_map *) mf->mf_private;
957
958   dlog("determining /defaults entry value");
959
960   /*
961    * Find out if amd.conf overrode any map-specific /defaults.
962    *
963    * HACK ALERT: there's no easy way to find out what the map mount point is
964    * at this point, so I am forced to initialize the mnt_map->cfm field here
965    * for the first time, upon the very first search for a /defaults entry in
966    * this map.  This initialization is much better done in mapc_create(),
967    * but it's impossible to do that there with the current code structure.
968    */
969   if (mm->cfm == NULL) {        /* then initialize it for first time */
970     mm->cfm = find_cf_map(mf->mf_mount);
971   }
972   if (mm->cfm && mm->cfm->cfm_defaults) {
973     dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
974     dflts = strdup(mm->cfm->cfm_defaults);
975   } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
976     dlog("/defaults gave %s", dflts);
977   } else {
978     return def_opts;            /* if nothing found */
979   }
980
981   /* trim leading '-' in case thee's one */
982   if (*dflts == '-')
983     dfl = dflts + 1;
984   else
985     dfl = dflts;
986
987   /*
988    * Chop the defaults up
989    */
990   rvec = strsplit(dfl, ' ', '\"');
991
992   if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
993     /*
994      * Pick whichever first entry matched the list of selectors.
995      * Strip the selectors from the string, and assign to dfl the
996      * rest of the string.
997      */
998     if (rvec) {
999       am_opts ap;
1000       am_ops *pt;
1001       char **sp = rvec;
1002       while (*sp) {             /* loop until you find something, if any */
1003         memset((char *) &ap, 0, sizeof(am_opts));
1004         /*
1005          * This next routine cause many spurious "expansion of ... is"
1006          * messages, which are ignored, b/c all we need out of this
1007          * routine is to match selectors.  These spurious messages may
1008          * be wrong, esp. if they try to expand ${key} b/c it will
1009          * get expanded to "/defaults"
1010          */
1011         pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1012                        mp->am_parent->am_mnt->mf_info);
1013         free_opts(&ap); /* don't leak */
1014         if (pt == &amfs_error_ops) {
1015           plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1016         } else {
1017           dfl = strip_selectors(*sp, "/defaults");
1018           plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1019           break;
1020         }
1021         ++sp;
1022       }
1023     }
1024   } else {                      /* not selectors_in_defaults */
1025     /*
1026      * Extract first value
1027      */
1028     dfl = rvec[0];
1029   }
1030
1031   /*
1032    * If there were any values at all...
1033    */
1034   if (dfl) {
1035     /*
1036      * Log error if there were other values
1037      */
1038     if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1039       dlog("/defaults chopped into %s", dfl);
1040       plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1041     }
1042
1043     /*
1044      * Prepend to existing defaults if they exist,
1045      * otherwise just use these defaults.
1046      */
1047     if (*def_opts && *dfl) {
1048       size_t l = strlen(def_opts) + strlen(dfl) + 2;
1049       char *nopts = (char *) xmalloc(l);
1050       xsnprintf(nopts, l, "%s;%s", dfl, def_opts);
1051       XFREE(def_opts);
1052       def_opts = nopts;
1053     } else if (*dfl) {
1054       def_opts = strealloc(def_opts, dfl);
1055     }
1056   }
1057
1058   XFREE(dflts);
1059
1060   /* don't need info vector any more */
1061   if (rvec)
1062     XFREE(rvec);
1063
1064   return def_opts;
1065 }
1066
1067
1068 am_node *
1069 amfs_generic_mount_child(am_node *new_mp, int *error_return)
1070 {
1071   int error;
1072   struct continuation *cp;      /* Continuation structure if need to mount */
1073
1074   dlog("in amfs_generic_mount_child");
1075
1076   *error_return = error = 0;    /* Error so far */
1077
1078   /* we have an errorfs attached to the am_node, free it */
1079   free_mntfs(new_mp->am_mnt);
1080   new_mp->am_mnt = 0;
1081
1082   /*
1083    * Construct a continuation
1084    */
1085   cp = ALLOC(struct continuation);
1086   cp->callout = 0;
1087   cp->mp = new_mp;
1088   cp->retry = TRUE;
1089   cp->start = clocktime(NULL);
1090   cp->mf = new_mp->am_mfarray;
1091
1092   /*
1093    * Try and mount the file system.  If this succeeds immediately (possible
1094    * for a ufs file system) then return the attributes, otherwise just
1095    * return an error.
1096    */
1097   error = amfs_bgmount(cp);
1098   reschedule_timeout_mp();
1099   if (!error)
1100     return new_mp;
1101
1102   /*
1103    * Code for quick reply.  If current_transp is set, then it's the
1104    * transp that's been passed down from nfs_program_2() or from
1105    * autofs_program_[123]().
1106    * If new_mp->am_transp is not already set, set it by copying in
1107    * current_transp.  Once am_transp is set, nfs_quick_reply() and
1108    * autofs_mount_succeeded() can use it to send a reply to the
1109    * client that requested this mount.
1110    */
1111   if (current_transp && !new_mp->am_transp) {
1112     dlog("Saving RPC transport for %s", new_mp->am_path);
1113     new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1114     *(new_mp->am_transp) = *current_transp;
1115   }
1116   if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1117     new_mp->am_error = error;
1118
1119   if (new_mp->am_error > 0)
1120     assign_error_mntfs(new_mp);
1121
1122   ereturn(error);
1123 }
1124
1125
1126 /*
1127  * Automount interface to RPC lookup routine
1128  * Find the corresponding entry and return
1129  * the file handle for it.
1130  */
1131 am_node *
1132 amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1133 {
1134   am_node *new_mp;
1135   mntfs **mf_array;
1136   int mp_error;
1137
1138   dlog("in amfs_generic_lookup_child");
1139
1140   *error_return = 0;
1141   new_mp = amfs_lookup_node(mp, fname, error_return);
1142
1143   /* return if we got an error */
1144   if (!new_mp || *error_return > 0)
1145     return new_mp;
1146
1147   /* also return if it's already mounted and known to be up */
1148   if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server))
1149     return new_mp;
1150
1151   switch (op) {
1152   case VLOOK_DELETE:
1153     /*
1154      * If doing a delete then don't create again!
1155      */
1156     ereturn(ENOENT);
1157   case VLOOK_LOOKUP:
1158     return new_mp;
1159   }
1160
1161   /* save error_return */
1162   mp_error = *error_return;
1163
1164   mf_array = amfs_lookup_mntfs(new_mp, error_return);
1165   if (!mf_array) {
1166     new_mp->am_error = new_mp->am_mnt->mf_error = *error_return;
1167     free_map(new_mp);
1168     return NULL;
1169   }
1170
1171   /*
1172    * Already mounted but known to be down:
1173    * check if we have any alternatives to mount
1174    */
1175   if (mp_error == 0) {
1176     mntfs **mfp;
1177     for (mfp = mf_array; *mfp; mfp++)
1178       if (*mfp != new_mp->am_mnt)
1179         break;
1180     if (*mfp != NULL) {
1181       /*
1182        * we found an alternative, so try mounting again.
1183        */
1184       *error_return = -1;
1185     } else {
1186       for (mfp = mf_array; *mfp; mfp++)
1187         free_mntfs(*mfp);
1188       XFREE(mf_array);
1189       if (new_mp->am_flags & AMF_SOFTLOOKUP) {
1190         ereturn(EIO);
1191       } else {
1192         *error_return = 0;
1193         return new_mp;
1194       }
1195     }
1196   }
1197
1198   /* store the array inside the am_node */
1199   new_mp->am_mfarray = mf_array;
1200
1201   /*
1202    * Note: while it might seem like a good idea to prioritize
1203    * the list of mntfs's we got here, it probably isn't.
1204    * It would ignore the ordering of entries specified by the user,
1205    * which is counterintuitive and confusing.
1206    */
1207   return new_mp;
1208 }
1209
1210
1211 void
1212 amfs_generic_mounted(mntfs *mf)
1213 {
1214   amfs_mkcacheref(mf);
1215 }
1216
1217
1218 /*
1219  * Unmount an automount sub-node
1220  */
1221 int
1222 amfs_generic_umount(am_node *mp, mntfs *mf)
1223 {
1224   int error = 0;
1225
1226 #ifdef HAVE_FS_AUTOFS
1227   int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1228   if (mf->mf_flags & MFF_IS_AUTOFS)
1229     error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
1230 #endif /* HAVE_FS_AUTOFS */
1231
1232   return error;
1233 }
1234
1235
1236 char *
1237 amfs_generic_match(am_opts *fo)
1238 {
1239   char *p;
1240
1241   if (!fo->opt_rfs) {
1242     plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1243     return 0;
1244   }
1245   if (!fo->opt_fs) {
1246     plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1247     return 0;
1248   }
1249
1250   /*
1251    * Swap round fs:= and rfs:= options
1252    * ... historical (jsp)
1253    */
1254   p = fo->opt_rfs;
1255   fo->opt_rfs = fo->opt_fs;
1256   fo->opt_fs = p;
1257
1258   /*
1259    * mtab entry turns out to be the name of the mount map
1260    */
1261   return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
1262 }