2 * Copyright (c) 1997-2006 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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
40 * File: am-utils/amd/amfs_host.c
45 * NFS host file system.
46 * Mounts all exported filesystems from a given host.
47 * This has now degenerated into a mess but will not
48 * be rewritten. Amd 6 will support the abstractions
49 * needed to make this work correctly.
54 #endif /* HAVE_CONFIG_H */
58 static char *amfs_host_match(am_opts *fo);
59 static int amfs_host_init(mntfs *mf);
60 static int amfs_host_mount(am_node *am, mntfs *mf);
61 static int amfs_host_umount(am_node *am, mntfs *mf);
62 static void amfs_host_umounted(mntfs *mf);
67 am_ops amfs_host_ops =
74 amfs_error_lookup_child,
75 amfs_error_mount_child,
77 0, /* amfs_host_readlink */
78 0, /* amfs_host_mounted */
81 0, /* amfs_host_get_wchan */
82 FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
85 #endif /* HAVE_FS_AUTOFS */
90 * Determine the mount point:
92 * The next change we put in to better handle PCs. This is a bit
93 * disgusting, so you'd better sit down. We change the make_mntpt function
94 * to look for exported file systems without a leading '/'. If they don't
95 * have a leading '/', we add one. If the export is 'a:' through 'z:'
96 * (without a leading slash), we change it to 'a%' (or b% or z%). This
97 * allows the entire PC disk to be mounted.
100 make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
102 if (ex->ex_dir[0] == '/') {
103 if (ex->ex_dir[1] == 0)
104 xstrlcpy(mntpt, mf_mount, l);
106 xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
107 } else if (ex->ex_dir[0] >= 'a' &&
108 ex->ex_dir[0] <= 'z' &&
109 ex->ex_dir[1] == ':' &&
110 ex->ex_dir[2] == '/' &&
112 xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
114 xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
119 * Execute needs the same as NFS plus a helper command
122 amfs_host_match(am_opts *fo)
124 extern am_ops nfs_ops;
127 * Make sure rfs is specified to keep nfs_match happy...
132 return (*nfs_ops.fs_match) (fo);
137 amfs_host_init(mntfs *mf)
141 if (strchr(mf->mf_info, ':') == 0)
145 * This is primarily to schedule a wakeup so that as soon
146 * as our fileserver is ready, we can continue setting up
147 * the host filesystem. If we don't do this, the standard
148 * amfs_auto code will set up a fileserver structure, but it will
149 * have to wait for another nfs request from the client to come
150 * in before finishing. Our way is faster since we don't have
151 * to wait for the client to resend its request (which could
152 * take a second or two).
155 * First, we find the fileserver for this mntfs and then call
156 * get_mountd_port with our mntfs passed as the wait channel.
157 * get_mountd_port will check some things and then schedule
158 * it so that when the fileserver is ready, a wakeup is done
159 * on this mntfs. amfs_cont() is already sleeping on this mntfs
160 * so as soon as that wakeup happens amfs_cont() is called and
161 * this mount is retried.
165 * We don't really care if there's an error returned.
166 * Since this is just to help speed things along, the
167 * error will get handled properly elsewhere.
169 get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
176 do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
180 dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
182 (void) mkdirs(mntdir, 0555);
183 if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
184 plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
188 return mount_nfs_fh(fhp, mntdir, fs_name, mf);
193 sortfun(const voidp x, const voidp y)
195 exports *a = (exports *) x;
196 exports *b = (exports *) y;
198 return strcmp((*a)->ex_dir, (*b)->ex_dir);
206 fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
209 enum clnt_stat clnt_stat;
212 struct am_mountres3 res3;
213 #endif /* HAVE_FS_NFS3 */
216 * Pick a number, any number...
221 dlog("Fetching fhandle for %s", dir);
224 * Call the mount daemon on the remote host to
225 * get the filehandle. Use NFS version specific call.
228 plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
230 if (nfs_version == NFS_VERSION3) {
231 memset((char *) &res3, 0, sizeof(res3));
232 clnt_stat = clnt_call(client,
234 (XDRPROC_T_TYPE) xdr_dirpath,
235 (SVC_IN_ARG_TYPE) &dir,
236 (XDRPROC_T_TYPE) xdr_am_mountres3,
237 (SVC_IN_ARG_TYPE) &res3,
239 if (clnt_stat != RPC_SUCCESS) {
240 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
243 /* Check the status of the filehandle */
244 if ((errno = res3.fhs_status)) {
245 dlog("fhandle fetch for mount version 3 failed: %m");
248 memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
249 fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
250 memmove(fhp->v3.am_fh3_data,
251 res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
252 fhp->v3.am_fh3_length);
253 } else { /* not NFS_VERSION3 mount */
254 #endif /* HAVE_FS_NFS3 */
255 clnt_stat = clnt_call(client,
257 (XDRPROC_T_TYPE) xdr_dirpath,
258 (SVC_IN_ARG_TYPE) &dir,
259 (XDRPROC_T_TYPE) xdr_fhstatus,
260 (SVC_IN_ARG_TYPE) &res,
262 if (clnt_stat != RPC_SUCCESS) {
263 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
266 /* Check status of filehandle */
267 if (res.fhs_status) {
268 errno = res.fhs_status;
269 dlog("fhandle fetch for mount version 1 failed: %m");
272 memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
274 } /* end of "if (nfs_version == NFS_VERSION3)" statement */
275 #endif /* HAVE_FS_NFS3 */
283 * Scan mount table to see if something already mounted
286 already_mounted(mntlist *mlist, char *dir)
290 for (ml = mlist; ml; ml = ml->mnext)
291 if (STREQ(ml->mnt->mnt_dir, dir))
298 amfs_host_mount(am_node *am, mntfs *mf)
302 enum clnt_stat clnt_stat;
305 exports exlist = 0, ex;
307 am_nfs_handle_t *fp = 0;
310 struct sockaddr_in sin;
311 int sock = RPC_ANYSOCK;
314 char fs_name[MAXPATHLEN], *rfs_dir;
315 char mntpt[MAXPATHLEN];
320 * WebNFS servers don't necessarily run mountd.
322 if (mf->mf_flags & MFF_WEBNFS) {
323 plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
328 * Read the mount list
330 mlist = read_mtab(mf->mf_mount, mnttab_file_name);
332 #ifdef MOUNT_TABLE_ON_FILE
334 * Unlock the mount list
337 #endif /* MOUNT_TABLE_ON_FILE */
340 * Take a copy of the server hostname, address, and nfs version
341 * to mount version conversion.
343 host = mf->mf_server->fs_host;
344 sin = *mf->mf_server->fs_ip;
345 plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
347 if (mf->mf_server->fs_version == NFS_VERSION3)
348 mnt_version = AM_MOUNTVERS3;
350 #endif /* HAVE_FS_NFS3 */
351 mnt_version = MOUNTVERS;
354 * The original 10 second per try timeout is WAY too large, especially
355 * if we're only waiting 10 or 20 seconds max for the response.
356 * That would mean we'd try only once in 10 seconds, and we could
357 * lose the transmit or receive packet, and never try again.
358 * A 2-second per try timeout here is much more reasonable.
359 * 09/28/92 Mike Mitchell, mcm@unx.sas.com
365 * Create a client attached to mountd
367 client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
368 if (client == NULL) {
369 #ifdef HAVE_CLNT_SPCREATEERROR
370 plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
371 host, clnt_spcreateerror(""));
372 #else /* not HAVE_CLNT_SPCREATEERROR */
373 plog(XLOG_ERROR, "get_mount_client failed for %s", host);
374 #endif /* not HAVE_CLNT_SPCREATEERROR */
379 error = make_nfs_auth();
383 client->cl_auth = nfs_auth;
385 dlog("Fetching export list from %s", host);
388 * Fetch the export list
392 clnt_stat = clnt_call(client,
394 (XDRPROC_T_TYPE) xdr_void,
396 (XDRPROC_T_TYPE) xdr_exports,
397 (SVC_IN_ARG_TYPE) & exlist,
399 if (clnt_stat != RPC_SUCCESS) {
400 const char *msg = clnt_sperrno(clnt_stat);
401 plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
402 /* clnt_perror(client, "rpc"); */
408 * Figure out how many exports were returned
410 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
415 * Allocate an array of pointers into the list
416 * so that they can be sorted. If the filesystem
417 * is already mounted then ignore it.
419 ep = (exports *) xmalloc(n_export * sizeof(exports));
420 for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
421 make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
422 if (already_mounted(mlist, mntpt))
423 /* we have at least one mounted f/s, so don't fail the mount */
432 * This way the mounts are done in order down the tree,
433 * instead of any random order returned by the mount
434 * daemon (the protocol doesn't specify...).
436 qsort(ep, n_export, sizeof(exports), sortfun);
439 * Allocate an array of filehandles
441 fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
444 * Try to obtain filehandles for each directory.
445 * If a fetch fails then just zero out the array
446 * reference but discard the error.
448 for (j = k = 0; j < n_export; j++) {
449 /* Check and avoid a duplicated export entry */
450 if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
451 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
455 error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
456 mf->mf_server->fs_version);
463 * Mount each filesystem for which we have a filehandle.
464 * If any of the mounts succeed then mark "ok" and return
465 * error code 0 at the end. If they all fail then return
466 * the last error code.
468 xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN);
469 if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
470 plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
475 for (j = 0; j < n_export; j++) {
479 * Note: the sizeof space left in rfs_dir is what's left in fs_name
480 * after strchr() above returned a pointer _inside_ fs_name. The
481 * calculation below also takes into account that rfs_dir was
482 * incremented by the ++ above.
484 xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
485 make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
486 if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
495 discard_mntlist(mlist);
500 if (sock != RPC_ANYSOCK)
501 (void) amu_close(sock);
503 clnt_destroy(client);
505 xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
513 * Return true if pref is a directory prefix of dir.
516 * Does not work if pref is "/".
519 directory_prefix(char *pref, char *dir)
521 int len = strlen(pref);
523 if (!NSTREQ(pref, dir, len))
525 if (dir[len] == '/' || dir[len] == '\0')
532 * Unmount a mount tree
535 amfs_host_umount(am_node *am, mntfs *mf)
538 int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
542 * Read the mount list
544 mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
546 #ifdef MOUNT_TABLE_ON_FILE
548 * Unlock the mount list
551 #endif /* MOUNT_TABLE_ON_FILE */
559 mntlist *ml2 = ml->mnext;
567 * Unmount all filesystems...
569 for (ml = mlist; ml && !xerror; ml = ml->mnext) {
570 char *dir = ml->mnt->mnt_dir;
571 if (directory_prefix(mf->mf_mount, dir)) {
573 dlog("amfs_host: unmounts %s", dir);
577 error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
579 * Keep track of errors
583 * If we have not already set xerror and error is not ENOENT,
584 * then set xerror equal to error and log it.
585 * 'xerror' is the return value for this function.
587 * We do not want to pass ENOENT as an error because if the
588 * directory does not exists our work is done anyway.
590 if (!xerror && error != ENOENT)
592 if (error != EBUSY) {
594 plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
603 * Throw away mount list
605 discard_mntlist(mlist);
608 * Try to remount, except when we are shutting down.
610 if (xerror && amd_state != Finishing) {
611 xerror = amfs_host_mount(am, mf);
614 * Don't log this - it's usually too verbose
615 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
625 * Tell mountd we're done.
626 * This is not quite right, because we may still
627 * have other filesystems mounted, but the existing
628 * mountd protocol is badly broken anyway.
631 amfs_host_umounted(mntfs *mf)
635 enum clnt_stat clnt_stat;
636 struct sockaddr_in sin;
637 int sock = RPC_ANYSOCK;
641 if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
645 * WebNFS servers shouldn't ever get here.
647 if (mf->mf_flags & MFF_WEBNFS) {
648 plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
653 * Take a copy of the server hostname, address, and NFS version
654 * to mount version conversion.
656 host = mf->mf_server->fs_host;
657 sin = *mf->mf_server->fs_ip;
658 plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
660 if (mf->mf_server->fs_version == NFS_VERSION3)
661 mnt_version = AM_MOUNTVERS3;
663 #endif /* HAVE_FS_NFS3 */
664 mnt_version = MOUNTVERS;
667 * Create a client attached to mountd
671 client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
672 if (client == NULL) {
673 #ifdef HAVE_CLNT_SPCREATEERROR
674 plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
675 host, clnt_spcreateerror(""));
676 #else /* not HAVE_CLNT_SPCREATEERROR */
677 plog(XLOG_ERROR, "get_mount_client failed for %s", host);
678 #endif /* not HAVE_CLNT_SPCREATEERROR */
686 client->cl_auth = nfs_auth;
688 dlog("Unmounting all from %s", host);
690 clnt_stat = clnt_call(client,
692 (XDRPROC_T_TYPE) xdr_void,
694 (XDRPROC_T_TYPE) xdr_void,
697 if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
698 /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
699 const char *msg = clnt_sperrno(clnt_stat);
700 plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
705 if (sock != RPC_ANYSOCK)
706 (void) amu_close(sock);
708 clnt_destroy(client);