MFC r1.27:
[dragonfly.git] / contrib / amd / amd / amfs_host.c
blob71b08df179b5b976b8779923e3c01ae47df15145
1 /*
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.
6 * All rights reserved.
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
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.
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.
39 * %W% (Berkeley) %G%
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.
53 #ifdef HAVE_CONFIG_H
54 # include <config.h>
55 #endif /* HAVE_CONFIG_H */
56 #include <am_defs.h>
57 #include <amd.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);
66 * Ops structure
68 am_ops amfs_host_ops =
70 "host",
71 amfs_host_match,
72 amfs_host_init,
73 amfs_auto_fmount,
74 amfs_host_fmount,
75 amfs_auto_fumount,
76 amfs_host_fumount,
77 amfs_error_lookuppn,
78 amfs_error_readdir,
79 0, /* amfs_host_readlink */
80 0, /* amfs_host_mounted */
81 amfs_host_umounted,
82 find_nfs_srvr,
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.
97 static void
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);
103 else
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] == '/' &&
109 ex->ex_dir[3] == 0)
110 sprintf(mntpt, "%s/%c%%", mf->mf_mount, ex->ex_dir[0]);
111 else
112 sprintf(mntpt, "%s/%s", mf->mf_mount, ex->ex_dir);
117 * Execute needs the same as NFS plus a helper command
119 static char *
120 amfs_host_match(am_opts *fo)
122 extern am_ops nfs_ops;
125 * Make sure rfs is specified to keep nfs_match happy...
127 if (!fo->opt_rfs)
128 fo->opt_rfs = "/";
130 return (*nfs_ops.fs_match) (fo);
134 static int
135 amfs_host_init(mntfs *mf)
137 fserver *fs;
138 u_short port;
140 if (strchr(mf->mf_info, ':') == 0)
141 return ENOENT;
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);
170 return 0;
174 static int
175 do_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
177 struct stat stb;
179 #ifdef DEBUG
180 dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir);
181 #endif /* DEBUG */
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);
186 return ENOENT;
189 return mount_nfs_fh(fhp, dir, fs_name, opts, mf);
193 static int
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);
204 * Get filehandle
206 static int
207 fetch_fhandle(CLIENT * client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
209 struct timeval tv;
210 enum clnt_stat clnt_stat;
213 * Pick a number, any number...
215 tv.tv_sec = 20;
216 tv.tv_usec = 0;
218 #ifdef DEBUG
219 dlog("Fetching fhandle for %s", dir);
220 #endif /* DEBUG */
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);
228 #ifdef HAVE_FS_NFS3
229 if (nfs_version == NFS_VERSION3) {
230 memset((char *) &fhp->v3, 0, sizeof(fhp->v3));
231 clnt_stat = clnt_call(client,
232 MOUNTPROC_MNT,
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,
237 tv);
238 if (clnt_stat != RPC_SUCCESS) {
239 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
240 return EIO;
242 /* Check the status of the filehandle */
243 if ((errno = fhp->v3.fhs_status)) {
244 #ifdef DEBUG
245 dlog("fhandle fetch for mount version 3 failed: %m");
246 #endif /* DEBUG */
247 return errno;
249 } else { /* not NFS_VERSION3 mount */
250 #endif /* HAVE_FS_NFS3 */
251 clnt_stat = clnt_call(client,
252 MOUNTPROC_MNT,
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,
257 tv);
258 if (clnt_stat != RPC_SUCCESS) {
259 const char *msg = clnt_sperrno(clnt_stat);
260 plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
261 return EIO;
263 /* Check status of filehandle */
264 if (fhp->v2.fhs_status) {
265 errno = fhp->v2.fhs_status;
266 #ifdef DEBUG
267 dlog("fhandle fetch for mount version 1 failed: %m");
268 #endif /* DEBUG */
269 return errno;
271 #ifdef HAVE_FS_NFS3
272 } /* end of "if (nfs_version == NFS_VERSION3)" statement */
273 #endif /* HAVE_FS_NFS3 */
275 /* all is well */
276 return 0;
281 * Scan mount table to see if something already mounted
283 static int
284 already_mounted(mntlist *mlist, char *dir)
286 mntlist *ml;
288 for (ml = mlist; ml; ml = ml->mnext)
289 if (STREQ(ml->mnt->mnt_dir, dir))
290 return 1;
291 return 0;
296 * Mount the export tree from a host
298 static int
299 amfs_host_fmount(mntfs *mf)
301 struct timeval tv2;
302 CLIENT *client;
303 enum clnt_stat clnt_stat;
304 int n_export;
305 int j, k;
306 exports exlist = 0, ex;
307 exports *ep = 0;
308 am_nfs_handle_t *fp = 0;
309 char *host;
310 int error = 0;
311 struct sockaddr_in sin;
312 int sock = RPC_ANYSOCK;
313 int ok = FALSE;
314 mntlist *mlist;
315 char fs_name[MAXPATHLEN], *rfs_dir;
316 char mntpt[MAXPATHLEN];
317 struct timeval tv;
318 u_long mnt_version;
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
329 unlock_mntlist();
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);
339 #ifdef HAVE_FS_NFS3
340 if (mf->mf_server->fs_version == NFS_VERSION3)
341 mnt_version = MOUNTVERS3;
342 else
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
354 tv.tv_sec = 2;
355 tv.tv_usec = 0;
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 */
368 error = EIO;
369 goto out;
371 if (!nfs_auth) {
372 error = make_nfs_auth();
373 if (error)
374 goto out;
376 client->cl_auth = nfs_auth;
378 #ifdef DEBUG
379 dlog("Fetching export list from %s", host);
380 #endif /* DEBUG */
383 * Fetch the export list
385 tv2.tv_sec = 10;
386 tv2.tv_usec = 0;
387 clnt_stat = clnt_call(client,
388 MOUNTPROC_EXPORT,
389 (XDRPROC_T_TYPE) xdr_void,
391 (XDRPROC_T_TYPE) xdr_exports,
392 (SVC_IN_ARG_TYPE) & exlist,
393 tv2);
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"); */
398 error = EIO;
399 goto out;
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); */
407 n_export++;
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))
419 ep[j++] = ex;
421 n_export = j;
424 * Sort into order.
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)) {
444 #ifdef DEBUG
445 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
446 #endif /* DEBUG */
447 ep[j] = 0;
448 } else {
449 k = j;
450 error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
451 mf->mf_server->fs_version);
452 if (error)
453 ep[j] = 0;
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");
466 error = EINVAL;
467 goto out;
469 ++rfs_dir;
470 for (j = 0; j < n_export; j++) {
471 ex = ep[j];
472 if (ex) {
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)
476 ok = TRUE;
481 * Clean up and exit
483 out:
484 discard_mntlist(mlist);
485 if (ep)
486 XFREE(ep);
487 if (fp)
488 XFREE(fp);
489 if (sock != RPC_ANYSOCK)
490 (void) amu_close(sock);
491 if (client)
492 clnt_destroy(client);
493 if (exlist)
494 xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
495 if (ok)
496 return 0;
497 return error;
502 * Return true if pref is a directory prefix of dir.
504 * XXX TODO:
505 * Does not work if pref is "/".
507 static int
508 directory_prefix(char *pref, char *dir)
510 int len = strlen(pref);
512 if (!NSTREQ(pref, dir, len))
513 return FALSE;
514 if (dir[len] == '/' || dir[len] == '\0')
515 return TRUE;
516 return FALSE;
521 * Unmount a mount tree
523 static int
524 amfs_host_fumount(mntfs *mf)
526 mntlist *ml, *mprev;
527 int xerror = 0;
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
538 unlock_mntlist();
539 #endif /* MOUNT_TABLE_ON_FILE */
542 * Reverse list...
544 ml = mlist;
545 mprev = 0;
546 while (ml) {
547 mntlist *ml2 = ml->mnext;
548 ml->mnext = mprev;
549 mprev = ml;
550 ml = ml2;
552 mlist = mprev;
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)) {
560 int error;
561 #ifdef DEBUG
562 dlog("amfs_host: unmounts %s", dir);
563 #endif /* DEBUG */
565 * Unmount "dir"
567 error = UMOUNT_FS(dir, mnttab_file_name);
569 * Keep track of errors
571 if (error) {
572 if (!xerror)
573 xerror = error;
574 if (error != EBUSY) {
575 errno = error;
576 plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
578 } else {
579 (void) rmdirs(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);
594 if (!xerror) {
596 * Don't log this - it's usually too verbose
597 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
599 xerror = EBUSY;
602 return xerror;
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.
612 static void
613 amfs_host_umounted(am_node *mp)
615 mntfs *mf = mp->am_mnt;
616 char *host;
617 CLIENT *client;
618 enum clnt_stat clnt_stat;
619 struct sockaddr_in sin;
620 int sock = RPC_ANYSOCK;
621 struct timeval tv;
622 u_long mnt_version;
624 if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
625 return;
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);
634 #ifdef HAVE_FS_NFS3
635 if (mf->mf_server->fs_version == NFS_VERSION3)
636 mnt_version = MOUNTVERS3;
637 else
638 #endif /* HAVE_FS_NFS3 */
639 mnt_version = MOUNTVERS;
642 * Create a client attached to mountd
644 tv.tv_sec = 10;
645 tv.tv_usec = 0;
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 */
654 goto out;
657 if (!nfs_auth) {
658 if (make_nfs_auth())
659 goto out;
661 client->cl_auth = nfs_auth;
663 #ifdef DEBUG
664 dlog("Unmounting all from %s", host);
665 #endif /* DEBUG */
667 clnt_stat = clnt_call(client,
668 MOUNTPROC_UMNTALL,
669 (XDRPROC_T_TYPE) xdr_void,
671 (XDRPROC_T_TYPE) xdr_void,
673 tv);
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);
678 goto out;
681 out:
682 if (sock != RPC_ANYSOCK)
683 (void) amu_close(sock);
684 if (client)
685 clnt_destroy(client);