1 /* $NetBSD: nfs_subr.c,v 1.3 2015/01/21 21:47:44 joerg Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2014 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * 38 * File: am-utils/amd/nfs_subr.c 39 * 40 */ 41 42 #ifdef HAVE_CONFIG_H 43 # include <config.h> 44 #endif /* HAVE_CONFIG_H */ 45 #include <am_defs.h> 46 #include <amd.h> 47 48 /* 49 * Convert from UN*X to NFS error code. 50 * Some systems like linux define their own (see 51 * conf/mount/mount_linux.h). 52 */ 53 #ifndef nfs_error 54 # define nfs_error(e) ((nfsstat)(e)) 55 #endif /* nfs_error */ 56 57 /* 58 * File Handle structure 59 * 60 * This is interpreted by indexing the exported array 61 * by fhh_id (for old-style filehandles), or by retrieving 62 * the node name from fhh_path (for new-style filehandles). 63 * 64 * The whole structure is mapped onto a standard fhandle_t 65 * when transmitted. 66 */ 67 struct am_fh { 68 u_int fhh_gen; /* generation number */ 69 union { 70 struct { 71 int fhh_type; /* old or new am_fh */ 72 pid_t fhh_pid; /* process id */ 73 int fhh_id; /* map id */ 74 } s; 75 char fhh_path[NFS_FHSIZE-sizeof(u_int)]; /* path to am_node */ 76 } u; 77 }; 78 79 struct am_fh3 { 80 u_int fhh_gen; /* generation number */ 81 union { 82 struct { 83 int fhh_type; /* old or new am_fh */ 84 pid_t fhh_pid; /* process id */ 85 int fhh_id; /* map id */ 86 } s; 87 char fhh_path[AM_FHSIZE3-sizeof(u_int)]; /* path to am_node */ 88 } u; 89 }; 90 91 /* forward declarations */ 92 /* converting am-filehandles to mount-points */ 93 static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop); 94 static am_node *fh_to_mp(am_nfs_fh *fhp); 95 static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail); 96 97 98 static char * 99 do_readlink(am_node *mp, int *error_return) 100 { 101 char *ln; 102 103 /* 104 * If there is a readlink method then use it, 105 * otherwise if a link exists use that, 106 * otherwise use the mount point. 107 */ 108 if (mp->am_al->al_mnt->mf_ops->readlink) { 109 int retry = 0; 110 mp = (*mp->am_al->al_mnt->mf_ops->readlink) (mp, &retry); 111 if (mp == NULL) { 112 *error_return = retry; 113 return 0; 114 } 115 /* reschedule_timeout_mp(); */ 116 } 117 118 if (mp->am_link) { 119 ln = mp->am_link; 120 } else { 121 ln = mp->am_al->al_mnt->mf_mount; 122 } 123 124 return ln; 125 } 126 127 128 voidp 129 nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp) 130 { 131 static char res; 132 133 return (voidp) &res; 134 } 135 136 137 nfsattrstat * 138 nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 139 { 140 static nfsattrstat res; 141 am_node *mp; 142 int retry = 0; 143 time_t now = clocktime(NULL); 144 145 if (amuDebug(D_TRACE)) 146 plog(XLOG_DEBUG, "getattr:"); 147 148 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); 149 if (mp == NULL) { 150 if (amuDebug(D_TRACE)) 151 plog(XLOG_DEBUG, "\tretry=%d", retry); 152 153 if (retry < 0) { 154 amd_stats.d_drops++; 155 return 0; 156 } 157 res.ns_status = nfs_error(retry); 158 return &res; 159 } 160 161 res = mp->am_attr; 162 if (amuDebug(D_TRACE)) 163 plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld", 164 mp->am_path, 165 (int) res.ns_u.ns_attr_u.na_size, 166 (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds, 167 (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds); 168 169 /* Delay unmount of what was looked up */ 170 if (mp->am_timeo_w < 4 * gopt.am_timeo_w) 171 mp->am_timeo_w += gopt.am_timeo_w; 172 mp->am_ttl = now + mp->am_timeo_w; 173 174 mp->am_stats.s_getattr++; 175 return &res; 176 } 177 178 179 nfsattrstat * 180 nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp) 181 { 182 static nfsattrstat res; 183 184 if (!fh_to_mp(&argp->sag_fhandle)) 185 res.ns_status = nfs_error(ESTALE); 186 else 187 res.ns_status = nfs_error(EROFS); 188 189 return &res; 190 } 191 192 193 voidp 194 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp) 195 { 196 static char res; 197 198 return (voidp) &res; 199 } 200 201 202 nfsdiropres * 203 nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 204 { 205 static nfsdiropres res; 206 am_node *mp; 207 int retry; 208 uid_t uid; 209 gid_t gid; 210 211 if (amuDebug(D_TRACE)) 212 plog(XLOG_DEBUG, "lookup:"); 213 214 /* finally, find the effective uid/gid from RPC request */ 215 if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0) 216 plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials"); 217 xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid); 218 xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid); 219 220 mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE); 221 if (mp == NULL) { 222 if (retry < 0) { 223 amd_stats.d_drops++; 224 return 0; 225 } 226 res.dr_status = nfs_error(retry); 227 } else { 228 int error; 229 am_node *ap; 230 if (amuDebug(D_TRACE)) 231 plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name); 232 ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE); 233 if (ap && error < 0) 234 ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error); 235 if (ap == 0) { 236 if (error < 0) { 237 amd_stats.d_drops++; 238 return 0; 239 } 240 res.dr_status = nfs_error(error); 241 } else { 242 /* 243 * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This 244 * should reduce the chance for race condition between unmounting an 245 * entry synchronously, and re-mounting it asynchronously. 246 */ 247 if (ap->am_ttl < mp->am_ttl) 248 ap->am_ttl = mp->am_ttl; 249 mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle); 250 res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr; 251 res.dr_status = NFS_OK; 252 } 253 mp->am_stats.s_lookup++; 254 /* reschedule_timeout_mp(); */ 255 } 256 257 return &res; 258 } 259 260 261 void 262 nfs_quick_reply(am_node *mp, int error) 263 { 264 SVCXPRT *transp = mp->am_transp; 265 nfsdiropres res; 266 xdrproc_t xdr_result = (xdrproc_t) xdr_diropres; 267 268 /* 269 * If there's a transp structure then we can reply to the client's 270 * nfs lookup request. 271 */ 272 if (transp) { 273 if (error == 0) { 274 /* 275 * Construct a valid reply to a lookup request. Same 276 * code as in nfsproc_lookup_2_svc() above. 277 */ 278 mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle); 279 res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr; 280 res.dr_status = NFS_OK; 281 } else 282 /* 283 * Return the error that was passed to us. 284 */ 285 res.dr_status = nfs_error(error); 286 287 /* 288 * Send off our reply 289 */ 290 if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res)) 291 svcerr_systemerr(transp); 292 293 /* 294 * Free up transp. It's only used for one reply. 295 */ 296 XFREE(mp->am_transp); 297 dlog("Quick reply sent for %s", mp->am_al->al_mnt->mf_mount); 298 } 299 } 300 301 302 nfsreadlinkres * 303 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 304 { 305 static nfsreadlinkres res; 306 am_node *mp; 307 int retry; 308 309 if (amuDebug(D_TRACE)) 310 plog(XLOG_DEBUG, "readlink:"); 311 312 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); 313 if (mp == NULL) { 314 readlink_retry: 315 if (retry < 0) { 316 amd_stats.d_drops++; 317 return 0; 318 } 319 res.rlr_status = nfs_error(retry); 320 } else { 321 char *ln = do_readlink(mp, &retry); 322 if (ln == 0) 323 goto readlink_retry; 324 res.rlr_status = NFS_OK; 325 if (amuDebug(D_TRACE) && ln) 326 plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln); 327 res.rlr_u.rlr_data_u = ln; 328 mp->am_stats.s_readlink++; 329 } 330 331 return &res; 332 } 333 334 335 nfsreadres * 336 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp) 337 { 338 static nfsreadres res; 339 340 memset((char *) &res, 0, sizeof(res)); 341 res.rr_status = nfs_error(EACCES); 342 343 return &res; 344 } 345 346 347 voidp 348 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp) 349 { 350 static char res; 351 352 return (voidp) &res; 353 } 354 355 356 nfsattrstat * 357 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp) 358 { 359 static nfsattrstat res; 360 361 if (!fh_to_mp(&argp->wra_fhandle)) 362 res.ns_status = nfs_error(ESTALE); 363 else 364 res.ns_status = nfs_error(EROFS); 365 366 return &res; 367 } 368 369 370 nfsdiropres * 371 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp) 372 { 373 static nfsdiropres res; 374 375 if (!fh_to_mp(&argp->ca_where.da_fhandle)) 376 res.dr_status = nfs_error(ESTALE); 377 else 378 res.dr_status = nfs_error(EROFS); 379 380 return &res; 381 } 382 383 384 static nfsstat * 385 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) 386 { 387 static nfsstat res; 388 int retry; 389 390 am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE); 391 if (mp == NULL) { 392 if (retry < 0) { 393 amd_stats.d_drops++; 394 return 0; 395 } 396 res = nfs_error(retry); 397 goto out; 398 } 399 400 if (mp->am_fattr.na_type != NFDIR) { 401 res = nfs_error(ENOTDIR); 402 goto out; 403 } 404 405 if (amuDebug(D_TRACE)) 406 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name); 407 408 mp = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE); 409 if (mp == NULL) { 410 /* 411 * Ignore retries... 412 */ 413 if (retry < 0) 414 retry = 0; 415 /* 416 * Usual NFS workaround... 417 */ 418 else if (retry == ENOENT) 419 retry = 0; 420 res = nfs_error(retry); 421 } else { 422 forcibly_timeout_mp(mp); 423 res = NFS_OK; 424 } 425 426 out: 427 return &res; 428 } 429 430 431 nfsstat * 432 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 433 { 434 return unlink_or_rmdir(argp, rqstp, TRUE); 435 } 436 437 438 nfsstat * 439 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp) 440 { 441 static nfsstat res; 442 443 if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle)) 444 res = nfs_error(ESTALE); 445 /* 446 * If the kernel is doing clever things with referenced files 447 * then let it pretend... 448 */ 449 else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4)) 450 res = NFS_OK; 451 /* 452 * otherwise a failure 453 */ 454 else 455 res = nfs_error(EROFS); 456 457 return &res; 458 } 459 460 461 nfsstat * 462 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp) 463 { 464 static nfsstat res; 465 466 if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle)) 467 res = nfs_error(ESTALE); 468 else 469 res = nfs_error(EROFS); 470 471 return &res; 472 } 473 474 475 nfsstat * 476 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp) 477 { 478 static nfsstat res; 479 480 if (!fh_to_mp(&argp->sla_from.da_fhandle)) 481 res = nfs_error(ESTALE); 482 else 483 res = nfs_error(EROFS); 484 485 return &res; 486 } 487 488 489 nfsdiropres * 490 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp) 491 { 492 static nfsdiropres res; 493 494 if (!fh_to_mp(&argp->ca_where.da_fhandle)) 495 res.dr_status = nfs_error(ESTALE); 496 else 497 res.dr_status = nfs_error(EROFS); 498 499 return &res; 500 } 501 502 503 nfsstat * 504 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) 505 { 506 return unlink_or_rmdir(argp, rqstp, FALSE); 507 } 508 509 510 nfsreaddirres * 511 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) 512 { 513 static nfsreaddirres res; 514 static nfsentry e_res[MAX_READDIR_ENTRIES]; 515 am_node *mp; 516 int retry; 517 518 if (amuDebug(D_TRACE)) 519 plog(XLOG_DEBUG, "readdir:"); 520 521 mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE); 522 if (mp == NULL) { 523 if (retry < 0) { 524 amd_stats.d_drops++; 525 return 0; 526 } 527 res.rdr_status = nfs_error(retry); 528 } else { 529 if (amuDebug(D_TRACE)) 530 plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path); 531 res.rdr_status = nfs_error((*mp->am_al->al_mnt->mf_ops->readdir) 532 (mp, argp->rda_cookie, 533 &res.rdr_u.rdr_reply_u, e_res, argp->rda_count)); 534 mp->am_stats.s_readdir++; 535 } 536 537 return &res; 538 } 539 540 541 nfsstatfsres * 542 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 543 { 544 static nfsstatfsres res; 545 am_node *mp; 546 int retry; 547 mntent_t mnt; 548 549 if (amuDebug(D_TRACE)) 550 plog(XLOG_DEBUG, "statfs:"); 551 552 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); 553 if (mp == NULL) { 554 if (retry < 0) { 555 amd_stats.d_drops++; 556 return 0; 557 } 558 res.sfr_status = nfs_error(retry); 559 } else { 560 nfsstatfsokres *fp; 561 if (amuDebug(D_TRACE)) 562 plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path); 563 564 /* 565 * just return faked up file system information 566 */ 567 fp = &res.sfr_u.sfr_reply_u; 568 569 fp->sfrok_tsize = 1024; 570 fp->sfrok_bsize = 1024; 571 572 /* check if map is browsable and show_statfs_entries=yes */ 573 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && 574 mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) { 575 mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts; 576 if (amu_hasmntopt(&mnt, "browsable")) { 577 count_map_entries(mp, 578 &fp->sfrok_blocks, 579 &fp->sfrok_bfree, 580 &fp->sfrok_bavail); 581 } 582 } else { 583 fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */ 584 fp->sfrok_bfree = 0; 585 fp->sfrok_bavail = 0; 586 } 587 588 res.sfr_status = NFS_OK; 589 mp->am_stats.s_statfs++; 590 } 591 592 return &res; 593 } 594 595 596 /* 597 * count how many total entries there are in a map, and how many 598 * of them are in use. 599 */ 600 static void 601 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail) 602 { 603 u_int blocks, bfree, bavail, i; 604 mntfs *mf; 605 mnt_map *mmp; 606 kv *k; 607 608 blocks = bfree = bavail = 0; 609 if (!mp) 610 goto out; 611 mf = mp->am_al->al_mnt; 612 if (!mf) 613 goto out; 614 mmp = (mnt_map *) mf->mf_private; 615 if (!mmp) 616 goto out; 617 618 /* iterate over keys */ 619 for (i = 0; i < NKVHASH; i++) { 620 for (k = mmp->kvhash[i]; k ; k = k->next) { 621 if (!k->key) 622 continue; 623 blocks++; 624 /* 625 * XXX: Need to count how many are actively in use and recompute 626 * bfree and bavail based on it. 627 */ 628 } 629 } 630 631 out: 632 *out_blocks = blocks; 633 *out_bfree = bfree; 634 *out_bavail = bavail; 635 } 636 637 static am_node * 638 validate_ap(am_node *node, int *rp, u_int fhh_gen) 639 { 640 am_node *ap = node; 641 /* 642 * Check the generation number in the node 643 * matches the one from the kernel. If not 644 * then the old node has been timed out and 645 * a new one allocated. 646 */ 647 if (node != NULL && node->am_gen != fhh_gen) 648 ap = NULL; 649 650 /* 651 * If it doesn't exists then drop the request 652 */ 653 if (!ap) 654 goto drop; 655 656 #if 0 657 /* 658 * If the node is hung then locate a new node 659 * for it. This implements the replicated filesystem 660 * retries. 661 */ 662 if (ap->am_al->al_mnt && FSRV_ISDOWN(ap->am_al->al_mnt->mf_server) && ap->am_parent) { 663 int error; 664 am_node *orig_ap = ap; 665 666 dlog("%s: %s (%s) is hung: lookup alternative file server", __func__, 667 orig_ap->am_path, orig_ap->am_al->al_mnt->mf_info); 668 669 /* 670 * Update modify time of parent node. 671 * With any luck the kernel will re-stat 672 * the child node and get new information. 673 */ 674 clocktime(&orig_ap->am_fattr.na_mtime); 675 676 /* 677 * Call the parent's lookup routine for an object 678 * with the same name. This may return -1 in error 679 * if a mount is in progress. In any case, if no 680 * mount node is returned the error code is propagated 681 * to the caller. 682 */ 683 if (vop == VLOOK_CREATE) { 684 ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop); 685 if (ap && error < 0) 686 ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->mount_child(ap, &error); 687 } else { 688 ap = NULL; 689 error = ESTALE; 690 } 691 if (ap == 0) { 692 if (error < 0 && amd_state == Finishing) 693 error = ENOENT; 694 *rp = error; 695 return 0; 696 } 697 698 /* 699 * Update last access to original node. This 700 * avoids timing it out and so sending ESTALE 701 * back to the kernel. 702 * XXX - Not sure we need this anymore (jsp, 90/10/6). 703 */ 704 new_ttl(orig_ap); 705 706 } 707 #endif /* 0 */ 708 709 /* 710 * Disallow references to objects being unmounted, unless 711 * they are automount points. 712 */ 713 if (ap->am_al->al_mnt && (ap->am_al->al_mnt->mf_flags & MFF_UNMOUNTING) && 714 !(ap->am_flags & AMF_ROOT)) { 715 if (amd_state == Finishing) 716 *rp = ENOENT; 717 else 718 *rp = -1; 719 return 0; 720 } 721 new_ttl(ap); 722 723 drop: 724 if (!ap || !ap->am_al->al_mnt) { 725 /* 726 * If we are shutting down then it is likely 727 * that this node has disappeared because of 728 * a fast timeout. To avoid things thrashing 729 * just pretend it doesn't exist at all. If 730 * ESTALE is returned, some NFS clients just 731 * keep retrying (stupid or what - if it's 732 * stale now, what's it going to be in 5 minutes?) 733 */ 734 if (amd_state == Finishing) 735 *rp = ENOENT; 736 else { 737 *rp = ESTALE; 738 amd_stats.d_stale++; 739 } 740 } 741 742 return ap; 743 } 744 745 /* 746 * Convert from file handle to automount node. 747 */ 748 static am_node * 749 fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) 750 { 751 struct am_fh *fp = (struct am_fh *) fhp; 752 am_node *ap = NULL; 753 754 if (fp->u.s.fhh_type != 0) { 755 /* New filehandle type */ 756 int len = sizeof(*fhp) - sizeof(fp->fhh_gen); 757 char *path = xmalloc(len+1); 758 /* 759 * Because fhp is treated as a filehandle we use memcpy 760 * instead of xstrlcpy. 761 */ 762 memcpy(path, (char *) fp->u.fhh_path, len); 763 path[len] = '\0'; 764 dlog("%s: new filehandle: %s", __func__, path); 765 766 ap = path_to_exported_ap(path); 767 XFREE(path); 768 } else { 769 dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id); 770 /* 771 * Check process id matches 772 * If it doesn't then it is probably 773 * from an old kernel-cached filehandle 774 * which is now out of date. 775 */ 776 if (fp->u.s.fhh_pid != get_server_pid()) { 777 dlog("%s: wrong pid %ld != my pid %ld", __func__, 778 (long) fp->u.s.fhh_pid, get_server_pid()); 779 goto done; 780 } 781 782 /* 783 * Get hold of the supposed mount node 784 */ 785 ap = get_exported_ap(fp->u.s.fhh_id); 786 } 787 done: 788 return validate_ap(ap, rp, fp->fhh_gen); 789 } 790 791 static am_node * 792 fh_to_mp(am_nfs_fh *fhp) 793 { 794 int dummy; 795 796 return fh_to_mp3(fhp, &dummy, VLOOK_CREATE); 797 } 798 799 static am_node * 800 fh3_to_mp3(am_nfs_fh3 *fhp, int *rp, int vop) 801 { 802 struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data; 803 am_node *ap = NULL; 804 805 if (fp->u.s.fhh_type != 0) { 806 /* New filehandle type */ 807 int len = sizeof(*fp) - sizeof(fp->fhh_gen); 808 char *path = xmalloc(len+1); 809 /* 810 * Because fhp is treated as a filehandle we use memcpy 811 * instead of xstrlcpy. 812 */ 813 memcpy(path, (char *) fp->u.fhh_path, len); 814 path[len] = '\0'; 815 dlog("%s: new filehandle: %s", __func__, path); 816 817 ap = path_to_exported_ap(path); 818 XFREE(path); 819 } else { 820 dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id); 821 /* 822 * Check process id matches 823 * If it doesn't then it is probably 824 * from an old kernel-cached filehandle 825 * which is now out of date. 826 */ 827 if (fp->u.s.fhh_pid != get_server_pid()) { 828 dlog("%s: wrong pid %ld != my pid %ld", __func__, 829 (long) fp->u.s.fhh_pid, get_server_pid()); 830 goto done; 831 } 832 833 /* 834 * Get hold of the supposed mount node 835 */ 836 ap = get_exported_ap(fp->u.s.fhh_id); 837 } 838 done: 839 return validate_ap(ap, rp, fp->fhh_gen); 840 } 841 842 static am_node * 843 fh3_to_mp(am_nfs_fh3 *fhp) 844 { 845 int dummy; 846 847 return fh3_to_mp3(fhp, &dummy, VLOOK_CREATE); 848 } 849 850 /* 851 * Convert from automount node to file handle. 852 */ 853 void 854 mp_to_fh(am_node *mp, am_nfs_fh *fhp) 855 { 856 u_int pathlen; 857 struct am_fh *fp = (struct am_fh *) fhp; 858 859 memset((char *) fhp, 0, sizeof(am_nfs_fh)); 860 861 /* Store the generation number */ 862 fp->fhh_gen = mp->am_gen; 863 864 pathlen = strlen(mp->am_path); 865 if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) { 866 /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */ 867 868 /* 869 * Because fhp is treated as a filehandle we use memcpy instead of 870 * xstrlcpy. 871 */ 872 memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */ 873 } else { 874 /* 875 * Take the process id 876 */ 877 fp->u.s.fhh_pid = get_server_pid(); 878 879 /* 880 * ... the map number 881 */ 882 fp->u.s.fhh_id = mp->am_mapno; 883 884 /* 885 * ... and the generation number (previously stored) 886 * to make a "unique" triple that will never 887 * be reallocated except across reboots (which doesn't matter) 888 * or if we are unlucky enough to be given the same 889 * pid as a previous amd (very unlikely). 890 */ 891 /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */ 892 } 893 } 894 void 895 mp_to_fh3(am_node *mp, am_nfs_fh3 *fhp) 896 { 897 u_int pathlen; 898 struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data; 899 900 memset((char *) fhp, 0, sizeof(am_nfs_fh3)); 901 fhp->am_fh3_length = AM_FHSIZE3; 902 903 /* Store the generation number */ 904 fp->fhh_gen = mp->am_gen; 905 906 pathlen = strlen(mp->am_path); 907 if (pathlen <= sizeof(*fp) - sizeof(fp->fhh_gen)) { 908 /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */ 909 910 /* 911 * Because fhp is treated as a filehandle we use memcpy instead of 912 * xstrlcpy. 913 */ 914 memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */ 915 } else { 916 /* 917 * Take the process id 918 */ 919 fp->u.s.fhh_pid = get_server_pid(); 920 921 /* 922 * ... the map number 923 */ 924 fp->u.s.fhh_id = mp->am_mapno; 925 926 /* 927 * ... and the generation number (previously stored) 928 * to make a "unique" triple that will never 929 * be reallocated except across reboots (which doesn't matter) 930 * or if we are unlucky enough to be given the same 931 * pid as a previous amd (very unlikely). 932 */ 933 /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */ 934 } 935 } 936 937 #ifdef HAVE_FS_NFS3 938 static am_ftype3 ftype_to_ftype3(nfsftype ftype) 939 { 940 if (ftype == NFFIFO) 941 return AM_NF3FIFO; 942 else 943 return ftype; 944 } 945 946 static void nfstime_to_am_nfstime3(nfstime *time, am_nfstime3 *time3) 947 { 948 time3->seconds = time->seconds; 949 time3->nseconds = time->useconds * 1000; 950 } 951 952 static void rdev_to_am_specdata3(u_int rdev, am_specdata3 *rdev3) 953 { 954 /* No device node here */ 955 rdev3->specdata1 = (u_int) -1; 956 rdev3->specdata2 = (u_int) -1; 957 } 958 959 static void fattr_to_fattr3(nfsfattr *fattr, am_fattr3 *fattr3) 960 { 961 fattr3->type = ftype_to_ftype3(fattr->na_type); 962 fattr3->mode = (am_mode3) fattr->na_mode; 963 fattr3->nlink = fattr->na_nlink; 964 fattr3->uid = (am_uid3) fattr->na_uid; 965 fattr3->gid = (am_uid3) fattr->na_gid; 966 fattr3->size = (am_size3) fattr->na_size; 967 fattr3->used = (am_size3) fattr->na_size; 968 rdev_to_am_specdata3(fattr->na_rdev, &fattr3->rdev); 969 fattr3->fsid = (uint64) fattr->na_fsid; 970 fattr3->fileid = (uint64) fattr->na_fileid; 971 nfstime_to_am_nfstime3(&fattr->na_atime, &fattr3->atime); 972 nfstime_to_am_nfstime3(&fattr->na_mtime, &fattr3->mtime); 973 nfstime_to_am_nfstime3(&fattr->na_ctime, &fattr3->ctime); 974 } 975 976 static void fattr_to_wcc_attr(nfsfattr *fattr, am_wcc_attr *wcc_attr) 977 { 978 wcc_attr->size = (am_size3) fattr->na_size; 979 nfstime_to_am_nfstime3(&fattr->na_mtime, &wcc_attr->mtime); 980 nfstime_to_am_nfstime3(&fattr->na_ctime, &wcc_attr->ctime); 981 } 982 983 static am_nfsstat3 return_estale_or_rofs(am_nfs_fh3 *fh, 984 am_pre_op_attr *pre_op, 985 am_post_op_attr *post_op) 986 { 987 am_node *mp; 988 989 mp = fh3_to_mp(fh); 990 if (!mp) { 991 pre_op->attributes_follow = 0; 992 post_op->attributes_follow = 0; 993 return nfs_error(ESTALE); 994 } else { 995 am_fattr3 *fattr3 = &post_op->am_post_op_attr_u.attributes; 996 am_wcc_attr *wcc_attr = &pre_op->am_pre_op_attr_u.attributes; 997 nfsfattr *fattr = &mp->am_fattr; 998 pre_op->attributes_follow = 1; 999 fattr_to_wcc_attr(fattr, wcc_attr); 1000 post_op->attributes_follow = 1; 1001 fattr_to_fattr3(fattr, fattr3); 1002 return nfs_error(EROFS); 1003 } 1004 } 1005 1006 static am_nfsstat3 unlink3_or_rmdir3(am_diropargs3 *argp, 1007 am_wcc_data *wcc_data, int unlinkp) 1008 { 1009 static am_nfsstat3 res; 1010 am_nfs_fh3 *dir = &argp->dir; 1011 am_filename3 name = argp->name; 1012 am_pre_op_attr *pre_op_dir = &wcc_data->before; 1013 am_post_op_attr *post_op_dir = &wcc_data->after; 1014 nfsfattr *fattr; 1015 am_wcc_attr *wcc_attr; 1016 am_node *mp, *ap; 1017 int retry; 1018 1019 post_op_dir->attributes_follow = 0; 1020 1021 mp = fh3_to_mp3(dir, &retry, VLOOK_DELETE); 1022 if (!mp) { 1023 pre_op_dir->attributes_follow = 0; 1024 if (retry < 0) { 1025 amd_stats.d_drops++; 1026 return 0; 1027 } 1028 res = nfs_error(retry); 1029 goto out; 1030 } 1031 1032 pre_op_dir->attributes_follow = 1; 1033 fattr = &mp->am_fattr; 1034 wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes; 1035 fattr_to_wcc_attr(fattr, wcc_attr); 1036 1037 if (mp->am_fattr.na_type != NFDIR) { 1038 res = nfs_error(ENOTDIR); 1039 goto out; 1040 } 1041 1042 if (amuDebug(D_TRACE)) 1043 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, name); 1044 1045 ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &retry, VLOOK_DELETE); 1046 if (!ap) { 1047 /* 1048 * Ignore retries... 1049 */ 1050 if (retry < 0) 1051 retry = 0; 1052 /* 1053 * Usual NFS workaround... 1054 */ 1055 else if (retry == ENOENT) 1056 retry = 0; 1057 res = nfs_error(retry); 1058 } else { 1059 forcibly_timeout_mp(mp); 1060 res = AM_NFS3_OK; 1061 } 1062 1063 out: 1064 return res; 1065 } 1066 1067 voidp 1068 am_nfs3_null_3_svc(voidp argp, struct svc_req *rqstp) 1069 { 1070 static char * result; 1071 1072 return (voidp) &result; 1073 } 1074 1075 am_GETATTR3res * 1076 am_nfs3_getattr_3_svc(am_GETATTR3args *argp, struct svc_req *rqstp) 1077 { 1078 static am_GETATTR3res result; 1079 am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object; 1080 am_fattr3 *fattr3; 1081 nfsfattr *fattr; 1082 am_node *mp; 1083 int retry = 0; 1084 time_t now = clocktime(NULL); 1085 1086 if (amuDebug(D_TRACE)) 1087 plog(XLOG_DEBUG, "getattr_3:"); 1088 1089 mp = fh3_to_mp3(fh, &retry, VLOOK_CREATE); 1090 if (!mp) { 1091 if (amuDebug(D_TRACE)) 1092 plog(XLOG_DEBUG, "\tretry=%d", retry); 1093 1094 if (retry < 0) { 1095 amd_stats.d_drops++; 1096 return 0; 1097 } 1098 result.status = nfs_error(retry); 1099 return &result; 1100 } 1101 1102 fattr = &mp->am_fattr; 1103 fattr3 = (am_fattr3 *) &result.res_u.ok.obj_attributes; 1104 fattr_to_fattr3(fattr, fattr3); 1105 1106 result.status = AM_NFS3_OK; 1107 1108 if (amuDebug(D_TRACE)) 1109 plog(XLOG_DEBUG, "\tstat(%s), size = %llu, mtime=%d.%d", 1110 mp->am_path, 1111 (unsigned long long) fattr3->size, 1112 (u_int) fattr3->mtime.seconds, 1113 (u_int) fattr3->mtime.nseconds); 1114 1115 /* Delay unmount of what was looked up */ 1116 if (mp->am_timeo_w < 4 * gopt.am_timeo_w) 1117 mp->am_timeo_w += gopt.am_timeo_w; 1118 mp->am_ttl = now + mp->am_timeo_w; 1119 1120 mp->am_stats.s_getattr++; 1121 1122 return &result; 1123 } 1124 1125 am_SETATTR3res * 1126 am_nfs3_setattr_3_svc(am_SETATTR3args *argp, struct svc_req *rqstp) 1127 { 1128 static am_SETATTR3res result; 1129 am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object; 1130 am_pre_op_attr *pre_op_obj = &result.res_u.fail.obj_wcc.before; 1131 am_post_op_attr *post_op_obj = &result.res_u.fail.obj_wcc.after; 1132 1133 if (amuDebug(D_TRACE)) 1134 plog(XLOG_DEBUG, "setattr_3:"); 1135 1136 result.status = return_estale_or_rofs(fh, pre_op_obj, post_op_obj); 1137 1138 return &result; 1139 } 1140 1141 am_LOOKUP3res * 1142 am_nfs3_lookup_3_svc(am_LOOKUP3args *argp, struct svc_req *rqstp) 1143 { 1144 static am_LOOKUP3res result; 1145 am_nfs_fh3 *dir = &argp->what.dir; 1146 am_post_op_attr *post_op_dir; 1147 am_post_op_attr *post_op_obj; 1148 am_node *mp; 1149 int retry; 1150 uid_t uid; 1151 gid_t gid; 1152 1153 if (amuDebug(D_TRACE)) 1154 plog(XLOG_DEBUG, "lookup_3:"); 1155 1156 /* finally, find the effective uid/gid from RPC request */ 1157 if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0) 1158 plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials"); 1159 xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid); 1160 xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid); 1161 1162 mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE); 1163 if (!mp) { 1164 post_op_dir = &result.res_u.fail.dir_attributes; 1165 post_op_dir->attributes_follow = 0; 1166 if (retry < 0) { 1167 amd_stats.d_drops++; 1168 return 0; 1169 } 1170 result.status = nfs_error(retry); 1171 } else { 1172 post_op_dir = &result.res_u.ok.dir_attributes; 1173 post_op_obj = &result.res_u.ok.obj_attributes; 1174 am_filename3 name; 1175 am_fattr3 *fattr3; 1176 nfsfattr *fattr; 1177 am_node *ap; 1178 int error; 1179 1180 /* dir attributes */ 1181 post_op_dir->attributes_follow = 1; 1182 fattr = &mp->am_fattr; 1183 fattr3 = &post_op_dir->am_post_op_attr_u.attributes; 1184 fattr_to_fattr3(fattr, fattr3); 1185 1186 post_op_obj->attributes_follow = 0; 1187 1188 name = argp->what.name; 1189 1190 if (amuDebug(D_TRACE)) 1191 plog(XLOG_DEBUG, "\tlookup_3(%s, %s)", mp->am_path, name); 1192 1193 ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &error, VLOOK_CREATE); 1194 if (ap && error < 0) 1195 ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error); 1196 if (ap == 0) { 1197 if (error < 0) { 1198 amd_stats.d_drops++; 1199 return 0; 1200 } 1201 result.status = nfs_error(error); 1202 } else { 1203 /* 1204 * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This 1205 * should reduce the chance for race condition between unmounting an 1206 * entry synchronously, and re-mounting it asynchronously. 1207 */ 1208 if (ap->am_ttl < mp->am_ttl) 1209 ap->am_ttl = mp->am_ttl; 1210 1211 mp_to_fh3(ap, &result.res_u.ok.object); 1212 1213 /* mount attributes */ 1214 post_op_obj->attributes_follow = 1; 1215 fattr = &ap->am_fattr; 1216 fattr3 = &post_op_obj->am_post_op_attr_u.attributes; 1217 fattr_to_fattr3(fattr, fattr3); 1218 1219 result.status = AM_NFS3_OK; 1220 } 1221 mp->am_stats.s_lookup++; 1222 } 1223 return &result; 1224 } 1225 1226 am_ACCESS3res * 1227 am_nfs3_access_3_svc(am_ACCESS3args *argp, struct svc_req *rqstp) 1228 { 1229 static am_ACCESS3res result; 1230 1231 am_nfs_fh3 *obj = &argp->object; 1232 u_int accessbits = argp->access; 1233 u_int accessmask = AM_ACCESS3_LOOKUP|AM_ACCESS3_READ; 1234 am_post_op_attr *post_op_obj; 1235 am_node *mp; 1236 1237 if (amuDebug(D_TRACE)) 1238 plog(XLOG_DEBUG, "access_3:"); 1239 1240 mp = fh3_to_mp(obj); 1241 if (!mp) { 1242 post_op_obj = &result.res_u.fail.obj_attributes; 1243 post_op_obj->attributes_follow = 0; 1244 result.status = nfs_error(ENOENT); 1245 if (amuDebug(D_TRACE)) 1246 plog(XLOG_DEBUG, "access_3: ENOENT"); 1247 } else { 1248 nfsfattr *fattr = &mp->am_fattr; 1249 am_fattr3 *fattr3; 1250 post_op_obj = &result.res_u.ok.obj_attributes; 1251 fattr3 = &post_op_obj->am_post_op_attr_u.attributes; 1252 post_op_obj->attributes_follow = 1; 1253 fattr_to_fattr3(fattr, fattr3); 1254 1255 result.res_u.ok.access = accessbits & accessmask; 1256 if (amuDebug(D_TRACE)) 1257 plog(XLOG_DEBUG, "access_3: b=%x m=%x", accessbits, accessmask); 1258 1259 result.status = AM_NFS3_OK; 1260 } 1261 1262 return &result; 1263 } 1264 1265 am_READLINK3res * 1266 am_nfs3_readlink_3_svc(am_READLINK3args *argp, struct svc_req *rqstp) 1267 { 1268 static am_READLINK3res result; 1269 1270 am_nfs_fh3 *symlink = (am_nfs_fh3 *) &argp->symlink; 1271 am_post_op_attr *post_op_sym; 1272 am_node *mp; 1273 int retry = 0; 1274 1275 if (amuDebug(D_TRACE)) 1276 plog(XLOG_DEBUG, "readlink_3:"); 1277 1278 mp = fh3_to_mp3(symlink, &retry, VLOOK_CREATE); 1279 if (!mp) { 1280 readlink_retry: 1281 if (retry < 0) { 1282 amd_stats.d_drops++; 1283 return 0; 1284 } 1285 post_op_sym = &result.res_u.fail.symlink_attributes; 1286 post_op_sym->attributes_follow = 0; 1287 result.status = nfs_error(retry); 1288 } else { 1289 nfsfattr *fattr; 1290 am_fattr3 *fattr3; 1291 char *ln; 1292 1293 ln = do_readlink(mp, &retry); 1294 if (!ln) 1295 goto readlink_retry; 1296 1297 if (amuDebug(D_TRACE) && ln) 1298 plog(XLOG_DEBUG, "\treadlink_3(%s) = %s", mp->am_path, ln); 1299 1300 result.res_u.ok.data = ln; 1301 1302 post_op_sym = &result.res_u.ok.symlink_attributes; 1303 post_op_sym->attributes_follow = 1; 1304 fattr = &mp->am_fattr; 1305 fattr3 = &post_op_sym->am_post_op_attr_u.attributes; 1306 fattr_to_fattr3(fattr, fattr3); 1307 1308 mp->am_stats.s_readlink++; 1309 result.status = AM_NFS3_OK; 1310 } 1311 1312 return &result; 1313 } 1314 1315 am_READ3res * 1316 am_nfs3_read_3_svc(am_READ3args *argp, struct svc_req *rqstp) 1317 { 1318 static am_READ3res result; 1319 1320 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; 1321 am_post_op_attr *post_op_file; 1322 am_node *mp; 1323 1324 if (amuDebug(D_TRACE)) 1325 plog(XLOG_DEBUG, "read_3:"); 1326 1327 post_op_file = &result.res_u.fail.file_attributes; 1328 result.status = nfs_error(EACCES); 1329 1330 mp = fh3_to_mp(file); 1331 if (!mp) 1332 post_op_file->attributes_follow = 0; 1333 else { 1334 nfsfattr *fattr = &mp->am_fattr; 1335 am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes; 1336 post_op_file->attributes_follow = 1; 1337 fattr_to_fattr3(fattr, fattr3); 1338 } 1339 1340 return &result; 1341 } 1342 1343 am_WRITE3res * 1344 am_nfs3_write_3_svc(am_WRITE3args *argp, struct svc_req *rqstp) 1345 { 1346 static am_WRITE3res result; 1347 1348 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; 1349 am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before; 1350 am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after; 1351 1352 if (amuDebug(D_TRACE)) 1353 plog(XLOG_DEBUG, "write_3:"); 1354 1355 result.status = return_estale_or_rofs(file, pre_op_file, post_op_file); 1356 1357 return &result; 1358 } 1359 1360 am_CREATE3res * 1361 am_nfs3_create_3_svc(am_CREATE3args *argp, struct svc_req *rqstp) 1362 { 1363 static am_CREATE3res result; 1364 1365 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; 1366 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; 1367 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; 1368 1369 if (amuDebug(D_TRACE)) 1370 plog(XLOG_DEBUG, "create_3:"); 1371 1372 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); 1373 1374 return &result; 1375 } 1376 1377 am_MKDIR3res * 1378 am_nfs3_mkdir_3_svc(am_MKDIR3args *argp, struct svc_req *rqstp) 1379 { 1380 static am_MKDIR3res result; 1381 1382 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; 1383 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; 1384 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; 1385 1386 if (amuDebug(D_TRACE)) 1387 plog(XLOG_DEBUG, "mkdir_3:"); 1388 1389 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); 1390 1391 return &result; 1392 } 1393 1394 am_SYMLINK3res * 1395 am_nfs3_symlink_3_svc(am_SYMLINK3args *argp, struct svc_req *rqstp) 1396 { 1397 static am_SYMLINK3res result; 1398 1399 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; 1400 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; 1401 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; 1402 1403 if (amuDebug(D_TRACE)) 1404 plog(XLOG_DEBUG, "symlink_3:"); 1405 1406 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); 1407 1408 return &result; 1409 } 1410 1411 am_MKNOD3res * 1412 am_nfs3_mknod_3_svc(am_MKNOD3args *argp, struct svc_req *rqstp) 1413 { 1414 static am_MKNOD3res result; 1415 1416 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir; 1417 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before; 1418 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after; 1419 1420 if (amuDebug(D_TRACE)) 1421 plog(XLOG_DEBUG, "mknod_3:"); 1422 1423 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir); 1424 return &result; 1425 } 1426 1427 am_REMOVE3res * 1428 am_nfs3_remove_3_svc(am_REMOVE3args *argp, struct svc_req *rqstp) 1429 { 1430 static am_REMOVE3res result; 1431 1432 am_diropargs3 *obj = &argp->object; 1433 am_wcc_data dir_wcc; 1434 1435 if (amuDebug(D_TRACE)) 1436 plog(XLOG_DEBUG, "remove_3:"); 1437 1438 result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE); 1439 1440 result.res_u.ok.dir_wcc = dir_wcc; 1441 1442 return &result; 1443 } 1444 1445 am_RMDIR3res * 1446 am_nfs3_rmdir_3_svc(am_RMDIR3args *argp, struct svc_req *rqstp) 1447 { 1448 static am_RMDIR3res result; 1449 1450 am_diropargs3 *obj = &argp->object; 1451 am_wcc_data dir_wcc; 1452 1453 if (amuDebug(D_TRACE)) 1454 plog(XLOG_DEBUG, "rmdir_3:"); 1455 1456 result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE); 1457 1458 result.res_u.ok.dir_wcc = dir_wcc; 1459 1460 return &result; 1461 } 1462 1463 am_RENAME3res * 1464 am_nfs3_rename_3_svc(am_RENAME3args *argp, struct svc_req *rqstp) 1465 { 1466 static am_RENAME3res result; 1467 1468 am_nfs_fh3 *fromdir = (am_nfs_fh3 *) &argp->from.dir; 1469 am_nfs_fh3 *todir = (am_nfs_fh3 *) &argp->to.dir; 1470 am_filename3 name = argp->to.name; 1471 am_node *to_mp, *from_mp; 1472 1473 if (amuDebug(D_TRACE)) 1474 plog(XLOG_DEBUG, "rename_3:"); 1475 1476 if (!(from_mp = fh3_to_mp(fromdir)) || !(to_mp = fh3_to_mp(todir))) 1477 result.status = nfs_error(ESTALE); 1478 /* 1479 * If the kernel is doing clever things with referenced files 1480 * then let it pretend... 1481 */ 1482 else { 1483 am_wcc_attr *wcc_attr; 1484 am_fattr3 *fattr3; 1485 am_wcc_data *to_wcc_data, *from_wcc_data; 1486 am_pre_op_attr *pre_op_to, *pre_op_from; 1487 am_post_op_attr *post_op_to, *post_op_from; 1488 nfsfattr *fattr; 1489 1490 to_wcc_data = &result.res_u.ok.todir_wcc; 1491 1492 pre_op_to = &to_wcc_data->before; 1493 post_op_to = &to_wcc_data->after; 1494 1495 pre_op_to->attributes_follow = 1; 1496 fattr = &to_mp->am_fattr; 1497 wcc_attr = &pre_op_to->am_pre_op_attr_u.attributes; 1498 fattr_to_wcc_attr(fattr, wcc_attr); 1499 post_op_to->attributes_follow = 1; 1500 fattr3 = &post_op_to->am_post_op_attr_u.attributes; 1501 fattr_to_fattr3(fattr, fattr3); 1502 1503 from_wcc_data = &result.res_u.ok.fromdir_wcc; 1504 1505 pre_op_from = &from_wcc_data->before; 1506 post_op_from = &from_wcc_data->after; 1507 1508 pre_op_from->attributes_follow = 1; 1509 fattr = &from_mp->am_fattr; 1510 wcc_attr = &pre_op_from->am_pre_op_attr_u.attributes; 1511 fattr_to_wcc_attr(fattr, wcc_attr); 1512 post_op_from->attributes_follow = 1; 1513 fattr3 = &post_op_from->am_post_op_attr_u.attributes; 1514 fattr_to_fattr3(fattr, fattr3); 1515 1516 if (NSTREQ(name, ".nfs", 4)) 1517 result.status = AM_NFS3_OK; 1518 /* 1519 * otherwise a failure 1520 */ 1521 else 1522 result.status = nfs_error(EROFS); 1523 } 1524 1525 return &result; 1526 } 1527 1528 am_LINK3res * 1529 am_nfs3_link_3_svc(am_LINK3args *argp, struct svc_req *rqstp) 1530 { 1531 static am_LINK3res result; 1532 1533 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; 1534 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->link.dir; 1535 am_post_op_attr *post_op_file; 1536 am_pre_op_attr *pre_op_dir; 1537 am_post_op_attr *post_op_dir; 1538 am_node *mp_file, *mp_dir; 1539 1540 if (amuDebug(D_TRACE)) 1541 plog(XLOG_DEBUG, "link_3:"); 1542 1543 post_op_file = &result.res_u.fail.file_attributes; 1544 post_op_file->attributes_follow = 0; 1545 1546 mp_file = fh3_to_mp(file); 1547 if (mp_file) { 1548 nfsfattr *fattr = &mp_file->am_fattr; 1549 am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes; 1550 fattr_to_fattr3(fattr, fattr3); 1551 } 1552 1553 pre_op_dir = &result.res_u.fail.linkdir_wcc.before; 1554 pre_op_dir->attributes_follow = 0; 1555 post_op_dir = &result.res_u.fail.linkdir_wcc.after; 1556 post_op_dir->attributes_follow = 0; 1557 1558 mp_dir = fh3_to_mp(dir); 1559 if (mp_dir) { 1560 nfsfattr *fattr = &mp_dir->am_fattr; 1561 am_fattr3 *fattr3 = &post_op_dir->am_post_op_attr_u.attributes; 1562 am_wcc_attr *wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes; 1563 1564 pre_op_dir->attributes_follow = 1; 1565 fattr_to_wcc_attr(fattr, wcc_attr); 1566 post_op_dir->attributes_follow = 1; 1567 fattr_to_fattr3(fattr, fattr3); 1568 } 1569 1570 if (!mp_file || !mp_dir) 1571 result.status = nfs_error(ESTALE); 1572 else 1573 result.status = nfs_error(EROFS); 1574 1575 return &result; 1576 } 1577 1578 am_READDIR3res * 1579 am_nfs3_readdir_3_svc(am_READDIR3args *argp, struct svc_req *rqstp) 1580 { 1581 static am_READDIR3res result; 1582 static am_entry3 entries[MAX_READDIR_ENTRIES]; 1583 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir; 1584 am_cookie3 cookie = argp->cookie; 1585 am_cookieverf3 cookieverf; 1586 am_count3 count = argp->count; 1587 am_post_op_attr *post_op_dir; 1588 am_node *mp; 1589 int retry; 1590 1591 if (amuDebug(D_TRACE)) 1592 plog(XLOG_DEBUG, "readdir_3:"); 1593 1594 memcpy(&cookieverf, &argp->cookieverf, sizeof(am_cookieverf3)); 1595 1596 mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE); 1597 if (mp == NULL) { 1598 if (retry < 0) { 1599 amd_stats.d_drops++; 1600 return 0; 1601 } 1602 post_op_dir = &result.res_u.fail.dir_attributes; 1603 post_op_dir->attributes_follow = 0; 1604 result.status = nfs_error(retry); 1605 } else { 1606 am_dirlist3 *list = &result.res_u.ok.reply; 1607 am_nfsstat3 status; 1608 1609 if (amuDebug(D_TRACE)) 1610 plog(XLOG_DEBUG, "\treaddir_3(%s)", mp->am_path); 1611 1612 status = mp->am_al->al_mnt->mf_ops->readdir(mp, 1613 (voidp)&cookie, list, entries, count); 1614 if (status == 0) { 1615 post_op_dir = &result.res_u.ok.dir_attributes; 1616 nfsfattr *fattr; 1617 am_fattr3 *fattr3; 1618 1619 fattr = &mp->am_fattr; 1620 fattr3 = &post_op_dir->am_post_op_attr_u.attributes; 1621 post_op_dir->attributes_follow = 1; 1622 fattr_to_fattr3(fattr, fattr3); 1623 result.status = AM_NFS3_OK; 1624 } else { 1625 post_op_dir = &result.res_u.fail.dir_attributes; 1626 post_op_dir->attributes_follow = 0; 1627 result.status = nfs_error(status); 1628 } 1629 1630 mp->am_stats.s_readdir++; 1631 } 1632 1633 return &result; 1634 } 1635 1636 am_READDIRPLUS3res * 1637 am_nfs3_readdirplus_3_svc(am_READDIRPLUS3args *argp, struct svc_req *rqstp) 1638 { 1639 static am_READDIRPLUS3res result; 1640 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir; 1641 am_post_op_attr *post_op_dir; 1642 nfsfattr *fattr; 1643 am_fattr3 *fattr3; 1644 am_node *mp; 1645 int retry; 1646 1647 mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE); 1648 if (mp == NULL) { 1649 if (retry < 0) { 1650 amd_stats.d_drops++; 1651 return 0; 1652 } 1653 post_op_dir = &result.res_u.fail.dir_attributes; 1654 post_op_dir->attributes_follow = 0; 1655 result.status = nfs_error(retry); 1656 } else { 1657 post_op_dir = &result.res_u.ok.dir_attributes; 1658 fattr = &mp->am_fattr; 1659 fattr3 = &post_op_dir->am_post_op_attr_u.attributes; 1660 post_op_dir->attributes_follow = 1; 1661 fattr_to_fattr3(fattr, fattr3); 1662 result.status = AM_NFS3ERR_NOTSUPP; 1663 } 1664 1665 return &result; 1666 } 1667 1668 am_FSSTAT3res * 1669 am_nfs3_fsstat_3_svc(am_FSSTAT3args *argp, struct svc_req *rqstp) 1670 { 1671 static am_FSSTAT3res result; 1672 1673 am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot; 1674 am_post_op_attr *post_op_fsroot; 1675 am_node *mp; 1676 int retry; 1677 1678 if (amuDebug(D_TRACE)) 1679 plog(XLOG_DEBUG, "fsstat_3:"); 1680 1681 mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE); 1682 if (!mp) { 1683 if (retry < 0) { 1684 amd_stats.d_drops++; 1685 return 0; 1686 } 1687 post_op_fsroot = &result.res_u.fail.obj_attributes; 1688 post_op_fsroot->attributes_follow = 0; 1689 result.status = nfs_error(retry); 1690 } else { 1691 am_FSSTAT3resok *ok = &result.res_u.ok; 1692 u_int blocks, bfree, bavail; 1693 nfsfattr *fattr; 1694 am_fattr3 *fattr3; 1695 mntent_t mnt; 1696 1697 if (amuDebug(D_TRACE)) 1698 plog(XLOG_DEBUG, "\tfsstat_3(%s)", mp->am_path); 1699 1700 fattr = &mp->am_fattr; 1701 post_op_fsroot = &ok->obj_attributes; 1702 post_op_fsroot->attributes_follow = 1; 1703 fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes; 1704 fattr_to_fattr3(fattr, fattr3); 1705 1706 /* 1707 * just return faked up file system information 1708 */ 1709 ok->tbytes = 1024; 1710 ok->invarsec = 0; 1711 1712 /* check if map is browsable and show_statfs_entries=yes */ 1713 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && 1714 mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) { 1715 mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts; 1716 blocks = 0; 1717 bfree = 0; 1718 bavail = 0; 1719 if (amu_hasmntopt(&mnt, "browsable")) { 1720 count_map_entries(mp, &blocks, &bfree, &bavail); 1721 } 1722 ok->fbytes = bfree; 1723 ok->abytes = bavail; 1724 ok->ffiles = bfree; 1725 ok->afiles = bavail; 1726 ok->tfiles = blocks; 1727 } else { 1728 ok->fbytes = 0; 1729 ok->abytes = 0; 1730 ok->ffiles = 0; 1731 ok->afiles = 0; 1732 ok->tfiles = 0; /* set to 1 if you don't want empty automounts */ 1733 } 1734 1735 result.status = AM_NFS3_OK; 1736 mp->am_stats.s_statfs++; 1737 } 1738 1739 return &result; 1740 } 1741 1742 #define FSF3_HOMOGENEOUS 0x0008 1743 1744 am_FSINFO3res * 1745 am_nfs3_fsinfo_3_svc(am_FSINFO3args *argp, struct svc_req *rqstp) 1746 { 1747 static am_FSINFO3res result; 1748 1749 am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot; 1750 am_post_op_attr *post_op_fsroot; 1751 am_node *mp; 1752 int retry; 1753 1754 if (amuDebug(D_TRACE)) 1755 plog(XLOG_DEBUG, "fsinfo_3:"); 1756 1757 mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE); 1758 if (!mp) { 1759 if (retry < 0) { 1760 amd_stats.d_drops++; 1761 return 0; 1762 } 1763 post_op_fsroot = &result.res_u.fail.obj_attributes; 1764 post_op_fsroot->attributes_follow = 0; 1765 result.status = nfs_error(retry); 1766 } else { 1767 am_FSINFO3resok *ok = &result.res_u.ok; 1768 nfsfattr *fattr; 1769 am_fattr3 *fattr3; 1770 1771 if (amuDebug(D_TRACE)) 1772 plog(XLOG_DEBUG, "\tfsinfo_3(%s)", mp->am_path); 1773 1774 fattr = &mp->am_fattr; 1775 post_op_fsroot = &ok->obj_attributes; 1776 post_op_fsroot->attributes_follow = 1; 1777 fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes; 1778 fattr_to_fattr3(fattr, fattr3); 1779 1780 /* 1781 * just return faked up file system information 1782 */ 1783 ok->rtmax = 0; 1784 ok->rtpref = 0; 1785 ok->rtmult = 0; 1786 ok->wtmax = 0; 1787 ok->wtpref = 0; 1788 ok->wtmult = 0; 1789 ok->dtpref = 1024; 1790 ok->maxfilesize = 0; 1791 ok->time_delta.seconds = 1; 1792 ok->time_delta.nseconds = 0; 1793 ok->properties = FSF3_HOMOGENEOUS; 1794 1795 result.status = AM_NFS3_OK; 1796 mp->am_stats.s_fsinfo++; 1797 } 1798 1799 return &result; 1800 } 1801 1802 am_PATHCONF3res * 1803 am_nfs3_pathconf_3_svc(am_PATHCONF3args *argp, struct svc_req *rqstp) 1804 { 1805 static am_PATHCONF3res result; 1806 1807 am_nfs_fh3 *obj = (am_nfs_fh3 *) &argp->object; 1808 am_post_op_attr *post_op_obj; 1809 am_node *mp; 1810 int retry; 1811 1812 if (amuDebug(D_TRACE)) 1813 plog(XLOG_DEBUG, "pathconf_3:"); 1814 1815 mp = fh3_to_mp3(obj, &retry, VLOOK_CREATE); 1816 if (!mp) { 1817 if (retry < 0) { 1818 amd_stats.d_drops++; 1819 return 0; 1820 } 1821 post_op_obj = &result.res_u.fail.obj_attributes; 1822 post_op_obj->attributes_follow = 0; 1823 result.status = nfs_error(retry); 1824 } else { 1825 am_PATHCONF3resok *ok = &result.res_u.ok; 1826 nfsfattr *fattr; 1827 am_fattr3 *fattr3; 1828 1829 if (amuDebug(D_TRACE)) 1830 plog(XLOG_DEBUG, "\tpathconf_3(%s)", mp->am_path); 1831 1832 fattr = &mp->am_fattr; 1833 post_op_obj = &ok->obj_attributes; 1834 post_op_obj->attributes_follow = 1; 1835 fattr3 = &post_op_obj->am_post_op_attr_u.attributes; 1836 fattr_to_fattr3(fattr, fattr3); 1837 1838 ok->linkmax = 0; 1839 ok->name_max = NAME_MAX; 1840 ok->no_trunc = 1; 1841 ok->chown_restricted = 1; 1842 ok->case_insensitive = 0; 1843 ok->case_preserving = 1; 1844 1845 result.status = AM_NFS3_OK; 1846 mp->am_stats.s_pathconf++; 1847 } 1848 1849 return &result; 1850 } 1851 1852 am_COMMIT3res * 1853 am_nfs3_commit_3_svc(am_COMMIT3args *argp, struct svc_req *rqstp) 1854 { 1855 static am_COMMIT3res result; 1856 1857 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file; 1858 am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before; 1859 am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after; 1860 1861 if (amuDebug(D_TRACE)) 1862 plog(XLOG_DEBUG, "commit_3:"); 1863 1864 result.status = return_estale_or_rofs(file, pre_op_file, post_op_file); 1865 1866 return &result; 1867 } 1868 #endif /* HAVE_FS_NFS3 */ 1869