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