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