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