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_host.c
47 * NFS host file system.
48 * Mounts all exported filesystems from a given host.
49 * This has now degenerated into a mess but will not
50 * be rewritten. Amd 6 will support the abstractions
51 * needed to make this work correctly.
56 #endif /* HAVE_CONFIG_H */
60 static char *amfs_host_match(am_opts
*fo
);
61 static int amfs_host_init(mntfs
*mf
);
62 static int amfs_host_mount(am_node
*am
, mntfs
*mf
);
63 static int amfs_host_umount(am_node
*am
, mntfs
*mf
);
64 static void amfs_host_umounted(mntfs
*mf
);
69 am_ops amfs_host_ops
=
76 amfs_error_lookup_child
,
77 amfs_error_mount_child
,
79 0, /* amfs_host_readlink */
80 0, /* amfs_host_mounted */
83 0, /* amfs_host_get_wchan */
84 FS_MKMNT
| FS_BACKGROUND
| FS_AMQINFO
,
87 #endif /* HAVE_FS_AUTOFS */
92 * Determine the mount point:
94 * The next change we put in to better handle PCs. This is a bit
95 * disgusting, so you'd better sit down. We change the make_mntpt function
96 * to look for exported file systems without a leading '/'. If they don't
97 * have a leading '/', we add one. If the export is 'a:' through 'z:'
98 * (without a leading slash), we change it to 'a%' (or b% or z%). This
99 * allows the entire PC disk to be mounted.
102 make_mntpt(char *mntpt
, size_t l
, const exports ex
, const char *mf_mount
)
104 if (ex
->ex_dir
[0] == '/') {
105 if (ex
->ex_dir
[1] == 0)
106 xstrlcpy(mntpt
, mf_mount
, l
);
108 xsnprintf(mntpt
, l
, "%s%s", mf_mount
, ex
->ex_dir
);
109 } else if (ex
->ex_dir
[0] >= 'a' &&
110 ex
->ex_dir
[0] <= 'z' &&
111 ex
->ex_dir
[1] == ':' &&
112 ex
->ex_dir
[2] == '/' &&
114 xsnprintf(mntpt
, l
, "%s/%c%%", mf_mount
, ex
->ex_dir
[0]);
116 xsnprintf(mntpt
, l
, "%s/%s", mf_mount
, ex
->ex_dir
);
121 * Execute needs the same as NFS plus a helper command
124 amfs_host_match(am_opts
*fo
)
126 extern am_ops nfs_ops
;
129 * Make sure rfs is specified to keep nfs_match happy...
134 return (*nfs_ops
.fs_match
) (fo
);
139 amfs_host_init(mntfs
*mf
)
143 if (strchr(mf
->mf_info
, ':') == 0)
147 * This is primarily to schedule a wakeup so that as soon
148 * as our fileserver is ready, we can continue setting up
149 * the host filesystem. If we don't do this, the standard
150 * amfs_auto code will set up a fileserver structure, but it will
151 * have to wait for another nfs request from the client to come
152 * in before finishing. Our way is faster since we don't have
153 * to wait for the client to resend its request (which could
154 * take a second or two).
157 * First, we find the fileserver for this mntfs and then call
158 * get_mountd_port with our mntfs passed as the wait channel.
159 * get_mountd_port will check some things and then schedule
160 * it so that when the fileserver is ready, a wakeup is done
161 * on this mntfs. amfs_cont() is already sleeping on this mntfs
162 * so as soon as that wakeup happens amfs_cont() is called and
163 * this mount is retried.
167 * We don't really care if there's an error returned.
168 * Since this is just to help speed things along, the
169 * error will get handled properly elsewhere.
171 get_mountd_port(mf
->mf_server
, &mountd_port
, get_mntfs_wchan(mf
));
178 do_mount(am_nfs_handle_t
*fhp
, char *mntdir
, char *fs_name
, mntfs
*mf
)
182 dlog("amfs_host: mounting fs %s on %s\n", fs_name
, mntdir
);
184 (void) mkdirs(mntdir
, 0555);
185 if (stat(mntdir
, &stb
) < 0 || (stb
.st_mode
& S_IFMT
) != S_IFDIR
) {
186 plog(XLOG_ERROR
, "No mount point for %s - skipping", mntdir
);
190 return mount_nfs_fh(fhp
, mntdir
, fs_name
, mf
);
195 sortfun(const voidp x
, const voidp y
)
197 exports
*a
= (exports
*) x
;
198 exports
*b
= (exports
*) y
;
200 return strcmp((*a
)->ex_dir
, (*b
)->ex_dir
);
208 fetch_fhandle(CLIENT
*client
, char *dir
, am_nfs_handle_t
*fhp
, u_long nfs_version
)
211 enum clnt_stat clnt_stat
;
214 struct am_mountres3 res3
;
215 #endif /* HAVE_FS_NFS3 */
218 * Pick a number, any number...
223 dlog("Fetching fhandle for %s", dir
);
226 * Call the mount daemon on the remote host to
227 * get the filehandle. Use NFS version specific call.
230 plog(XLOG_INFO
, "fetch_fhandle: NFS version %d", (int) nfs_version
);
232 if (nfs_version
== NFS_VERSION3
) {
233 memset((char *) &res3
, 0, sizeof(res3
));
234 clnt_stat
= clnt_call(client
,
236 (XDRPROC_T_TYPE
) xdr_dirpath
,
237 (SVC_IN_ARG_TYPE
) &dir
,
238 (XDRPROC_T_TYPE
) xdr_am_mountres3
,
239 (SVC_IN_ARG_TYPE
) &res3
,
241 if (clnt_stat
!= RPC_SUCCESS
) {
242 plog(XLOG_ERROR
, "mountd rpc failed: %s", clnt_sperrno(clnt_stat
));
245 /* Check the status of the filehandle */
246 if ((errno
= res3
.fhs_status
)) {
247 dlog("fhandle fetch for mount version 3 failed: %m");
250 memset((voidp
) &fhp
->v3
, 0, sizeof(am_nfs_fh3
));
251 fhp
->v3
.am_fh3_length
= res3
.mountres3_u
.mountinfo
.fhandle
.fhandle3_len
;
252 memmove(fhp
->v3
.am_fh3_data
,
253 res3
.mountres3_u
.mountinfo
.fhandle
.fhandle3_val
,
254 fhp
->v3
.am_fh3_length
);
255 } else { /* not NFS_VERSION3 mount */
256 #endif /* HAVE_FS_NFS3 */
257 clnt_stat
= clnt_call(client
,
259 (XDRPROC_T_TYPE
) xdr_dirpath
,
260 (SVC_IN_ARG_TYPE
) &dir
,
261 (XDRPROC_T_TYPE
) xdr_fhstatus
,
262 (SVC_IN_ARG_TYPE
) &res
,
264 if (clnt_stat
!= RPC_SUCCESS
) {
265 plog(XLOG_ERROR
, "mountd rpc failed: %s", clnt_sperrno(clnt_stat
));
268 /* Check status of filehandle */
269 if (res
.fhs_status
) {
270 errno
= res
.fhs_status
;
271 dlog("fhandle fetch for mount version 1 failed: %m");
274 memmove(&fhp
->v2
, &res
.fhs_fh
, NFS_FHSIZE
);
276 } /* end of "if (nfs_version == NFS_VERSION3)" statement */
277 #endif /* HAVE_FS_NFS3 */
285 * Scan mount table to see if something already mounted
288 already_mounted(mntlist
*mlist
, char *dir
)
292 for (ml
= mlist
; ml
; ml
= ml
->mnext
)
293 if (STREQ(ml
->mnt
->mnt_dir
, dir
))
300 amfs_host_mount(am_node
*am
, mntfs
*mf
)
304 enum clnt_stat clnt_stat
;
307 exports exlist
= 0, ex
;
309 am_nfs_handle_t
*fp
= NULL
;
312 struct sockaddr_in sin
;
313 int sock
= RPC_ANYSOCK
;
316 char fs_name
[MAXPATHLEN
], *rfs_dir
;
317 char mntpt
[MAXPATHLEN
];
322 * WebNFS servers don't necessarily run mountd.
324 if (mf
->mf_flags
& MFF_WEBNFS
) {
325 plog(XLOG_ERROR
, "amfs_host_mount: cannot support WebNFS");
330 * Read the mount list
332 mlist
= read_mtab(mf
->mf_mount
, mnttab_file_name
);
334 #ifdef MOUNT_TABLE_ON_FILE
336 * Unlock the mount list
339 #endif /* MOUNT_TABLE_ON_FILE */
342 * Take a copy of the server hostname, address, and nfs version
343 * to mount version conversion.
345 host
= mf
->mf_server
->fs_host
;
346 sin
= *mf
->mf_server
->fs_ip
;
347 plog(XLOG_INFO
, "amfs_host_mount: NFS version %d", (int) mf
->mf_server
->fs_version
);
349 if (mf
->mf_server
->fs_version
== NFS_VERSION3
)
350 mnt_version
= AM_MOUNTVERS3
;
352 #endif /* HAVE_FS_NFS3 */
353 mnt_version
= MOUNTVERS
;
356 * The original 10 second per try timeout is WAY too large, especially
357 * if we're only waiting 10 or 20 seconds max for the response.
358 * That would mean we'd try only once in 10 seconds, and we could
359 * lose the transmit or receive packet, and never try again.
360 * A 2-second per try timeout here is much more reasonable.
361 * 09/28/92 Mike Mitchell, mcm@unx.sas.com
367 * Create a client attached to mountd
369 client
= get_mount_client(host
, &sin
, &tv
, &sock
, mnt_version
);
370 if (client
== NULL
) {
371 #ifdef HAVE_CLNT_SPCREATEERROR
372 plog(XLOG_ERROR
, "get_mount_client failed for %s: %s",
373 host
, clnt_spcreateerror(""));
374 #else /* not HAVE_CLNT_SPCREATEERROR */
375 plog(XLOG_ERROR
, "get_mount_client failed for %s", host
);
376 #endif /* not HAVE_CLNT_SPCREATEERROR */
381 error
= make_nfs_auth();
385 client
->cl_auth
= nfs_auth
;
387 dlog("Fetching export list from %s", host
);
390 * Fetch the export list
394 clnt_stat
= clnt_call(client
,
396 (XDRPROC_T_TYPE
) xdr_void
,
398 (XDRPROC_T_TYPE
) xdr_exports
,
399 (SVC_IN_ARG_TYPE
) & exlist
,
401 if (clnt_stat
!= RPC_SUCCESS
) {
402 const char *msg
= clnt_sperrno(clnt_stat
);
403 plog(XLOG_ERROR
, "host_mount rpc failed: %s", msg
);
404 /* clnt_perror(client, "rpc"); */
410 * Figure out how many exports were returned
412 for (n_export
= 0, ex
= exlist
; ex
; ex
= ex
->ex_next
) {
417 * Allocate an array of pointers into the list
418 * so that they can be sorted. If the filesystem
419 * is already mounted then ignore it.
421 ep
= (exports
*) xmalloc(n_export
* sizeof(exports
));
422 for (j
= 0, ex
= exlist
; ex
; ex
= ex
->ex_next
) {
423 make_mntpt(mntpt
, sizeof(mntpt
), ex
, mf
->mf_mount
);
424 if (already_mounted(mlist
, mntpt
))
425 /* we have at least one mounted f/s, so don't fail the mount */
434 * This way the mounts are done in order down the tree,
435 * instead of any random order returned by the mount
436 * daemon (the protocol doesn't specify...).
438 qsort(ep
, n_export
, sizeof(exports
), sortfun
);
441 * Allocate an array of filehandles
443 fp
= (am_nfs_handle_t
*) xmalloc(n_export
* sizeof(am_nfs_handle_t
));
446 * Try to obtain filehandles for each directory.
447 * If a fetch fails then just zero out the array
448 * reference but discard the error.
450 for (j
= k
= 0; j
< n_export
; j
++) {
451 /* Check and avoid a duplicated export entry */
452 if (j
> k
&& ep
[k
] && STREQ(ep
[j
]->ex_dir
, ep
[k
]->ex_dir
)) {
453 dlog("avoiding dup fhandle requested for %s", ep
[j
]->ex_dir
);
457 error
= fetch_fhandle(client
, ep
[j
]->ex_dir
, &fp
[j
],
458 mf
->mf_server
->fs_version
);
465 * Mount each filesystem for which we have a filehandle.
466 * If any of the mounts succeed then mark "ok" and return
467 * error code 0 at the end. If they all fail then return
468 * the last error code.
470 xstrlcpy(fs_name
, mf
->mf_info
, sizeof(fs_name
));
471 if ((rfs_dir
= strchr(fs_name
, ':')) == (char *) NULL
) {
472 plog(XLOG_FATAL
, "amfs_host_mount: mf_info has no colon");
477 for (j
= 0; j
< n_export
; j
++) {
481 * Note: the sizeof space left in rfs_dir is what's left in fs_name
482 * after strchr() above returned a pointer _inside_ fs_name. The
483 * calculation below also takes into account that rfs_dir was
484 * incremented by the ++ above.
486 xstrlcpy(rfs_dir
, ex
->ex_dir
, sizeof(fs_name
) - (rfs_dir
- fs_name
));
487 make_mntpt(mntpt
, sizeof(mntpt
), ex
, mf
->mf_mount
);
488 if (do_mount(&fp
[j
], mntpt
, fs_name
, mf
) == 0)
497 discard_mntlist(mlist
);
502 if (sock
!= RPC_ANYSOCK
)
503 (void) amu_close(sock
);
505 clnt_destroy(client
);
507 xdr_pri_free((XDRPROC_T_TYPE
) xdr_exports
, (caddr_t
) &exlist
);
515 * Return true if pref is a directory prefix of dir.
518 * Does not work if pref is "/".
521 directory_prefix(char *pref
, char *dir
)
523 int len
= strlen(pref
);
525 if (!NSTREQ(pref
, dir
, len
))
527 if (dir
[len
] == '/' || dir
[len
] == '\0')
534 * Unmount a mount tree
537 amfs_host_umount(am_node
*am
, mntfs
*mf
)
540 int unmount_flags
= (mf
->mf_flags
& MFF_ON_AUTOFS
) ? AMU_UMOUNT_AUTOFS
: 0;
544 * Read the mount list
546 mntlist
*mlist
= read_mtab(mf
->mf_mount
, mnttab_file_name
);
548 #ifdef MOUNT_TABLE_ON_FILE
550 * Unlock the mount list
553 #endif /* MOUNT_TABLE_ON_FILE */
561 mntlist
*ml2
= ml
->mnext
;
569 * Unmount all filesystems...
571 for (ml
= mlist
; ml
&& !xerror
; ml
= ml
->mnext
) {
572 char *dir
= ml
->mnt
->mnt_dir
;
573 if (directory_prefix(mf
->mf_mount
, dir
)) {
575 dlog("amfs_host: unmounts %s", dir
);
579 error
= UMOUNT_FS(dir
, mnttab_file_name
, unmount_flags
);
581 * Keep track of errors
585 * If we have not already set xerror and error is not ENOENT,
586 * then set xerror equal to error and log it.
587 * 'xerror' is the return value for this function.
589 * We do not want to pass ENOENT as an error because if the
590 * directory does not exists our work is done anyway.
592 if (!xerror
&& error
!= ENOENT
)
594 if (error
!= EBUSY
) {
596 plog(XLOG_ERROR
, "Tree unmount of %s failed: %m", ml
->mnt
->mnt_dir
);
605 * Throw away mount list
607 discard_mntlist(mlist
);
610 * Try to remount, except when we are shutting down.
612 if (xerror
&& amd_state
!= Finishing
) {
613 xerror
= amfs_host_mount(am
, mf
);
616 * Don't log this - it's usually too verbose
617 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
627 * Tell mountd we're done.
628 * This is not quite right, because we may still
629 * have other filesystems mounted, but the existing
630 * mountd protocol is badly broken anyway.
633 amfs_host_umounted(mntfs
*mf
)
637 enum clnt_stat clnt_stat
;
638 struct sockaddr_in sin
;
639 int sock
= RPC_ANYSOCK
;
643 if (mf
->mf_error
|| mf
->mf_refc
> 1 || !mf
->mf_server
)
647 * WebNFS servers shouldn't ever get here.
649 if (mf
->mf_flags
& MFF_WEBNFS
) {
650 plog(XLOG_ERROR
, "amfs_host_umounted: cannot support WebNFS");
655 * Take a copy of the server hostname, address, and NFS version
656 * to mount version conversion.
658 host
= mf
->mf_server
->fs_host
;
659 sin
= *mf
->mf_server
->fs_ip
;
660 plog(XLOG_INFO
, "amfs_host_umounted: NFS version %d", (int) mf
->mf_server
->fs_version
);
662 if (mf
->mf_server
->fs_version
== NFS_VERSION3
)
663 mnt_version
= AM_MOUNTVERS3
;
665 #endif /* HAVE_FS_NFS3 */
666 mnt_version
= MOUNTVERS
;
669 * Create a client attached to mountd
673 client
= get_mount_client(host
, &sin
, &tv
, &sock
, mnt_version
);
674 if (client
== NULL
) {
675 #ifdef HAVE_CLNT_SPCREATEERROR
676 plog(XLOG_ERROR
, "get_mount_client failed for %s: %s",
677 host
, clnt_spcreateerror(""));
678 #else /* not HAVE_CLNT_SPCREATEERROR */
679 plog(XLOG_ERROR
, "get_mount_client failed for %s", host
);
680 #endif /* not HAVE_CLNT_SPCREATEERROR */
688 client
->cl_auth
= nfs_auth
;
690 dlog("Unmounting all from %s", host
);
692 clnt_stat
= clnt_call(client
,
694 (XDRPROC_T_TYPE
) xdr_void
,
696 (XDRPROC_T_TYPE
) xdr_void
,
699 if (clnt_stat
!= RPC_SUCCESS
&& clnt_stat
!= RPC_SYSTEMERROR
) {
700 /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
701 const char *msg
= clnt_sperrno(clnt_stat
);
702 plog(XLOG_ERROR
, "unmount all from %s rpc failed: %s", host
, msg
);
707 if (sock
!= RPC_ANYSOCK
)
708 (void) amu_close(sock
);
710 clnt_destroy(client
);