 |
|
|
| |
1 /*
2 * Copyright (c) 1997-2005 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 sprintf(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 def_opts = str3cat((char *) 0, def_opts, ";", *cur_ivec + 1);
418 dlog("Setting def_opts to \"%s\"", def_opts);
419 continue;
420 } else
421 num_ivecs++;
422 }
423
424 mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
425
426 /* construct the array of struct mntfs for this mount point */
427 for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
428 mntfs *new_mf;
429
430 if (**cur_ivec == '-') {
431 XFREE(def_opts);
432 if ((*cur_ivec)[1] == '\0') {
433 /*
434 * If we have a single dash '-' than we need to reset the
435 * default options.
436 */
437 def_opts = strdup(orig_def_opts);
438 dlog("Resetting the default options, a single dash '-' was found.");
439 } else {
440 /* append options to /default options */
441 def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1);
442 dlog("Resetting def_opts to \"%s\"", def_opts);
443 }
444 continue;
445 }
446
447 /*
448 * If a mntfs has already been found, and we find
449 * a cut then don't try any more locations.
450 *
451 * XXX: we do not know when the "/" was added as an equivalent for "||".
452 * It's undocumented, it might go away at any time. Caveat emptor.
453 */
454 if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
455 if (count > 0) {
456 dlog("Cut: not trying any more locations for %s", mp->am_path);
457 break;
458 }
459 continue;
460 }
461
462 new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname);
463 if (new_mf == NULL)
464 continue;
465 mf_array[count++] = new_mf;
466 }
467
468 /* We're done with ivecs */
469 XFREE(ivecs);
470 XFREE(info);
471 XFREE(orig_def_opts);
472 XFREE(def_opts);
473 if (count == 0) { /* no match */
474 XFREE(mf_array);
475 ereturn(ENOENT);
476 }
477
478 return mf_array;
479 }
480
481
482 /*
483 * The continuation function. This is called by
484 * the task notifier when a background mount attempt
485 * completes.
486 */
487 static void
488 amfs_cont(int rc, int term, opaque_t arg)
489 {
490 struct continuation *cp = (struct continuation *) arg;
491 am_node *mp = cp->mp;
492 mntfs *mf = mp->am_mnt;
493
494 dlog("amfs_cont: '%s'", mp->am_path);
495
496 /*
497 * Definitely not trying to mount at the moment
498 */
499 mf->mf_flags &= ~MFF_MOUNTING;
500
501 /*
502 * While we are mounting - try to avoid race conditions
503 */
504 new_ttl(mp);
505
506 /*
507 * Wakeup anything waiting for this mount
508 */
509 wakeup(get_mntfs_wchan(mf));
510
511 /*
512 * Check for termination signal or exit status...
513 */
514 if (rc || term) {
515 #ifdef HAVE_FS_AUTOFS
516 if (mf->mf_flags & MFF_IS_AUTOFS &&
517 !(mf->mf_flags & MFF_MOUNTED))
518 autofs_release_fh(mp);
519 #endif /* HAVE_FS_AUTOFS */
520
521 if (term) {
522 /*
523 * Not sure what to do for an error code.
524 */
525 mf->mf_error = EIO; /* XXX ? */
526 mf->mf_flags |= MFF_ERROR;
527 plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
528 } else {
529 /*
530 * Check for exit status...
531 */
532 #ifdef __linux__
533 /*
534 * HACK ALERT!
535 *
536 * On Linux (and maybe not only) it's possible to run
537 * an amd which "knows" how to mount certain combinations
538 * of nfs_proto/nfs_version which the kernel doesn't grok.
539 * So if we got an EINVAL and we have a server that's not
540 * using NFSv2/UDP, try again with NFSv2/UDP.
541 *
542 * Too bad that there is no way to dynamically determine
543 * what combinations the _client_ supports, as opposed to
544 * what the _server_ supports...
545 */
546 if (rc == EINVAL &&
547 mf->mf_server &&
548 (mf->mf_server->fs_version != 2 ||
549 !STREQ(mf->mf_server->fs_proto, "udp")))
550 mf->mf_flags |= MFF_NFS_SCALEDOWN;
551 else
552 #endif /* __linux__ */
553 {
554 mf->mf_error = rc;
555 mf->mf_flags |= MFF_ERROR;
556 errno = rc; /* XXX */
557 if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx"))
558 plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
559 }
560 }
561
562 if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
563 /*
564 * If we get here then that attempt didn't work, so
565 * move the info vector pointer along by one and
566 * call the background mount routine again
567 */
568 amd_stats.d_merr++;
569 cp->mf++;
570 }
571 amfs_bgmount(cp);
572 if (mp->am_error > 0)
573 assign_error_mntfs(mp);
574 } else {
575 /*
576 * The mount worked.
577 */
578 dlog("Mounting %s returned success", cp->mp->am_path);
579 am_mounted(cp->mp);
580 free_continuation(cp);
581 }
582
583 reschedule_timeout_mp();
584 }
585
586
587 /*
588 * Retry a mount
589 */
590 static void
591 amfs_retry(int rc, int term, opaque_t arg)
592 {
593 struct continuation *cp = (struct continuation *) arg;
594 am_node *mp = cp->mp;
595 int error = 0;
596
597 dlog("Commencing retry for mount of %s", mp->am_path);
598
599 new_ttl(mp);
600
601 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
602 /*
603 * The entire mount has timed out. Set the error code and skip past all
604 * the mntfs's so that amfs_bgmount will not have any more
605 * ways to try the mount, thus causing an error.
606 */
607 plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
608 error = ETIMEDOUT;
609 while (*cp->mf)
610 cp->mf++;
611 /* explicitly forbid further retries after timeout */
612 cp->retry = FALSE;
613 }
614 if (error || !IN_PROGRESS(cp))
615 error = amfs_bgmount(cp);
616
617 reschedule_timeout_mp();
618 }
619
620
621 /*
622 * Discard an old continuation
623 */
624 static void
625 free_continuation(struct continuation *cp)
626 {
627 mntfs **mfp;
628
629 dlog("free_continuation");
630 if (cp->callout)
631 untimeout(cp->callout);
632 /*
633 * we must free the mntfs's in the list.
634 * so free all of them if there was an error,
635 * or free all but the used one, if the mount succeeded.
636 */
637 for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
638 free_mntfs(*mfp);
639 }
640 XFREE(cp->mp->am_mfarray);
641 cp->mp->am_mfarray = 0;
642 XFREE(cp);
643 }
644
645
646 /*
647 * Pick a file system to try mounting and
648 * do that in the background if necessary
649 *
650 For each location:
651 discard previous mount location if required
652 fetch next mount location
653 if the filesystem failed to be mounted then
654 this_error = error from filesystem
655 goto failed
656 if the filesystem is mounting or unmounting then
657 goto retry;
658 if the fileserver is down then
659 this_error = EIO
660 continue;
661 if the filesystem is already mounted
662 break
663 fi
664
665 this_error = initialize mount point
666
667 if no error on this mount and mount is delayed then
668 this_error = -1
669 fi
670 if this_error < 0 then
671 retry = true
672 fi
673 if no error on this mount then
674 if mount in background then
675 run mount in background
676 return -1
677 else
678 this_error = mount in foreground
679 fi
680 fi
681 if an error occurred on this mount then
682 update stats
683 save error in mount point
684 fi
685 endfor
686 */
687 static int
688 amfs_bgmount(struct continuation *cp)
689 {
690 am_node *mp = cp->mp;
691 mntfs *mf; /* Current mntfs */
692 int this_error = -1; /* Per-mount error */
693 int hard_error = -1; /* Cumulative per-node error */
694
695 if (mp->am_mnt)
696 free_mntfs(mp->am_mnt);
697
698 /*
699 * Try to mount each location.
700 * At the end:
701 * hard_error == 0 indicates something was mounted.
702 * hard_error > 0 indicates everything failed with a hard error
703 * hard_error < 0 indicates nothing could be mounted now
704 */
705 for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
706 am_ops *p;
707
708 mf = dup_mntfs(mp->am_mnt);
709 p = mf->mf_ops;
710
711 if (hard_error < 0)
712 hard_error = this_error;
713 this_error = 0;
714
715 if (mf->mf_error > 0) {
716 this_error = mf->mf_error;
717 goto failed;
718 }
719
720 if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
721 /*
722 * Still mounting - retry later
723 */
724 dlog("mount of \"%s\" already pending", mf->mf_info);
725 goto retry;
726 }
727
728 if (FSRV_ISDOWN(mf->mf_server)) {
729 /*
730 * Would just mount from the same place
731 * as a hung mount - so give up
732 */
733 dlog("%s is already hung - giving up", mf->mf_server->fs_host);
734 this_error = EIO;
735 goto failed;
736 }
737
738 if (mp->am_link) {
739 XFREE(mp->am_link);
740 mp->am_link = NULL;
741 }
742 if (mf->mf_fo && mf->mf_fo->opt_sublink)
743 mp->am_link = strdup(mf->mf_fo->opt_sublink);
744
745 /*
746 * Will usually need to play around with the mount nodes
747 * file attribute structure. This must be done here.
748 * Try and get things initialized, even if the fileserver
749 * is not known to be up. In the common case this will
750 * progress things faster.
751 */
752
753 /*
754 * Fill in attribute fields.
755 */
756 if (mf->mf_fsflags & FS_DIRECTORY)
757 mk_fattr(&mp->am_fattr, NFDIR);
758 else
759 mk_fattr(&mp->am_fattr, NFLNK);
760
761 if (mf->mf_flags & MFF_MOUNTED) {
762 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
763 /*
764 * Skip initial processing of the mountpoint if already mounted.
765 * This could happen if we have multiple sublinks into the same f/s,
766 * or if we are restarting an already-mounted filesystem.
767 */
768 goto already_mounted;
769 }
770
771 if (mf->mf_fo->fs_mtab) {
772 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
773 mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
774 mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
775 }
776
777 if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
778 this_error = p->fs_init(mf);
779
780 if (this_error > 0)
781 goto failed;
782 if (this_error < 0)
783 goto retry;
784
785 if (mf->mf_fo->opt_delay) {
786 /*
787 * If there is a delay timer on the mount
788 * then don't try to mount if the timer
789 * has not expired.
790 */
791 int i = atoi(mf->mf_fo->opt_delay);
792 if (i > 0 && clocktime() < (cp->start + i)) {
793 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start));
794 goto retry;
795 }
796 }
797
798 /*
799 * If the directory is not yet made and it needs to be made, then make it!
800 */
801 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
802 plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
803 this_error = mkdirs(mf->mf_mount, 0555);
804 if (this_error) {
805 plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
806 goto failed;
807 }
808 mf->mf_flags |= MFF_MKMNT;
809 }
810
811 #ifdef HAVE_FS_AUTOFS
812 if (mf->mf_flags & MFF_IS_AUTOFS)
813 if ((this_error = autofs_get_fh(mp)))
814 goto failed;
815 #endif /* HAVE_FS_AUTOFS */
816
817 already_mounted:
818 mf->mf_flags |= MFF_MOUNTING;
819 if (mf->mf_fsflags & FS_MBACKGROUND) {
820 dlog("backgrounding mount of \"%s\"", mf->mf_mount);
821 if (cp->callout) {
822 untimeout(cp->callout);
823 cp->callout = 0;
824 }
825
826 /* actually run the task, backgrounding as necessary */
827 run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
828 return -1;
829 } else {
830 dlog("foreground mount of \"%s\" ...", mf->mf_mount);
831 this_error = mount_node((opaque_t) mp);
832 }
833
834 mf->mf_flags &= ~MFF_MOUNTING;
835 if (this_error > 0)
836 goto failed;
837 if (this_error == 0) {
838 am_mounted(mp);
839 break; /* Success */
840 }
841
842 retry:
843 if (!cp->retry)
844 continue;
845 dlog("will retry ...\n");
846
847 /*
848 * Arrange that amfs_bgmount is called
849 * after anything else happens.
850 */
851 dlog("Arranging to retry mount of %s", mp->am_path);
852 sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
853 if (cp->callout)
854 untimeout(cp->callout);
855 cp->callout = timeout(RETRY_INTERVAL, wakeup,
856 (opaque_t) get_mntfs_wchan(mf));
857
858 mp->am_ttl = clocktime() + RETRY_INTERVAL;
859
860 /*
861 * Not done yet - so don't return anything
862 */
863 return -1;
864
865 failed:
866 amd_stats.d_merr++;
867 mf->mf_error = this_error;
868 mf->mf_flags |= MFF_ERROR;
869 #ifdef HAVE_FS_AUTOFS
870 if (mp->am_autofs_fh)
871 autofs_release_fh(mp);
872 #endif /* HAVE_FS_AUTOFS */
873 if (mf->mf_flags & MFF_MKMNT) {
874 rmdirs(mf->mf_mount);
875 mf->mf_flags &= ~MFF_MKMNT;
876 }
877 /*
878 * Wakeup anything waiting for this mount
879 */
880 wakeup(get_mntfs_wchan(mf));
881 free_mntfs(mf);
882 /* continue */
883 }
884
885 /*
886 * If we get here, then either the mount succeeded or
887 * there is no more mount information available.
888 */
889 if (this_error) {
890 mp->am_mnt = mf = new_mntfs();
891
892 #ifdef HAVE_FS_AUTOFS
893 if (mp->am_flags & AMF_AUTOFS)
894 autofs_mount_failed(mp);
895 else
896 #endif /* HAVE_FS_AUTOFS */
897 nfs_quick_reply(mp, this_error);
898
899 if (hard_error <= 0)
900 hard_error = this_error;
901 if (hard_error < 0)
902 hard_error = ETIMEDOUT;
903
904 /*
905 * Set a small(ish) timeout on an error node if
906 * the error was not a time out.
907 */
908 switch (hard_error) {
909 case ETIMEDOUT:
910 case EWOULDBLOCK:
911 case EIO:
912 mp->am_timeo = 17;
913 break;
914 default:
915 mp->am_timeo = 5;
916 break;
917 }
918 new_ttl(mp);
919 } else {
920 mf = mp->am_mnt;
921 /*
922 * Wakeup anything waiting for this mount
923 */
924 wakeup(get_mntfs_wchan(mf));
925 hard_error = 0;
926 }
927
928 /*
929 * Make sure that the error value in the mntfs has a
930 * reasonable value.
931 */
932 if (mf->mf_error < 0) {
933 mf->mf_error = hard_error;
934 if (hard_error)
935 mf->mf_flags |= MFF_ERROR;
936 }
937
938 /*
939 * In any case we don't need the continuation any more
940 */
941 free_continuation(cp);
942
943 return hard_error;
944 }
945
946
947 static char *
948 amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
949 {
950 char *dflts;
951 char *dfl;
952 char **rvec = NULL;
953 struct mnt_map *mm = (mnt_map *) mf->mf_private;
954
955 dlog("determining /defaults entry value");
956
957 /*
958 * Find out if amd.conf overrode any map-specific /defaults.
959 *
960 * HACK ALERT: there's no easy way to find out what the map mount point is
961 * at this point, so I am forced to initialize the mnt_map->cfm field here
962 * for the first time, upon the very first search for a /defaults entry in
963 * this map. This initialization is much better done in mapc_create(),
964 * but it's impossible to do that there with the current code structure.
965 */
966 if (mm->cfm == NULL) { /* then initialize it for first time */
967 mm->cfm = find_cf_map(mf->mf_mount);
968 }
969 if (mm->cfm && mm->cfm->cfm_defaults) {
970 dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
971 dflts = strdup(mm->cfm->cfm_defaults);
972 } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
973 dlog("/defaults gave %s", dflts);
974 } else {
975 return def_opts; /* if nothing found */
976 }
977
978 /* trim leading '-' in case thee's one */
979 if (*dflts == '-')
980 dfl = dflts + 1;
981 else
982 dfl = dflts;
983
984 /*
985 * Chop the defaults up
986 */
987 rvec = strsplit(dfl, ' ', '\"');
988
989 if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
990 /*
991 * Pick whichever first entry matched the list of selectors.
992 * Strip the selectors from the string, and assign to dfl the
993 * rest of the string.
994 */
995 if (rvec) {
996 am_opts ap;
997 am_ops *pt;
998 char **sp = rvec;
999 while (*sp) { /* loop until you find something, if any */
1000 memset((char *) &ap, 0, sizeof(am_opts));
1001 /*
1002 * This next routine cause many spurious "expansion of ... is"
1003 * messages, which are ignored, b/c all we need out of this
1004 * routine is to match selectors. These spurious messages may
1005 * be wrong, esp. if they try to expand ${key} b/c it will
1006 * get expanded to "/defaults"
1007 */
1008 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1009 mp->am_parent->am_mnt->mf_info);
1010 free_opts(&ap); /* don't leak */
1011 if (pt == &amfs_error_ops) {
1012 plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1013 } else {
1014 dfl = strip_selectors(*sp, "/defaults");
1015 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1016 break;
1017 }
1018 ++sp;
1019 }
1020 }
1021 } else { /* not selectors_in_defaults */
1022 /*
1023 * Extract first value
1024 */
1025 dfl = rvec[0];
1026 }
1027
1028 /*
1029 * If there were any values at all...
1030 */
1031 if (dfl) {
1032 /*
1033 * Log error if there were other values
1034 */
1035 if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1036 dlog("/defaults chopped into %s", dfl);
1037 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1038 }
1039
1040 /*
1041 * Prepend to existing defaults if they exist,
1042 * otherwise just use these defaults.
1043 */
1044 if (*def_opts && *dfl) {
1045 char *nopts = (char *) xmalloc(strlen(def_opts) + strlen(dfl) + 2);
1046 sprintf(nopts, "%s;%s", dfl, def_opts);
1047 XFREE(def_opts);
1048 def_opts = nopts;
1049 } else if (*dfl) {
1050 def_opts = strealloc(def_opts, dfl);
1051 }
1052 }
1053
1054 XFREE(dflts);
1055
1056 /* don't need info vector any more */
1057 if (rvec)
1058 XFREE(rvec);
1059
1060 return def_opts;
1061 }
1062
1063
1064 am_node *
1065 amfs_generic_mount_child(am_node *new_mp, int *error_return)
1066 {
1067 int error;
1068 struct continuation *cp; /* Continuation structure if need to mount */
1069
1070 dlog("in amfs_generic_mount_child");
1071
1072 *error_return = error = 0; /* Error so far */
1073
1074 /* we have an errorfs attached to the am_node, free it */
1075 free_mntfs(new_mp->am_mnt);
1076 new_mp->am_mnt = 0;
1077
1078 /*
1079 * Construct a continuation
1080 */
1081 cp = ALLOC(struct continuation);
1082 cp->callout = 0;
1083 cp->mp = new_mp;
1084 cp->retry = TRUE;
1085 cp->start = clocktime();
1086 cp->mf = new_mp->am_mfarray;
1087
1088 /*
1089 * Try and mount the file system. If this succeeds immediately (possible
1090 * for a ufs file system) then return the attributes, otherwise just
1091 * return an error.
1092 */
1093 error = amfs_bgmount(cp);
1094 reschedule_timeout_mp();
1095 if (!error)
1096 return new_mp;
1097
1098 /*
1099 * Code for quick reply. If current_transp is set, then it's the
1100 * transp that's been passed down from nfs_program_2() or from
1101 * autofs_program_[123]().
1102 * If new_mp->am_transp is not already set, set it by copying in
1103 * current_transp. Once am_transp is set, nfs_quick_reply() and
1104 * autofs_mount_succeeded() can use it to send a reply to the
1105 * client that requested this mount.
1106 */
1107 if (current_transp && !new_mp->am_transp) {
1108 dlog("Saving RPC transport for %s", new_mp->am_path);
1109 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1110 *(new_mp->am_transp) = *current_transp;
1111 }
1112 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1113 new_mp->am_error = error;
1114
1115 if (new_mp->am_error > 0)
1116 assign_error_mntfs(new_mp);
1117
1118 ereturn(error);
1119 }
1120
1121
1122 /*
1123 * Automount interface to RPC lookup routine
1124 * Find the corresponding entry and return
1125 * the file handle for it.
1126 */
1127 am_node *
1128 amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1129 {
1130 am_node *new_mp;
1131 mntfs **mf_array;
1132 int mp_error;
1133
1134 dlog("in amfs_generic_lookup_child");
1135
1136 *error_return = 0;
1137 new_mp = amfs_lookup_node(mp, fname, error_return);
1138
1139 /* return if we got an error */
1140 if (!new_mp || *error_return > 0)
1141 return new_mp;
1142
1143 /* also return if it's already mounted and known to be up */
1144 if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server))
1145 return new_mp;
1146
1147 switch (op) {
1148 case VLOOK_DELETE:
1149 /*
1150 * If doing a delete then don't create again!
1151 */
1152 ereturn(ENOENT);
1153 case VLOOK_LOOKUP:
1154 return new_mp;
1155 }
1156
1157 /* save error_return */
1158 mp_error = *error_return;
1159
1160 mf_array = amfs_lookup_mntfs(new_mp, error_return);
1161 if (!mf_array) {
1162 new_mp->am_error = new_mp->am_mnt->mf_error = *error_return;
1163 free_map(new_mp);
1164 return NULL;
1165 }
1166
1167 /*
1168 * Already mounted but known to be down:
1169 * check if we have any alternatives to mount
1170 */
1171 if (mp_error == 0) {
1172 mntfs **mfp;
1173 for (mfp = mf_array; *mfp; mfp++)
1174 if (*mfp != new_mp->am_mnt)
1175 break;
1176 if (*mfp != NULL) {
1177 /*
1178 * we found an alternative, so try mounting again.
1179 */
1180 *error_return = -1;
1181 } else {
1182 for (mfp = mf_array; *mfp; mfp++)
1183 free_mntfs(*mfp);
1184 XFREE(mf_array);
1185 if (new_mp->am_flags & AMF_SOFTLOOKUP) {
1186 ereturn(EIO);
1187 } else {
1188 *error_return = 0;
1189 return new_mp;
1190 }
1191 }
1192 }
1193
1194 /* store the array inside the am_node */
1195 new_mp->am_mfarray = mf_array;
1196
1197 /*
1198 * Note: while it might seem like a good idea to prioritize
1199 * the list of mntfs's we got here, it probably isn't.
1200 * It would ignore the ordering of entries specified by the user,
1201 * which is counterintuitive and confusing.
1202 */
1203 return new_mp;
1204 }
1205
1206
1207 void
1208 amfs_generic_mounted(mntfs *mf)
1209 {
1210