Print this page
2988 nfssrv: need ability to go to submounts for v3 and v2 protocols
Portions contributed by: Marcel Telka <marcel.telka@nexenta.com>
Portions contributed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Change-Id: I6fdf110cc17e789353c4442b83a46cb80643456e


 365 /* ARGSUSED */
 366 void
 367 rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
 368     struct svc_req *req, cred_t *cr, bool_t ro)
 369 {
 370         int error;
 371         vnode_t *vp;
 372         vnode_t *dvp;
 373         struct vattr *vap;
 374         struct vattr va;
 375         struct vattr *dvap;
 376         struct vattr dva;
 377         nfs_fh3 *fhp;
 378         struct sec_ol sec = {0, 0};
 379         bool_t publicfh_flag = FALSE, auth_weak = FALSE;
 380         struct sockaddr *ca;
 381         char *name = NULL;
 382 
 383         dvap = NULL;
 384 



 385         /*
 386          * Allow lookups from the root - the default
 387          * location of the public filehandle.
 388          */
 389         if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
 390                 dvp = rootdir;
 391                 VN_HOLD(dvp);
 392 
 393                 DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
 394                     cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
 395         } else {
 396                 dvp = nfs3_fhtovp(&args->what.dir, exi);
 397 
 398                 DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
 399                     cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
 400 
 401                 if (dvp == NULL) {
 402                         error = ESTALE;
 403                         goto out;
 404                 }
 405         }
 406 
 407         dva.va_mask = AT_ALL;
 408         dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
 409 
 410         if (args->what.name == nfs3nametoolong) {
 411                 resp->status = NFS3ERR_NAMETOOLONG;
 412                 goto out1;
 413         }
 414 
 415         if (args->what.name == NULL || *(args->what.name) == '\0') {
 416                 resp->status = NFS3ERR_ACCES;
 417                 goto out1;
 418         }
 419 
 420         fhp = &args->what.dir;
 421         if (strcmp(args->what.name, "..") == 0 &&
 422             EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {










 423                 resp->status = NFS3ERR_NOENT;
 424                 goto out1;
 425         }

 426 
 427         ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
 428         name = nfscmd_convname(ca, exi, args->what.name,
 429             NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
 430 
 431         if (name == NULL) {
 432                 resp->status = NFS3ERR_ACCES;
 433                 goto out1;
 434         }
 435 
 436         /*
 437          * If the public filehandle is used then allow
 438          * a multi-component lookup
 439          */
 440         if (PUBLIC_FH3(&args->what.dir)) {
 441                 publicfh_flag = TRUE;



 442                 error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
 443                     &exi, &sec);
 444                 if (error && exi != NULL)
 445                         exi_rele(exi); /* See comment below Re: publicfh_flag */
 446                 /*
 447                  * Since WebNFS may bypass MOUNT, we need to ensure this
 448                  * request didn't come from an unlabeled admin_low client.
 449                  */
 450                 if (is_system_labeled() && error == 0) {
 451                         int             addr_type;
 452                         void            *ipaddr;
 453                         tsol_tpc_t      *tp;
 454 
 455                         if (ca->sa_family == AF_INET) {
 456                                 addr_type = IPV4_VERSION;
 457                                 ipaddr = &((struct sockaddr_in *)ca)->sin_addr;
 458                         } else if (ca->sa_family == AF_INET6) {
 459                                 addr_type = IPV6_VERSION;
 460                                 ipaddr = &((struct sockaddr_in6 *)
 461                                     ca)->sin6_addr;
 462                         }
 463                         tp = find_tpc(ipaddr, addr_type, B_FALSE);
 464                         if (tp == NULL || tp->tpc_tp.tp_doi !=
 465                             l_admin_low->tsl_doi || tp->tpc_tp.host_type !=
 466                             SUN_CIPSO) {
 467                                 if (exi != NULL)
 468                                         exi_rele(exi);
 469                                 VN_RELE(vp);
 470                                 error = EACCES;
 471                         }
 472                         if (tp != NULL)
 473                                 TPC_RELE(tp);
 474                 }
 475         } else {
 476                 error = VOP_LOOKUP(dvp, name, &vp,
 477                     NULL, 0, NULL, cr, NULL, NULL, NULL);
 478         }
 479 
 480         if (name != args->what.name)
 481                 kmem_free(name, MAXPATHLEN + 1);
 482 






 483         if (is_system_labeled() && error == 0) {
 484                 bslabel_t *clabel = req->rq_label;
 485 
 486                 ASSERT(clabel != NULL);
 487                 DTRACE_PROBE2(tx__rfs3__log__info__oplookup__clabel, char *,
 488                     "got client label from request(1)", struct svc_req *, req);
 489 
 490                 if (!blequal(&l_admin_low->tsl_label, clabel)) {
 491                         if (!do_rfs_label_check(clabel, dvp,
 492                             DOMINANCE_CHECK, exi)) {
 493                                 if (publicfh_flag && exi != NULL)
 494                                         exi_rele(exi);
 495                                 VN_RELE(vp);
 496                                 error = EACCES;
 497                         }
 498                 }
 499         }
 500 
 501         dva.va_mask = AT_ALL;
 502         dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
 503 
 504         if (error)
 505                 goto out;
 506 
 507         if (sec.sec_flags & SEC_QUERY) {
 508                 error = makefh3_ol(&resp->resok.object, exi, sec.sec_index);
 509         } else {
 510                 error = makefh3(&resp->resok.object, vp, exi);
 511                 if (!error && publicfh_flag && !chk_clnt_sec(exi, req))
 512                         auth_weak = TRUE;
 513         }
 514 
 515         /*
 516          * If publicfh_flag is true then we have called rfs_publicfh_mclookup
 517          * and have obtained a new exportinfo in exi which needs to be
 518          * released. Note that the original exportinfo pointed to by exi
 519          * will be released by the caller, common_dispatch.
 520          */
 521         if (publicfh_flag)
 522                 exi_rele(exi);
 523 
 524         if (error) {
 525                 VN_RELE(vp);
 526                 goto out;
 527         }
 528 
 529         va.va_mask = AT_ALL;
 530         vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 531 

 532         VN_RELE(vp);
 533 
 534         resp->status = NFS3_OK;
 535         vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 536         vattr_to_post_op_attr(dvap, &resp->resok.dir_attributes);
 537 
 538         /*
 539          * If it's public fh, no 0x81, and client's flavor is
 540          * invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.
 541          * Then set RPC status to AUTH_TOOWEAK in common_dispatch.
 542          */
 543         if (auth_weak)
 544                 resp->status = (enum nfsstat3)WNFSERR_CLNT_FLAVOR;
 545 
 546         DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
 547             cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
 548         VN_RELE(dvp);
 549 
 550         return;
 551 
 552 out:
 553         if (curthread->t_flag & T_WOULDBLOCK) {
 554                 curthread->t_flag &= ~T_WOULDBLOCK;
 555                 resp->status = NFS3ERR_JUKEBOX;
 556         } else
 557                 resp->status = puterrno3(error);
 558 out1:



 559         DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
 560             cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
 561 
 562         if (dvp != NULL)
 563                 VN_RELE(dvp);
 564         vattr_to_post_op_attr(dvap, &resp->resfail.dir_attributes);
 565 
 566 }
 567 
 568 void *
 569 rfs3_lookup_getfh(LOOKUP3args *args)
 570 {
 571 
 572         return (&args->what.dir);
 573 }
 574 
 575 /* ARGSUSED */
 576 void
 577 rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi,
 578     struct svc_req *req, cred_t *cr, bool_t ro)


3592                 }
3593 
3594                 infop[i].namelen = namlen[i];
3595 
3596                 error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr,
3597                     NULL, NULL, NULL);
3598                 if (error) {
3599                         infop[i].attr.attributes = FALSE;
3600                         infop[i].fh.handle_follows = FALSE;
3601                         dp = nextdp(dp);
3602                         continue;
3603                 }
3604 
3605                 nva.va_mask = AT_ALL;
3606                 nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva;
3607 
3608                 /* Lie about the object type for a referral */
3609                 if (vn_is_nfs_reparse(nvp, cr))
3610                         nvap->va_type = VLNK;
3611 




3612                 vattr_to_post_op_attr(nvap, &infop[i].attr);
3613 
3614                 error = makefh3(&infop[i].fh.handle, nvp, exi);
3615                 if (!error)
3616                         infop[i].fh.handle_follows = TRUE;
3617                 else
3618                         infop[i].fh.handle_follows = FALSE;

3619 
3620                 VN_RELE(nvp);
3621                 dp = nextdp(dp);
3622         }
3623 
3624         ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
3625         ret = nfscmd_convdirplus(ca, exi, data, nents, args->dircount, &ndata);
3626         if (ndata == NULL)
3627                 ndata = data;
3628 
3629         if (ret > 0) {
3630                 /*
3631                  * We had to drop one or more entries in order to fit
3632                  * during the character conversion.  We need to patch
3633                  * up the size and eof info.
3634                  */
3635                 if (iseof)
3636                         iseof = FALSE;
3637 
3638                 ret = nfscmd_dropped_entrysize((struct dirent64 *)data,




 365 /* ARGSUSED */
 366 void
 367 rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
 368     struct svc_req *req, cred_t *cr, bool_t ro)
 369 {
 370         int error;
 371         vnode_t *vp;
 372         vnode_t *dvp;
 373         struct vattr *vap;
 374         struct vattr va;
 375         struct vattr *dvap;
 376         struct vattr dva;
 377         nfs_fh3 *fhp;
 378         struct sec_ol sec = {0, 0};
 379         bool_t publicfh_flag = FALSE, auth_weak = FALSE;
 380         struct sockaddr *ca;
 381         char *name = NULL;
 382 
 383         dvap = NULL;
 384 
 385         if (exi != NULL)
 386                 exi_hold(exi);
 387 
 388         /*
 389          * Allow lookups from the root - the default
 390          * location of the public filehandle.
 391          */
 392         if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
 393                 dvp = rootdir;
 394                 VN_HOLD(dvp);
 395 
 396                 DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
 397                     cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
 398         } else {
 399                 dvp = nfs3_fhtovp(&args->what.dir, exi);
 400 
 401                 DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
 402                     cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
 403 
 404                 if (dvp == NULL) {
 405                         error = ESTALE;
 406                         goto out;
 407                 }
 408         }
 409 
 410         dva.va_mask = AT_ALL;
 411         dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
 412 
 413         if (args->what.name == nfs3nametoolong) {
 414                 resp->status = NFS3ERR_NAMETOOLONG;
 415                 goto out1;
 416         }
 417 
 418         if (args->what.name == NULL || *(args->what.name) == '\0') {
 419                 resp->status = NFS3ERR_ACCES;
 420                 goto out1;
 421         }
 422 
 423         fhp = &args->what.dir;
 424         if (strcmp(args->what.name, "..") == 0 &&
 425             EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
 426                 if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
 427                     (dvp->v_flag & VROOT)) {
 428                         /*
 429                          * special case for ".." and 'nohide'exported root
 430                          */
 431                         if (rfs_climb_crossmnt(&dvp, &exi, cr) != 0) {
 432                                 resp->status = NFS3ERR_ACCES;
 433                                 goto out1;
 434                         }
 435                 } else {
 436                         resp->status = NFS3ERR_NOENT;
 437                         goto out1;
 438                 }
 439         }
 440 
 441         ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
 442         name = nfscmd_convname(ca, exi, args->what.name,
 443             NFSCMD_CONV_INBOUND, MAXPATHLEN + 1);
 444 
 445         if (name == NULL) {
 446                 resp->status = NFS3ERR_ACCES;
 447                 goto out1;
 448         }
 449 
 450         /*
 451          * If the public filehandle is used then allow
 452          * a multi-component lookup
 453          */
 454         if (PUBLIC_FH3(&args->what.dir)) {
 455                 publicfh_flag = TRUE;
 456 
 457                 exi_rele(exi);
 458 
 459                 error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
 460                     &exi, &sec);
 461 

 462                 /*
 463                  * Since WebNFS may bypass MOUNT, we need to ensure this
 464                  * request didn't come from an unlabeled admin_low client.
 465                  */
 466                 if (is_system_labeled() && error == 0) {
 467                         int             addr_type;
 468                         void            *ipaddr;
 469                         tsol_tpc_t      *tp;
 470 
 471                         if (ca->sa_family == AF_INET) {
 472                                 addr_type = IPV4_VERSION;
 473                                 ipaddr = &((struct sockaddr_in *)ca)->sin_addr;
 474                         } else if (ca->sa_family == AF_INET6) {
 475                                 addr_type = IPV6_VERSION;
 476                                 ipaddr = &((struct sockaddr_in6 *)
 477                                     ca)->sin6_addr;
 478                         }
 479                         tp = find_tpc(ipaddr, addr_type, B_FALSE);
 480                         if (tp == NULL || tp->tpc_tp.tp_doi !=
 481                             l_admin_low->tsl_doi || tp->tpc_tp.host_type !=
 482                             SUN_CIPSO) {


 483                                 VN_RELE(vp);
 484                                 error = EACCES;
 485                         }
 486                         if (tp != NULL)
 487                                 TPC_RELE(tp);
 488                 }
 489         } else {
 490                 error = VOP_LOOKUP(dvp, name, &vp,
 491                     NULL, 0, NULL, cr, NULL, NULL, NULL);
 492         }
 493 
 494         if (name != args->what.name)
 495                 kmem_free(name, MAXPATHLEN + 1);
 496 
 497         if (error == 0 && vn_ismntpt(vp)) {
 498                 error = rfs_cross_mnt(&vp, &exi);
 499                 if (error)
 500                         VN_RELE(vp);
 501         }
 502 
 503         if (is_system_labeled() && error == 0) {
 504                 bslabel_t *clabel = req->rq_label;
 505 
 506                 ASSERT(clabel != NULL);
 507                 DTRACE_PROBE2(tx__rfs3__log__info__oplookup__clabel, char *,
 508                     "got client label from request(1)", struct svc_req *, req);
 509 
 510                 if (!blequal(&l_admin_low->tsl_label, clabel)) {
 511                         if (!do_rfs_label_check(clabel, dvp,
 512                             DOMINANCE_CHECK, exi)) {


 513                                 VN_RELE(vp);
 514                                 error = EACCES;
 515                         }
 516                 }
 517         }
 518 
 519         dva.va_mask = AT_ALL;
 520         dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
 521 
 522         if (error)
 523                 goto out;
 524 
 525         if (sec.sec_flags & SEC_QUERY) {
 526                 error = makefh3_ol(&resp->resok.object, exi, sec.sec_index);
 527         } else {
 528                 error = makefh3(&resp->resok.object, vp, exi);
 529                 if (!error && publicfh_flag && !chk_clnt_sec(exi, req))
 530                         auth_weak = TRUE;
 531         }
 532 









 533         if (error) {
 534                 VN_RELE(vp);
 535                 goto out;
 536         }
 537 
 538         va.va_mask = AT_ALL;
 539         vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 540 
 541         exi_rele(exi);
 542         VN_RELE(vp);
 543 
 544         resp->status = NFS3_OK;
 545         vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
 546         vattr_to_post_op_attr(dvap, &resp->resok.dir_attributes);
 547 
 548         /*
 549          * If it's public fh, no 0x81, and client's flavor is
 550          * invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.
 551          * Then set RPC status to AUTH_TOOWEAK in common_dispatch.
 552          */
 553         if (auth_weak)
 554                 resp->status = (enum nfsstat3)WNFSERR_CLNT_FLAVOR;
 555 
 556         DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
 557             cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
 558         VN_RELE(dvp);
 559 
 560         return;
 561 
 562 out:
 563         if (curthread->t_flag & T_WOULDBLOCK) {
 564                 curthread->t_flag &= ~T_WOULDBLOCK;
 565                 resp->status = NFS3ERR_JUKEBOX;
 566         } else
 567                 resp->status = puterrno3(error);
 568 out1:
 569         if (exi != NULL)
 570                 exi_rele(exi);
 571 
 572         DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
 573             cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
 574 
 575         if (dvp != NULL)
 576                 VN_RELE(dvp);
 577         vattr_to_post_op_attr(dvap, &resp->resfail.dir_attributes);
 578 
 579 }
 580 
 581 void *
 582 rfs3_lookup_getfh(LOOKUP3args *args)
 583 {
 584 
 585         return (&args->what.dir);
 586 }
 587 
 588 /* ARGSUSED */
 589 void
 590 rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi,
 591     struct svc_req *req, cred_t *cr, bool_t ro)


3605                 }
3606 
3607                 infop[i].namelen = namlen[i];
3608 
3609                 error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr,
3610                     NULL, NULL, NULL);
3611                 if (error) {
3612                         infop[i].attr.attributes = FALSE;
3613                         infop[i].fh.handle_follows = FALSE;
3614                         dp = nextdp(dp);
3615                         continue;
3616                 }
3617 
3618                 nva.va_mask = AT_ALL;
3619                 nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva;
3620 
3621                 /* Lie about the object type for a referral */
3622                 if (vn_is_nfs_reparse(nvp, cr))
3623                         nvap->va_type = VLNK;
3624 
3625                 if (vn_ismntpt(nvp)) {
3626                         infop[i].attr.attributes = FALSE;
3627                         infop[i].fh.handle_follows = FALSE;
3628                 } else {
3629                         vattr_to_post_op_attr(nvap, &infop[i].attr);
3630 
3631                         error = makefh3(&infop[i].fh.handle, nvp, exi);
3632                         if (!error)
3633                                 infop[i].fh.handle_follows = TRUE;
3634                         else
3635                                 infop[i].fh.handle_follows = FALSE;
3636                 }
3637 
3638                 VN_RELE(nvp);
3639                 dp = nextdp(dp);
3640         }
3641 
3642         ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
3643         ret = nfscmd_convdirplus(ca, exi, data, nents, args->dircount, &ndata);
3644         if (ndata == NULL)
3645                 ndata = data;
3646 
3647         if (ret > 0) {
3648                 /*
3649                  * We had to drop one or more entries in order to fit
3650                  * during the character conversion.  We need to patch
3651                  * up the size and eof info.
3652                  */
3653                 if (iseof)
3654                         iseof = FALSE;
3655 
3656                 ret = nfscmd_dropped_entrysize((struct dirent64 *)data,