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