amfs_host.c revision 42629
1/* 2 * Copyright (c) 1997-1998 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. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 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. 26 * 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.1.1.1 1998/11/05 02:04:46 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", 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 const char *msg = clnt_sperrno(clnt_stat); 260 plog(XLOG_ERROR, "mountd rpc failed: %s", msg); 261 return EIO; 262 } 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; 270 } 271#ifdef HAVE_FS_NFS3 272 } /* end of "if (nfs_version == NFS_VERSION3)" statement */ 273#endif /* HAVE_FS_NFS3 */ 274 275 /* all is well */ 276 return 0; 277} 278 279 280/* 281 * Scan mount table to see if something already mounted 282 */ 283static int 284already_mounted(mntlist *mlist, char *dir) 285{ 286 mntlist *ml; 287 288 for (ml = mlist; ml; ml = ml->mnext) 289 if (STREQ(ml->mnt->mnt_dir, dir)) 290 return 1; 291 return 0; 292} 293 294 295/* 296 * Mount the export tree from a host 297 */ 298static int 299amfs_host_fmount(mntfs *mf) 300{ 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; 319 320 /* 321 * Read the mount list 322 */ 323 mlist = read_mtab(mf->mf_mount, mnttab_file_name); 324 325#ifdef MOUNT_TABLE_ON_FILE 326 /* 327 * Unlock the mount list 328 */ 329 unlock_mntlist(); 330#endif /* MOUNT_TABLE_ON_FILE */ 331 332 /* 333 * Take a copy of the server hostname, address, and nfs version 334 * to mount version conversion. 335 */ 336 host = mf->mf_server->fs_host; 337 sin = *mf->mf_server->fs_ip; 338 plog(XLOG_INFO, "amfs_host_fmount: NFS version %d", 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; 345 346 /* 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 353 */ 354 tv.tv_sec = 2; 355 tv.tv_usec = 0; 356 357 /* 358 * Create a client attached to mountd 359 */ 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; 370 } 371 if (!nfs_auth) { 372 error = make_nfs_auth(); 373 if (error) 374 goto out; 375 } 376 client->cl_auth = nfs_auth; 377 378#ifdef DEBUG 379 dlog("Fetching export list from %s", host); 380#endif /* DEBUG */ 381 382 /* 383 * Fetch the export list 384 */ 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, 390 0, 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; 400 } 401 402 /* 403 * Figure out how many exports were returned 404 */ 405 for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { 406 /* printf("export %s\n", ex->ex_dir); */ 407 n_export++; 408 } 409 410 /* 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. 414 */ 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; 420 } 421 n_export = j; 422 423 /* 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...). 428 */ 429 qsort(ep, n_export, sizeof(exports), sortfun); 430 431 /* 432 * Allocate an array of filehandles 433 */ 434 fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); 435 436 /* 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. 440 */ 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; 454 } 455 } 456 457 /* 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. 462 */ 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; 468 } 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; 477 } 478 } 479 480 /* 481 * Clean up and exit 482 */ 483out: 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; 498} 499 500 501/* 502 * Return true if pref is a directory prefix of dir. 503 * 504 * XXX TODO: 505 * Does not work if pref is "/". 506 */ 507static int 508directory_prefix(char *pref, char *dir) 509{ 510 int len = strlen(pref); 511 512 if (!NSTREQ(pref, dir, len)) 513 return FALSE; 514 if (dir[len] == '/' || dir[len] == '\0') 515 return TRUE; 516 return FALSE; 517} 518 519 520/* 521 * Unmount a mount tree 522 */ 523static int 524amfs_host_fumount(mntfs *mf) 525{ 526 mntlist *ml, *mprev; 527 int xerror = 0; 528 529 /* 530 * Read the mount list 531 */ 532 mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); 533 534#ifdef MOUNT_TABLE_ON_FILE 535 /* 536 * Unlock the mount list 537 */ 538 unlock_mntlist(); 539#endif /* MOUNT_TABLE_ON_FILE */ 540 541 /* 542 * Reverse list... 543 */ 544 ml = mlist; 545 mprev = 0; 546 while (ml) { 547 mntlist *ml2 = ml->mnext; 548 ml->mnext = mprev; 549 mprev = ml; 550 ml = ml2; 551 } 552 mlist = mprev; 553 554 /* 555 * Unmount all filesystems... 556 */ 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 */ 564 /* 565 * Unmount "dir" 566 */ 567 error = UMOUNT_FS(dir, mnttab_file_name); 568 /* 569 * Keep track of errors 570 */ 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); 577 } 578 } else { 579 (void) rmdirs(dir); 580 } 581 } 582 } 583 584 /* 585 * Throw away mount list 586 */ 587 discard_mntlist(mlist); 588 589 /* 590 * Try to remount, except when we are shutting down. 591 */ 592 if (xerror && amd_state != Finishing) { 593 xerror = amfs_host_fmount(mf); 594 if (!xerror) { 595 /* 596 * Don't log this - it's usually too verbose 597 plog(XLOG_INFO, "Remounted host %s", mf->mf_info); 598 */ 599 xerror = EBUSY; 600 } 601 } 602 return xerror; 603} 604 605 606/* 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. 611 */ 612static void 613amfs_host_umounted(am_node *mp) 614{ 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; 623 624 if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) 625 return; 626 627 /* 628 * Take a copy of the server hostname, address, and NFS version 629 * to mount version conversion. 630 */ 631 host = mf->mf_server->fs_host; 632 sin = *mf->mf_server->fs_ip; 633 plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", 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; 640 641 /* 642 * Create a client attached to mountd 643 */ 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; 655 } 656 657 if (!nfs_auth) { 658 if (make_nfs_auth()) 659 goto out; 660 } 661 client->cl_auth = nfs_auth; 662 663#ifdef DEBUG 664 dlog("Unmounting all from %s", host); 665#endif /* DEBUG */ 666 667 clnt_stat = clnt_call(client, 668 MOUNTPROC_UMNTALL, 669 (XDRPROC_T_TYPE) xdr_void, 670 0, 671 (XDRPROC_T_TYPE) xdr_void, 672 0, 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, clnt_stat); 678 goto out; 679 } 680 681out: 682 if (sock != RPC_ANYSOCK) 683 (void) amu_close(sock); 684 if (client) 685 clnt_destroy(client); 686} 687