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
acl2_getacl(GETACL2args * args,GETACL2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl2_getacl_getfh(GETACL2args * args)165 acl2_getacl_getfh(GETACL2args *args)
166 {
167
168 return (&args->fh);
169 }
170
171 void
acl2_getacl_free(GETACL2res * resp)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
acl2_setacl(SETACL2args * args,SETACL2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl2_setacl_getfh(SETACL2args * args)238 acl2_setacl_getfh(SETACL2args *args)
239 {
240
241 return (&args->fh);
242 }
243
244 /* ARGSUSED */
245 void
acl2_getattr(GETATTR2args * args,GETATTR2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl2_getattr_getfh(GETATTR2args * args)277 acl2_getattr_getfh(GETATTR2args *args)
278 {
279
280 return (&args->fh);
281 }
282
283 /* ARGSUSED */
284 void
acl2_access(ACCESS2args * args,ACCESS2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl2_access_getfh(ACCESS2args * args)373 acl2_access_getfh(ACCESS2args *args)
374 {
375
376 return (&args->fh);
377 }
378
379 /* ARGSUSED */
380 void
acl2_getxattrdir(GETXATTRDIR2args * args,GETXATTRDIR2res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl2_getxattrdir_getfh(GETXATTRDIR2args * args)439 acl2_getxattrdir_getfh(GETXATTRDIR2args *args)
440 {
441 return (&args->fh);
442 }
443
444 /* ARGSUSED */
445 void
acl3_getacl(GETACL3args * args,GETACL3res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl3_getacl_getfh(GETACL3args * args)547 acl3_getacl_getfh(GETACL3args *args)
548 {
549
550 return (&args->fh);
551 }
552
553 void
acl3_getacl_free(GETACL3res * resp)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
acl3_setacl(SETACL3args * args,SETACL3res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl3_setacl_getfh(SETACL3args * args)645 acl3_setacl_getfh(SETACL3args *args)
646 {
647
648 return (&args->fh);
649 }
650
651 /* ARGSUSED */
652 void
acl3_getxattrdir(GETXATTRDIR3args * args,GETXATTRDIR3res * resp,struct exportinfo * exi,struct svc_req * req,cred_t * cr)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 *
acl3_getxattrdir_getfh(GETXATTRDIR3args * args)711 acl3_getxattrdir_getfh(GETXATTRDIR3args *args)
712 {
713 return (&args->fh);
714 }
715