2 * Copyright (c) 1997-1999 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
41 * $Id: amfs_host.c,v 1.3 1999/01/13 23:30:57 ezk Exp $
46 * NFS host file system.
47 * Mounts all exported filesystems from a given host.
48 * This has now degenerated into a mess but will not
49 * be rewritten. Amd 6 will support the abstractions
50 * needed to make this work correctly.
55 #endif /* HAVE_CONFIG_H */
59 static char *amfs_host_match(am_opts
*fo
);
60 static int amfs_host_fmount(mntfs
*mf
);
61 static int amfs_host_fumount(mntfs
*mf
);
62 static int amfs_host_init(mntfs
*mf
);
63 static void amfs_host_umounted(am_node
*mp
);
68 am_ops amfs_host_ops
=
79 0, /* amfs_host_readlink */
80 0, /* amfs_host_mounted */
83 FS_MKMNT
| FS_BACKGROUND
| FS_AMQINFO
88 * Determine the mount point:
90 * The next change we put in to better handle PCs. This is a bit
91 * disgusting, so you'd better sit down. We change the make_mntpt function
92 * to look for exported file systems without a leading '/'. If they don't
93 * have a leading '/', we add one. If the export is 'a:' through 'z:'
94 * (without a leading slash), we change it to 'a%' (or b% or z%). This
95 * allows the entire PC disk to be mounted.
98 make_mntpt(char *mntpt
, const exports ex
, const mntfs
*mf
)
100 if (ex
->ex_dir
[0] == '/') {
101 if (ex
->ex_dir
[1] == 0)
102 strcpy(mntpt
, (mf
)->mf_mount
);
104 sprintf(mntpt
, "%s%s", mf
->mf_mount
, ex
->ex_dir
);
105 } else if (ex
->ex_dir
[0] >= 'a' &&
106 ex
->ex_dir
[0] <= 'z' &&
107 ex
->ex_dir
[1] == ':' &&
108 ex
->ex_dir
[2] == '/' &&
110 sprintf(mntpt
, "%s/%c%%", mf
->mf_mount
, ex
->ex_dir
[0]);
112 sprintf(mntpt
, "%s/%s", mf
->mf_mount
, ex
->ex_dir
);
117 * Execute needs the same as NFS plus a helper command
120 amfs_host_match(am_opts
*fo
)
122 extern am_ops nfs_ops
;
125 * Make sure rfs is specified to keep nfs_match happy...
130 return (*nfs_ops
.fs_match
) (fo
);
135 amfs_host_init(mntfs
*mf
)
140 if (strchr(mf
->mf_info
, ':') == 0)
144 * This is primarily to schedule a wakeup so that as soon
145 * as our fileserver is ready, we can continue setting up
146 * the host filesystem. If we don't do this, the standard
147 * amfs_auto code will set up a fileserver structure, but it will
148 * have to wait for another nfs request from the client to come
149 * in before finishing. Our way is faster since we don't have
150 * to wait for the client to resend its request (which could
151 * take a second or two).
154 * First, we find the fileserver for this mntfs and then call
155 * nfs_srvr_port with our mntfs passed as the wait channel.
156 * nfs_srvr_port will check some things and then schedule
157 * it so that when the fileserver is ready, a wakeup is done
158 * on this mntfs. amfs_auto_cont() is already sleeping on this mntfs
159 * so as soon as that wakeup happens amfs_auto_cont() is called and
160 * this mount is retried.
162 if ((fs
= mf
->mf_server
))
164 * We don't really care if there's an error returned.
165 * Since this is just to help speed things along, the
166 * error will get handled properly elsewhere.
168 (void) nfs_srvr_port(fs
, &port
, (voidp
) mf
);
175 do_mount(am_nfs_handle_t
*fhp
, char *dir
, char *fs_name
, char *opts
, mntfs
*mf
)
180 dlog("amfs_host: mounting fs %s on %s\n", fs_name
, dir
);
183 (void) mkdirs(dir
, 0555);
184 if (stat(dir
, &stb
) < 0 || (stb
.st_mode
& S_IFMT
) != S_IFDIR
) {
185 plog(XLOG_ERROR
, "No mount point for %s - skipping", dir
);
189 return mount_nfs_fh(fhp
, dir
, fs_name
, opts
, mf
);
194 sortfun(const voidp x
, const voidp y
)
196 exports
*a
= (exports
*) x
;
197 exports
*b
= (exports
*) y
;
199 return strcmp((*a
)->ex_dir
, (*b
)->ex_dir
);
207 fetch_fhandle(CLIENT
* client
, char *dir
, am_nfs_handle_t
*fhp
, u_long nfs_version
)
210 enum clnt_stat clnt_stat
;
213 * Pick a number, any number...
219 dlog("Fetching fhandle for %s", dir
);
223 * Call the mount daemon on the remote host to
224 * get the filehandle. Use NFS version specific call.
227 plog(XLOG_INFO
, "fetch_fhandle: NFS version %d", (int) nfs_version
);
229 if (nfs_version
== NFS_VERSION3
) {
230 memset((char *) &fhp
->v3
, 0, sizeof(fhp
->v3
));
231 clnt_stat
= clnt_call(client
,
233 (XDRPROC_T_TYPE
) xdr_dirpath
,
234 (SVC_IN_ARG_TYPE
) &dir
,
235 (XDRPROC_T_TYPE
) xdr_mountres3
,
236 (SVC_IN_ARG_TYPE
) &fhp
->v3
,
238 if (clnt_stat
!= RPC_SUCCESS
) {
239 plog(XLOG_ERROR
, "mountd rpc failed: %s", clnt_sperrno(clnt_stat
));
242 /* Check the status of the filehandle */
243 if ((errno
= fhp
->v3
.fhs_status
)) {
245 dlog("fhandle fetch for mount version 3 failed: %m");
249 } else { /* not NFS_VERSION3 mount */
250 #endif /* HAVE_FS_NFS3 */
251 clnt_stat
= clnt_call(client
,
253 (XDRPROC_T_TYPE
) xdr_dirpath
,
254 (SVC_IN_ARG_TYPE
) &dir
,
255 (XDRPROC_T_TYPE
) xdr_fhstatus
,
256 (SVC_IN_ARG_TYPE
) &fhp
->v2
,
258 if (clnt_stat
!= RPC_SUCCESS
) {
259 const char *msg
= clnt_sperrno(clnt_stat
);
260 plog(XLOG_ERROR
, "mountd rpc failed: %s", msg
);
263 /* Check status of filehandle */
264 if (fhp
->v2
.fhs_status
) {
265 errno
= fhp
->v2
.fhs_status
;
267 dlog("fhandle fetch for mount version 1 failed: %m");
272 } /* end of "if (nfs_version == NFS_VERSION3)" statement */
273 #endif /* HAVE_FS_NFS3 */
281 * Scan mount table to see if something already mounted
284 already_mounted(mntlist
*mlist
, char *dir
)
288 for (ml
= mlist
; ml
; ml
= ml
->mnext
)
289 if (STREQ(ml
->mnt
->mnt_dir
, dir
))
296 * Mount the export tree from a host
299 amfs_host_fmount(mntfs
*mf
)
303 enum clnt_stat clnt_stat
;
306 exports exlist
= 0, ex
;
308 am_nfs_handle_t
*fp
= 0;
311 struct sockaddr_in sin
;
312 int sock
= RPC_ANYSOCK
;
315 char fs_name
[MAXPATHLEN
], *rfs_dir
;
316 char mntpt
[MAXPATHLEN
];
321 * Read the mount list
323 mlist
= read_mtab(mf
->mf_mount
, mnttab_file_name
);
325 #ifdef MOUNT_TABLE_ON_FILE
327 * Unlock the mount list
330 #endif /* MOUNT_TABLE_ON_FILE */
333 * Take a copy of the server hostname, address, and nfs version
334 * to mount version conversion.
336 host
= mf
->mf_server
->fs_host
;
337 sin
= *mf
->mf_server
->fs_ip
;
338 plog(XLOG_INFO
, "amfs_host_fmount: NFS version %d", (int) mf
->mf_server
->fs_version
);
340 if (mf
->mf_server
->fs_version
== NFS_VERSION3
)
341 mnt_version
= MOUNTVERS3
;
343 #endif /* HAVE_FS_NFS3 */
344 mnt_version
= MOUNTVERS
;
347 * The original 10 second per try timeout is WAY too large, especially
348 * if we're only waiting 10 or 20 seconds max for the response.
349 * That would mean we'd try only once in 10 seconds, and we could
350 * lose the transmit or receive packet, and never try again.
351 * A 2-second per try timeout here is much more reasonable.
352 * 09/28/92 Mike Mitchell, mcm@unx.sas.com
358 * Create a client attached to mountd
360 client
= get_mount_client(host
, &sin
, &tv
, &sock
, mnt_version
);
361 if (client
== NULL
) {
362 #ifdef HAVE_CLNT_SPCREATEERROR
363 plog(XLOG_ERROR
, "get_mount_client failed for %s: %s",
364 host
, clnt_spcreateerror(""));
365 #else /* not HAVE_CLNT_SPCREATEERROR */
366 plog(XLOG_ERROR
, "get_mount_client failed for %s", host
);
367 #endif /* not HAVE_CLNT_SPCREATEERROR */
372 error
= make_nfs_auth();
376 client
->cl_auth
= nfs_auth
;
379 dlog("Fetching export list from %s", host
);
383 * Fetch the export list
387 clnt_stat
= clnt_call(client
,
389 (XDRPROC_T_TYPE
) xdr_void
,
391 (XDRPROC_T_TYPE
) xdr_exports
,
392 (SVC_IN_ARG_TYPE
) & exlist
,
394 if (clnt_stat
!= RPC_SUCCESS
) {
395 const char *msg
= clnt_sperrno(clnt_stat
);
396 plog(XLOG_ERROR
, "host_fmount rpc failed: %s", msg
);
397 /* clnt_perror(client, "rpc"); */
403 * Figure out how many exports were returned
405 for (n_export
= 0, ex
= exlist
; ex
; ex
= ex
->ex_next
) {
406 /* printf("export %s\n", ex->ex_dir); */
411 * Allocate an array of pointers into the list
412 * so that they can be sorted. If the filesystem
413 * is already mounted then ignore it.
415 ep
= (exports
*) xmalloc(n_export
* sizeof(exports
));
416 for (j
= 0, ex
= exlist
; ex
; ex
= ex
->ex_next
) {
417 make_mntpt(mntpt
, ex
, mf
);
418 if (!already_mounted(mlist
, mntpt
))
425 * This way the mounts are done in order down the tree,
426 * instead of any random order returned by the mount
427 * daemon (the protocol doesn't specify...).
429 qsort(ep
, n_export
, sizeof(exports
), sortfun
);
432 * Allocate an array of filehandles
434 fp
= (am_nfs_handle_t
*) xmalloc(n_export
* sizeof(am_nfs_handle_t
));
437 * Try to obtain filehandles for each directory.
438 * If a fetch fails then just zero out the array
439 * reference but discard the error.
441 for (j
= k
= 0; j
< n_export
; j
++) {
442 /* Check and avoid a duplicated export entry */
443 if (j
> k
&& ep
[k
] && STREQ(ep
[j
]->ex_dir
, ep
[k
]->ex_dir
)) {
445 dlog("avoiding dup fhandle requested for %s", ep
[j
]->ex_dir
);
450 error
= fetch_fhandle(client
, ep
[j
]->ex_dir
, &fp
[j
],
451 mf
->mf_server
->fs_version
);
458 * Mount each filesystem for which we have a filehandle.
459 * If any of the mounts succeed then mark "ok" and return
460 * error code 0 at the end. If they all fail then return
461 * the last error code.
463 strncpy(fs_name
, mf
->mf_info
, sizeof(fs_name
));
464 if ((rfs_dir
= strchr(fs_name
, ':')) == (char *) 0) {
465 plog(XLOG_FATAL
, "amfs_host_fmount: mf_info has no colon");
470 for (j
= 0; j
< n_export
; j
++) {
473 strcpy(rfs_dir
, ex
->ex_dir
);
474 make_mntpt(mntpt
, ex
, mf
);
475 if (do_mount(&fp
[j
], mntpt
, fs_name
, mf
->mf_mopts
, mf
) == 0)
484 discard_mntlist(mlist
);
489 if (sock
!= RPC_ANYSOCK
)
490 (void) amu_close(sock
);
492 clnt_destroy(client
);
494 xdr_pri_free((XDRPROC_T_TYPE
) xdr_exports
, (caddr_t
) &exlist
);
502 * Return true if pref is a directory prefix of dir.
505 * Does not work if pref is "/".
508 directory_prefix(char *pref
, char *dir
)
510 int len
= strlen(pref
);
512 if (!NSTREQ(pref
, dir
, len
))
514 if (dir
[len
] == '/' || dir
[len
] == '\0')
521 * Unmount a mount tree
524 amfs_host_fumount(mntfs
*mf
)
530 * Read the mount list
532 mntlist
*mlist
= read_mtab(mf
->mf_mount
, mnttab_file_name
);
534 #ifdef MOUNT_TABLE_ON_FILE
536 * Unlock the mount list
539 #endif /* MOUNT_TABLE_ON_FILE */
547 mntlist
*ml2
= ml
->mnext
;
555 * Unmount all filesystems...
557 for (ml
= mlist
; ml
&& !xerror
; ml
= ml
->mnext
) {
558 char *dir
= ml
->mnt
->mnt_dir
;
559 if (directory_prefix(mf
->mf_mount
, dir
)) {
562 dlog("amfs_host: unmounts %s", dir
);
567 error
= UMOUNT_FS(dir
, mnttab_file_name
);
569 * Keep track of errors
574 if (error
!= EBUSY
) {
576 plog(XLOG_ERROR
, "Tree unmount of %s failed: %m", ml
->mnt
->mnt_dir
);
585 * Throw away mount list
587 discard_mntlist(mlist
);
590 * Try to remount, except when we are shutting down.
592 if (xerror
&& amd_state
!= Finishing
) {
593 xerror
= amfs_host_fmount(mf
);
596 * Don't log this - it's usually too verbose
597 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
607 * Tell mountd we're done.
608 * This is not quite right, because we may still
609 * have other filesystems mounted, but the existing
610 * mountd protocol is badly broken anyway.
613 amfs_host_umounted(am_node
*mp
)
615 mntfs
*mf
= mp
->am_mnt
;
618 enum clnt_stat clnt_stat
;
619 struct sockaddr_in sin
;
620 int sock
= RPC_ANYSOCK
;
624 if (mf
->mf_error
|| mf
->mf_refc
> 1 || !mf
->mf_server
)
628 * Take a copy of the server hostname, address, and NFS version
629 * to mount version conversion.
631 host
= mf
->mf_server
->fs_host
;
632 sin
= *mf
->mf_server
->fs_ip
;
633 plog(XLOG_INFO
, "amfs_host_umounted: NFS version %d", (int) mf
->mf_server
->fs_version
);
635 if (mf
->mf_server
->fs_version
== NFS_VERSION3
)
636 mnt_version
= MOUNTVERS3
;
638 #endif /* HAVE_FS_NFS3 */
639 mnt_version
= MOUNTVERS
;
642 * Create a client attached to mountd
646 client
= get_mount_client(host
, &sin
, &tv
, &sock
, mnt_version
);
647 if (client
== NULL
) {
648 #ifdef HAVE_CLNT_SPCREATEERROR
649 plog(XLOG_ERROR
, "get_mount_client failed for %s: %s",
650 host
, clnt_spcreateerror(""));
651 #else /* not HAVE_CLNT_SPCREATEERROR */
652 plog(XLOG_ERROR
, "get_mount_client failed for %s", host
);
653 #endif /* not HAVE_CLNT_SPCREATEERROR */
661 client
->cl_auth
= nfs_auth
;
664 dlog("Unmounting all from %s", host
);
667 clnt_stat
= clnt_call(client
,
669 (XDRPROC_T_TYPE
) xdr_void
,
671 (XDRPROC_T_TYPE
) xdr_void
,
674 if (clnt_stat
!= RPC_SUCCESS
&& clnt_stat
!= RPC_SYSTEMERROR
) {
675 /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
676 const char *msg
= clnt_sperrno(clnt_stat
);
677 plog(XLOG_ERROR
, "unmount all from %s rpc failed: %s", host
, msg
);
682 if (sock
!= RPC_ANYSOCK
)
683 (void) amu_close(sock
);
685 clnt_destroy(client
);