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 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26 */
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
acl2_getacl(GETACL2args * args,GETACL2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)79 acl2_getacl(GETACL2args *args, GETACL2res *resp, struct exportinfo *exi,
80 struct svc_req *req, cred_t *cr, bool_t ro)
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) && !(exi->exi_export.ex_flags & EX_NOACLFAB)) {
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 *
acl2_getacl_getfh(GETACL2args * args)167 acl2_getacl_getfh(GETACL2args *args)
168 {
169
170 return (&args->fh);
171 }
172
173 void
acl2_getacl_free(GETACL2res * resp)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
acl2_setacl(SETACL2args * args,SETACL2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)193 acl2_setacl(SETACL2args *args, SETACL2res *resp, struct exportinfo *exi,
194 struct svc_req *req, cred_t *cr, bool_t ro)
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(ro, 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 *
acl2_setacl_getfh(SETACL2args * args)240 acl2_setacl_getfh(SETACL2args *args)
241 {
242
243 return (&args->fh);
244 }
245
246 /* ARGSUSED */
247 void
acl2_getattr(GETATTR2args * args,GETATTR2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)248 acl2_getattr(GETATTR2args *args, GETATTR2res *resp, struct exportinfo *exi,
249 struct svc_req *req, cred_t *cr, bool_t ro)
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 *
acl2_getattr_getfh(GETATTR2args * args)279 acl2_getattr_getfh(GETATTR2args *args)
280 {
281
282 return (&args->fh);
283 }
284
285 /* ARGSUSED */
286 void
acl2_access(ACCESS2args * args,ACCESS2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)287 acl2_access(ACCESS2args *args, ACCESS2res *resp, struct exportinfo *exi,
288 struct svc_req *req, cred_t *cr, bool_t ro)
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(ro, vp) && (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 *
acl2_access_getfh(ACCESS2args * args)375 acl2_access_getfh(ACCESS2args *args)
376 {
377
378 return (&args->fh);
379 }
380
381 /* ARGSUSED */
382 void
acl2_getxattrdir(GETXATTRDIR2args * args,GETXATTRDIR2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)383 acl2_getxattrdir(GETXATTRDIR2args *args, GETXATTRDIR2res *resp,
384 struct exportinfo *exi, struct svc_req *req, cred_t *cr, bool_t ro)
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 *
acl2_getxattrdir_getfh(GETXATTRDIR2args * args)441 acl2_getxattrdir_getfh(GETXATTRDIR2args *args)
442 {
443 return (&args->fh);
444 }
445
446 void
acl3_getacl(GETACL3args * args,GETACL3res * resp,struct exportinfo * exi,struct svc_req * req __unused,cred_t * cr,bool_t ro __unused)447 acl3_getacl(GETACL3args *args, GETACL3res *resp, struct exportinfo *exi,
448 struct svc_req *req __unused, cred_t *cr, bool_t ro __unused)
449 {
450 int error;
451 vnode_t *vp;
452 vattr_t *vap;
453 vattr_t va;
454
455 vap = NULL;
456
457 vp = nfs3_fhtovp(&args->fh, exi);
458 if (vp == NULL) {
459 error = ESTALE;
460 goto out;
461 }
462
463 va.va_mask = AT_ALL;
464 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
465
466 bzero((caddr_t)&resp->resok.acl, sizeof (resp->resok.acl));
467
468 resp->resok.acl.vsa_mask = args->mask;
469
470 error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr, NULL);
471
472 if ((error == ENOSYS) && !(exi->exi_export.ex_flags & EX_NOACLFAB)) {
473 /*
474 * If the underlying file system doesn't support
475 * aclent_t type acls, fabricate an acl. This is
476 * required in order to to support existing clients
477 * that require the call to VOP_GETSECATTR to
478 * succeed while making the assumption that all
479 * file systems support aclent_t type acls. This
480 * causes problems for servers exporting ZFS file
481 * systems because ZFS supports ace_t type acls,
482 * and fails (with ENOSYS) when asked for aclent_t
483 * type acls.
484 *
485 * Note: if the fs_fab_acl() fails, we have other problems.
486 * This error should be returned to the caller.
487 */
488 error = fs_fab_acl(vp, &resp->resok.acl, 0, cr, NULL);
489 }
490
491 if (error)
492 goto out;
493
494 va.va_mask = AT_ALL;
495 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
496
497 VN_RELE(vp);
498
499 resp->status = NFS3_OK;
500 vattr_to_post_op_attr(vap, &resp->resok.attr);
501 if (!(args->mask & NA_ACL)) {
502 if (resp->resok.acl.vsa_aclcnt > 0 &&
503 resp->resok.acl.vsa_aclentp != NULL) {
504 kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
505 resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
506 }
507 resp->resok.acl.vsa_aclentp = NULL;
508 }
509 if (!(args->mask & NA_DFACL)) {
510 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
511 resp->resok.acl.vsa_dfaclentp != NULL) {
512 kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
513 resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
514 }
515 resp->resok.acl.vsa_dfaclentp = NULL;
516 }
517 return;
518
519 out:
520 if (curthread->t_flag & T_WOULDBLOCK) {
521 curthread->t_flag &= ~T_WOULDBLOCK;
522 resp->status = NFS3ERR_JUKEBOX;
523 } else {
524 resp->status = puterrno3(error);
525 }
526
527 if (vp != NULL)
528 VN_RELE(vp);
529 vattr_to_post_op_attr(vap, &resp->resfail.attr);
530 }
531
532 void *
acl3_getacl_getfh(GETACL3args * args)533 acl3_getacl_getfh(GETACL3args *args)
534 {
535
536 return (&args->fh);
537 }
538
539 void
acl3_getacl_free(GETACL3res * resp)540 acl3_getacl_free(GETACL3res *resp)
541 {
542
543 if (resp->status == NFS3_OK) {
544 if (resp->resok.acl.vsa_aclcnt > 0 &&
545 resp->resok.acl.vsa_aclentp != NULL) {
546 kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
547 resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
548 }
549 if (resp->resok.acl.vsa_dfaclcnt > 0 &&
550 resp->resok.acl.vsa_dfaclentp != NULL) {
551 kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
552 resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
553 }
554 }
555 }
556
557 /* ARGSUSED */
558 void
acl3_setacl(SETACL3args * args,SETACL3res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)559 acl3_setacl(SETACL3args *args, SETACL3res *resp, struct exportinfo *exi,
560 struct svc_req *req, cred_t *cr, bool_t ro)
561 {
562 int error;
563 vnode_t *vp;
564 vattr_t *vap;
565 vattr_t va;
566
567 vap = NULL;
568
569 vp = nfs3_fhtovp(&args->fh, exi);
570 if (vp == NULL) {
571 error = ESTALE;
572 goto out1;
573 }
574
575 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
576
577 va.va_mask = AT_ALL;
578 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
579
580 if (rdonly(ro, vp)) {
581 resp->status = NFS3ERR_ROFS;
582 goto out1;
583 }
584
585 error = VOP_SETSECATTR(vp, &args->acl, 0, cr, NULL);
586
587 va.va_mask = AT_ALL;
588 vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
589
590 if (error)
591 goto out;
592
593 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
594 VN_RELE(vp);
595
596 resp->status = NFS3_OK;
597 vattr_to_post_op_attr(vap, &resp->resok.attr);
598 return;
599
600 out:
601 if (curthread->t_flag & T_WOULDBLOCK) {
602 curthread->t_flag &= ~T_WOULDBLOCK;
603 resp->status = NFS3ERR_JUKEBOX;
604 } else
605 resp->status = puterrno3(error);
606 out1:
607 if (vp != NULL) {
608 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
609 VN_RELE(vp);
610 }
611 vattr_to_post_op_attr(vap, &resp->resfail.attr);
612 }
613
614 void *
acl3_setacl_getfh(SETACL3args * args)615 acl3_setacl_getfh(SETACL3args *args)
616 {
617
618 return (&args->fh);
619 }
620
621 /* ARGSUSED */
622 void
acl3_getxattrdir(GETXATTRDIR3args * args,GETXATTRDIR3res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr,bool_t ro)623 acl3_getxattrdir(GETXATTRDIR3args *args, GETXATTRDIR3res *resp,
624 struct exportinfo *exi, struct svc_req *req, cred_t *cr, bool_t ro)
625 {
626 int error;
627 int flags;
628 vnode_t *vp, *avp;
629
630 vp = nfs3_fhtovp(&args->fh, exi);
631 if (vp == NULL) {
632 resp->status = NFS3ERR_STALE;
633 return;
634 }
635
636 flags = LOOKUP_XATTR;
637 if (args->create)
638 flags |= CREATE_XATTR_DIR;
639 else {
640 ulong_t val = 0;
641
642 error = VOP_PATHCONF(vp, _PC_SATTR_EXISTS, &val, cr, NULL);
643 if (!error && val == 0) {
644 error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS,
645 &val, cr, NULL);
646 if (!error && val == 0) {
647 VN_RELE(vp);
648 resp->status = NFS3ERR_NOENT;
649 return;
650 }
651 }
652 }
653
654 error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr,
655 NULL, NULL, NULL);
656 if (!error && avp == vp) { /* lookup of "" on old FS? */
657 error = EINVAL;
658 VN_RELE(avp);
659 }
660 if (!error) {
661 struct vattr va;
662 va.va_mask = AT_ALL;
663 error = rfs4_delegated_getattr(avp, &va, 0, cr);
664 if (!error) {
665 vattr_to_post_op_attr(&va, &resp->resok.attr);
666 error = makefh3(&resp->resok.fh, avp, exi);
667 }
668 VN_RELE(avp);
669 }
670
671 VN_RELE(vp);
672
673 if (error) {
674 resp->status = puterrno3(error);
675 return;
676 }
677 resp->status = NFS3_OK;
678 }
679
680 void *
acl3_getxattrdir_getfh(GETXATTRDIR3args * args)681 acl3_getxattrdir_getfh(GETXATTRDIR3args *args)
682 {
683 return (&args->fh);
684 }
685