1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/param.h>
  27 #include <sys/types.h>
  28 #include <sys/systm.h>
  29 #include <sys/cred.h>
  30 #include <sys/proc.h>
  31 #include <sys/user.h>
  32 #include <sys/buf.h>
  33 #include <sys/vfs.h>
  34 #include <sys/vnode.h>
  35 #include <sys/pathname.h>
  36 #include <sys/uio.h>
  37 #include <sys/file.h>
  38 #include <sys/stat.h>
  39 #include <sys/errno.h>
  40 #include <sys/socket.h>
  41 #include <sys/sysmacros.h>
  42 #include <sys/siginfo.h>
  43 #include <sys/tiuser.h>
  44 #include <sys/statvfs.h>
  45 #include <sys/t_kuser.h>
  46 #include <sys/kmem.h>
  47 #include <sys/kstat.h>
  48 #include <sys/acl.h>
  49 #include <sys/dirent.h>
  50 #include <sys/cmn_err.h>
  51 #include <sys/debug.h>
  52 #include <sys/unistd.h>
  53 #include <sys/vtrace.h>
  54 #include <sys/mode.h>
  55 
  56 #include <rpc/types.h>
  57 #include <rpc/auth.h>
  58 #include <rpc/svc.h>
  59 #include <rpc/xdr.h>
  60 
  61 #include <nfs/nfs.h>
  62 #include <nfs/export.h>
  63 #include <nfs/nfssys.h>
  64 #include <nfs/nfs_clnt.h>
  65 #include <nfs/nfs_acl.h>
  66 
  67 #include <fs/fs_subr.h>
  68 
  69 /*
  70  * These are the interface routines for the server side of the
  71  * NFS ACL server.  See the NFS ACL protocol specification
  72  * for a description of this interface.
  73  */
  74 
  75 /* ARGSUSED */
  76 void
  77 acl2_getacl(GETACL2args *args, GETACL2res *resp, struct exportinfo *exi,
  78         struct svc_req *req, cred_t *cr)
  79 {
  80         int error;
  81         vnode_t *vp;
  82         vattr_t va;
  83 
  84         vp = nfs_fhtovp(&args->fh, exi);
  85         if (vp == NULL) {
  86                 resp->status = NFSERR_STALE;
  87                 return;
  88         }
  89 
  90         bzero((caddr_t)&resp->resok.acl, sizeof (resp->resok.acl));
  91 
  92         resp->resok.acl.vsa_mask = args->mask;
  93 
  94         error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr, NULL);
  95 
  96         if ((error == ENOSYS) && !(exi->exi_export.ex_flags & EX_NOACLFAB)) {
  97                 /*
  98                  * If the underlying file system doesn't support
  99                  * aclent_t type acls, fabricate an acl.  This is
 100                  * required in order to to support existing clients
 101                  * that require the call to VOP_GETSECATTR to
 102                  * succeed while making the assumption that all
 103                  * file systems support aclent_t type acls.  This
 104                  * causes problems for servers exporting ZFS file
 105                  * systems because ZFS supports ace_t type acls,
 106                  * and fails (with ENOSYS) when asked for aclent_t
 107                  * type acls.
 108                  *
 109                  * Note: if the fs_fab_acl() fails, we have other problems.
 110                  * This error should be returned to the caller.
 111                  */
 112                 error = fs_fab_acl(vp, &resp->resok.acl, 0, cr, NULL);
 113         }
 114 
 115         if (error) {
 116                 VN_RELE(vp);
 117                 resp->status = puterrno(error);
 118                 return;
 119         }
 120 
 121         va.va_mask = AT_ALL;
 122         error = rfs4_delegated_getattr(vp, &va, 0, cr);
 123 
 124         VN_RELE(vp);
 125 
 126         /* check for overflowed values */
 127         if (!error) {
 128                 error = vattr_to_nattr(&va, &resp->resok.attr);
 129         }
 130         if (error) {
 131                 resp->status = puterrno(error);
 132                 if (resp->resok.acl.vsa_aclcnt > 0 &&
 133                     resp->resok.acl.vsa_aclentp != NULL) {
 134                         kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
 135                             resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
 136                 }
 137                 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
 138                     resp->resok.acl.vsa_dfaclentp != NULL) {
 139                         kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
 140                             resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
 141                 }
 142                 return;
 143         }
 144 
 145         resp->status = NFS_OK;
 146         if (!(args->mask & NA_ACL)) {
 147                 if (resp->resok.acl.vsa_aclcnt > 0 &&
 148                     resp->resok.acl.vsa_aclentp != NULL) {
 149                         kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
 150                             resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
 151                 }
 152                 resp->resok.acl.vsa_aclentp = NULL;
 153         }
 154         if (!(args->mask & NA_DFACL)) {
 155                 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
 156                     resp->resok.acl.vsa_dfaclentp != NULL) {
 157                         kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
 158                             resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
 159                 }
 160                 resp->resok.acl.vsa_dfaclentp = NULL;
 161         }
 162 }
 163 
 164 void *
 165 acl2_getacl_getfh(GETACL2args *args)
 166 {
 167 
 168         return (&args->fh);
 169 }
 170 
 171 void
 172 acl2_getacl_free(GETACL2res *resp)
 173 {
 174 
 175         if (resp->status == NFS_OK) {
 176                 if (resp->resok.acl.vsa_aclcnt > 0 &&
 177                     resp->resok.acl.vsa_aclentp != NULL) {
 178                         kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
 179                             resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
 180                 }
 181                 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
 182                     resp->resok.acl.vsa_dfaclentp != NULL) {
 183                         kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
 184                             resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
 185                 }
 186         }
 187 }
 188 
 189 /* ARGSUSED */
 190 void
 191 acl2_setacl(SETACL2args *args, SETACL2res *resp, struct exportinfo *exi,
 192         struct svc_req *req, cred_t *cr)
 193 {
 194         int error;
 195         vnode_t *vp;
 196         vattr_t va;
 197 
 198         vp = nfs_fhtovp(&args->fh, exi);
 199         if (vp == NULL) {
 200                 resp->status = NFSERR_STALE;
 201                 return;
 202         }
 203 
 204         if (rdonly(exi, req) || vn_is_readonly(vp)) {
 205                 VN_RELE(vp);
 206                 resp->status = NFSERR_ROFS;
 207                 return;
 208         }
 209 
 210         (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
 211         error = VOP_SETSECATTR(vp, &args->acl, 0, cr, NULL);
 212         if (error) {
 213                 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
 214                 VN_RELE(vp);
 215                 resp->status = puterrno(error);
 216                 return;
 217         }
 218 
 219         va.va_mask = AT_ALL;
 220         error = rfs4_delegated_getattr(vp, &va, 0, cr);
 221 
 222         VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
 223         VN_RELE(vp);
 224 
 225         /* check for overflowed values */
 226         if (!error) {
 227                 error = vattr_to_nattr(&va, &resp->resok.attr);
 228         }
 229         if (error) {
 230                 resp->status = puterrno(error);
 231                 return;
 232         }
 233 
 234         resp->status = NFS_OK;
 235 }
 236 
 237 void *
 238 acl2_setacl_getfh(SETACL2args *args)
 239 {
 240 
 241         return (&args->fh);
 242 }
 243 
 244 /* ARGSUSED */
 245 void
 246 acl2_getattr(GETATTR2args *args, GETATTR2res *resp, struct exportinfo *exi,
 247         struct svc_req *req, cred_t *cr)
 248 {
 249         int error;
 250         vnode_t *vp;
 251         vattr_t va;
 252 
 253         vp = nfs_fhtovp(&args->fh, exi);
 254         if (vp == NULL) {
 255                 resp->status = NFSERR_STALE;
 256                 return;
 257         }
 258 
 259         va.va_mask = AT_ALL;
 260         error = rfs4_delegated_getattr(vp, &va, 0, cr);
 261 
 262         VN_RELE(vp);
 263 
 264         /* check for overflowed values */
 265         if (!error) {
 266                 error = vattr_to_nattr(&va, &resp->resok.attr);
 267         }
 268         if (error) {
 269                 resp->status = puterrno(error);
 270                 return;
 271         }
 272 
 273         resp->status = NFS_OK;
 274 }
 275 
 276 void *
 277 acl2_getattr_getfh(GETATTR2args *args)
 278 {
 279 
 280         return (&args->fh);
 281 }
 282 
 283 /* ARGSUSED */
 284 void
 285 acl2_access(ACCESS2args *args, ACCESS2res *resp, struct exportinfo *exi,
 286         struct svc_req *req, cred_t *cr)
 287 {
 288         int error;
 289         vnode_t *vp;
 290         vattr_t va;
 291         int checkwriteperm;
 292 
 293         vp = nfs_fhtovp(&args->fh, exi);
 294         if (vp == NULL) {
 295                 resp->status = NFSERR_STALE;
 296                 return;
 297         }
 298 
 299         /*
 300          * If the file system is exported read only, it is not appropriate
 301          * to check write permissions for regular files and directories.
 302          * Special files are interpreted by the client, so the underlying
 303          * permissions are sent back to the client for interpretation.
 304          */
 305         if (rdonly(exi, req) && (vp->v_type == VREG || vp->v_type == VDIR))
 306                 checkwriteperm = 0;
 307         else
 308                 checkwriteperm = 1;
 309 
 310         /*
 311          * We need the mode so that we can correctly determine access
 312          * permissions relative to a mandatory lock file.  Access to
 313          * mandatory lock files is denied on the server, so it might
 314          * as well be reflected to the server during the open.
 315          */
 316         va.va_mask = AT_MODE;
 317         error = VOP_GETATTR(vp, &va, 0, cr, NULL);
 318         if (error) {
 319                 VN_RELE(vp);
 320                 resp->status = puterrno(error);
 321                 return;
 322         }
 323 
 324         resp->resok.access = 0;
 325 
 326         if (args->access & ACCESS2_READ) {
 327                 error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
 328                 if (!error && !MANDLOCK(vp, va.va_mode))
 329                         resp->resok.access |= ACCESS2_READ;
 330         }
 331         if ((args->access & ACCESS2_LOOKUP) && vp->v_type == VDIR) {
 332                 error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL);
 333                 if (!error)
 334                         resp->resok.access |= ACCESS2_LOOKUP;
 335         }
 336         if (checkwriteperm &&
 337             (args->access & (ACCESS2_MODIFY|ACCESS2_EXTEND))) {
 338                 error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL);
 339                 if (!error && !MANDLOCK(vp, va.va_mode))
 340                         resp->resok.access |=
 341                             (args->access & (ACCESS2_MODIFY|ACCESS2_EXTEND));
 342         }
 343         if (checkwriteperm &&
 344             (args->access & ACCESS2_DELETE) && (vp->v_type == VDIR)) {
 345                 error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL);
 346                 if (!error)
 347                         resp->resok.access |= ACCESS2_DELETE;
 348         }
 349         if (args->access & ACCESS2_EXECUTE) {
 350                 error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL);
 351                 if (!error && !MANDLOCK(vp, va.va_mode))
 352                         resp->resok.access |= ACCESS2_EXECUTE;
 353         }
 354 
 355         va.va_mask = AT_ALL;
 356         error = rfs4_delegated_getattr(vp, &va, 0, cr);
 357 
 358         VN_RELE(vp);
 359 
 360         /* check for overflowed values */
 361         if (!error) {
 362                 error = vattr_to_nattr(&va, &resp->resok.attr);
 363         }
 364         if (error) {
 365                 resp->status = puterrno(error);
 366                 return;
 367         }
 368 
 369         resp->status = NFS_OK;
 370 }
 371 
 372 void *
 373 acl2_access_getfh(ACCESS2args *args)
 374 {
 375 
 376         return (&args->fh);
 377 }
 378 
 379 /* ARGSUSED */
 380 void
 381 acl2_getxattrdir(GETXATTRDIR2args *args, GETXATTRDIR2res *resp,
 382         struct exportinfo *exi, struct svc_req *req, cred_t *cr)
 383 {
 384         int error;
 385         int flags;
 386         vnode_t *vp, *avp;
 387 
 388         vp = nfs_fhtovp(&args->fh, exi);
 389         if (vp == NULL) {
 390                 resp->status = NFSERR_STALE;
 391                 return;
 392         }
 393 
 394         flags = LOOKUP_XATTR;
 395         if (args->create)
 396                 flags |= CREATE_XATTR_DIR;
 397         else {
 398                 ulong_t val = 0;
 399                 error = VOP_PATHCONF(vp, _PC_SATTR_EXISTS, &val, cr, NULL);
 400                 if (!error && val == 0) {
 401                         error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS,
 402                             &val, cr, NULL);
 403                         if (!error && val == 0) {
 404                                 VN_RELE(vp);
 405                                 resp->status = NFSERR_NOENT;
 406                                 return;
 407                         }
 408                 }
 409         }
 410 
 411         error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr,
 412             NULL, NULL, NULL);
 413         if (!error && avp == vp) {      /* lookup of "" on old FS? */
 414                 error = EINVAL;
 415                 VN_RELE(avp);
 416         }
 417         if (!error) {
 418                 struct vattr va;
 419                 va.va_mask = AT_ALL;
 420                 error = rfs4_delegated_getattr(avp, &va, 0, cr);
 421                 if (!error) {
 422                         error = vattr_to_nattr(&va, &resp->resok.attr);
 423                         if (!error)
 424                                 error = makefh(&resp->resok.fh, avp, exi);
 425                 }
 426                 VN_RELE(avp);
 427         }
 428 
 429         VN_RELE(vp);
 430 
 431         if (error) {
 432                 resp->status = puterrno(error);
 433                 return;
 434         }
 435         resp->status = NFS_OK;
 436 }
 437 
 438 void *
 439 acl2_getxattrdir_getfh(GETXATTRDIR2args *args)
 440 {
 441         return (&args->fh);
 442 }
 443 
 444 /* ARGSUSED */
 445 void
 446 acl3_getacl(GETACL3args *args, GETACL3res *resp, struct exportinfo *exi,
 447         struct svc_req *req, cred_t *cr)
 448 {
 449         int error;
 450         vnode_t *vp;
 451         vattr_t *vap;
 452         vattr_t va;
 453 
 454         vap = NULL;
 455 
 456         vp = nfs3_fhtovp(&args->fh, exi);
 457         if (vp == NULL) {
 458                 error = ESTALE;
 459                 goto out;
 460         }
 461 
 462 #ifdef DEBUG
 463         if (rfs3_do_post_op_attr) {
 464                 va.va_mask = AT_ALL;
 465                 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 466         } else
 467                 vap = NULL;
 468 #else
 469         va.va_mask = AT_ALL;
 470         vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 471 #endif
 472 
 473         bzero((caddr_t)&resp->resok.acl, sizeof (resp->resok.acl));
 474 
 475         resp->resok.acl.vsa_mask = args->mask;
 476 
 477         error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr, NULL);
 478 
 479         if ((error == ENOSYS) && !(exi->exi_export.ex_flags & EX_NOACLFAB)) {
 480                 /*
 481                  * If the underlying file system doesn't support
 482                  * aclent_t type acls, fabricate an acl.  This is
 483                  * required in order to to support existing clients
 484                  * that require the call to VOP_GETSECATTR to
 485                  * succeed while making the assumption that all
 486                  * file systems support aclent_t type acls.  This
 487                  * causes problems for servers exporting ZFS file
 488                  * systems because ZFS supports ace_t type acls,
 489                  * and fails (with ENOSYS) when asked for aclent_t
 490                  * type acls.
 491                  *
 492                  * Note: if the fs_fab_acl() fails, we have other problems.
 493                  * This error should be returned to the caller.
 494                  */
 495                 error = fs_fab_acl(vp, &resp->resok.acl, 0, cr, NULL);
 496         }
 497 
 498         if (error)
 499                 goto out;
 500 
 501 #ifdef DEBUG
 502         if (rfs3_do_post_op_attr) {
 503                 va.va_mask = AT_ALL;
 504                 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 505         } else
 506                 vap = NULL;
 507 #else
 508         va.va_mask = AT_ALL;
 509         vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 510 #endif
 511 
 512         VN_RELE(vp);
 513 
 514         resp->status = NFS3_OK;
 515         vattr_to_post_op_attr(vap, &resp->resok.attr);
 516         if (!(args->mask & NA_ACL)) {
 517                 if (resp->resok.acl.vsa_aclcnt > 0 &&
 518                     resp->resok.acl.vsa_aclentp != NULL) {
 519                         kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
 520                             resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
 521                 }
 522                 resp->resok.acl.vsa_aclentp = NULL;
 523         }
 524         if (!(args->mask & NA_DFACL)) {
 525                 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
 526                     resp->resok.acl.vsa_dfaclentp != NULL) {
 527                         kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
 528                             resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
 529                 }
 530                 resp->resok.acl.vsa_dfaclentp = NULL;
 531         }
 532         return;
 533 
 534 out:
 535         if (curthread->t_flag & T_WOULDBLOCK) {
 536                 curthread->t_flag &= ~T_WOULDBLOCK;
 537                 resp->status = NFS3ERR_JUKEBOX;
 538         } else
 539                 resp->status = puterrno3(error);
 540 out1:
 541         if (vp != NULL)
 542                 VN_RELE(vp);
 543         vattr_to_post_op_attr(vap, &resp->resfail.attr);
 544 }
 545 
 546 void *
 547 acl3_getacl_getfh(GETACL3args *args)
 548 {
 549 
 550         return (&args->fh);
 551 }
 552 
 553 void
 554 acl3_getacl_free(GETACL3res *resp)
 555 {
 556 
 557         if (resp->status == NFS3_OK) {
 558                 if (resp->resok.acl.vsa_aclcnt > 0 &&
 559                     resp->resok.acl.vsa_aclentp != NULL) {
 560                         kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
 561                             resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
 562                 }
 563                 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
 564                     resp->resok.acl.vsa_dfaclentp != NULL) {
 565                         kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
 566                             resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
 567                 }
 568         }
 569 }
 570 
 571 /* ARGSUSED */
 572 void
 573 acl3_setacl(SETACL3args *args, SETACL3res *resp, struct exportinfo *exi,
 574         struct svc_req *req, cred_t *cr)
 575 {
 576         int error;
 577         vnode_t *vp;
 578         vattr_t *vap;
 579         vattr_t va;
 580 
 581         vap = NULL;
 582 
 583         vp = nfs3_fhtovp(&args->fh, exi);
 584         if (vp == NULL) {
 585                 error = ESTALE;
 586                 goto out1;
 587         }
 588 
 589         (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
 590 
 591 #ifdef DEBUG
 592         if (rfs3_do_post_op_attr) {
 593                 va.va_mask = AT_ALL;
 594                 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 595         } else
 596                 vap = NULL;
 597 #else
 598         va.va_mask = AT_ALL;
 599         vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 600 #endif
 601 
 602         if (rdonly(exi, req) || vn_is_readonly(vp)) {
 603                 resp->status = NFS3ERR_ROFS;
 604                 goto out1;
 605         }
 606 
 607         error = VOP_SETSECATTR(vp, &args->acl, 0, cr, NULL);
 608 
 609 #ifdef DEBUG
 610         if (rfs3_do_post_op_attr) {
 611                 va.va_mask = AT_ALL;
 612                 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 613         } else
 614                 vap = NULL;
 615 #else
 616         va.va_mask = AT_ALL;
 617         vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 618 #endif
 619 
 620         if (error)
 621                 goto out;
 622 
 623         VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
 624         VN_RELE(vp);
 625 
 626         resp->status = NFS3_OK;
 627         vattr_to_post_op_attr(vap, &resp->resok.attr);
 628         return;
 629 
 630 out:
 631         if (curthread->t_flag & T_WOULDBLOCK) {
 632                 curthread->t_flag &= ~T_WOULDBLOCK;
 633                 resp->status = NFS3ERR_JUKEBOX;
 634         } else
 635                 resp->status = puterrno3(error);
 636 out1:
 637         if (vp != NULL) {
 638                 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
 639                 VN_RELE(vp);
 640         }
 641         vattr_to_post_op_attr(vap, &resp->resfail.attr);
 642 }
 643 
 644 void *
 645 acl3_setacl_getfh(SETACL3args *args)
 646 {
 647 
 648         return (&args->fh);
 649 }
 650 
 651 /* ARGSUSED */
 652 void
 653 acl3_getxattrdir(GETXATTRDIR3args *args, GETXATTRDIR3res *resp,
 654         struct exportinfo *exi, struct svc_req *req, cred_t *cr)
 655 {
 656         int error;
 657         int flags;
 658         vnode_t *vp, *avp;
 659 
 660         vp = nfs3_fhtovp(&args->fh, exi);
 661         if (vp == NULL) {
 662                 resp->status = NFS3ERR_STALE;
 663                 return;
 664         }
 665 
 666         flags = LOOKUP_XATTR;
 667         if (args->create)
 668                 flags |= CREATE_XATTR_DIR;
 669         else {
 670                 ulong_t val = 0;
 671 
 672                 error = VOP_PATHCONF(vp, _PC_SATTR_EXISTS, &val, cr, NULL);
 673                 if (!error && val == 0) {
 674                         error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS,
 675                             &val, cr, NULL);
 676                         if (!error && val == 0) {
 677                                 VN_RELE(vp);
 678                                 resp->status = NFS3ERR_NOENT;
 679                                 return;
 680                         }
 681                 }
 682         }
 683 
 684         error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr,
 685             NULL, NULL, NULL);
 686         if (!error && avp == vp) {      /* lookup of "" on old FS? */
 687                 error = EINVAL;
 688                 VN_RELE(avp);
 689         }
 690         if (!error) {
 691                 struct vattr va;
 692                 va.va_mask = AT_ALL;
 693                 error = rfs4_delegated_getattr(avp, &va, 0, cr);
 694                 if (!error) {
 695                         vattr_to_post_op_attr(&va, &resp->resok.attr);
 696                         error = makefh3(&resp->resok.fh, avp, exi);
 697                 }
 698                 VN_RELE(avp);
 699         }
 700 
 701         VN_RELE(vp);
 702 
 703         if (error) {
 704                 resp->status = puterrno3(error);
 705                 return;
 706         }
 707         resp->status = NFS3_OK;
 708 }
 709 
 710 void *
 711 acl3_getxattrdir_getfh(GETXATTRDIR3args *args)
 712 {
 713         return (&args->fh);
 714 }