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