ops_nfs.c revision 82794
1/* 2 * Copyright (c) 1997-2001 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: ops_nfs.c,v 1.6.2.3 2001/04/14 21:08:22 ezk Exp $ 42 * 43 */ 44 45/* 46 * Network file system 47 */ 48 49#ifdef HAVE_CONFIG_H 50# include <config.h> 51#endif /* HAVE_CONFIG_H */ 52#include <am_defs.h> 53#include <amd.h> 54 55/* 56 * Convert from nfsstat to UN*X error code 57 */ 58#define unx_error(e) ((int)(e)) 59 60/* 61 * FH_TTL is the time a file handle will remain in the cache since 62 * last being used. If the file handle becomes invalid, then it 63 * will be flushed anyway. 64 */ 65#define FH_TTL (5 * 60) /* five minutes */ 66#define FH_TTL_ERROR (30) /* 30 seconds */ 67#define FHID_ALLOC(struct) (++fh_id) 68 69/* 70 * The NFS layer maintains a cache of file handles. 71 * This is *fundamental* to the implementation and 72 * also allows quick remounting when a filesystem 73 * is accessed soon after timing out. 74 * 75 * The NFS server layer knows to flush this cache 76 * when a server goes down so avoiding stale handles. 77 * 78 * Each cache entry keeps a hard reference to 79 * the corresponding server. This ensures that 80 * the server keepalive information is maintained. 81 * 82 * The copy of the sockaddr_in here is taken so 83 * that the port can be twiddled to talk to mountd 84 * instead of portmap or the NFS server as used 85 * elsewhere. 86 * The port# is flushed if a server goes down. 87 * The IP address is never flushed - we assume 88 * that the address of a mounted machine never 89 * changes. If it does, then you have other 90 * problems... 91 */ 92typedef struct fh_cache fh_cache; 93struct fh_cache { 94 qelem fh_q; /* List header */ 95 voidp fh_wchan; /* Wait channel */ 96 int fh_error; /* Valid data? */ 97 int fh_id; /* Unique id */ 98 int fh_cid; /* Callout id */ 99 u_long fh_nfs_version; /* highest NFS version on host */ 100 am_nfs_handle_t fh_nfs_handle; /* Handle on filesystem */ 101 struct sockaddr_in fh_sin; /* Address of mountd */ 102 fserver *fh_fs; /* Server holding filesystem */ 103 char *fh_path; /* Filesystem on host */ 104}; 105 106/* forward definitions */ 107static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan); 108static int fh_id = 0; 109 110/* globals */ 111AUTH *nfs_auth; 112qelem fh_head = {&fh_head, &fh_head}; 113 114/* 115 * Network file system operations 116 */ 117am_ops nfs_ops = 118{ 119 "nfs", 120 nfs_match, 121 nfs_init, 122 amfs_auto_fmount, 123 nfs_fmount, 124 amfs_auto_fumount, 125 nfs_fumount, 126 amfs_error_lookuppn, 127 amfs_error_readdir, 128 0, /* nfs_readlink */ 129 0, /* nfs_mounted */ 130 nfs_umounted, 131 find_nfs_srvr, 132 FS_MKMNT | FS_BACKGROUND | FS_AMQINFO 133}; 134 135 136static fh_cache * 137find_nfs_fhandle_cache(voidp idv, int done) 138{ 139 fh_cache *fp, *fp2 = 0; 140 int id = (long) idv; /* for 64-bit archs */ 141 142 ITER(fp, fh_cache, &fh_head) { 143 if (fp->fh_id == id) { 144 fp2 = fp; 145 break; 146 } 147 } 148 149#ifdef DEBUG 150 if (fp2) { 151 dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path); 152 } else { 153 dlog("fh cache search failed"); 154 } 155#endif /* DEBUG */ 156 157 if (fp2 && !done) { 158 fp2->fh_error = ETIMEDOUT; 159 return 0; 160 } 161 162 return fp2; 163} 164 165 166/* 167 * Called when a filehandle appears 168 */ 169static void 170got_nfs_fh(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done) 171{ 172 fh_cache *fp; 173 174 fp = find_nfs_fhandle_cache(idv, done); 175 if (!fp) 176 return; 177 178 /* 179 * retrieve the correct RPC reply for the file handle, based on the 180 * NFS protocol version. 181 */ 182#ifdef HAVE_FS_NFS3 183 if (fp->fh_nfs_version == NFS_VERSION3) 184 fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v3, 185 (XDRPROC_T_TYPE) xdr_mountres3); 186 else 187#endif /* HAVE_FS_NFS3 */ 188 fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v2, 189 (XDRPROC_T_TYPE) xdr_fhstatus); 190 191 if (!fp->fh_error) { 192#ifdef DEBUG 193 dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 194#endif /* DEBUG */ 195 196 /* 197 * Wakeup anything sleeping on this filehandle 198 */ 199 if (fp->fh_wchan) { 200#ifdef DEBUG 201 dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); 202#endif /* DEBUG */ 203 wakeup(fp->fh_wchan); 204 } 205 } 206} 207 208 209void 210flush_nfs_fhandle_cache(fserver *fs) 211{ 212 fh_cache *fp; 213 214 ITER(fp, fh_cache, &fh_head) { 215 if (fp->fh_fs == fs || fs == 0) { 216 fp->fh_sin.sin_port = (u_short) 0; 217 fp->fh_error = -1; 218 } 219 } 220} 221 222 223static void 224discard_fh(voidp v) 225{ 226 fh_cache *fp = v; 227 228 rem_que(&fp->fh_q); 229 if (fp->fh_fs) { 230#ifdef DEBUG 231 dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 232#endif /* DEBUG */ 233 free_srvr(fp->fh_fs); 234 } 235 if (fp->fh_path) 236 XFREE(fp->fh_path); 237 XFREE(fp); 238} 239 240 241/* 242 * Determine the file handle for a node 243 */ 244static int 245prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp wchan) 246{ 247 fh_cache *fp, *fp_save = 0; 248 int error; 249 int reuse_id = FALSE; 250 251#ifdef DEBUG 252 dlog("Searching cache for %s:%s", fs->fs_host, path); 253#endif /* DEBUG */ 254 255 /* 256 * First search the cache 257 */ 258 ITER(fp, fh_cache, &fh_head) { 259 if (fs == fp->fh_fs && STREQ(path, fp->fh_path)) { 260 switch (fp->fh_error) { 261 case 0: 262 plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version); 263#ifdef HAVE_FS_NFS3 264 if (fp->fh_nfs_version == NFS_VERSION3) 265 error = fp->fh_error = unx_error(fp->fh_nfs_handle.v3.fhs_status); 266 else 267#endif /* HAVE_FS_NFS3 */ 268 error = fp->fh_error = unx_error(fp->fh_nfs_handle.v2.fhs_status); 269 if (error == 0) { 270 if (fhbuf) { 271#ifdef HAVE_FS_NFS3 272 if (fp->fh_nfs_version == NFS_VERSION3) 273 memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3), 274 sizeof(fp->fh_nfs_handle.v3)); 275 else 276#endif /* HAVE_FS_NFS3 */ 277 memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2), 278 sizeof(fp->fh_nfs_handle.v2)); 279 } 280 if (fp->fh_cid) 281 untimeout(fp->fh_cid); 282 fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp); 283 } else if (error == EACCES) { 284 /* 285 * Now decode the file handle return code. 286 */ 287 plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"", 288 fs->fs_host, path); 289 } else { 290 errno = error; /* XXX */ 291 plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m", 292 fs->fs_host, path); 293 } 294 295 /* 296 * The error was returned from the remote mount daemon. 297 * Policy: this error will be cached for now... 298 */ 299 return error; 300 301 case -1: 302 /* 303 * Still thinking about it, but we can re-use. 304 */ 305 fp_save = fp; 306 reuse_id = TRUE; 307 break; 308 309 default: 310 /* 311 * Return the error. 312 * Policy: make sure we recompute if required again 313 * in case this was caused by a network failure. 314 * This can thrash mountd's though... If you find 315 * your mountd going slowly then: 316 * 1. Add a fork() loop to main. 317 * 2. Remove the call to innetgr() and don't use 318 * netgroups, especially if you don't use YP. 319 */ 320 error = fp->fh_error; 321 fp->fh_error = -1; 322 return error; 323 } 324 break; 325 } 326 } 327 328 /* 329 * Not in cache 330 */ 331 if (fp_save) { 332 fp = fp_save; 333 /* 334 * Re-use existing slot 335 */ 336 untimeout(fp->fh_cid); 337 free_srvr(fp->fh_fs); 338 XFREE(fp->fh_path); 339 } else { 340 fp = ALLOC(struct fh_cache); 341 memset((voidp) fp, 0, sizeof(struct fh_cache)); 342 ins_que(&fp->fh_q, &fh_head); 343 } 344 if (!reuse_id) 345 fp->fh_id = FHID_ALLOC(struct ); 346 fp->fh_wchan = wchan; 347 fp->fh_error = -1; 348 fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp); 349 350 /* 351 * if fs->fs_ip is null, remote server is probably down. 352 */ 353 if (!fs->fs_ip) { 354 /* Mark the fileserver down and invalid again */ 355 fs->fs_flags &= ~FSF_VALID; 356 fs->fs_flags |= FSF_DOWN; 357 error = AM_ERRNO_HOST_DOWN; 358 return error; 359 } 360 361 /* 362 * If the address has changed then don't try to re-use the 363 * port information 364 */ 365 if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { 366 fp->fh_sin = *fs->fs_ip; 367 fp->fh_sin.sin_port = 0; 368 fp->fh_nfs_version = fs->fs_version; 369 } 370 fp->fh_fs = dup_srvr(fs); 371 fp->fh_path = strdup(path); 372 373 error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan); 374 if (error) { 375 /* 376 * Local error - cache for a short period 377 * just to prevent thrashing. 378 */ 379 untimeout(fp->fh_cid); 380 fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, 381 discard_fh, (voidp) fp); 382 fp->fh_error = error; 383 } else { 384 error = fp->fh_error; 385 } 386 387 return error; 388} 389 390 391int 392make_nfs_auth(void) 393{ 394 AUTH_CREATE_GIDLIST_TYPE group_wheel = 0; 395 396 /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */ 397 398#ifdef HAVE_TRANSPORT_TYPE_TLI 399 if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { 400 plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); 401 nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel); 402 } else { 403 nfs_auth = authsys_create_default(); 404 } 405#else /* not HAVE_TRANSPORT_TYPE_TLI */ 406 if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { 407 plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); 408 nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel); 409 } else { 410 nfs_auth = authunix_create_default(); 411 } 412#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 413 414 if (!nfs_auth) 415 return ENOBUFS; 416 417 return 0; 418} 419 420 421static int 422call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan) 423{ 424 struct rpc_msg mnt_msg; 425 int len; 426 char iobuf[8192]; 427 int error; 428 u_long mnt_version; 429 430 if (!nfs_auth) { 431 error = make_nfs_auth(); 432 if (error) 433 return error; 434 } 435 436 if (fp->fh_sin.sin_port == 0) { 437 u_short port; 438 error = nfs_srvr_port(fp->fh_fs, &port, wchan); 439 if (error) 440 return error; 441 fp->fh_sin.sin_port = port; 442 } 443 444 /* find the right version of the mount protocol */ 445#ifdef HAVE_FS_NFS3 446 if (fp->fh_nfs_version == NFS_VERSION3) 447 mnt_version = MOUNTVERS3; 448 else 449#endif /* HAVE_FS_NFS3 */ 450 mnt_version = MOUNTVERS; 451 plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d", 452 (int) fp->fh_nfs_version, (int) mnt_version); 453 454 rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL); 455 len = make_rpc_packet(iobuf, 456 sizeof(iobuf), 457 proc, 458 &mnt_msg, 459 (voidp) &fp->fh_path, 460 (XDRPROC_T_TYPE) xdr_nfspath, 461 nfs_auth); 462 463 if (len > 0) { 464 error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), 465 (voidp) iobuf, 466 len, 467 &fp->fh_sin, 468 &fp->fh_sin, 469 (voidp) ((long) fp->fh_id), /* for 64-bit archs */ 470 f); 471 } else { 472 error = -len; 473 } 474 475/* 476 * It may be the case that we're sending to the wrong MOUNTD port. This 477 * occurs if mountd is restarted on the server after the port has been 478 * looked up and stored in the filehandle cache somewhere. The correct 479 * solution, if we're going to cache port numbers is to catch the ICMP 480 * port unreachable reply from the server and cause the portmap request 481 * to be redone. The quick solution here is to invalidate the MOUNTD 482 * port. 483 */ 484 fp->fh_sin.sin_port = 0; 485 486 return error; 487} 488 489 490/* 491 * NFS needs the local filesystem, remote filesystem 492 * remote hostname. 493 * Local filesystem defaults to remote and vice-versa. 494 */ 495char * 496nfs_match(am_opts *fo) 497{ 498 char *xmtab; 499 500 if (fo->opt_fs && !fo->opt_rfs) 501 fo->opt_rfs = fo->opt_fs; 502 if (!fo->opt_rfs) { 503 plog(XLOG_USER, "nfs: no remote filesystem specified"); 504 return NULL; 505 } 506 if (!fo->opt_rhost) { 507 plog(XLOG_USER, "nfs: no remote host specified"); 508 return NULL; 509 } 510 511 /* 512 * Determine magic cookie to put in mtab 513 */ 514 xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2); 515 sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs); 516#ifdef DEBUG 517 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 518 fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 519#endif /* DEBUG */ 520 521 return xmtab; 522} 523 524 525/* 526 * Initialize am structure for nfs 527 */ 528int 529nfs_init(mntfs *mf) 530{ 531 int error; 532 am_nfs_handle_t fhs; 533 char *colon; 534 535 if (mf->mf_private) 536 return 0; 537 538 colon = strchr(mf->mf_info, ':'); 539 if (colon == 0) 540 return ENOENT; 541 542 error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, (voidp) mf); 543 if (!error) { 544 mf->mf_private = (voidp) ALLOC(am_nfs_handle_t); 545 mf->mf_prfree = (void (*)(voidp)) free; 546 memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs)); 547 } 548 return error; 549} 550 551 552int 553mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf) 554{ 555 MTYPE_TYPE type; 556 char *colon; 557 char *xopts; 558 char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; 559 fserver *fs = mf->mf_server; 560 u_long nfs_version = fs->fs_version; 561 char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */ 562 int error; 563 int genflags; 564 int retry; 565 mntent_t mnt; 566 nfs_args_t nfs_args; 567 568 /* 569 * Extract HOST name to give to kernel. 570 * Some systems like osf1/aix3/bsd44 variants may need old code 571 * for NFS_ARGS_NEEDS_PATH. 572 */ 573 if (!(colon = strchr(fs_name, ':'))) 574 return ENOENT; 575#ifdef MOUNT_TABLE_ON_FILE 576 *colon = '\0'; 577#endif /* MOUNT_TABLE_ON_FILE */ 578 strncpy(host, fs_name, sizeof(host)); 579#ifdef MOUNT_TABLE_ON_FILE 580 *colon = ':'; 581#endif /* MOUNT_TABLE_ON_FILE */ 582#ifdef MAXHOSTNAMELEN 583 /* most kernels have a name length restriction */ 584 if (strlen(host) >= MAXHOSTNAMELEN) 585 strcpy(host + MAXHOSTNAMELEN - 3, ".."); 586#endif /* MAXHOSTNAMELEN */ 587 588 if (mf->mf_remopts && *mf->mf_remopts && 589 !islocalnet(fs->fs_ip->sin_addr.s_addr)) { 590 plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts); 591 xopts = strdup(mf->mf_remopts); 592 } else { 593 xopts = strdup(opts); 594 } 595 596 memset((voidp) &mnt, 0, sizeof(mnt)); 597 mnt.mnt_dir = dir; 598 mnt.mnt_fsname = fs_name; 599 mnt.mnt_opts = xopts; 600 601 /* 602 * Set mount types accordingly 603 */ 604#ifndef HAVE_FS_NFS3 605 type = MOUNT_TYPE_NFS; 606 mnt.mnt_type = MNTTAB_TYPE_NFS; 607#else /* HAVE_FS_NFS3 */ 608 if (nfs_version == NFS_VERSION3) { 609 type = MOUNT_TYPE_NFS3; 610 /* 611 * Systems that include the mount table "vers" option generally do not 612 * set the mnttab entry to "nfs3", but to "nfs" and then they set 613 * "vers=3". Setting it to "nfs3" works, but it may break some things 614 * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix). 615 * So on those systems, set it to "nfs". 616 * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h). 617 */ 618# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) 619 mnt.mnt_type = MNTTAB_TYPE_NFS; 620# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ 621 mnt.mnt_type = MNTTAB_TYPE_NFS3; 622# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ 623 } else { 624 type = MOUNT_TYPE_NFS; 625 mnt.mnt_type = MNTTAB_TYPE_NFS; 626 } 627#endif /* HAVE_FS_NFS3 */ 628 plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version); 629#if defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI) 630 plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto); 631#endif /* defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI) */ 632 633 retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); 634 if (retry <= 0) 635 retry = 1; /* XXX */ 636 637 genflags = compute_mount_flags(&mnt); 638 639 /* setup the many fields and flags within nfs_args */ 640#ifdef HAVE_TRANSPORT_TYPE_TLI 641 compute_nfs_args(&nfs_args, 642 &mnt, 643 genflags, 644 NULL, /* struct netconfig *nfsncp */ 645 fs->fs_ip, 646 nfs_version, 647 nfs_proto, 648 fhp, 649 host, 650 fs_name); 651#else /* not HAVE_TRANSPORT_TYPE_TLI */ 652 compute_nfs_args(&nfs_args, 653 &mnt, 654 genflags, 655 fs->fs_ip, 656 nfs_version, 657 nfs_proto, 658 fhp, 659 host, 660 fs_name); 661#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 662 663 /* finally call the mounting function */ 664#ifdef DEBUG 665 amuDebug(D_TRACE) { 666 print_nfs_args(&nfs_args, nfs_version); 667 plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags); 668 } 669#endif /* DEBUG */ 670 error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 671 nfs_version, nfs_proto, mnttab_file_name); 672 XFREE(xopts); 673 674#ifdef HAVE_TRANSPORT_TYPE_TLI 675 free_knetconfig(nfs_args.knconf); 676 if (nfs_args.addr) 677 XFREE(nfs_args.addr); /* allocated in compute_nfs_args() */ 678#endif /* HAVE_TRANSPORT_TYPE_TLI */ 679 680 return error; 681} 682 683 684static int 685mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf) 686{ 687 if (!mf->mf_private) { 688 plog(XLOG_ERROR, "Missing filehandle for %s", fs_name); 689 return EINVAL; 690 } 691 692 return mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, dir, fs_name, opts, mf); 693} 694 695 696int 697nfs_fmount(mntfs *mf) 698{ 699 int error = 0; 700 701 error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf); 702 703#ifdef DEBUG 704 if (error) { 705 errno = error; 706 dlog("mount_nfs: %m"); 707 } 708#endif /* DEBUG */ 709 710 return error; 711} 712 713 714int 715nfs_fumount(mntfs *mf) 716{ 717 int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name); 718 719 /* 720 * Here is some code to unmount 'restarted' file systems. 721 * The restarted file systems are marked as 'nfs', not 722 * 'host', so we only have the map information for the 723 * the top-level mount. The unmount will fail (EBUSY) 724 * if there are anything else from the NFS server mounted 725 * below the mount-point. This code checks to see if there 726 * is anything mounted with the same prefix as the 727 * file system to be unmounted ("/a/b/c" when unmounting "/a/b"). 728 * If there is, and it is a 'restarted' file system, we unmount 729 * it. 730 * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93 731 */ 732 if (error == EBUSY) { 733 mntfs *new_mf; 734 int len = strlen(mf->mf_mount); 735 int didsome = 0; 736 737 ITER(new_mf, mntfs, &mfhead) { 738 if (new_mf->mf_ops != mf->mf_ops || 739 new_mf->mf_refc > 1 || 740 mf == new_mf || 741 ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART))) 742 continue; 743 744 if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) && 745 new_mf->mf_mount[len] == '/') { 746 UMOUNT_FS(new_mf->mf_mount, mnttab_file_name); 747 didsome = 1; 748 } 749 } 750 if (didsome) 751 error = UMOUNT_FS(mf->mf_mount, mnttab_file_name); 752 } 753 if (error) 754 return error; 755 756 return 0; 757} 758 759 760void 761nfs_umounted(am_node *mp) 762{ 763 /* 764 * Don't bother to inform remote mountd that we are finished. Until a 765 * full track of filehandles is maintained the mountd unmount callback 766 * cannot be done correctly anyway... 767 */ 768 mntfs *mf = mp->am_mnt; 769 fserver *fs; 770 char *colon, *path; 771 772 if (mf->mf_error || mf->mf_refc > 1) 773 return; 774 775 fs = mf->mf_server; 776 777 /* 778 * Call the mount daemon on the server to announce that we are not using 779 * the fs any more. 780 * 781 * This is *wrong*. The mountd should be called when the fhandle is 782 * flushed from the cache, and a reference held to the cached entry while 783 * the fs is mounted... 784 */ 785 colon = path = strchr(mf->mf_info, ':'); 786 if (fs && colon) { 787 fh_cache f; 788 789#ifdef DEBUG 790 dlog("calling mountd for %s", mf->mf_info); 791#endif /* DEBUG */ 792 *path++ = '\0'; 793 f.fh_path = path; 794 f.fh_sin = *fs->fs_ip; 795 f.fh_sin.sin_port = (u_short) 0; 796 f.fh_nfs_version = fs->fs_version; 797 f.fh_fs = fs; 798 f.fh_id = 0; 799 f.fh_error = 0; 800 prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, (voidp) mf); 801 call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0); 802 *colon = ':'; 803 } 804} 805