1 /* $OpenBSD: nfs_ops.c,v 1.19 2009/10/27 23:59:50 deraadt Exp $ */ 2 3 /*- 4 * Copyright (c) 1990 Jan-Simon Pendry 5 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 6 * Copyright (c) 1990, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Jan-Simon Pendry at Imperial College, London. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include "am.h" 38 #include <sys/stat.h> 39 40 #ifdef HAS_NFS 41 42 #define NFS 43 #define NFSCLIENT 44 #ifdef NFS_3 45 typedef nfs_fh fhandle_t; 46 #endif /* NFS_3 */ 47 #ifdef NFS_HDR 48 #include NFS_HDR 49 #endif /* NFS_HDR */ 50 #include "mount.h" 51 52 /* 53 * Network file system 54 */ 55 56 /* 57 * Convert from nfsstat to UN*X error code 58 */ 59 #define unx_error(e) ((int)(e)) 60 61 /* 62 * The NFS layer maintains a cache of file handles. 63 * This is *fundamental* to the implementation and 64 * also allows quick remounting when a filesystem 65 * is accessed soon after timing out. 66 * 67 * The NFS server layer knows to flush this cache 68 * when a server goes down so avoiding stale handles. 69 * 70 * Each cache entry keeps a hard reference to 71 * the corresponding server. This ensures that 72 * the server keepalive information is maintained. 73 * 74 * The copy of the sockaddr_in here is taken so 75 * that the port can be twiddled to talk to mountd 76 * instead of portmap or the NFS server as used 77 * elsewhere. 78 * The port# is flushed if a server goes down. 79 * The IP address is never flushed - we assume 80 * that the address of a mounted machine never 81 * changes. If it does, then you have other 82 * problems... 83 */ 84 typedef struct fh_cache fh_cache; 85 struct fh_cache { 86 qelem fh_q; /* List header */ 87 void *fh_wchan; /* Wait channel */ 88 int fh_error; /* Valid data? */ 89 int fh_id; /* Unique id */ 90 int fh_cid; /* Callout id */ 91 fhstatus fh_handle; /* Handle on filesystem */ 92 struct sockaddr_in fh_sin; /* Address of mountd */ 93 fserver *fh_fs; /* Server holding filesystem */ 94 char *fh_path; /* Filesystem on host */ 95 }; 96 97 /* 98 * FH_TTL is the time a file handle will remain in the cache since 99 * last being used. If the file handle becomes invalid, then it 100 * will be flushed anyway. 101 */ 102 #define FH_TTL (5 * 60) /* five minutes */ 103 #define FH_TTL_ERROR (30) /* 30 seconds */ 104 105 static int fh_id = 0; 106 #define FHID_ALLOC() (++fh_id) 107 extern qelem fh_head; 108 qelem fh_head = { &fh_head, &fh_head }; 109 110 static int call_mountd(fh_cache*, unsigned long, fwd_fun, void *); 111 112 AUTH *nfs_auth; 113 114 static fh_cache * 115 find_nfs_fhandle_cache(void *idv, int done) 116 { 117 fh_cache *fp, *fp2 = 0; 118 /* XXX EVIL XXX */ 119 int id = (int) ((long)idv); 120 121 ITER(fp, fh_cache, &fh_head) { 122 if (fp->fh_id == id) { 123 fp2 = fp; 124 break; 125 } 126 } 127 128 #ifdef DEBUG 129 if (fp2) { 130 dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path); 131 } else { 132 dlog("fh cache search failed"); 133 } 134 #endif /* DEBUG */ 135 136 if (fp2 && !done) { 137 fp2->fh_error = ETIMEDOUT; 138 return 0; 139 } 140 141 return fp2; 142 } 143 144 /* 145 * Called when a filehandle appears 146 */ 147 static void 148 got_nfs_fh(void *pkt, int len, struct sockaddr_in *sa, 149 struct sockaddr_in *ia, void *idv, int done) 150 { 151 fh_cache *fp = find_nfs_fhandle_cache(idv, done); 152 if (fp) { 153 #if NFS_PROTOCOL_VERSION >= 3 154 fp->fh_handle.fhs_vers = MOUNTVERS; 155 #endif 156 fp->fh_error = pickup_rpc_reply(pkt, len, (void *)&fp->fh_handle, xdr_fhstatus); 157 if (!fp->fh_error) { 158 #ifdef DEBUG 159 dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 160 #endif /* DEBUG */ 161 /* 162 * Wakeup anything sleeping on this filehandle 163 */ 164 if (fp->fh_wchan) { 165 #ifdef DEBUG 166 dlog("Calling wakeup on %#x", fp->fh_wchan); 167 #endif /* DEBUG */ 168 wakeup(fp->fh_wchan); 169 } 170 } 171 } 172 } 173 174 void 175 flush_nfs_fhandle_cache(fserver *fs) 176 { 177 fh_cache *fp; 178 ITER(fp, fh_cache, &fh_head) { 179 if (fp->fh_fs == fs || fs == 0) { 180 fp->fh_sin.sin_port = (u_short) 0; 181 fp->fh_error = -1; 182 } 183 } 184 } 185 186 static void 187 discard_fh(fh_cache *fp) 188 { 189 rem_que(&fp->fh_q); 190 #ifdef DEBUG 191 dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 192 #endif /* DEBUG */ 193 free_srvr(fp->fh_fs); 194 free((void *)fp->fh_path); 195 free((void *)fp); 196 } 197 198 /* 199 * Determine the file handle for a node 200 */ 201 static int 202 prime_nfs_fhandle_cache(char *path, fserver *fs, fhstatus *fhbuf, void *wchan) 203 { 204 fh_cache *fp, *fp_save = 0; 205 int error; 206 int reuse_id = FALSE; 207 208 #ifdef DEBUG 209 dlog("Searching cache for %s:%s", fs->fs_host, path); 210 #endif /* DEBUG */ 211 212 /* 213 * First search the cache 214 */ 215 ITER(fp, fh_cache, &fh_head) { 216 if (fs == fp->fh_fs && strcmp(path, fp->fh_path) == 0) { 217 switch (fp->fh_error) { 218 case 0: 219 error = fp->fh_error = unx_error(fp->fh_handle.fhs_stat); 220 if (error == 0) { 221 if (fhbuf) 222 bcopy((void *)&fp->fh_handle, (void *)fhbuf, 223 sizeof(fp->fh_handle)); 224 if (fp->fh_cid) 225 untimeout(fp->fh_cid); 226 fp->fh_cid = timeout(FH_TTL, discard_fh, (void *)fp); 227 } else if (error == EACCES) { 228 /* 229 * Now decode the file handle return code. 230 */ 231 plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"", 232 fs->fs_host, path); 233 } else { 234 errno = error; /* XXX */ 235 plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m", 236 fs->fs_host, path); 237 } 238 239 /* 240 * The error was returned from the remote mount daemon. 241 * Policy: this error will be cached for now... 242 */ 243 return error; 244 245 case -1: 246 /* 247 * Still thinking about it, but we can re-use. 248 */ 249 fp_save = fp; 250 reuse_id = TRUE; 251 break; 252 253 default: 254 /* 255 * Return the error. 256 * Policy: make sure we recompute if required again 257 * in case this was caused by a network failure. 258 * This can thrash mountd's though... If you find 259 * your mountd going slowly then: 260 * 1. Add a fork() loop to main. 261 * 2. Remove the call to innetgr() and don't use 262 * netgroups, especially if you don't use YP. 263 */ 264 error = fp->fh_error; 265 fp->fh_error = -1; 266 return error; 267 } 268 break; 269 } 270 } 271 272 /* 273 * Not in cache 274 */ 275 if (fp_save) { 276 fp = fp_save; 277 /* 278 * Re-use existing slot 279 */ 280 untimeout(fp->fh_cid); 281 free_srvr(fp->fh_fs); 282 free(fp->fh_path); 283 } else { 284 fp = ALLOC(fh_cache); 285 bzero((void *)fp, sizeof(*fp)); 286 ins_que(&fp->fh_q, &fh_head); 287 } 288 if (!reuse_id) 289 fp->fh_id = FHID_ALLOC(); 290 fp->fh_wchan = wchan; 291 fp->fh_error = -1; 292 fp->fh_cid = timeout(FH_TTL, discard_fh, (void *)fp); 293 294 /* 295 * If the address has changed then don't try to re-use the 296 * port information 297 */ 298 if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { 299 fp->fh_sin = *fs->fs_ip; 300 fp->fh_sin.sin_port = 0; 301 } 302 fp->fh_fs = dup_srvr(fs); 303 fp->fh_path = strdup(path); 304 305 error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan); 306 if (error) { 307 /* 308 * Local error - cache for a short period 309 * just to prevent thrashing. 310 */ 311 untimeout(fp->fh_cid); 312 fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, 313 discard_fh, (void *)fp); 314 fp->fh_error = error; 315 } else { 316 error = fp->fh_error; 317 } 318 return error; 319 } 320 321 int 322 make_nfs_auth(void) 323 { 324 #ifdef HAS_NFS_QUALIFIED_NAMES 325 /* 326 * From: Chris Metcalf <metcalf@masala.lcs.mit.edu> 327 * Use hostd, not just hostname. Note that uids 328 * and gids and the gidlist are type *int* and not the 329 * system uid_t and gid_t types. 330 */ 331 static int group_wheel = 0; 332 nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel); 333 #else 334 nfs_auth = authunix_create_default(); 335 #endif 336 if (!nfs_auth) 337 return ENOBUFS; 338 return 0; 339 } 340 341 static int 342 call_mountd(fh_cache *fp, u_long proc, fwd_fun f, void *wchan) 343 { 344 struct rpc_msg mnt_msg; 345 int len; 346 char iobuf[8192]; 347 int error; 348 349 if (!nfs_auth) { 350 error = make_nfs_auth(); 351 if (error) 352 return error; 353 } 354 355 if (fp->fh_sin.sin_port == 0) { 356 u_short port; 357 error = nfs_srvr_port(fp->fh_fs, &port, wchan); 358 if (error) 359 return error; 360 fp->fh_sin.sin_port = port; 361 } 362 363 rpc_msg_init(&mnt_msg, MOUNTPROG, MOUNTVERS, (unsigned long) 0); 364 len = make_rpc_packet(iobuf, sizeof(iobuf), proc, 365 &mnt_msg, (void *)&fp->fh_path, xdr_nfspath, nfs_auth); 366 367 /* 368 * XXX EVIL! We cast fh_id to a pointer, then back to an int 369 * XXX later. 370 */ 371 if (len > 0) { 372 error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), 373 (void *)iobuf, len, &fp->fh_sin, &fp->fh_sin, (void *)((long)fp->fh_id), f); 374 } else { 375 error = -len; 376 } 377 /* 378 * It may be the case that we're sending to the wrong MOUNTD port. This 379 * occurs if mountd is restarted on the server after the port has been 380 * looked up and stored in the filehandle cache somewhere. The correct 381 * solution, if we're going to cache port numbers is to catch the ICMP 382 * port unreachable reply from the server and cause the portmap request 383 * to be redone. The quick solution here is to invalidate the MOUNTD 384 * port. 385 */ 386 fp->fh_sin.sin_port = 0; 387 388 return error; 389 } 390 391 /*-------------------------------------------------------------------------*/ 392 393 /* 394 * NFS needs the local filesystem, remote filesystem 395 * remote hostname. 396 * Local filesystem defaults to remote and vice-versa. 397 */ 398 static char * 399 nfs_match(am_opts *fo) 400 { 401 char *xmtab; 402 if (fo->opt_fs && !fo->opt_rfs) 403 fo->opt_rfs = fo->opt_fs; 404 if (!fo->opt_rfs) { 405 plog(XLOG_USER, "nfs: no remote filesystem specified"); 406 return FALSE; 407 } 408 if (!fo->opt_rhost) { 409 plog(XLOG_USER, "nfs: no remote host specified"); 410 return FALSE; 411 } 412 /* 413 * Determine magic cookie to put in mtab 414 */ 415 xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2); 416 snprintf(xmtab, strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2, 417 "%s:%s", fo->opt_rhost, fo->opt_rfs); 418 #ifdef DEBUG 419 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 420 fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 421 #endif /* DEBUG */ 422 423 return xmtab; 424 } 425 426 /* 427 * Initialise am structure for nfs 428 */ 429 static int 430 nfs_init(mntfs *mf) 431 { 432 if (!mf->mf_private) { 433 int error; 434 fhstatus fhs; 435 436 char *colon = strchr(mf->mf_info, ':'); 437 if (colon == 0) 438 return ENOENT; 439 440 error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, (void *)mf); 441 if (!error) { 442 mf->mf_private = (void *)ALLOC(fhstatus); 443 mf->mf_prfree = (void (*)()) free; 444 bcopy((void *)&fhs, mf->mf_private, sizeof(fhs)); 445 } 446 return error; 447 } 448 449 return 0; 450 } 451 452 int 453 mount_nfs_fh(fhstatus *fhp, char *dir, char *fs_name, char *opts, 454 mntfs *mf) 455 { 456 struct nfs_args nfs_args; 457 struct mntent mnt; 458 int retry; 459 char *colon; 460 /*char *path;*/ 461 char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; 462 fserver *fs = mf->mf_server; 463 int flags; 464 char *xopts; 465 int error; 466 #ifdef notdef 467 unsigned short port; 468 #endif /* notdef */ 469 470 MTYPE_TYPE type = MOUNT_TYPE_NFS; 471 472 bzero((void *)&nfs_args, sizeof(nfs_args)); /* Paranoid */ 473 474 /* 475 * Extract host name to give to kernel 476 */ 477 if (!(colon = strchr(fs_name, ':'))) 478 return ENOENT; 479 #ifndef NFS_ARGS_NEEDS_PATH 480 *colon = '\0'; 481 #endif 482 strlcpy(host, fs_name, sizeof(host)); 483 #ifndef NFS_ARGS_NEEDS_PATH 484 *colon = ':'; 485 #endif /* NFS_ARGS_NEEDS_PATH */ 486 /*path = colon + 1;*/ 487 488 if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr)) 489 xopts = strdup(mf->mf_remopts); 490 else 491 xopts = strdup(opts); 492 493 bzero((void *)&nfs_args, sizeof(nfs_args)); 494 495 mnt.mnt_dir = dir; 496 mnt.mnt_fsname = fs_name; 497 mnt.mnt_type = MTAB_TYPE_NFS; 498 mnt.mnt_opts = xopts; 499 mnt.mnt_freq = 0; 500 mnt.mnt_passno = 0; 501 502 retry = hasmntval(&mnt, "retry"); 503 if (retry <= 0) 504 retry = 1; /* XXX */ 505 506 /*again:*/ 507 508 /* 509 * set mount args 510 */ 511 NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp->fhs_fhandle); 512 513 #if NFS_PROTOCOL_VERSION >= 3 514 nfs_args.fhsize = fhp->fhs_size; 515 nfs_args.version = NFS_ARGSVERSION; 516 #endif 517 #ifdef ULTRIX_HACK 518 nfs_args.optstr = mnt.mnt_opts; 519 #endif /* ULTRIX_HACK */ 520 521 nfs_args.hostname = host; 522 nfs_args.flags |= NFSMNT_HOSTNAME; 523 #ifdef HOSTNAMESZ 524 /* 525 * Most kernels have a name length restriction. 526 */ 527 if (strlen(host) >= HOSTNAMESZ) 528 strlcpy(host + HOSTNAMESZ - 3, "..", sizeof host - HOSTNAMESZ + 3); 529 #endif /* HOSTNAMESZ */ 530 531 if ((nfs_args.rsize = hasmntval(&mnt, "rsize"))) 532 nfs_args.flags |= NFSMNT_RSIZE; 533 534 #ifdef NFSMNT_READDIRSIZE 535 if ((nfs_args.readdirsize = hasmntval(&mnt, "readdirsize"))) { 536 nfs_args.flags |= NFSMNT_READDIRSIZE; 537 } else if (nfs_args.rsize) { 538 nfs_args.readdirsize = nfs_args.rsize; 539 nfs_args.flags |= NFSMNT_READDIRSIZE; 540 } 541 #endif 542 543 if ((nfs_args.wsize = hasmntval(&mnt, "wsize"))) 544 nfs_args.flags |= NFSMNT_WSIZE; 545 546 if ((nfs_args.timeo = hasmntval(&mnt, "timeo"))) 547 nfs_args.flags |= NFSMNT_TIMEO; 548 549 if ((nfs_args.retrans = hasmntval(&mnt, "retrans"))) 550 nfs_args.flags |= NFSMNT_RETRANS; 551 552 #ifdef NFSMNT_BIODS 553 if (nfs_args.biods = hasmntval(&mnt, "biods")) 554 nfs_args.flags |= NFSMNT_BIODS; 555 556 #endif /* NFSMNT_BIODS */ 557 558 #ifdef NFSMNT_MAXGRPS 559 if ((nfs_args.maxgrouplist = hasmntval(&mnt, "maxgroups"))) 560 nfs_args.flags |= NFSMNT_MAXGRPS; 561 #endif /* NFSMNT_MAXGRPS */ 562 563 #ifdef NFSMNT_READAHEAD 564 if (nfs_args.readahead = hasmntval(&mnt, "readahead")) 565 nfs_args.flags |= NFSMNT_READAHEAD; 566 #endif /* NFSMNT_READAHEAD */ 567 568 #ifdef notdef 569 /* 570 * This isn't supported by the ping algorithm yet. 571 * In any case, it is all done in nfs_init(). 572 */ 573 if (port = hasmntval(&mnt, "port")) 574 sin.sin_port = htons(port); 575 else 576 sin.sin_port = htons(NFS_PORT); /* XXX should use portmapper */ 577 #endif /* notdef */ 578 579 if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL) 580 nfs_args.flags |= NFSMNT_SOFT; 581 582 #ifdef NFSMNT_SPONGY 583 if (hasmntopt(&mnt, "spongy") != NULL) { 584 nfs_args.flags |= NFSMNT_SPONGY; 585 if (nfs_args.flags & NFSMNT_SOFT) { 586 plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored"); 587 nfs_args.flags &= ~NFSMNT_SOFT; 588 } 589 } 590 #endif /* MNTOPT_SPONGY */ 591 592 #ifdef MNTOPT_INTR 593 if (hasmntopt(&mnt, MNTOPT_INTR) != NULL) 594 nfs_args.flags |= NFSMNT_INT; 595 #endif /* MNTOPT_INTR */ 596 597 #ifdef MNTOPT_NODEVS 598 if (hasmntopt(&mnt, MNTOPT_NODEVS) != NULL) 599 nfs_args.flags |= NFSMNT_NODEVS; 600 #endif /* MNTOPT_NODEVS */ 601 602 #ifdef MNTOPT_COMPRESS 603 if (hasmntopt(&mnt, MNTOPT_COMPRESS) != NULL) 604 nfs_args.flags |= NFSMNT_COMPRESS; 605 #endif /* MNTOPT_COMPRESS */ 606 607 #ifdef MNTOPT_NOCONN 608 if (hasmntopt(&mnt, MNTOPT_NOCONN) != NULL) 609 nfs_args.flags |= NFSMNT_NOCONN; 610 #endif /* MNTOPT_NOCONN */ 611 612 #ifdef MNTOPT_RESVPORT 613 if (hasmntopt(&mnt, MNTOPT_RESVPORT) != NULL) 614 nfs_args.flags |= NFSMNT_RESVPORT; 615 #endif /* MNTOPT_RESVPORT */ 616 617 #ifdef MNTOPT_NQNFS 618 if (hasmntopt(&mnt, MNTOPT_NQNFS) != NULL) 619 nfs_args.flags |= NFSMNT_NQNFS; 620 #ifdef NFSMNT_NQLOOKLEASE 621 if (hasmntopt(&mnt, "nolooklease") == NULL) 622 nfs_args.flags |= NFSMNT_NQLOOKLEASE; 623 #endif /* NFSMNT_NQLOOKLEASE */ 624 #endif /* MNTOPT_NQNFS */ 625 626 #ifdef NFSMNT_PGTHRESH 627 if (nfs_args.pg_thresh = hasmntval(&mnt, "pgthresh")) 628 nfs_args.flags |= NFSMNT_PGTHRESH; 629 #endif /* NFSMNT_PGTHRESH */ 630 631 NFS_SA_DREF(nfs_args, fs->fs_ip); 632 633 flags = compute_mount_flags(&mnt); 634 635 #ifdef NFSMNT_NOCTO 636 if (hasmntopt(&mnt, "nocto") != NULL) 637 nfs_args.flags |= NFSMNT_NOCTO; 638 #endif /* NFSMNT_NOCTO */ 639 640 #ifdef HAS_TCP_NFS 641 if (hasmntopt(&mnt, "tcp") != NULL) 642 nfs_args.sotype = SOCK_STREAM; 643 #endif /* HAS_TCP_NFS */ 644 645 646 #ifdef ULTRIX_HACK 647 /* 648 * Ultrix passes the flags argument as part of the 649 * mount data structure, rather than using the 650 * flags argument to the system call. This is 651 * confusing... 652 */ 653 if (!(nfs_args.flags & NFSMNT_PGTHRESH)) { 654 nfs_args.pg_thresh = 64; /* 64k - XXX */ 655 nfs_args.flags |= NFSMNT_PGTHRESH; 656 } 657 nfs_args.gfs_flags = flags; 658 flags &= M_RDONLY; 659 if (flags & M_RDONLY) 660 nfs_args.flags |= NFSMNT_RONLY; 661 #endif /* ULTRIX_HACK */ 662 663 error = mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type); 664 free(xopts); 665 return error; 666 } 667 668 static int 669 mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf) 670 { 671 #ifdef notdef 672 int error; 673 fhstatus fhs; 674 char *colon; 675 676 if (!(colon = strchr(fs_name, ':'))) 677 return ENOENT; 678 679 #ifdef DEBUG 680 dlog("locating fhandle for %s", fs_name); 681 #endif /* DEBUG */ 682 error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, (void *)0); 683 684 if (error) 685 return error; 686 687 return mount_nfs_fh(&fhs, dir, fs_name, opts, mf); 688 #endif 689 if (!mf->mf_private) { 690 plog(XLOG_ERROR, "Missing filehandle for %s", fs_name); 691 return EINVAL; 692 } 693 694 return mount_nfs_fh((fhstatus *) mf->mf_private, dir, fs_name, opts, mf); 695 } 696 697 static int 698 nfs_fmount(mntfs *mf) 699 { 700 int error; 701 702 error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf); 703 704 #ifdef DEBUG 705 if (error) { 706 errno = error; 707 dlog("mount_nfs: %m"); 708 } 709 #endif /* DEBUG */ 710 return error; 711 } 712 713 static int 714 nfs_fumount(mntfs *mf) 715 { 716 int error = UMOUNT_FS(mf->mf_mount); 717 if (error) 718 return error; 719 720 return 0; 721 } 722 723 static void 724 nfs_umounted(am_node *mp) 725 { 726 #ifdef INFORM_MOUNTD 727 /* 728 * Don't bother to inform remote mountd 729 * that we are finished. Until a full 730 * track of filehandles is maintained 731 * the mountd unmount callback cannot 732 * be done correctly anyway... 733 */ 734 735 mntfs *mf = mp->am_mnt; 736 fserver *fs; 737 char *colon, *path; 738 739 if (mf->mf_error || mf->mf_refc > 1) 740 return; 741 742 fs = mf->mf_server; 743 744 /* 745 * Call the mount daemon on the server to 746 * announce that we are not using the fs any more. 747 * 748 * This is *wrong*. The mountd should be called 749 * when the fhandle is flushed from the cache, and 750 * a reference held to the cached entry while the 751 * fs is mounted... 752 */ 753 colon = path = strchr(mf->mf_info, ':'); 754 if (fs && colon) { 755 fh_cache f; 756 #ifdef DEBUG 757 dlog("calling mountd for %s", mf->mf_info); 758 #endif /* DEBUG */ 759 *path++ = '\0'; 760 f.fh_path = path; 761 f.fh_sin = *fs->fs_ip; 762 f.fh_sin.sin_port = (u_short) 0; 763 f.fh_fs = fs; 764 f.fh_id = 0; 765 f.fh_error = 0; 766 (void) prime_nfs_fhandle_cache(colon+1, mf->mf_server, (fhstatus *) 0, (void *)mf); 767 (void) call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (void *)0); 768 *colon = ':'; 769 } 770 #endif /* INFORM_MOUNTD */ 771 772 #ifdef KICK_KERNEL 773 /* This should go into the mainline code, not in nfs_ops... */ 774 775 /* 776 * Run lstat over the underlying directory in 777 * case this was a direct mount. This will 778 * get the kernel back in sync with reality. 779 */ 780 if (mp->am_parent && mp->am_parent->am_path && 781 STREQ(mp->am_parent->am_mnt->mf_ops->fs_type, "direct")) { 782 struct stat stb; 783 pid_t pid; 784 if ((pid = background()) == 0) { 785 if (lstat(mp->am_parent->am_path, &stb) < 0) { 786 plog(XLOG_ERROR, "lstat(%s) after unmount: %m", mp->am_parent->am_path); 787 #ifdef DEBUG 788 } else { 789 dlog("hack lstat(%s): ok", mp->am_parent->am_path); 790 #endif /* DEBUG */ 791 } 792 _exit(0); 793 } 794 } 795 #endif /* KICK_KERNEL */ 796 } 797 798 /* 799 * Network file system 800 */ 801 am_ops nfs_ops = { 802 "nfs", 803 nfs_match, 804 nfs_init, 805 auto_fmount, 806 nfs_fmount, 807 auto_fumount, 808 nfs_fumount, 809 efs_lookuppn, 810 efs_readdir, 811 0, /* nfs_readlink */ 812 0, /* nfs_mounted */ 813 nfs_umounted, 814 find_nfs_srvr, 815 FS_MKMNT|FS_BACKGROUND|FS_AMQINFO 816 }; 817 818 #endif /* HAS_NFS */ 819