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