1 /* 2 * $Id: nfs_ops.c,v 5.2.1.4 91/03/17 17:45:55 jsp Alpha $ 3 * 4 * Copyright (c) 1990 Jan-Simon Pendry 5 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 6 * Copyright (c) 1990 The Regents of the University of California. 7 * 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 * %sccs.include.redist.c% 13 * 14 * @(#)nfs_ops.c 5.2 (Berkeley) 03/17/91 15 */ 16 17 #include "am.h" 18 19 #ifdef HAS_NFS 20 21 #define NFS 22 #define NFSCLIENT 23 #ifdef NFS_3 24 typedef nfs_fh fhandle_t; 25 #endif /* NFS_3 */ 26 #ifdef NFS_HDR 27 #include NFS_HDR 28 #endif /* NFS_HDR */ 29 #include <sys/mount.h> 30 #include "mount.h" 31 32 /* 33 * Network file system 34 */ 35 36 /* 37 * Convert from nfsstat to UN*X error code 38 */ 39 #define unx_error(e) ((int)(e)) 40 41 /* 42 * The NFS layer maintains a cache of file handles. 43 * This is *fundamental* to the implementation and 44 * also allows quick remounting when a filesystem 45 * is accessed soon after timing out. 46 * 47 * The NFS server layer knows to flush this cache 48 * when a server goes down so avoiding stale handles. 49 * 50 * Each cache entry keeps a hard reference to 51 * the corresponding server. This ensures that 52 * the server keepalive information is maintained. 53 * 54 * The copy of the sockaddr_in here is taken so 55 * that the port can be twiddled to talk to mountd 56 * instead of portmap or the NFS server as used 57 * elsewhere. 58 * The port# is flushed if a server goes down. 59 * The IP address is never flushed - we assume 60 * that the address of a mounted machine never 61 * changes. If it does, then you have other 62 * problems... 63 */ 64 typedef struct fh_cache fh_cache; 65 struct fh_cache { 66 qelem fh_q; /* List header */ 67 voidp fh_wchan; /* Wait channel */ 68 int fh_error; /* Valid data? */ 69 int fh_id; /* Unique id */ 70 int fh_cid; /* Callout id */ 71 struct fhstatus fh_handle; /* Handle on filesystem */ 72 struct sockaddr_in fh_sin; /* Address of mountd */ 73 fserver *fh_fs; /* Server holding filesystem */ 74 char *fh_path; /* Filesystem on host */ 75 }; 76 77 /* 78 * FH_TTL is the time a file handle will remain in the cache since 79 * last being used. If the file handle becomes invalid, then it 80 * will be flushed anyway. 81 */ 82 #define FH_TTL (5 * 60) /* five minutes */ 83 #define FH_TTL_ERROR (30) /* 30 seconds */ 84 85 static int fh_id = 0; 86 #define FHID_ALLOC() (++fh_id) 87 extern qelem fh_head; 88 qelem fh_head = { &fh_head, &fh_head }; 89 90 static int call_mountd P((fh_cache*, unsigned long, fwd_fun, voidp)); 91 92 AUTH *nfs_auth; 93 94 static fh_cache *find_nfs_fhandle_cache P((voidp idv, int done)); 95 static fh_cache *find_nfs_fhandle_cache(idv, done) 96 voidp idv; 97 int done; 98 { 99 fh_cache *fp, *fp2 = 0; 100 int id = (int) idv; 101 102 ITER(fp, fh_cache, &fh_head) { 103 if (fp->fh_id == id) { 104 fp2 = fp; 105 break; 106 } 107 } 108 109 #ifdef DEBUG 110 if (fp2) { 111 dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path); 112 } else { 113 dlog("fh cache search failed"); 114 } 115 #endif /* DEBUG */ 116 117 if (fp2 && !done) { 118 fp2->fh_error = ETIMEDOUT; 119 return 0; 120 } 121 122 return fp2; 123 } 124 125 /* 126 * Called when a filehandle appears 127 */ 128 static void got_nfs_fh P((voidp pkt, int len, struct sockaddr_in *sa, 129 struct sockaddr_in *ia, voidp idv, int done)); 130 static void got_nfs_fh(pkt, len, sa, ia, idv, done) 131 voidp pkt; 132 int len; 133 struct sockaddr_in *sa, *ia; 134 voidp idv; 135 int done; 136 { 137 fh_cache *fp = find_nfs_fhandle_cache(idv, done); 138 if (fp) { 139 fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_handle, xdr_fhstatus); 140 if (!fp->fh_error) { 141 #ifdef DEBUG 142 dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 143 #endif /* DEBUG */ 144 /* 145 * Wakeup anything sleeping on this filehandle 146 */ 147 if (fp->fh_wchan) { 148 #ifdef DEBUG 149 dlog("Calling wakeup on %#x", fp->fh_wchan); 150 #endif /* DEBUG */ 151 wakeup(fp->fh_wchan); 152 } 153 } 154 } 155 } 156 157 void flush_nfs_fhandle_cache P((fserver *fs)); 158 void flush_nfs_fhandle_cache(fs) 159 fserver *fs; 160 { 161 fh_cache *fp; 162 ITER(fp, fh_cache, &fh_head) { 163 if (fp->fh_fs == fs || fs == 0) { 164 fp->fh_sin.sin_port = (u_short) 0; 165 fp->fh_error = -1; 166 } 167 } 168 } 169 170 static void discard_fh P((fh_cache *fp)); 171 static void discard_fh(fp) 172 fh_cache *fp; 173 { 174 rem_que(&fp->fh_q); 175 #ifdef DEBUG 176 dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); 177 #endif /* DEBUG */ 178 free_srvr(fp->fh_fs); 179 free((voidp) fp->fh_path); 180 free((voidp) fp); 181 } 182 183 /* 184 * Determine the file handle for a node 185 */ 186 static int prime_nfs_fhandle_cache P((char *path, fserver *fs, struct fhstatus *fhbuf, voidp wchan)); 187 static int prime_nfs_fhandle_cache(path, fs, fhbuf, wchan) 188 char *path; 189 fserver *fs; 190 struct fhstatus *fhbuf; 191 voidp wchan; 192 { 193 fh_cache *fp, *fp_save = 0; 194 int error; 195 int reuse_id = FALSE; 196 197 #ifdef DEBUG 198 dlog("Searching cache for %s:%s", fs->fs_host, path); 199 #endif /* DEBUG */ 200 201 /* 202 * First search the cache 203 */ 204 ITER(fp, fh_cache, &fh_head) { 205 if (fs == fp->fh_fs && strcmp(path, fp->fh_path) == 0) { 206 switch (fp->fh_error) { 207 case 0: 208 error = fp->fh_error = unx_error(fp->fh_handle.fhs_status); 209 if (error == 0) { 210 if (fhbuf) 211 bcopy((voidp) &fp->fh_handle, (voidp) fhbuf, 212 sizeof(fp->fh_handle)); 213 if (fp->fh_cid) 214 untimeout(fp->fh_cid); 215 fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp); 216 } else if (error == EACCES) { 217 /* 218 * Now decode the file handle return code. 219 */ 220 plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"", 221 fs->fs_host, path); 222 } else { 223 errno = error; /* XXX */ 224 plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m", 225 fs->fs_host, path); 226 } 227 228 /* 229 * The error was returned from the remote mount daemon. 230 * Policy: this error will be cached for now... 231 */ 232 return error; 233 234 case -1: 235 /* 236 * Still thinking about it, but we can re-use. 237 */ 238 fp_save = fp; 239 reuse_id = TRUE; 240 break; 241 242 default: 243 /* 244 * Return the error. 245 * Policy: make sure we recompute if required again 246 * in case this was caused by a network failure. 247 * This can thrash mountd's though... If you find 248 * your mountd going slowly then: 249 * 1. Add a fork() loop to main. 250 * 2. Remove the call to innetgr() and don't use 251 * netgroups, especially if you don't use YP. 252 */ 253 error = fp->fh_error; 254 fp->fh_error = -1; 255 return error; 256 } 257 break; 258 } 259 } 260 261 /* 262 * Not in cache 263 */ 264 if (fp_save) { 265 fp = fp_save; 266 /* 267 * Re-use existing slot 268 */ 269 untimeout(fp->fh_cid); 270 free_srvr(fp->fh_fs); 271 free(fp->fh_path); 272 } else { 273 fp = ALLOC(fh_cache); 274 bzero((voidp) fp, sizeof(*fp)); 275 ins_que(&fp->fh_q, &fh_head); 276 } 277 if (!reuse_id) 278 fp->fh_id = FHID_ALLOC(); 279 fp->fh_wchan = wchan; 280 fp->fh_error = -1; 281 fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp); 282 283 /* 284 * If the address has changed then don't try to re-use the 285 * port information 286 */ 287 if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { 288 fp->fh_sin = *fs->fs_ip; 289 fp->fh_sin.sin_port = 0; 290 } 291 fp->fh_fs = dup_srvr(fs); 292 fp->fh_path = strdup(path); 293 294 error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan); 295 if (error) { 296 /* 297 * Local error - cache for a short period 298 * just to prevent thrashing. 299 */ 300 untimeout(fp->fh_cid); 301 fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, 302 discard_fh, (voidp) fp); 303 fp->fh_error = error; 304 } else { 305 error = fp->fh_error; 306 } 307 return error; 308 } 309 310 static int call_mountd P((fh_cache *fp, u_long proc, fwd_fun f, voidp wchan)); 311 static int call_mountd(fp, proc, f, wchan) 312 fh_cache *fp; 313 u_long proc; 314 fwd_fun f; 315 voidp wchan; 316 { 317 struct rpc_msg mnt_msg; 318 int len; 319 char iobuf[8192]; 320 int error; 321 322 if (!nfs_auth) { 323 nfs_auth = authunix_create_default(); 324 if (!nfs_auth) 325 return ENOBUFS; 326 } 327 328 if (fp->fh_sin.sin_port == 0) { 329 u_short port; 330 error = nfs_srvr_port(fp->fh_fs, &port, wchan); 331 if (error) 332 return error; 333 fp->fh_sin.sin_port = port; 334 } 335 336 rpc_msg_init(&mnt_msg, MOUNTPROG, MOUNTVERS, (unsigned long) 0); 337 len = make_rpc_packet(iobuf, sizeof(iobuf), proc, 338 &mnt_msg, (voidp) &fp->fh_path, xdr_nfspath, nfs_auth); 339 340 if (len > 0) { 341 error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), 342 (voidp) iobuf, len, &fp->fh_sin, &fp->fh_sin, (voidp) fp->fh_id, f); 343 } else { 344 error = -len; 345 } 346 return error; 347 } 348 349 /*-------------------------------------------------------------------------*/ 350 351 /* 352 * NFS needs the local filesystem, remote filesystem 353 * remote hostname. 354 * Local filesystem defaults to remote and vice-versa. 355 */ 356 static char *nfs_match(fo) 357 am_opts *fo; 358 { 359 char *xmtab; 360 if (fo->opt_fs && !fo->opt_rfs) 361 fo->opt_rfs = fo->opt_fs; 362 if (!fo->opt_rfs) { 363 plog(XLOG_USER, "nfs: no remote filesystem specified"); 364 return FALSE; 365 } 366 if (!fo->opt_rhost) { 367 plog(XLOG_USER, "nfs: no remote host specified"); 368 return FALSE; 369 } 370 /* 371 * Determine magic cookie to put in mtab 372 */ 373 xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2); 374 sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs); 375 #ifdef DEBUG 376 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", 377 fo->opt_rhost, fo->opt_rfs, fo->opt_fs); 378 #endif /* DEBUG */ 379 380 return xmtab; 381 } 382 383 /* 384 * Initialise am structure for nfs 385 */ 386 static int nfs_init(mf) 387 mntfs *mf; 388 { 389 if (!mf->mf_private) { 390 int error; 391 struct fhstatus fhs; 392 393 char *colon = strchr(mf->mf_info, ':'); 394 if (colon == 0) 395 return ENOENT; 396 397 error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, (voidp) mf); 398 if (!error) { 399 mf->mf_private = (voidp) ALLOC(fhstatus); 400 mf->mf_prfree = (void (*)()) free; 401 bcopy((voidp) &fhs, mf->mf_private, sizeof(fhs)); 402 } 403 return error; 404 } 405 406 return 0; 407 } 408 409 int mount_nfs_fh(fhp, dir, fs_name, opts, mf) 410 struct fhstatus *fhp; 411 char *dir; 412 char *fs_name; 413 char *opts; 414 mntfs *mf; 415 { 416 struct nfs_args nfs_args; 417 struct mntent mnt; 418 int retry; 419 char *colon; 420 /*char *path;*/ 421 char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; 422 fserver *fs = mf->mf_server; 423 int flags; 424 #ifdef notdef 425 unsigned short port; 426 #endif /* notdef */ 427 428 MTYPE_TYPE type = MOUNT_TYPE_NFS; 429 430 bzero((voidp) &nfs_args, sizeof(nfs_args)); /* Paranoid */ 431 432 /* 433 * Extract host name to give to kernel 434 */ 435 if (!(colon = strchr(fs_name, ':'))) 436 return ENOENT; 437 #ifndef NFS_ARGS_NEEDS_PATH 438 *colon = '\0'; 439 #endif 440 strncpy(host, fs_name, sizeof(host)); 441 #ifndef NFS_ARGS_NEEDS_PATH 442 *colon = ':'; 443 #endif /* NFS_ARGS_NEEDS_PATH */ 444 /*path = colon + 1;*/ 445 446 bzero((voidp) &nfs_args, sizeof(nfs_args)); 447 448 mnt.mnt_dir = dir; 449 mnt.mnt_fsname = fs_name; 450 mnt.mnt_type = MTAB_TYPE_NFS; 451 mnt.mnt_opts = opts; 452 mnt.mnt_freq = 0; 453 mnt.mnt_passno = 0; 454 455 retry = hasmntval(&mnt, "retry"); 456 if (retry <= 0) 457 retry = 1; /* XXX */ 458 459 /*again:*/ 460 461 /* 462 * set mount args 463 */ 464 NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp->fhstatus_u.fhs_fhandle); 465 466 #ifdef ULTRIX_HACK 467 nfs_args.optstr = mnt.mnt_opts; 468 #endif /* ULTRIX_HACK */ 469 470 nfs_args.hostname = host; 471 nfs_args.flags |= NFSMNT_HOSTNAME; 472 #ifdef HOSTNAMESZ 473 /* 474 * Most kernels have a name length restriction. 475 */ 476 if (strlen(host) >= HOSTNAMESZ) 477 strcpy(host + HOSTNAMESZ - 3, ".."); 478 #endif /* HOSTNAMESZ */ 479 480 if (nfs_args.rsize = hasmntval(&mnt, "rsize")) 481 nfs_args.flags |= NFSMNT_RSIZE; 482 483 if (nfs_args.wsize = hasmntval(&mnt, "wsize")) 484 nfs_args.flags |= NFSMNT_WSIZE; 485 486 if (nfs_args.timeo = hasmntval(&mnt, "timeo")) 487 nfs_args.flags |= NFSMNT_TIMEO; 488 489 if (nfs_args.retrans = hasmntval(&mnt, "retrans")) 490 nfs_args.flags |= NFSMNT_RETRANS; 491 492 #ifdef NFSMNT_BIODS 493 if (nfs_args.biods = hasmntval(&mnt, "biods")) 494 nfs_args.flags |= NFSMNT_BIODS; 495 496 #endif /* NFSMNT_BIODS */ 497 498 #ifdef notdef 499 /* 500 * This isn't supported by the ping algorithm yet. 501 * In any case, it is all done in nfs_init(). 502 */ 503 if (port = hasmntval(&mnt, "port")) 504 sin.sin_port = htons(port); 505 else 506 sin.sin_port = htons(NFS_PORT); /* XXX should use portmapper */ 507 #endif /* notdef */ 508 509 if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL) 510 nfs_args.flags |= NFSMNT_SOFT; 511 512 #ifdef NFSMNT_SPONGY 513 if (hasmntopt(&mnt, "spongy") != NULL) { 514 nfs_args.flags |= NFSMNT_SPONGY; 515 if (nfs_args.flags & NFSMNT_SOFT) { 516 plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored"); 517 nfs_args.flags &= ~NFSMNT_SOFT; 518 } 519 } 520 #endif /* MNTOPT_SPONGY */ 521 522 #ifdef MNTOPT_INTR 523 if (hasmntopt(&mnt, MNTOPT_INTR) != NULL) 524 nfs_args.flags |= NFSMNT_INT; 525 #endif /* MNTOPT_INTR */ 526 527 #ifdef MNTOPT_NODEVS 528 if (hasmntopt(&mnt, MNTOPT_NODEVS) != NULL) 529 nfs_args.flags |= NFSMNT_NODEVS; 530 #endif /* MNTOPT_NODEVS */ 531 532 #ifdef MNTOPT_COMPRESS 533 if (hasmntopt(&mnt, "compress") != NULL) 534 nfs_args.flags |= NFSMNT_COMPRESS; 535 #endif /* MNTOPT_COMPRESS */ 536 537 #ifdef MNTOPT_NOCONN 538 if (hasmntopt(&mnt, "noconn") != NULL) 539 nfs_args.flags |= NFSMNT_NOCONN; 540 #endif /* MNTOPT_NOCONN */ 541 542 #ifdef NFSMNT_PGTHRESH 543 if (nfs_args.pg_thresh = hasmntval(&mnt, "pgthresh")) 544 nfs_args.flags |= NFSMNT_PGTHRESH; 545 #endif /* NFSMNT_PGTHRESH */ 546 547 NFS_SA_DREF(nfs_args, fs->fs_ip); 548 549 flags = compute_mount_flags(&mnt); 550 551 #ifdef NFSMNT_NOCTO 552 if (hasmntopt(&mnt, "nocto") != NULL) 553 nfs_args.flags |= NFSMNT_NOCTO; 554 #endif /* NFSMNT_NOCTO */ 555 556 #ifdef HAS_TCP_NFS 557 if (hasmntopt(&mnt, "tcp") != NULL) 558 nfs_args.sotype = SOCK_STREAM; 559 #endif /* HAS_TCP_NFS */ 560 561 562 #ifdef ULTRIX_HACK 563 /* 564 * Ultrix passes the flags argument as part of the 565 * mount data structure, rather than using the 566 * flags argument to the system call. This is 567 * confusing... 568 */ 569 if (!(nfs_args.flags & NFSMNT_PGTHRESH)) { 570 nfs_args.pg_thresh = 64; /* 64k - XXX */ 571 nfs_args.flags |= NFSMNT_PGTHRESH; 572 } 573 nfs_args.gfs_flags = flags; 574 flags &= M_RDONLY; 575 if (flags & M_RDONLY) 576 nfs_args.flags |= NFSMNT_RONLY; 577 #endif /* ULTRIX_HACK */ 578 579 return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type); 580 } 581 582 static int mount_nfs(dir, fs_name, opts, mf) 583 char *dir; 584 char *fs_name; 585 char *opts; 586 mntfs *mf; 587 { 588 #ifdef notdef 589 int error; 590 struct fhstatus fhs; 591 char *colon; 592 593 if (!(colon = strchr(fs_name, ':'))) 594 return ENOENT; 595 596 #ifdef DEBUG 597 dlog("locating fhandle for %s", fs_name); 598 #endif /* DEBUG */ 599 error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, (voidp) 0); 600 601 if (error) 602 return error; 603 604 return mount_nfs_fh(&fhs, dir, fs_name, opts, mf); 605 #endif 606 if (!mf->mf_private) { 607 plog(XLOG_ERROR, "Missing filehandle for %s", fs_name); 608 return EINVAL; 609 } 610 611 return mount_nfs_fh((struct fhstatus *) mf->mf_private, dir, fs_name, opts, mf); 612 } 613 614 static int nfs_fmount(mf) 615 mntfs *mf; 616 { 617 int error; 618 619 error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf); 620 621 #ifdef DEBUG 622 if (error) { 623 errno = error; 624 dlog("mount_nfs: %m"); 625 } 626 #endif /* DEBUG */ 627 return error; 628 } 629 630 static int nfs_fumount(mf) 631 mntfs *mf; 632 { 633 int error = UMOUNT_FS(mf->mf_mount); 634 if (error) 635 return error; 636 637 return 0; 638 } 639 640 static void nfs_umounted(mp) 641 am_node *mp; 642 { 643 #ifdef INFORM_MOUNTD 644 /* 645 * Don't bother to inform remote mountd 646 * that we are finished. Until a full 647 * track of filehandles is maintained 648 * the mountd unmount callback cannot 649 * be done correctly anyway... 650 */ 651 652 mntfs *mf = mp->am_mnt; 653 fserver *fs; 654 char *colon, *path; 655 656 if (mf->mf_error || mf->mf_refc > 1) 657 return; 658 659 fs = mf->mf_server; 660 661 /* 662 * Call the mount daemon on the server to 663 * announce that we are not using the fs any more. 664 * 665 * This is *wrong*. The mountd should be called 666 * when the fhandle is flushed from the cache, and 667 * a reference held to the cached entry while the 668 * fs is mounted... 669 */ 670 colon = path = strchr(mf->mf_info, ':'); 671 if (fs && colon) { 672 fh_cache f; 673 #ifdef DEBUG 674 dlog("calling mountd for %s", mf->mf_info); 675 #endif /* DEBUG */ 676 *path++ = '\0'; 677 f.fh_path = path; 678 f.fh_sin = *fs->fs_ip; 679 f.fh_sin.sin_port = (u_short) 0; 680 f.fh_fs = fs; 681 f.fh_id = 0; 682 f.fh_error = 0; 683 (void) prime_nfs_fhandle_cache(colon+1, mf->mf_server, (struct fhstatus *) 0, (voidp) mf); 684 (void) call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0); 685 *colon = ':'; 686 } 687 #endif /* INFORM_MOUNTD */ 688 } 689 690 /* 691 * Network file system 692 */ 693 am_ops nfs_ops = { 694 "nfs", 695 nfs_match, 696 nfs_init, 697 auto_fmount, 698 nfs_fmount, 699 auto_fumount, 700 nfs_fumount, 701 efs_lookuppn, 702 efs_readdir, 703 0, /* nfs_readlink */ 704 0, /* nfs_mounted */ 705 nfs_umounted, 706 find_nfs_srvr, 707 FS_MKMNT|FS_BACKGROUND|FS_AMQINFO 708 }; 709 710 #endif /* HAS_NFS */ 711