4 * Copyright (c) 1997-2009 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.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/amfs_generic.c
47 * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
52 #endif /* HAVE_CONFIG_H */
57 /****************************************************************************
59 ****************************************************************************/
60 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
63 /****************************************************************************
65 ****************************************************************************/
67 * Mounting a file system may take a significant period of time. The
68 * problem is that if this is done in the main process thread then the
69 * entire automounter could be blocked, possibly hanging lots of processes
70 * on the system. Instead we use a continuation scheme to allow mounts to
71 * be attempted in a sub-process. When the sub-process exits we pick up the
72 * exit status (by convention a UN*X error number) and continue in a
73 * notifier. The notifier gets handed a data structure and can then
74 * determine whether the mount was successful or not. If not, it updates
75 * the data structure and tries again until there are no more ways to try
76 * the mount, or some other permanent error occurs. In the mean time no RPC
77 * reply is sent, even after the mount is successful. We rely on the RPC
78 * retry mechanism to resend the lookup request which can then be handled.
81 am_node
*mp
; /* Node we are trying to mount */
82 int retry
; /* Try again? */
83 time_t start
; /* Time we started this mount */
84 int callout
; /* Callout identifier */
85 mntfs
**mf
; /* Current mntfs */
89 /****************************************************************************
90 *** FORWARD DEFINITIONS ***
91 ****************************************************************************/
92 static am_node
*amfs_lookup_node(am_node
*mp
, char *fname
, int *error_return
);
93 static mntfs
*amfs_lookup_one_mntfs(am_node
*new_mp
, mntfs
*mf
, char *ivec
,
94 char *def_opts
, char *pfname
);
95 static mntfs
**amfs_lookup_mntfs(am_node
*new_mp
, int *error_return
);
96 static void amfs_cont(int rc
, int term
, opaque_t arg
);
97 static void amfs_retry(int rc
, int term
, opaque_t arg
);
98 static void free_continuation(struct continuation
*cp
);
99 static int amfs_bgmount(struct continuation
*cp
);
100 static char *amfs_parse_defaults(am_node
*mp
, mntfs
*mf
, char *def_opts
);
103 /****************************************************************************
105 ****************************************************************************/
107 amfs_lookup_node(am_node
*mp
, char *fname
, int *error_return
)
110 int error
= 0; /* Error so far */
111 int in_progress
= 0; /* # of (un)mount in progress */
113 char *expanded_fname
= NULL
;
115 dlog("in amfs_lookup_node");
118 * If the server is shutting down
119 * then don't return information
120 * about the mount point.
122 if (amd_state
== Finishing
) {
123 if (mp
->am_mnt
== 0 || mp
->am_mnt
->mf_fsflags
& FS_DIRECT
) {
124 dlog("%s mount ignored - going down", fname
);
126 dlog("%s/%s mount ignored - going down", mp
->am_path
, fname
);
132 * Handle special case of "." and ".."
134 if (fname
[0] == '.') {
135 if (fname
[1] == '\0')
136 return mp
; /* "." is the current node */
137 if (fname
[1] == '.' && fname
[2] == '\0') {
139 dlog(".. in %s gives %s", mp
->am_path
, mp
->am_parent
->am_path
);
140 return mp
->am_parent
; /* ".." is the parent node */
147 * Check for valid key name.
148 * If it is invalid then pretend it doesn't exist.
150 if (!valid_key(fname
)) {
151 plog(XLOG_WARNING
, "Key \"%s\" contains a disallowed character", fname
);
157 * expanded_fname is now a private copy.
159 expanded_fname
= expand_selectors(fname
);
162 * Search children of this node
164 for (new_mp
= mp
->am_child
; new_mp
; new_mp
= new_mp
->am_osib
) {
165 if (FSTREQ(new_mp
->am_name
, expanded_fname
)) {
166 if (new_mp
->am_error
) {
167 error
= new_mp
->am_error
;
172 * If the error code is undefined then it must be
176 if (mf
->mf_error
< 0)
180 * If there was a previous error with this node
181 * then return that error code.
183 if (mf
->mf_flags
& MFF_ERROR
) {
184 error
= mf
->mf_error
;
187 if (!(mf
->mf_flags
& MFF_MOUNTED
) || (mf
->mf_flags
& MFF_UNMOUNTING
)) {
190 * If the fs is not mounted or it is unmounting then there
191 * is a background (un)mount in progress. In this case
192 * we just drop the RPC request (return nil) and
193 * wait for a retry, by which time the (un)mount may
196 dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
197 expanded_fname
, mf
->mf_mount
,
198 (mf
->mf_flags
& MFF_UNMOUNTING
) ? "un" : "", mf
->mf_flags
);
200 if (mf
->mf_flags
& MFF_UNMOUNTING
) {
201 dlog("will remount later");
202 new_mp
->am_flags
|= AMF_REMOUNT
;
208 * Otherwise we have a hit: return the current mount point.
210 dlog("matched %s in %s", expanded_fname
, new_mp
->am_path
);
211 XFREE(expanded_fname
);
217 dlog("Waiting while %d mount(s) in progress", in_progress
);
218 XFREE(expanded_fname
);
223 * If an error occurred then return it.
226 dlog("Returning error: %s", strerror(error
));
227 XFREE(expanded_fname
);
232 * If the server is going down then just return,
233 * don't try to mount any more file systems
235 if ((int) amd_state
>= (int) Finishing
) {
236 dlog("not found - server going down anyway");
243 new_mp
= get_ap_child(mp
, expanded_fname
);
244 XFREE(expanded_fname
);
255 amfs_lookup_one_mntfs(am_node
*new_mp
, mntfs
*mf
, char *ivec
,
256 char *def_opts
, char *pfname
)
262 #ifdef HAVE_FS_AUTOFS
264 #endif /* HAVE_FS_AUTOFS */
266 /* match the operators */
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]) {
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.
282 mp_dir
= fs_opts
->opt_fs
;
284 if (p
->autofs_fs_flags
& FS_ON_AUTOFS
) {
285 mp_dir
= new_mp
->am_path
;
287 mp_dir
= fs_opts
->opt_fs
;
292 #endif /* HAVE_FS_AUTOFS */
293 mp_dir
= fs_opts
->opt_fs
;
296 * Find or allocate a filesystem for this node.
298 new_mf
= find_mntfs(p
, fs_opts
,
303 fs_opts
->opt_remopts
);
306 * See whether this is a real filesystem
309 if (p
== &amfs_error_ops
) {
310 plog(XLOG_MAP
, "Map entry %s for %s did not match", ivec
, new_mp
->am_path
);
315 dlog("Got a hit with %s", p
->fs_type
);
317 #ifdef HAVE_FS_AUTOFS
318 if (new_mp
->am_flags
& AMF_AUTOFS
&& on_autofs
) {
319 new_mf
->mf_flags
|= MFF_ON_AUTOFS
;
320 new_mf
->mf_fsflags
= new_mf
->mf_ops
->autofs_fs_flags
;
323 * A new filesystem is an autofs filesystems if:
324 * 1. it claims it can be one (has the FS_AUTOFS flag)
325 * 2. autofs is enabled system-wide
326 * 3. either has an autofs parent,
327 * or it is explicitly requested to be autofs.
329 if (new_mf
->mf_ops
->autofs_fs_flags
& FS_AUTOFS
&&
331 ((mf
->mf_flags
& MFF_IS_AUTOFS
) ||
332 (new_mf
->mf_fo
&& new_mf
->mf_fo
->opt_mount_type
&&
333 STREQ(new_mf
->mf_fo
->opt_mount_type
, "autofs"))))
334 new_mf
->mf_flags
|= MFF_IS_AUTOFS
;
335 #endif /* HAVE_FS_AUTOFS */
342 amfs_lookup_mntfs(am_node
*new_mp
, int *error_return
)
345 char *info
; /* Mount info - where to get the file system */
346 char **ivecs
, **cur_ivec
; /* Split version of info */
348 char *orig_def_opts
; /* Original Automount options */
349 char *def_opts
; /* Automount options */
350 int error
= 0; /* Error so far */
351 char path_name
[MAXPATHLEN
]; /* General path name buffer */
352 char *pfname
; /* Path for database lookup */
353 mntfs
*mf
, **mf_array
;
356 dlog("in amfs_lookup_mntfs");
358 mp
= new_mp
->am_parent
;
361 * If we get here then this is a reference to an,
362 * as yet, unknown name so we need to search the mount
366 if (strlen(mp
->am_pref
) + strlen(new_mp
->am_name
) >= sizeof(path_name
))
367 ereturn(ENAMETOOLONG
);
368 xsnprintf(path_name
, sizeof(path_name
), "%s%s", mp
->am_pref
, new_mp
->am_name
);
371 pfname
= new_mp
->am_name
;
376 dlog("will search map info in %s to find %s", mf
->mf_info
, pfname
);
378 * Consult the oracle for some mount information.
379 * info is malloc'ed and belongs to this routine.
380 * It ends up being free'd in free_continuation().
382 * Note that this may return -1 indicating that information
383 * is not yet available.
385 error
= mapc_search((mnt_map
*) mf
->mf_private
, pfname
, &info
);
388 plog(XLOG_MAP
, "No map entry for %s", pfname
);
390 plog(XLOG_MAP
, "Waiting on map entry for %s", pfname
);
393 dlog("mount info is %s", info
);
396 * Split info into an argument vector.
397 * The vector is malloc'ed and belongs to
398 * this routine. It is free'd further down.
400 * Note: the vector pointers point into info, so don't free it!
402 ivecs
= strsplit(info
, ' ', '\"');
405 def_opts
= mf
->mf_auto
;
409 orig_def_opts
= amfs_parse_defaults(mp
, mf
, strdup(def_opts
));
410 def_opts
= strdup(orig_def_opts
);
412 /* first build our defaults */
414 for (cur_ivec
= ivecs
; *cur_ivec
; cur_ivec
++) {
415 if (**cur_ivec
== '-') {
417 * Pick up new defaults
419 char *new_def_opts
= str3cat(NULL
, def_opts
, ";", *cur_ivec
+ 1);
421 def_opts
= new_def_opts
;
422 dlog("Setting def_opts to \"%s\"", def_opts
);
428 mf_array
= calloc(num_ivecs
+ 1, sizeof(mntfs
*));
430 /* construct the array of struct mntfs for this mount point */
431 for (count
= 0, cur_ivec
= ivecs
; *cur_ivec
; cur_ivec
++) {
434 if (**cur_ivec
== '-') {
436 if ((*cur_ivec
)[1] == '\0') {
438 * If we have a single dash '-' than we need to reset the
441 def_opts
= strdup(orig_def_opts
);
442 dlog("Resetting the default options, a single dash '-' was found.");
444 /* append options to /default options */
445 def_opts
= str3cat((char *) NULL
, orig_def_opts
, ";", *cur_ivec
+ 1);
446 dlog("Resetting def_opts to \"%s\"", def_opts
);
452 * If a mntfs has already been found, and we find
453 * a cut then don't try any more locations.
455 * XXX: we do not know when the "/" was added as an equivalent for "||".
456 * It's undocumented, it might go away at any time. Caveat emptor.
458 if (STREQ(*cur_ivec
, "/") || STREQ(*cur_ivec
, "||")) {
460 dlog("Cut: not trying any more locations for %s", mp
->am_path
);
466 new_mf
= amfs_lookup_one_mntfs(new_mp
, mf
, *cur_ivec
, def_opts
, pfname
);
469 mf_array
[count
++] = new_mf
;
472 /* We're done with ivecs */
475 XFREE(orig_def_opts
);
477 if (count
== 0) { /* no match */
487 * The continuation function. This is called by
488 * the task notifier when a background mount attempt
492 amfs_cont(int rc
, int term
, opaque_t arg
)
494 struct continuation
*cp
= (struct continuation
*) arg
;
495 am_node
*mp
= cp
->mp
;
496 mntfs
*mf
= mp
->am_mnt
;
498 dlog("amfs_cont: '%s'", mp
->am_path
);
501 * Definitely not trying to mount at the moment
503 mf
->mf_flags
&= ~MFF_MOUNTING
;
506 * While we are mounting - try to avoid race conditions
511 * Wakeup anything waiting for this mount
513 wakeup(get_mntfs_wchan(mf
));
516 * Check for termination signal or exit status...
519 #ifdef HAVE_FS_AUTOFS
520 if (mf
->mf_flags
& MFF_IS_AUTOFS
&&
521 !(mf
->mf_flags
& MFF_MOUNTED
))
522 autofs_release_fh(mp
);
523 #endif /* HAVE_FS_AUTOFS */
527 * Not sure what to do for an error code.
529 mf
->mf_error
= EIO
; /* XXX ? */
530 mf
->mf_flags
|= MFF_ERROR
;
531 plog(XLOG_ERROR
, "mount for %s got signal %d", mp
->am_path
, term
);
534 * Check for exit status...
540 * On Linux (and maybe not only) it's possible to run
541 * an amd which "knows" how to mount certain combinations
542 * of nfs_proto/nfs_version which the kernel doesn't grok.
543 * So if we got an EINVAL and we have a server that's not
544 * using NFSv2/UDP, try again with NFSv2/UDP.
546 * Too bad that there is no way to dynamically determine
547 * what combinations the _client_ supports, as opposed to
548 * what the _server_ supports...
552 (mf
->mf_server
->fs_version
!= 2 ||
553 !STREQ(mf
->mf_server
->fs_proto
, "udp")))
554 mf
->mf_flags
|= MFF_NFS_SCALEDOWN
;
556 #endif /* __linux__ */
559 mf
->mf_flags
|= MFF_ERROR
;
560 errno
= rc
; /* XXX */
561 if (!STREQ(mp
->am_mnt
->mf_ops
->fs_type
, "linkx"))
562 plog(XLOG_ERROR
, "%s: mount (amfs_cont): %m", mp
->am_path
);
566 if (!(mf
->mf_flags
& MFF_NFS_SCALEDOWN
)) {
568 * If we get here then that attempt didn't work, so
569 * move the info vector pointer along by one and
570 * call the background mount routine again
576 if (mp
->am_error
> 0)
577 assign_error_mntfs(mp
);
582 dlog("Mounting %s returned success", cp
->mp
->am_path
);
584 free_continuation(cp
);
587 reschedule_timeout_mp();
595 amfs_retry(int rc
, int term
, opaque_t arg
)
597 struct continuation
*cp
= (struct continuation
*) arg
;
598 am_node
*mp
= cp
->mp
;
601 dlog("Commencing retry for mount of %s", mp
->am_path
);
605 if ((cp
->start
+ ALLOWED_MOUNT_TIME
) < clocktime(NULL
)) {
607 * The entire mount has timed out. Set the error code and skip past all
608 * the mntfs's so that amfs_bgmount will not have any more
609 * ways to try the mount, thus causing an error.
611 plog(XLOG_INFO
, "mount of \"%s\" has timed out", mp
->am_path
);
615 /* explicitly forbid further retries after timeout */
618 if (error
|| !IN_PROGRESS(cp
))
619 error
= amfs_bgmount(cp
);
621 reschedule_timeout_mp();
626 * Discard an old continuation
629 free_continuation(struct continuation
*cp
)
633 dlog("free_continuation");
635 untimeout(cp
->callout
);
637 * we must free the mntfs's in the list.
638 * so free all of them if there was an error,
639 * or free all but the used one, if the mount succeeded.
641 for (mfp
= cp
->mp
->am_mfarray
; *mfp
; mfp
++) {
644 XFREE(cp
->mp
->am_mfarray
);
650 * Pick a file system to try mounting and
651 * do that in the background if necessary
654 discard previous mount location if required
655 fetch next mount location
656 if the filesystem failed to be mounted then
657 this_error = error from filesystem
659 if the filesystem is mounting or unmounting then
661 if the fileserver is down then
664 if the filesystem is already mounted
668 this_error = initialize mount point
670 if no error on this mount and mount is delayed then
673 if this_error < 0 then
676 if no error on this mount then
677 if mount in background then
678 run mount in background
681 this_error = mount in foreground
684 if an error occurred on this mount then
686 save error in mount point
691 amfs_bgmount(struct continuation
*cp
)
693 am_node
*mp
= cp
->mp
;
694 mntfs
*mf
; /* Current mntfs */
695 int this_error
= -1; /* Per-mount error */
696 int hard_error
= -1; /* Cumulative per-node error */
699 free_mntfs(mp
->am_mnt
);
702 * Try to mount each location.
704 * hard_error == 0 indicates something was mounted.
705 * hard_error > 0 indicates everything failed with a hard error
706 * hard_error < 0 indicates nothing could be mounted now
708 for (mp
->am_mnt
= *cp
->mf
; *cp
->mf
; cp
->mf
++, mp
->am_mnt
= *cp
->mf
) {
711 mf
= dup_mntfs(mp
->am_mnt
);
715 hard_error
= this_error
;
718 if (mf
->mf_error
> 0) {
719 this_error
= mf
->mf_error
;
723 if (mf
->mf_flags
& (MFF_MOUNTING
| MFF_UNMOUNTING
)) {
725 * Still mounting - retry later
727 dlog("mount of \"%s\" already pending", mf
->mf_info
);
731 if (FSRV_ISDOWN(mf
->mf_server
)) {
733 * Would just mount from the same place
734 * as a hung mount - so give up
736 dlog("%s is already hung - giving up", mf
->mf_server
->fs_host
);
745 if (mf
->mf_fo
&& mf
->mf_fo
->opt_sublink
&& mf
->mf_fo
->opt_sublink
[0])
746 mp
->am_link
= strdup(mf
->mf_fo
->opt_sublink
);
749 * Will usually need to play around with the mount nodes
750 * file attribute structure. This must be done here.
751 * Try and get things initialized, even if the fileserver
752 * is not known to be up. In the common case this will
753 * progress things faster.
757 * Fill in attribute fields.
759 if (mf
->mf_fsflags
& FS_DIRECTORY
)
760 mk_fattr(&mp
->am_fattr
, NFDIR
);
762 mk_fattr(&mp
->am_fattr
, NFLNK
);
764 if (mf
->mf_flags
& MFF_MOUNTED
) {
765 dlog("duplicate mount of \"%s\" ...", mf
->mf_info
);
767 * Skip initial processing of the mountpoint if already mounted.
768 * This could happen if we have multiple sublinks into the same f/s,
769 * or if we are restarting an already-mounted filesystem.
771 goto already_mounted
;
774 if (mf
->mf_fo
&& mf
->mf_fo
->fs_mtab
) {
775 plog(XLOG_MAP
, "Trying mount of %s on %s fstype %s mount_type %s",
776 mf
->mf_fo
->fs_mtab
, mf
->mf_mount
, p
->fs_type
,
777 mp
->am_flags
& AMF_AUTOFS
? "autofs" : "non-autofs");
780 if (p
->fs_init
&& !(mf
->mf_flags
& MFF_RESTART
))
781 this_error
= p
->fs_init(mf
);
788 if (mf
->mf_fo
&& mf
->mf_fo
->opt_delay
) {
790 * If there is a delay timer on the mount
791 * then don't try to mount if the timer
794 int i
= atoi(mf
->mf_fo
->opt_delay
);
795 time_t now
= clocktime(NULL
);
796 if (i
> 0 && now
< (cp
->start
+ i
)) {
797 dlog("Mount of %s delayed by %lds", mf
->mf_mount
, (long) (i
- now
+ cp
->start
));
803 * If the directory is not yet made and it needs to be made, then make it!
805 if (!(mf
->mf_flags
& MFF_MKMNT
) && mf
->mf_fsflags
& FS_MKMNT
) {
806 plog(XLOG_INFO
, "creating mountpoint directory '%s'", mf
->mf_mount
);
807 this_error
= mkdirs(mf
->mf_mount
, 0555);
809 plog(XLOG_ERROR
, "mkdirs failed: %s", strerror(this_error
));
812 mf
->mf_flags
|= MFF_MKMNT
;
815 #ifdef HAVE_FS_AUTOFS
816 if (mf
->mf_flags
& MFF_IS_AUTOFS
)
817 if ((this_error
= autofs_get_fh(mp
)))
819 #endif /* HAVE_FS_AUTOFS */
822 mf
->mf_flags
|= MFF_MOUNTING
;
823 if (mf
->mf_fsflags
& FS_MBACKGROUND
) {
824 dlog("backgrounding mount of \"%s\"", mf
->mf_mount
);
826 untimeout(cp
->callout
);
830 /* actually run the task, backgrounding as necessary */
831 run_task(mount_node
, (opaque_t
) mp
, amfs_cont
, (opaque_t
) cp
);
834 dlog("foreground mount of \"%s\" ...", mf
->mf_mount
);
835 this_error
= mount_node((opaque_t
) mp
);
838 mf
->mf_flags
&= ~MFF_MOUNTING
;
841 if (this_error
== 0) {
849 dlog("will retry ...\n");
852 * Arrange that amfs_bgmount is called
853 * after anything else happens.
855 dlog("Arranging to retry mount of %s", mp
->am_path
);
856 sched_task(amfs_retry
, (opaque_t
) cp
, get_mntfs_wchan(mf
));
858 untimeout(cp
->callout
);
859 cp
->callout
= timeout(RETRY_INTERVAL
, wakeup
,
860 (opaque_t
) get_mntfs_wchan(mf
));
862 mp
->am_ttl
= clocktime(NULL
) + RETRY_INTERVAL
;
865 * Not done yet - so don't return anything
871 mf
->mf_error
= this_error
;
872 mf
->mf_flags
|= MFF_ERROR
;
873 #ifdef HAVE_FS_AUTOFS
874 if (mp
->am_autofs_fh
)
875 autofs_release_fh(mp
);
876 #endif /* HAVE_FS_AUTOFS */
877 if (mf
->mf_flags
& MFF_MKMNT
) {
878 rmdirs(mf
->mf_mount
);
879 mf
->mf_flags
&= ~MFF_MKMNT
;
882 * Wakeup anything waiting for this mount
884 wakeup(get_mntfs_wchan(mf
));
890 * If we get here, then either the mount succeeded or
891 * there is no more mount information available.
894 mp
->am_mnt
= mf
= new_mntfs();
896 #ifdef HAVE_FS_AUTOFS
897 if (mp
->am_flags
& AMF_AUTOFS
)
898 autofs_mount_failed(mp
);
900 #endif /* HAVE_FS_AUTOFS */
901 nfs_quick_reply(mp
, this_error
);
904 hard_error
= this_error
;
906 hard_error
= ETIMEDOUT
;
909 * Set a small(ish) timeout on an error node if
910 * the error was not a time out.
912 switch (hard_error
) {
926 * Wakeup anything waiting for this mount
928 wakeup(get_mntfs_wchan(mf
));
933 * Make sure that the error value in the mntfs has a
936 if (mf
->mf_error
< 0) {
937 mf
->mf_error
= hard_error
;
939 mf
->mf_flags
|= MFF_ERROR
;
943 * In any case we don't need the continuation any more
945 free_continuation(cp
);
952 amfs_parse_defaults(am_node
*mp
, mntfs
*mf
, char *def_opts
)
957 struct mnt_map
*mm
= (mnt_map
*) mf
->mf_private
;
959 dlog("determining /defaults entry value");
962 * Find out if amd.conf overrode any map-specific /defaults.
964 if (mm
->cfm
&& mm
->cfm
->cfm_defaults
) {
965 dlog("map %s map_defaults override: %s", mf
->mf_mount
, mm
->cfm
->cfm_defaults
);
966 dflts
= strdup(mm
->cfm
->cfm_defaults
);
967 } else if (mapc_search(mm
, "/defaults", &dflts
) == 0) {
968 dlog("/defaults gave %s", dflts
);
970 return def_opts
; /* if nothing found */
973 /* trim leading '-' in case thee's one */
980 * Chop the defaults up
982 rvec
= strsplit(dfl
, ' ', '\"');
984 if (gopt
.flags
& CFM_SELECTORS_IN_DEFAULTS
) {
986 * Pick whichever first entry matched the list of selectors.
987 * Strip the selectors from the string, and assign to dfl the
988 * rest of the string.
994 while (*sp
) { /* loop until you find something, if any */
995 memset((char *) &ap
, 0, sizeof(am_opts
));
997 * This next routine cause many spurious "expansion of ... is"
998 * messages, which are ignored, b/c all we need out of this
999 * routine is to match selectors. These spurious messages may
1000 * be wrong, esp. if they try to expand ${key} b/c it will
1001 * get expanded to "/defaults"
1003 pt
= ops_match(&ap
, *sp
, "", mp
->am_path
, "/defaults",
1004 mp
->am_parent
->am_mnt
->mf_info
);
1005 free_opts(&ap
); /* don't leak */
1006 if (pt
== &amfs_error_ops
) {
1007 plog(XLOG_MAP
, "did not match defaults for \"%s\"", *sp
);
1009 dfl
= strip_selectors(*sp
, "/defaults");
1010 plog(XLOG_MAP
, "matched default selectors \"%s\"", dfl
);
1016 } else { /* not selectors_in_defaults */
1018 * Extract first value
1024 * If there were any values at all...
1028 * Log error if there were other values
1030 if (!(gopt
.flags
& CFM_SELECTORS_IN_DEFAULTS
) && rvec
[1]) {
1031 dlog("/defaults chopped into %s", dfl
);
1032 plog(XLOG_USER
, "More than a single value for /defaults in %s", mf
->mf_info
);
1036 * Prepend to existing defaults if they exist,
1037 * otherwise just use these defaults.
1039 if (*def_opts
&& *dfl
) {
1040 size_t l
= strlen(def_opts
) + strlen(dfl
) + 2;
1041 char *nopts
= (char *) xmalloc(l
);
1042 xsnprintf(nopts
, l
, "%s;%s", dfl
, def_opts
);
1046 def_opts
= strealloc(def_opts
, dfl
);
1052 /* don't need info vector any more */
1061 amfs_generic_mount_child(am_node
*new_mp
, int *error_return
)
1064 struct continuation
*cp
; /* Continuation structure if need to mount */
1066 dlog("in amfs_generic_mount_child");
1068 *error_return
= error
= 0; /* Error so far */
1070 /* we have an errorfs attached to the am_node, free it */
1071 free_mntfs(new_mp
->am_mnt
);
1072 new_mp
->am_mnt
= NULL
;
1075 * Construct a continuation
1077 cp
= ALLOC(struct continuation
);
1081 cp
->start
= clocktime(NULL
);
1082 cp
->mf
= new_mp
->am_mfarray
;
1085 * Try and mount the file system. If this succeeds immediately (possible
1086 * for a ufs file system) then return the attributes, otherwise just
1089 error
= amfs_bgmount(cp
);
1090 reschedule_timeout_mp();
1095 * Code for quick reply. If current_transp is set, then it's the
1096 * transp that's been passed down from nfs_program_2() or from
1097 * autofs_program_[123]().
1098 * If new_mp->am_transp is not already set, set it by copying in
1099 * current_transp. Once am_transp is set, nfs_quick_reply() and
1100 * autofs_mount_succeeded() can use it to send a reply to the
1101 * client that requested this mount.
1103 if (current_transp
&& !new_mp
->am_transp
) {
1104 dlog("Saving RPC transport for %s", new_mp
->am_path
);
1105 new_mp
->am_transp
= (SVCXPRT
*) xmalloc(sizeof(SVCXPRT
));
1106 *(new_mp
->am_transp
) = *current_transp
;
1108 if (error
&& new_mp
->am_mnt
&& (new_mp
->am_mnt
->mf_ops
== &amfs_error_ops
))
1109 new_mp
->am_error
= error
;
1111 if (new_mp
->am_error
> 0)
1112 assign_error_mntfs(new_mp
);
1119 * Automount interface to RPC lookup routine
1120 * Find the corresponding entry and return
1121 * the file handle for it.
1124 amfs_generic_lookup_child(am_node
*mp
, char *fname
, int *error_return
, int op
)
1130 dlog("in amfs_generic_lookup_child");
1133 new_mp
= amfs_lookup_node(mp
, fname
, error_return
);
1135 /* return if we got an error */
1136 if (!new_mp
|| *error_return
> 0)
1139 /* also return if it's already mounted and known to be up */
1140 if (*error_return
== 0 && FSRV_ISUP(new_mp
->am_mnt
->mf_server
))
1146 * If doing a delete then don't create again!
1153 /* save error_return */
1154 mp_error
= *error_return
;
1156 mf_array
= amfs_lookup_mntfs(new_mp
, error_return
);
1158 new_mp
->am_error
= new_mp
->am_mnt
->mf_error
= *error_return
;
1164 * Already mounted but known to be down:
1165 * check if we have any alternatives to mount
1167 if (mp_error
== 0) {
1169 for (mfp
= mf_array
; *mfp
; mfp
++)
1170 if (*mfp
!= new_mp
->am_mnt
)
1174 * we found an alternative, so try mounting again.
1178 for (mfp
= mf_array
; *mfp
; mfp
++)
1181 if (new_mp
->am_flags
& AMF_SOFTLOOKUP
) {
1190 /* store the array inside the am_node */
1191 new_mp
->am_mfarray
= mf_array
;
1194 * Note: while it might seem like a good idea to prioritize
1195 * the list of mntfs's we got here, it probably isn't.
1196 * It would ignore the ordering of entries specified by the user,
1197 * which is counterintuitive and confusing.
1204 amfs_generic_mounted(mntfs
*mf
)
1206 amfs_mkcacheref(mf
);
1211 * Unmount an automount sub-node
1214 amfs_generic_umount(am_node
*mp
, mntfs
*mf
)
1218 #ifdef HAVE_FS_AUTOFS
1219 int unmount_flags
= (mf
->mf_flags
& MFF_ON_AUTOFS
) ? AMU_UMOUNT_AUTOFS
: 0;
1220 if (mf
->mf_flags
& MFF_IS_AUTOFS
)
1221 error
= UMOUNT_FS(mp
->am_path
, mnttab_file_name
, unmount_flags
);
1222 #endif /* HAVE_FS_AUTOFS */
1229 amfs_generic_match(am_opts
*fo
)
1234 plog(XLOG_USER
, "amfs_generic_match: no mount point named (rfs:=)");
1238 plog(XLOG_USER
, "amfs_generic_match: no map named (fs:=)");
1243 * Swap round fs:= and rfs:= options
1244 * ... historical (jsp)
1247 fo
->opt_rfs
= fo
->opt_fs
;
1251 * mtab entry turns out to be the name of the mount map
1253 return strdup(fo
->opt_rfs
? fo
->opt_rfs
: ".");