No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / am-utils / dist / amd / amfs_host.c
bloba7dfa1516221e308acd550d241880ac6abcb6111
1 /* $NetBSD$ */
3 /*
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.
8 * All rights reserved.
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
15 * are met:
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
39 * SUCH DAMAGE.
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.
54 #ifdef HAVE_CONFIG_H
55 # include <config.h>
56 #endif /* HAVE_CONFIG_H */
57 #include <am_defs.h>
58 #include <amd.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);
67 * Ops structure
69 am_ops amfs_host_ops =
71 "host",
72 amfs_host_match,
73 amfs_host_init,
74 amfs_host_mount,
75 amfs_host_umount,
76 amfs_error_lookup_child,
77 amfs_error_mount_child,
78 amfs_error_readdir,
79 0, /* amfs_host_readlink */
80 0, /* amfs_host_mounted */
81 amfs_host_umounted,
82 find_nfs_srvr,
83 0, /* amfs_host_get_wchan */
84 FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
85 #ifdef HAVE_FS_AUTOFS
86 AUTOFS_HOST_FS_FLAGS,
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.
101 static void
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);
107 else
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] == '/' &&
113 ex->ex_dir[3] == 0)
114 xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
115 else
116 xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
121 * Execute needs the same as NFS plus a helper command
123 static char *
124 amfs_host_match(am_opts *fo)
126 extern am_ops nfs_ops;
129 * Make sure rfs is specified to keep nfs_match happy...
131 if (!fo->opt_rfs)
132 fo->opt_rfs = "/";
134 return (*nfs_ops.fs_match) (fo);
138 static int
139 amfs_host_init(mntfs *mf)
141 u_short mountd_port;
143 if (strchr(mf->mf_info, ':') == 0)
144 return ENOENT;
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.
165 if (mf->mf_server)
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));
173 return 0;
177 static int
178 do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
180 struct stat stb;
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);
187 return ENOENT;
190 return mount_nfs_fh(fhp, mntdir, fs_name, mf);
194 static int
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);
205 * Get filehandle
207 static int
208 fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
210 struct timeval tv;
211 enum clnt_stat clnt_stat;
212 struct fhstatus res;
213 #ifdef HAVE_FS_NFS3
214 struct am_mountres3 res3;
215 #endif /* HAVE_FS_NFS3 */
218 * Pick a number, any number...
220 tv.tv_sec = 20;
221 tv.tv_usec = 0;
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);
231 #ifdef HAVE_FS_NFS3
232 if (nfs_version == NFS_VERSION3) {
233 memset((char *) &res3, 0, sizeof(res3));
234 clnt_stat = clnt_call(client,
235 MOUNTPROC_MNT,
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,
240 tv);
241 if (clnt_stat != RPC_SUCCESS) {
242 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
243 return EIO;
245 /* Check the status of the filehandle */
246 if ((errno = res3.fhs_status)) {
247 dlog("fhandle fetch for mount version 3 failed: %m");
248 return errno;
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,
258 MOUNTPROC_MNT,
259 (XDRPROC_T_TYPE) xdr_dirpath,
260 (SVC_IN_ARG_TYPE) &dir,
261 (XDRPROC_T_TYPE) xdr_fhstatus,
262 (SVC_IN_ARG_TYPE) &res,
263 tv);
264 if (clnt_stat != RPC_SUCCESS) {
265 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
266 return EIO;
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");
272 return errno;
274 memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
275 #ifdef HAVE_FS_NFS3
276 } /* end of "if (nfs_version == NFS_VERSION3)" statement */
277 #endif /* HAVE_FS_NFS3 */
279 /* all is well */
280 return 0;
285 * Scan mount table to see if something already mounted
287 static int
288 already_mounted(mntlist *mlist, char *dir)
290 mntlist *ml;
292 for (ml = mlist; ml; ml = ml->mnext)
293 if (STREQ(ml->mnt->mnt_dir, dir))
294 return 1;
295 return 0;
299 static int
300 amfs_host_mount(am_node *am, mntfs *mf)
302 struct timeval tv2;
303 CLIENT *client;
304 enum clnt_stat clnt_stat;
305 int n_export;
306 int j, k;
307 exports exlist = 0, ex;
308 exports *ep = NULL;
309 am_nfs_handle_t *fp = NULL;
310 char *host;
311 int error = 0;
312 struct sockaddr_in sin;
313 int sock = RPC_ANYSOCK;
314 int ok = FALSE;
315 mntlist *mlist;
316 char fs_name[MAXPATHLEN], *rfs_dir;
317 char mntpt[MAXPATHLEN];
318 struct timeval tv;
319 u_long mnt_version;
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");
326 return EIO;
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
338 unlock_mntlist();
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);
348 #ifdef HAVE_FS_NFS3
349 if (mf->mf_server->fs_version == NFS_VERSION3)
350 mnt_version = AM_MOUNTVERS3;
351 else
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
363 tv.tv_sec = 2;
364 tv.tv_usec = 0;
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 */
377 error = EIO;
378 goto out;
380 if (!nfs_auth) {
381 error = make_nfs_auth();
382 if (error)
383 goto out;
385 client->cl_auth = nfs_auth;
387 dlog("Fetching export list from %s", host);
390 * Fetch the export list
392 tv2.tv_sec = 10;
393 tv2.tv_usec = 0;
394 clnt_stat = clnt_call(client,
395 MOUNTPROC_EXPORT,
396 (XDRPROC_T_TYPE) xdr_void,
398 (XDRPROC_T_TYPE) xdr_exports,
399 (SVC_IN_ARG_TYPE) & exlist,
400 tv2);
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"); */
405 error = EIO;
406 goto out;
410 * Figure out how many exports were returned
412 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
413 n_export++;
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 */
426 ok = TRUE;
427 else
428 ep[j++] = ex;
430 n_export = j;
433 * Sort into order.
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);
454 ep[j] = NULL;
455 } else {
456 k = j;
457 error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
458 mf->mf_server->fs_version);
459 if (error)
460 ep[j] = NULL;
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");
473 error = EINVAL;
474 goto out;
476 ++rfs_dir;
477 for (j = 0; j < n_export; j++) {
478 ex = ep[j];
479 if (ex) {
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)
489 ok = TRUE;
494 * Clean up and exit
496 out:
497 discard_mntlist(mlist);
498 if (ep)
499 XFREE(ep);
500 if (fp)
501 XFREE(fp);
502 if (sock != RPC_ANYSOCK)
503 (void) amu_close(sock);
504 if (client)
505 clnt_destroy(client);
506 if (exlist)
507 xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
508 if (ok)
509 return 0;
510 return error;
515 * Return true if pref is a directory prefix of dir.
517 * XXX TODO:
518 * Does not work if pref is "/".
520 static int
521 directory_prefix(char *pref, char *dir)
523 int len = strlen(pref);
525 if (!NSTREQ(pref, dir, len))
526 return FALSE;
527 if (dir[len] == '/' || dir[len] == '\0')
528 return TRUE;
529 return FALSE;
534 * Unmount a mount tree
536 static int
537 amfs_host_umount(am_node *am, mntfs *mf)
539 mntlist *ml, *mprev;
540 int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
541 int xerror = 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
552 unlock_mntlist();
553 #endif /* MOUNT_TABLE_ON_FILE */
556 * Reverse list...
558 ml = mlist;
559 mprev = NULL;
560 while (ml) {
561 mntlist *ml2 = ml->mnext;
562 ml->mnext = mprev;
563 mprev = ml;
564 ml = ml2;
566 mlist = mprev;
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)) {
574 int error;
575 dlog("amfs_host: unmounts %s", dir);
577 * Unmount "dir"
579 error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
581 * Keep track of errors
583 if (error) {
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)
593 xerror = error;
594 if (error != EBUSY) {
595 errno = error;
596 plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
598 } else {
599 (void) rmdirs(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);
614 if (!xerror) {
616 * Don't log this - it's usually too verbose
617 plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
619 xerror = EBUSY;
622 return xerror;
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.
632 static void
633 amfs_host_umounted(mntfs *mf)
635 char *host;
636 CLIENT *client;
637 enum clnt_stat clnt_stat;
638 struct sockaddr_in sin;
639 int sock = RPC_ANYSOCK;
640 struct timeval tv;
641 u_long mnt_version;
643 if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
644 return;
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");
651 return;
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);
661 #ifdef HAVE_FS_NFS3
662 if (mf->mf_server->fs_version == NFS_VERSION3)
663 mnt_version = AM_MOUNTVERS3;
664 else
665 #endif /* HAVE_FS_NFS3 */
666 mnt_version = MOUNTVERS;
669 * Create a client attached to mountd
671 tv.tv_sec = 10;
672 tv.tv_usec = 0;
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 */
681 goto out;
684 if (!nfs_auth) {
685 if (make_nfs_auth())
686 goto out;
688 client->cl_auth = nfs_auth;
690 dlog("Unmounting all from %s", host);
692 clnt_stat = clnt_call(client,
693 MOUNTPROC_UMNTALL,
694 (XDRPROC_T_TYPE) xdr_void,
696 (XDRPROC_T_TYPE) xdr_void,
698 tv);
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);
703 goto out;
706 out:
707 if (sock != RPC_ANYSOCK)
708 (void) amu_close(sock);
709 if (client)
710 clnt_destroy(client);