amfs_host.c revision 119679
1221431Sjonathan/* 2221431Sjonathan * Copyright (c) 1997-2003 Erez Zadok 3221431Sjonathan * Copyright (c) 1990 Jan-Simon Pendry 4224651Sjonathan * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 5224651Sjonathan * Copyright (c) 1990 The Regents of the University of California. 6224651Sjonathan * All rights reserved. 7224651Sjonathan * 8224989Sjonathan * This code is derived from software contributed to Berkeley by 9224989Sjonathan * Jan-Simon Pendry at Imperial College, London. 10224793Sjonathan * 11224793Sjonathan * Redistribution and use in source and binary forms, with or without 12224793Sjonathan * modification, are permitted provided that the following conditions 13221431Sjonathan * are met: 14264400Simp * 1. Redistributions of source code must retain the above copyright 15221431Sjonathan * notice, this list of conditions and the following disclaimer. 16224651Sjonathan * 2. Redistributions in binary form must reproduce the above copyright 17224651Sjonathan * notice, this list of conditions and the following disclaimer in the 18224651Sjonathan * documentation and/or other materials provided with the distribution. 19224651Sjonathan * 3. All advertising materials mentioning features or use of this software 20224651Sjonathan * must display the following acknowledgment: 21224651Sjonathan * This product includes software developed by the University of 22224651Sjonathan * California, Berkeley and its contributors. 23224651Sjonathan * 4. Neither the name of the University nor the names of its contributors 24224651Sjonathan * may be used to endorse or promote products derived from this software 25224651Sjonathan * without specific prior written permission. 26221431Sjonathan * 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. 38 * 39 * %W% (Berkeley) %G% 40 * 41 * $Id: amfs_host.c,v 1.4.2.6 2002/12/27 22:44:30 ezk Exp $ 42 * 43 */ 44 45/* 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. 51 */ 52 53#ifdef HAVE_CONFIG_H 54# include <config.h> 55#endif /* HAVE_CONFIG_H */ 56#include <am_defs.h> 57#include <amd.h> 58 59static char *amfs_host_match(am_opts *fo); 60static int amfs_host_fmount(mntfs *mf); 61static int amfs_host_fumount(mntfs *mf); 62static int amfs_host_init(mntfs *mf); 63static void amfs_host_umounted(am_node *mp); 64 65/* 66 * Ops structure 67 */ 68am_ops amfs_host_ops = 69{ 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 84}; 85 86 87/* 88 * Determine the mount point: 89 * 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. 96 */ 97static void 98make_mntpt(char *mntpt, const exports ex, const mntfs *mf) 99{ 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); 113} 114 115 116/* 117 * Execute needs the same as NFS plus a helper command 118 */ 119static char * 120amfs_host_match(am_opts *fo) 121{ 122 extern am_ops nfs_ops; 123 124 /* 125 * Make sure rfs is specified to keep nfs_match happy... 126 */ 127 if (!fo->opt_rfs) 128 fo->opt_rfs = "/"; 129 130 return (*nfs_ops.fs_match) (fo); 131} 132 133 134static int 135amfs_host_init(mntfs *mf) 136{ 137 fserver *fs; 138 u_short port; 139 140 if (strchr(mf->mf_info, ':') == 0) 141 return ENOENT; 142 143 /* 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). 152 */ 153 /* 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. 161 */ 162 if ((fs = mf->mf_server)) 163 /* 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. 167 */ 168 (void) nfs_srvr_port(fs, &port, (voidp) mf); 169 170 return 0; 171} 172 173 174static int 175do_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf) 176{ 177 struct stat stb; 178 179#ifdef DEBUG 180 dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir); 181#endif /* DEBUG */ 182 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; 187 } 188 189 return mount_nfs_fh(fhp, dir, fs_name, opts, mf); 190} 191 192 193static int 194sortfun(const voidp x, const voidp y) 195{ 196 exports *a = (exports *) x; 197 exports *b = (exports *) y; 198 199 return strcmp((*a)->ex_dir, (*b)->ex_dir); 200} 201 202 203/* 204 * Get filehandle 205 */ 206static int 207fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version) 208{ 209 struct timeval tv; 210 enum clnt_stat clnt_stat; 211 212 /* 213 * Pick a number, any number... 214 */ 215 tv.tv_sec = 20; 216 tv.tv_usec = 0; 217 218#ifdef DEBUG 219 dlog("Fetching fhandle for %s", dir); 220#endif /* DEBUG */ 221 222 /* 223 * Call the mount daemon on the remote host to 224 * get the filehandle. Use NFS version specific call. 225 */ 226 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; 241 } 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; 248 } 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 plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); 260 return EIO; 261 } 262 /* Check status of filehandle */ 263 if (fhp->v2.fhs_status) { 264 errno = fhp->v2.fhs_status; 265#ifdef DEBUG 266 dlog("fhandle fetch for mount version 1 failed: %m"); 267#endif /* DEBUG */ 268 return errno; 269 } 270#ifdef HAVE_FS_NFS3 271 } /* end of "if (nfs_version == NFS_VERSION3)" statement */ 272#endif /* HAVE_FS_NFS3 */ 273 274 /* all is well */ 275 return 0; 276} 277 278 279/* 280 * Scan mount table to see if something already mounted 281 */ 282static int 283already_mounted(mntlist *mlist, char *dir) 284{ 285 mntlist *ml; 286 287 for (ml = mlist; ml; ml = ml->mnext) 288 if (STREQ(ml->mnt->mnt_dir, dir)) 289 return 1; 290 return 0; 291} 292 293 294/* 295 * Mount the export tree from a host 296 */ 297static int 298amfs_host_fmount(mntfs *mf) 299{ 300 struct timeval tv2; 301 CLIENT *client; 302 enum clnt_stat clnt_stat; 303 int n_export; 304 int j, k; 305 exports exlist = 0, ex; 306 exports *ep = 0; 307 am_nfs_handle_t *fp = 0; 308 char *host; 309 int error = 0; 310 struct sockaddr_in sin; 311 int sock = RPC_ANYSOCK; 312 int ok = FALSE; 313 mntlist *mlist; 314 char fs_name[MAXPATHLEN], *rfs_dir; 315 char mntpt[MAXPATHLEN]; 316 struct timeval tv; 317 u_long mnt_version; 318 319 /* 320 * Read the mount list 321 */ 322 mlist = read_mtab(mf->mf_mount, mnttab_file_name); 323 324#ifdef MOUNT_TABLE_ON_FILE 325 /* 326 * Unlock the mount list 327 */ 328 unlock_mntlist(); 329#endif /* MOUNT_TABLE_ON_FILE */ 330 331 /* 332 * Take a copy of the server hostname, address, and nfs version 333 * to mount version conversion. 334 */ 335 host = mf->mf_server->fs_host; 336 sin = *mf->mf_server->fs_ip; 337 plog(XLOG_INFO, "amfs_host_fmount: NFS version %d", (int) mf->mf_server->fs_version); 338#ifdef HAVE_FS_NFS3 339 if (mf->mf_server->fs_version == NFS_VERSION3) 340 mnt_version = MOUNTVERS3; 341 else 342#endif /* HAVE_FS_NFS3 */ 343 mnt_version = MOUNTVERS; 344 345 /* 346 * The original 10 second per try timeout is WAY too large, especially 347 * if we're only waiting 10 or 20 seconds max for the response. 348 * That would mean we'd try only once in 10 seconds, and we could 349 * lose the transmit or receive packet, and never try again. 350 * A 2-second per try timeout here is much more reasonable. 351 * 09/28/92 Mike Mitchell, mcm@unx.sas.com 352 */ 353 tv.tv_sec = 2; 354 tv.tv_usec = 0; 355 356 /* 357 * Create a client attached to mountd 358 */ 359 client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 360 if (client == NULL) { 361#ifdef HAVE_CLNT_SPCREATEERROR 362 plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 363 host, clnt_spcreateerror("")); 364#else /* not HAVE_CLNT_SPCREATEERROR */ 365 plog(XLOG_ERROR, "get_mount_client failed for %s", host); 366#endif /* not HAVE_CLNT_SPCREATEERROR */ 367 error = EIO; 368 goto out; 369 } 370 if (!nfs_auth) { 371 error = make_nfs_auth(); 372 if (error) 373 goto out; 374 } 375 client->cl_auth = nfs_auth; 376 377#ifdef DEBUG 378 dlog("Fetching export list from %s", host); 379#endif /* DEBUG */ 380 381 /* 382 * Fetch the export list 383 */ 384 tv2.tv_sec = 10; 385 tv2.tv_usec = 0; 386 clnt_stat = clnt_call(client, 387 MOUNTPROC_EXPORT, 388 (XDRPROC_T_TYPE) xdr_void, 389 0, 390 (XDRPROC_T_TYPE) xdr_exports, 391 (SVC_IN_ARG_TYPE) & exlist, 392 tv2); 393 if (clnt_stat != RPC_SUCCESS) { 394 const char *msg = clnt_sperrno(clnt_stat); 395 plog(XLOG_ERROR, "host_fmount rpc failed: %s", msg); 396 /* clnt_perror(client, "rpc"); */ 397 error = EIO; 398 goto out; 399 } 400 401 /* 402 * Figure out how many exports were returned 403 */ 404 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 405 n_export++; 406 } 407 408 /* 409 * Allocate an array of pointers into the list 410 * so that they can be sorted. If the filesystem 411 * is already mounted then ignore it. 412 */ 413 ep = (exports *) xmalloc(n_export * sizeof(exports)); 414 for (j = 0, ex = exlist; ex; ex = ex->ex_next) { 415 make_mntpt(mntpt, ex, mf); 416 if (already_mounted(mlist, mntpt)) 417 /* we have at least one mounted f/s, so don't fail the mount */ 418 ok = TRUE; 419 else 420 ep[j++] = ex; 421 } 422 n_export = j; 423 424 /* 425 * Sort into order. 426 * This way the mounts are done in order down the tree, 427 * instead of any random order returned by the mount 428 * daemon (the protocol doesn't specify...). 429 */ 430 qsort(ep, n_export, sizeof(exports), sortfun); 431 432 /* 433 * Allocate an array of filehandles 434 */ 435 fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); 436 437 /* 438 * Try to obtain filehandles for each directory. 439 * If a fetch fails then just zero out the array 440 * reference but discard the error. 441 */ 442 for (j = k = 0; j < n_export; j++) { 443 /* Check and avoid a duplicated export entry */ 444 if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { 445#ifdef DEBUG 446 dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); 447#endif /* DEBUG */ 448 ep[j] = 0; 449 } else { 450 k = j; 451 error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], 452 mf->mf_server->fs_version); 453 if (error) 454 ep[j] = 0; 455 } 456 } 457 458 /* 459 * Mount each filesystem for which we have a filehandle. 460 * If any of the mounts succeed then mark "ok" and return 461 * error code 0 at the end. If they all fail then return 462 * the last error code. 463 */ 464 strncpy(fs_name, mf->mf_info, sizeof(fs_name)); 465 if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { 466 plog(XLOG_FATAL, "amfs_host_fmount: mf_info has no colon"); 467 error = EINVAL; 468 goto out; 469 } 470 ++rfs_dir; 471 for (j = 0; j < n_export; j++) { 472 ex = ep[j]; 473 if (ex) { 474 strcpy(rfs_dir, ex->ex_dir); 475 make_mntpt(mntpt, ex, mf); 476 if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0) 477 ok = TRUE; 478 } 479 } 480 481 /* 482 * Clean up and exit 483 */ 484out: 485 discard_mntlist(mlist); 486 if (ep) 487 XFREE(ep); 488 if (fp) 489 XFREE(fp); 490 if (sock != RPC_ANYSOCK) 491 (void) amu_close(sock); 492 if (client) 493 clnt_destroy(client); 494 if (exlist) 495 xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); 496 if (ok) 497 return 0; 498 return error; 499} 500 501 502/* 503 * Return true if pref is a directory prefix of dir. 504 * 505 * XXX TODO: 506 * Does not work if pref is "/". 507 */ 508static int 509directory_prefix(char *pref, char *dir) 510{ 511 int len = strlen(pref); 512 513 if (!NSTREQ(pref, dir, len)) 514 return FALSE; 515 if (dir[len] == '/' || dir[len] == '\0') 516 return TRUE; 517 return FALSE; 518} 519 520 521/* 522 * Unmount a mount tree 523 */ 524static int 525amfs_host_fumount(mntfs *mf) 526{ 527 mntlist *ml, *mprev; 528 int xerror = 0; 529 530 /* 531 * Read the mount list 532 */ 533 mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); 534 535#ifdef MOUNT_TABLE_ON_FILE 536 /* 537 * Unlock the mount list 538 */ 539 unlock_mntlist(); 540#endif /* MOUNT_TABLE_ON_FILE */ 541 542 /* 543 * Reverse list... 544 */ 545 ml = mlist; 546 mprev = 0; 547 while (ml) { 548 mntlist *ml2 = ml->mnext; 549 ml->mnext = mprev; 550 mprev = ml; 551 ml = ml2; 552 } 553 mlist = mprev; 554 555 /* 556 * Unmount all filesystems... 557 */ 558 for (ml = mlist; ml && !xerror; ml = ml->mnext) { 559 char *dir = ml->mnt->mnt_dir; 560 if (directory_prefix(mf->mf_mount, dir)) { 561 int error; 562#ifdef DEBUG 563 dlog("amfs_host: unmounts %s", dir); 564#endif /* DEBUG */ 565 /* 566 * Unmount "dir" 567 */ 568 error = UMOUNT_FS(dir, mnttab_file_name); 569 /* 570 * Keep track of errors 571 */ 572 if (error) { 573 if (!xerror) 574 xerror = error; 575 if (error != EBUSY) { 576 errno = error; 577 plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); 578 } 579 } else { 580 (void) rmdirs(dir); 581 } 582 } 583 } 584 585 /* 586 * Throw away mount list 587 */ 588 discard_mntlist(mlist); 589 590 /* 591 * Try to remount, except when we are shutting down. 592 */ 593 if (xerror && amd_state != Finishing) { 594 xerror = amfs_host_fmount(mf); 595 if (!xerror) { 596 /* 597 * Don't log this - it's usually too verbose 598 plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 599 */ 600 xerror = EBUSY; 601 } 602 } 603 return xerror; 604} 605 606 607/* 608 * Tell mountd we're done. 609 * This is not quite right, because we may still 610 * have other filesystems mounted, but the existing 611 * mountd protocol is badly broken anyway. 612 */ 613static void 614amfs_host_umounted(am_node *mp) 615{ 616 mntfs *mf = mp->am_mnt; 617 char *host; 618 CLIENT *client; 619 enum clnt_stat clnt_stat; 620 struct sockaddr_in sin; 621 int sock = RPC_ANYSOCK; 622 struct timeval tv; 623 u_long mnt_version; 624 625 if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) 626 return; 627 628 /* 629 * Take a copy of the server hostname, address, and NFS version 630 * to mount version conversion. 631 */ 632 host = mf->mf_server->fs_host; 633 sin = *mf->mf_server->fs_ip; 634 plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); 635#ifdef HAVE_FS_NFS3 636 if (mf->mf_server->fs_version == NFS_VERSION3) 637 mnt_version = MOUNTVERS3; 638 else 639#endif /* HAVE_FS_NFS3 */ 640 mnt_version = MOUNTVERS; 641 642 /* 643 * Create a client attached to mountd 644 */ 645 tv.tv_sec = 10; 646 tv.tv_usec = 0; 647 client = get_mount_client(host, &sin, &tv, &sock, mnt_version); 648 if (client == NULL) { 649#ifdef HAVE_CLNT_SPCREATEERROR 650 plog(XLOG_ERROR, "get_mount_client failed for %s: %s", 651 host, clnt_spcreateerror("")); 652#else /* not HAVE_CLNT_SPCREATEERROR */ 653 plog(XLOG_ERROR, "get_mount_client failed for %s", host); 654#endif /* not HAVE_CLNT_SPCREATEERROR */ 655 goto out; 656 } 657 658 if (!nfs_auth) { 659 if (make_nfs_auth()) 660 goto out; 661 } 662 client->cl_auth = nfs_auth; 663 664#ifdef DEBUG 665 dlog("Unmounting all from %s", host); 666#endif /* DEBUG */ 667 668 clnt_stat = clnt_call(client, 669 MOUNTPROC_UMNTALL, 670 (XDRPROC_T_TYPE) xdr_void, 671 0, 672 (XDRPROC_T_TYPE) xdr_void, 673 0, 674 tv); 675 if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) { 676 /* RPC_SYSTEMERROR seems to be returned for no good reason ... */ 677 const char *msg = clnt_sperrno(clnt_stat); 678 plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg); 679 goto out; 680 } 681 682out: 683 if (sock != RPC_ANYSOCK) 684 (void) amu_close(sock); 685 if (client) 686 clnt_destroy(client); 687} 688