xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c (revision 6faf52448e142b151fa3deade474be359e7c8698)
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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2018 Nexenta Systems, Inc.
29  * Copyright 2020 RackTop Systems, Inc.
30  */
31 
32 #include <sys/systm.h>
33 #include <sys/cmn_err.h>
34 #include <nfs/nfs.h>
35 #include <nfs/export.h>
36 #include <nfs/nfs4.h>
37 #include <sys/ddi.h>
38 #include <sys/door.h>
39 #include <sys/sdt.h>
40 #include <nfs/nfssys.h>
41 
42 void	rfs4_init_compound_state(struct compound_state *);
43 
44 bitmap4 rfs4_supported_attrs;
45 int MSG_PRT_DEBUG = FALSE;
46 
47 /* If building with DEBUG enabled, enable mandattr tunable by default */
48 #ifdef DEBUG
49 #ifndef RFS4_SUPPORT_MANDATTR_ONLY
50 #define	RFS4_SUPPORT_MANDATTR_ONLY
51 #endif
52 #endif
53 
54 /*
55  * If building with mandattr only code, disable it by default.
56  * To enable, set rfs4_mandattr_only in /etc/system and reboot.
57  * When building without mandattr ifdef, the compiler should
58  * optimize away the the comparisons because RFS4_MANDATTR_ONLY
59  * is defined to be 0.
60  */
61 #ifdef RFS4_SUPPORT_MANDATTR_ONLY
62 #define	NFS4_LAST_MANDATTR FATTR4_RDATTR_ERROR
63 #define	RFS4_MANDATTR_ONLY rfs4_mandattr_only
64 int rfs4_mandattr_only = 0;
65 #else
66 #define	RFS4_MANDATTR_ONLY 0
67 #endif
68 
69 
70 static void rfs4_ntov_init(void);
71 static int rfs4_fattr4_supported_attrs();
72 static int rfs4_fattr4_type();
73 static int rfs4_fattr4_fh_expire_type();
74 static int rfs4_fattr4_change();
75 static int rfs4_fattr4_size();
76 static int rfs4_fattr4_link_support();
77 static int rfs4_fattr4_symlink_support();
78 static int rfs4_fattr4_named_attr();
79 static int rfs4_fattr4_fsid();
80 static int rfs4_fattr4_unique_handles();
81 static int rfs4_fattr4_lease_time();
82 static int rfs4_fattr4_rdattr_error();
83 static int rfs4_fattr4_acl();
84 static int rfs4_fattr4_aclsupport();
85 static int rfs4_fattr4_archive();
86 static int rfs4_fattr4_cansettime();
87 static int rfs4_fattr4_case_insensitive();
88 static int rfs4_fattr4_case_preserving();
89 static int rfs4_fattr4_chown_restricted();
90 static int rfs4_fattr4_filehandle();
91 static int rfs4_fattr4_fileid();
92 static int rfs4_fattr4_files_avail();
93 static int rfs4_fattr4_files_free();
94 static int rfs4_fattr4_files_total();
95 static int rfs4_fattr4_fs_locations();
96 static int rfs4_fattr4_hidden();
97 static int rfs4_fattr4_homogeneous();
98 static int rfs4_fattr4_maxfilesize();
99 static int rfs4_fattr4_maxlink();
100 static int rfs4_fattr4_maxname();
101 static int rfs4_fattr4_maxread();
102 static int rfs4_fattr4_maxwrite();
103 static int rfs4_fattr4_mimetype();
104 static int rfs4_fattr4_mode();
105 static int rfs4_fattr4_no_trunc();
106 static int rfs4_fattr4_numlinks();
107 static int rfs4_fattr4_owner();
108 static int rfs4_fattr4_owner_group();
109 static int rfs4_fattr4_quota_avail_hard();
110 static int rfs4_fattr4_quota_avail_soft();
111 static int rfs4_fattr4_quota_used();
112 static int rfs4_fattr4_rawdev();
113 static int rfs4_fattr4_space_avail();
114 static int rfs4_fattr4_space_free();
115 static int rfs4_fattr4_space_total();
116 static int rfs4_fattr4_space_used();
117 static int rfs4_fattr4_system();
118 static int rfs4_fattr4_time_access();
119 static int rfs4_fattr4_time_access_set();
120 static int rfs4_fattr4_time_backup();
121 static int rfs4_fattr4_time_create();
122 static int rfs4_fattr4_time_delta();
123 static int rfs4_fattr4_time_metadata();
124 static int rfs4_fattr4_time_modify();
125 static int rfs4_fattr4_time_modify_set();
126 
127 /*
128  * Initialize the supported attributes
129  */
130 bitmap4 supported_attrs[3];
131 
132 static void
133 init_supported_attrs(void)
134 {
135 	supported_attrs[0] = supported_attrs[1] = supported_attrs[2] =
136 	    rfs4_supported_attrs;
137 
138 	/* restrict to nfsv4.0 */
139 	supported_attrs[0] &= ~(FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL |
140 	    FATTR4_SEC_LABEL_MASK_LOCAL);
141 
142 	/* restrict to nfsv4.1 */
143 	supported_attrs[1] &= ~FATTR4_SEC_LABEL_MASK_LOCAL;
144 }
145 
146 void
147 rfs4_attr_init(void)
148 {
149 	int i;
150 	struct nfs4_svgetit_arg sarg;
151 	struct compound_state cs;
152 	struct statvfs64 sb;
153 
154 	rfs4_init_compound_state(&cs);
155 	/*
156 	 * This is global state checking, called once. We might be in
157 	 * non-global-zone context here (say a modload happens from a zone
158 	 * process) so in this case, we want the global-zone root vnode.
159 	 */
160 	cs.vp = rootvp;
161 	cs.fh.nfs_fh4_val = NULL;
162 	cs.cr = kcred;
163 	cs.minorversion = NFS_PROT_V4_MINORVERSION(NFS_VERS_4_2);
164 
165 	/*
166 	 * Get all the supported attributes
167 	 */
168 	sarg.op = NFS4ATTR_SUPPORTED;
169 	sarg.cs = &cs;
170 	sarg.vap->va_mask = AT_ALL;
171 	sarg.sbp = &sb;
172 	sarg.flag = 0;
173 	sarg.rdattr_error = NFS4_OK;
174 	sarg.rdattr_error_req = FALSE;
175 	sarg.is_referral = B_FALSE;
176 
177 	rfs4_ntov_init();
178 
179 	rfs4_supported_attrs = 0;
180 	for (i = 0; i < NFS4_MAXNUM_ATTRS; i++) {
181 #ifdef RFS4_SUPPORT_MANDATTR_ONLY
182 		if (rfs4_mandattr_only == TRUE && i > NFS4_LAST_MANDATTR)
183 			continue;
184 #endif
185 		if ((*nfs4_ntov_map[i].sv_getit)(NFS4ATTR_SUPPORTED,
186 		    &sarg, NULL) == 0) {
187 			rfs4_supported_attrs |= nfs4_ntov_map[i].fbit;
188 		}
189 	}
190 
191 	init_supported_attrs();
192 }
193 
194 /*
195  * The following rfs4_fattr4_* functions convert between the fattr4
196  * arguments/attributes and the system (e.g. vattr) values. The following
197  * commands are currently in use:
198  *
199  * NFS4ATTR_SUPPORTED: checks if the attribute in question is supported:
200  *	sarg.op = SUPPORTED - all supported attrs
201  *	sarg.op = GETIT - only supported readable attrs
202  *	sarg.op = SETIT - only supported writable attrs
203  *
204  * NFS4ATTR_GETIT: getattr type conversion - convert system values
205  * (e.g. vattr struct) to fattr4 type values to be returned to the
206  * user - usually in response to nfsv4 getattr request.
207  *
208  * NFS4ATTR_SETIT: convert fattr4 type values to system values to use by
209  * setattr. Allows only read/write and write attributes,
210  * even if not supported by the filesystem. Note that ufs only allows setattr
211  * of owner/group, mode, size, atime/mtime.
212  *
213  * NFS4ATTR_VERIT: convert fattr4 type values to system values to use by
214  * verify/nverify. Implemented to allow
215  * almost everything that can be returned by getattr into known structs
216  * (like vfsstat64 or vattr_t), that is, both read only and read/write attrs.
217  * The function will return -1 if it found that the arguments don't match.
218  * This applies to system-wide values that don't require a VOP_GETATTR
219  * or other further checks to verify. It will return no error if they
220  * either match or were retrieved successfully for later checking.
221  *
222  * NFS4ATTR_FREEIT: free up any space allocated by either of the above.
223  * The sargp->op should be either NFS4ATTR_GETIT or NFS4ATTR_SETIT
224  * to indicate which op was used to allocate the space.
225  *
226  * XXX Note: these functions are currently used by the server only. A
227  * XXX different method of conversion is used on the client side.
228  * XXX Eventually combining the two (possibly by adding NFS4ATTR_CLNT_GETIT
229  * XXX and SETIT) may be a cleaner approach.
230  */
231 
232 /*
233  * Mandatory attributes
234  */
235 
236 /* ARGSUSED */
237 static int
238 rfs4_fattr4_supported_attrs(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
239     union nfs4_attr_u *na)
240 {
241 	int	error = 0;
242 	bitmap4 supported = supported_attrs[sarg->cs->minorversion];
243 
244 	switch (cmd) {
245 	case NFS4ATTR_SUPPORTED:
246 		if (sarg->op == NFS4ATTR_SETIT)
247 			error = EINVAL;
248 		break;		/* this attr is supported */
249 	case NFS4ATTR_GETIT:
250 		na->supported_attrs = supported;
251 		break;
252 	case NFS4ATTR_SETIT:
253 		/*
254 		 * read-only attr
255 		 */
256 		error = EINVAL;
257 		break;
258 	case NFS4ATTR_VERIT:
259 		/*
260 		 * Compare the input bitmap to the server's bitmap
261 		 */
262 		if (na->supported_attrs != supported) {
263 			error = -1;	/* no match */
264 		}
265 		break;
266 	case NFS4ATTR_FREEIT:
267 		break;
268 	}
269 	return (error);
270 }
271 
272 /*
273  * Translate vnode vtype to nfsv4_ftype.
274  */
275 static nfs_ftype4 vt_to_nf4[] = {
276 	0, NF4REG, NF4DIR, NF4BLK, NF4CHR, NF4LNK, NF4FIFO, 0, 0, NF4SOCK, 0
277 };
278 
279 /* ARGSUSED */
280 static int
281 rfs4_fattr4_type(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
282     union nfs4_attr_u *na)
283 {
284 	int		error = 0;
285 
286 	switch (cmd) {
287 	case NFS4ATTR_SUPPORTED:
288 		if (sarg->op == NFS4ATTR_SETIT)
289 			error = EINVAL;
290 		break;		/* this attr is supported */
291 	case NFS4ATTR_GETIT:
292 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_TYPE)) {
293 			error = -1;	/* may be okay if rdattr_error */
294 			break;
295 		}
296 		ASSERT(sarg->vap->va_mask & AT_TYPE);
297 
298 		/*
299 		 * if xattr flag not set, use v4_to_nf4 mapping;
300 		 * otherwise verify xattr flag is in sync with va_type
301 		 * and set xattr types.
302 		 */
303 		if (! (sarg->xattr & (FH4_NAMEDATTR | FH4_ATTRDIR)))
304 			na->type = vt_to_nf4[sarg->vap->va_type];
305 		else {
306 			/*
307 			 * FH4 flag was set.  Dir type maps to attrdir,
308 			 * and all other types map to namedattr.
309 			 */
310 			if (sarg->vap->va_type == VDIR)
311 				na->type = NF4ATTRDIR;
312 			else
313 				na->type = NF4NAMEDATTR;
314 		}
315 		break;
316 	case NFS4ATTR_SETIT:
317 		/*
318 		 * read-only attr
319 		 */
320 		error = EINVAL;
321 		break;
322 	case NFS4ATTR_VERIT:
323 		/*
324 		 * Compare the input type to the object type on server
325 		 */
326 		ASSERT(sarg->vap->va_mask & AT_TYPE);
327 		if (sarg->vap->va_type != nf4_to_vt[na->type])
328 			error = -1;	/* no match */
329 		break;
330 	case NFS4ATTR_FREEIT:
331 		break;
332 	}
333 	return (error);
334 }
335 
336 /* ARGSUSED */
337 static int
338 fattr4_get_fh_expire_type(struct exportinfo *exi, uint32_t *fh_expire_typep)
339 {
340 #ifdef	VOLATILE_FH_TEST
341 	int	ex_flags;
342 
343 	if (exi == NULL)
344 		return (ESTALE);
345 	ex_flags = exi->exi_export.ex_flags;
346 	if ((ex_flags & (EX_VOLFH | EX_VOLRNM | EX_VOLMIG | EX_NOEXPOPEN))
347 	    == 0) {
348 		*fh_expire_typep = FH4_PERSISTENT;
349 		return (0);
350 	}
351 	*fh_expire_typep = 0;
352 
353 	if (ex_flags & EX_NOEXPOPEN) {
354 		/* file handles should not expire with open - not used */
355 		*fh_expire_typep = FH4_NOEXPIRE_WITH_OPEN;
356 	}
357 	if (ex_flags & EX_VOLFH) {
358 		/*
359 		 * file handles may expire any time - on share here.
360 		 * If volatile any, no need to check other flags.
361 		 */
362 		*fh_expire_typep |= FH4_VOLATILE_ANY;
363 		return (0);
364 	}
365 	if (ex_flags & EX_VOLRNM) {
366 		/* file handles may expire on rename */
367 		*fh_expire_typep |= FH4_VOL_RENAME;
368 	}
369 	if (ex_flags & EX_VOLMIG) {
370 		/* file handles may expire on migration - not used */
371 		*fh_expire_typep |= FH4_VOL_MIGRATION;
372 	}
373 #else	/* not VOLATILE_FH_TEST */
374 	*fh_expire_typep = FH4_PERSISTENT;
375 #endif	/* VOLATILE_FH_TEST */
376 
377 	return (0);
378 }
379 
380 /*
381  * At this point the only volatile filehandles we allow (for test purposes
382  * only) are either fh's that expire when the filesystem is shared (reshared),
383  * fh's that expire on a rename and persistent ones.
384  */
385 /* ARGSUSED */
386 static int
387 rfs4_fattr4_fh_expire_type(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
388     union nfs4_attr_u *na)
389 {
390 	uint32_t fh_expire_type;
391 	int error = 0;
392 
393 	switch (cmd) {
394 	case NFS4ATTR_SUPPORTED:
395 		if (sarg->op == NFS4ATTR_SETIT)
396 			error = EINVAL;
397 		break;		/* this attr is supported */
398 	case NFS4ATTR_GETIT:
399 		error = fattr4_get_fh_expire_type(sarg->cs->exi,
400 		    &na->fh_expire_type);
401 		break;
402 	case NFS4ATTR_SETIT:
403 		/*
404 		 * read-only attr
405 		 */
406 		error = EINVAL;
407 		break;
408 	case NFS4ATTR_VERIT:
409 		error = fattr4_get_fh_expire_type(sarg->cs->exi,
410 		    &fh_expire_type);
411 		if (!error && (na->fh_expire_type != fh_expire_type))
412 			error = -1;	/* no match */
413 		break;
414 	case NFS4ATTR_FREEIT:
415 		break;
416 	}
417 	return (error);
418 }
419 
420 static int
421 fattr4_get_change(struct nfs4_svgetit_arg *sarg, fattr4_change *changep)
422 {
423 	vattr_t vap2[1], *vap = sarg->vap;
424 	struct compound_state *cs = sarg->cs;
425 	vnode_t *vp = cs->vp;
426 	nfsstat4 status;
427 	timespec_t vis_change;
428 
429 	if ((vap->va_mask & AT_CTIME) == 0) {
430 		if (sarg->rdattr_error && (vp == NULL)) {
431 			return (-1);	/* may be okay if rdattr_error */
432 		}
433 		ASSERT(vp != NULL);
434 		vap = vap2;
435 		vap->va_mask = AT_CTIME;
436 		status = rfs4_vop_getattr(vp, vap, 0, cs->cr);
437 		if (status != NFS4_OK)
438 			return (geterrno4(status));
439 	}
440 	NFS4_SET_FATTR4_CHANGE(*changep, vap->va_ctime);
441 
442 	if (nfs_visible_change(cs->exi, vp, &vis_change)) {
443 		fattr4_change visch;
444 		NFS4_SET_FATTR4_CHANGE(visch, vis_change);
445 		if (visch > *changep)
446 			*changep = visch;
447 	}
448 
449 	return (0);
450 }
451 
452 /* ARGSUSED */
453 static int
454 rfs4_fattr4_change(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
455     union nfs4_attr_u *na)
456 {
457 	int error = 0;
458 	fattr4_change change;
459 	uint_t mask;
460 	vattr_t *vap = sarg->vap;
461 
462 	switch (cmd) {
463 	case NFS4ATTR_SUPPORTED:
464 		if (sarg->op == NFS4ATTR_SETIT)
465 			error = EINVAL;
466 		break;		/* this attr is supported */
467 	case NFS4ATTR_GETIT:
468 		error = fattr4_get_change(sarg, &na->change);
469 		break;
470 	case NFS4ATTR_SETIT:
471 		/*
472 		 * read-only attr
473 		 */
474 		error = EINVAL;
475 		break;
476 	case NFS4ATTR_VERIT:
477 		mask = vap->va_mask;
478 		vap->va_mask &= ~AT_CTIME;	/* force a VOP_GETATTR */
479 		error = fattr4_get_change(sarg, &change);
480 		vap->va_mask = mask;
481 		if (!error && (na->change != change))
482 			error = -1;
483 		break;
484 	case NFS4ATTR_FREEIT:
485 		break;
486 	}
487 	return (error);
488 }
489 
490 /* ARGSUSED */
491 static int
492 rfs4_fattr4_size(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
493     union nfs4_attr_u *na)
494 {
495 	int	error = 0;
496 
497 	switch (cmd) {
498 	case NFS4ATTR_SUPPORTED:
499 		break;		/* this attr is supported */
500 	case NFS4ATTR_GETIT:
501 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_SIZE)) {
502 			error = -1;	/* may be okay if rdattr_error */
503 			break;
504 		}
505 		ASSERT(sarg->vap->va_mask & AT_SIZE);
506 		na->size = sarg->vap->va_size;
507 		break;
508 	case NFS4ATTR_SETIT:
509 		ASSERT(sarg->vap->va_mask & AT_SIZE);
510 		sarg->vap->va_size = na->size;
511 		break;
512 	case NFS4ATTR_VERIT:
513 		ASSERT(sarg->vap->va_mask & AT_SIZE);
514 		if (sarg->vap->va_size != na->size)
515 			error = -1;	/* no match */
516 		break;
517 	case NFS4ATTR_FREEIT:
518 		break;
519 	}
520 	return (error);
521 }
522 
523 /*
524  * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports
525  * hard links.
526  */
527 /* ARGSUSED */
528 static int
529 rfs4_fattr4_link_support(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
530     union nfs4_attr_u *na)
531 {
532 	int error = 0;
533 
534 	switch (cmd) {
535 	case NFS4ATTR_SUPPORTED:
536 		if (sarg->op == NFS4ATTR_SETIT)
537 			error = EINVAL;
538 		break;		/* this attr is supported */
539 	case NFS4ATTR_GETIT:
540 		na->link_support = TRUE;
541 		break;
542 	case NFS4ATTR_SETIT:
543 		/*
544 		 * read-only attr
545 		 */
546 		error = EINVAL;
547 		break;
548 	case NFS4ATTR_VERIT:
549 		if (!na->link_support)
550 			error = -1;	/* no match */
551 		break;
552 	case NFS4ATTR_FREEIT:
553 		break;
554 	}
555 	return (error);
556 }
557 
558 /*
559  * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports
560  * sym links.
561  */
562 /* ARGSUSED */
563 static int
564 rfs4_fattr4_symlink_support(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
565     union nfs4_attr_u *na)
566 {
567 	int error = 0;
568 
569 	switch (cmd) {
570 	case NFS4ATTR_SUPPORTED:
571 		if (sarg->op == NFS4ATTR_SETIT)
572 			error = EINVAL;
573 		break;		/* this attr is supported */
574 	case NFS4ATTR_GETIT:
575 		na->symlink_support = TRUE;
576 		break;
577 	case NFS4ATTR_SETIT:
578 		/*
579 		 * read-only attr
580 		 */
581 		error = EINVAL;
582 		break;
583 	case NFS4ATTR_VERIT:
584 		if (!na->symlink_support)
585 			error = -1;	/* no match */
586 		break;
587 	case NFS4ATTR_FREEIT:
588 		break;
589 	}
590 	return (error);
591 }
592 
593 /* ARGSUSED */
594 static int
595 rfs4_fattr4_named_attr(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
596     union nfs4_attr_u *na)
597 {
598 	int error = 0;
599 	ulong_t val;
600 
601 	switch (cmd) {
602 	case NFS4ATTR_SUPPORTED:
603 		if (sarg->op == NFS4ATTR_SETIT)
604 			error = EINVAL;
605 		break;		/* this attr is supported */
606 	case NFS4ATTR_GETIT:
607 		if (sarg->rdattr_error && (sarg->cs->vp == NULL)) {
608 			error = -1;	/* may be okay if rdattr_error */
609 			break;
610 		}
611 		ASSERT(sarg->cs->vp != NULL);
612 
613 		/*
614 		 * Solaris xattr model requires that VFS_XATTR is set
615 		 * in file systems enabled for generic xattr.  If VFS_XATTR
616 		 * not set, no need to call pathconf for _PC_XATTR_EXISTS..
617 		 *
618 		 * However the VFS_XATTR flag doesn't indicate sysattr support
619 		 * so always check for sysattrs and then only do the
620 		 * _PC_XATTR_EXISTS pathconf if needed.
621 		 */
622 
623 		val = 0;
624 		error = VOP_PATHCONF(sarg->cs->vp, _PC_SATTR_EXISTS,
625 		    &val, sarg->cs->cr, NULL);
626 		if ((error || val == 0) &&
627 		    sarg->cs->vp->v_vfsp->vfs_flag & VFS_XATTR) {
628 			error = VOP_PATHCONF(sarg->cs->vp,
629 			    _PC_XATTR_EXISTS, &val, sarg->cs->cr, NULL);
630 			if (error)
631 				break;
632 		}
633 		na->named_attr = (val ? TRUE : FALSE);
634 		break;
635 	case NFS4ATTR_SETIT:
636 		/*
637 		 * read-only attr
638 		 */
639 		error = EINVAL;
640 		break;
641 	case NFS4ATTR_VERIT:
642 		ASSERT(sarg->cs->vp != NULL);
643 		if (sarg->cs->vp->v_vfsp->vfs_flag & VFS_XATTR) {
644 			error = VOP_PATHCONF(sarg->cs->vp, _PC_SATTR_EXISTS,
645 			    &val, sarg->cs->cr, NULL);
646 			if (error || val == 0)
647 				error = VOP_PATHCONF(sarg->cs->vp,
648 				    _PC_XATTR_EXISTS, &val,
649 				    sarg->cs->cr, NULL);
650 			if (error)
651 				break;
652 		} else
653 			val = 0;
654 		if (na->named_attr != (val ? TRUE : FALSE))
655 			error = -1;	/* no match */
656 		break;
657 	case NFS4ATTR_FREEIT:
658 		break;
659 	}
660 	return (error);
661 }
662 
663 /* ARGSUSED */
664 static int
665 rfs4_fattr4_fsid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
666     union nfs4_attr_u *na)
667 {
668 	int error = 0;
669 	int *pmaj = (int *)&na->fsid.major;
670 
671 	/*
672 	 * fsid_t is 64bits so it fits completely in fattr4_fsid.major.
673 	 * fattr4_fsid.minor is always set to 0 since it isn't needed (yet).
674 	 */
675 	switch (cmd) {
676 	case NFS4ATTR_SUPPORTED:
677 		if (sarg->op == NFS4ATTR_SETIT)
678 			error = EINVAL;
679 		break;		/* this attr is supported */
680 	case NFS4ATTR_GETIT:
681 		if (sarg->is_referral) {
682 			na->fsid.major = 1;
683 			na->fsid.minor = 0;
684 		} else if (sarg->cs->exi->exi_volatile_dev) {
685 			pmaj[0] = sarg->cs->exi->exi_fsid.val[0];
686 			pmaj[1] = sarg->cs->exi->exi_fsid.val[1];
687 			na->fsid.minor = 0;
688 		} else {
689 			na->fsid.major = getmajor(sarg->vap->va_fsid);
690 			na->fsid.minor = getminor(sarg->vap->va_fsid);
691 		}
692 		break;
693 	case NFS4ATTR_SETIT:
694 		error = EINVAL;
695 		break;
696 	case NFS4ATTR_VERIT:
697 		if (sarg->is_referral) {
698 			if (na->fsid.major != 1 ||
699 			    na->fsid.minor != 0)
700 				error = -1;
701 		} else if (sarg->cs->exi->exi_volatile_dev) {
702 			if (pmaj[0] != sarg->cs->exi->exi_fsid.val[0] ||
703 			    pmaj[1] != sarg->cs->exi->exi_fsid.val[1] ||
704 			    na->fsid.minor != 0)
705 				error = -1;
706 		} else {
707 			if (na->fsid.major != getmajor(sarg->vap->va_fsid) ||
708 			    na->fsid.minor != getminor(sarg->vap->va_fsid))
709 				error = -1;
710 		}
711 		break;
712 	case NFS4ATTR_FREEIT:
713 		break;
714 	}
715 	return (error);
716 }
717 
718 /* ARGSUSED */
719 static int
720 rfs4_fattr4_unique_handles(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
721     union nfs4_attr_u *na)
722 {
723 	/*
724 	 * XXX
725 	 * For now, we can't support this. Problem of /export, beinging
726 	 * a file system, /export/a and /export/b shared separately,
727 	 * and /export/a/l and /export/b/l are ahrd links of each other.
728 	 */
729 	int error = 0;
730 
731 	switch (cmd) {
732 	case NFS4ATTR_SUPPORTED:
733 		if (sarg->op == NFS4ATTR_SETIT)
734 			error = EINVAL;
735 		break;		/* this attr is supported */
736 	case NFS4ATTR_GETIT:
737 		na->unique_handles = FALSE;
738 		break;
739 	case NFS4ATTR_SETIT:
740 		/*
741 		 * read-only attr
742 		 */
743 		error = EINVAL;
744 		break;
745 	case NFS4ATTR_VERIT:
746 		if (na->unique_handles)
747 			error = -1;	/* no match */
748 		break;
749 	case NFS4ATTR_FREEIT:
750 		break;
751 	}
752 	return (error);
753 }
754 
755 /* ARGSUSED */
756 static int
757 rfs4_fattr4_lease_time(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
758     union nfs4_attr_u *na)
759 {
760 	int error = 0;
761 
762 	switch (cmd) {
763 	case NFS4ATTR_SUPPORTED:
764 		if (sarg->op == NFS4ATTR_SETIT)
765 			error = EINVAL;
766 		break;		/* this attr is supported */
767 	case NFS4ATTR_GETIT:
768 		na->lease_time = rfs4_lease_time;
769 		break;
770 	case NFS4ATTR_SETIT:
771 		/*
772 		 * read-only attr
773 		 */
774 		error = EINVAL;
775 		break;
776 	case NFS4ATTR_VERIT:
777 		if (na->lease_time != rfs4_lease_time)
778 			error = -1;	/* no match */
779 		break;
780 	case NFS4ATTR_FREEIT:
781 		break;
782 	}
783 	return (error);
784 }
785 
786 /* ARGSUSED */
787 static int
788 rfs4_fattr4_rdattr_error(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
789     union nfs4_attr_u *na)
790 {
791 	int error = 0;
792 
793 	switch (cmd) {
794 	case NFS4ATTR_SUPPORTED:
795 		if ((sarg->op == NFS4ATTR_SETIT) ||
796 		    (sarg->op == NFS4ATTR_VERIT))
797 			error = EINVAL;
798 		break;		/* this attr is supported */
799 	case NFS4ATTR_GETIT:
800 		ASSERT(sarg->rdattr_error_req);
801 		na->rdattr_error = sarg->rdattr_error;
802 		break;
803 	case NFS4ATTR_SETIT:
804 	case NFS4ATTR_VERIT:
805 		/*
806 		 * read-only attr
807 		 */
808 		error = EINVAL;
809 		break;
810 	case NFS4ATTR_FREEIT:
811 		break;
812 	}
813 	return (error);
814 }
815 
816 /*
817  * Server side compare of a filehandle from the wire to a native
818  * server filehandle.
819  */
820 static int
821 rfs4fhcmp(nfs_fh4 *wirefh, nfs_fh4 *srvfh)
822 {
823 	nfs_fh4_fmt_t fh;
824 
825 	ASSERT(IS_P2ALIGNED(wirefh->nfs_fh4_val, sizeof (uint32_t)));
826 
827 	bzero(&fh, sizeof (nfs_fh4_fmt_t));
828 	if (!xdr_inline_decode_nfs_fh4((uint32_t *)wirefh->nfs_fh4_val, &fh,
829 	    wirefh->nfs_fh4_len))
830 		return (1);
831 
832 	return (bcmp(srvfh->nfs_fh4_val, &fh, srvfh->nfs_fh4_len));
833 }
834 
835 /* ARGSUSED */
836 static int
837 rfs4_fattr4_filehandle(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
838     union nfs4_attr_u *na)
839 {
840 	nfs_fh4 *fh;
841 
842 	switch (cmd) {
843 	case NFS4ATTR_SUPPORTED:
844 		if (sarg->op == NFS4ATTR_SETIT)
845 			return (EINVAL);
846 		return (0);	/* this attr is supported */
847 	case NFS4ATTR_GETIT:
848 		/*
849 		 * If sarg->cs->fh is all zeros then should makefh a new
850 		 * one, otherwise, copy that one over.
851 		 */
852 		fh = &sarg->cs->fh;
853 		if (sarg->cs->fh.nfs_fh4_len == 0) {
854 			if (sarg->rdattr_error && (sarg->cs->vp == NULL))
855 				return (-1);	/* okay if rdattr_error */
856 			ASSERT(sarg->cs->vp != NULL);
857 			na->filehandle.nfs_fh4_val =
858 			    kmem_alloc(NFS_FH4_LEN, KM_SLEEP);
859 			return (makefh4(&na->filehandle, sarg->cs->vp,
860 			    sarg->cs->exi));
861 		}
862 		na->filehandle.nfs_fh4_val =
863 		    kmem_alloc(fh->nfs_fh4_len, KM_SLEEP);
864 		nfs_fh4_copy(fh, &na->filehandle);
865 		return (0);
866 	case NFS4ATTR_SETIT:
867 		/*
868 		 * read-only attr
869 		 */
870 		return (EINVAL);
871 	case NFS4ATTR_VERIT:
872 		/*
873 		 * A verify of a filehandle will have the client sending
874 		 * the raw format which needs to be compared to the
875 		 * native format.
876 		 */
877 		if (rfs4fhcmp(&na->filehandle, &sarg->cs->fh) == 1)
878 			return (-1);	/* no match */
879 		return (0);
880 	case NFS4ATTR_FREEIT:
881 		if (sarg->op != NFS4ATTR_GETIT)
882 			return (0);
883 		if (na->filehandle.nfs_fh4_val == NULL)
884 			return (0);
885 		kmem_free(na->filehandle.nfs_fh4_val,
886 		    na->filehandle.nfs_fh4_len);
887 		na->filehandle.nfs_fh4_val = NULL;
888 		na->filehandle.nfs_fh4_len = 0;
889 		return (0);
890 	}
891 	return (0);
892 }
893 
894 /*
895  * Recommended attributes
896  */
897 
898 /* ARGSUSED */
899 static int
900 rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
901     union nfs4_attr_u *na)
902 {
903 	int error = 0;
904 	vsecattr_t vs_native, vs_ace4;
905 	ulong_t whichacl;
906 	nfsstat4 status;
907 	vattr_t va, *vap = sarg->vap;
908 	vnode_t *vp = sarg->cs->vp;
909 
910 	if (RFS4_MANDATTR_ONLY)
911 		return (ENOTSUP);
912 
913 	switch (cmd) {
914 	case NFS4ATTR_SUPPORTED:
915 		break;
916 
917 	case NFS4ATTR_VERIT:
918 	case NFS4ATTR_GETIT:
919 		if (sarg->rdattr_error && (vp == NULL)) {
920 			return (-1);
921 		}
922 		ASSERT(vp != NULL);
923 		bzero(&vs_native, sizeof (vs_native));
924 
925 		/* see which ACLs fs supports */
926 		error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl,
927 		    sarg->cs->cr, NULL);
928 		if (error != 0) {
929 			/*
930 			 * If we got an error, then the filesystem
931 			 * likely does not understand the _PC_ACL_ENABLED
932 			 * pathconf.  In this case, we fall back to trying
933 			 * POSIX-draft (aka UFS-style) ACLs, since that's
934 			 * the behavior used by earlier version of NFS.
935 			 */
936 			error = 0;
937 			whichacl = _ACL_ACLENT_ENABLED;
938 		}
939 
940 		if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) {
941 			/*
942 			 * If the file system supports neither ACE nor
943 			 * ACLENT ACLs we will fall back to UFS-style ACLs
944 			 * like we did above if there was an error upon
945 			 * calling VOP_PATHCONF.
946 			 *
947 			 * ACE and ACLENT type ACLs are the only interfaces
948 			 * supported thus far.  If any other bits are set on
949 			 * 'whichacl' upon return from VOP_PATHCONF, we will
950 			 * ignore them.
951 			 */
952 			whichacl = _ACL_ACLENT_ENABLED;
953 		}
954 
955 		if (whichacl & _ACL_ACE_ENABLED)
956 			vs_native.vsa_mask = VSA_ACE | VSA_ACECNT;
957 		else if (whichacl & _ACL_ACLENT_ENABLED)
958 			vs_native.vsa_mask = VSA_ACL | VSA_ACLCNT |
959 			    VSA_DFACL | VSA_DFACLCNT;
960 
961 		if (error != 0)
962 			break;
963 
964 		/* get the ACL, and translate it into nfsace4 style */
965 		error = VOP_GETSECATTR(vp, &vs_native,
966 		    0, sarg->cs->cr, NULL);
967 		if (error != 0)
968 			break;
969 		if (whichacl & _ACL_ACE_ENABLED) {
970 			error = vs_acet_to_ace4(&vs_native, &vs_ace4, TRUE);
971 			vs_acet_destroy(&vs_native);
972 		} else {
973 			error = vs_aent_to_ace4(&vs_native, &vs_ace4,
974 			    vp->v_type == VDIR, TRUE);
975 			vs_aent_destroy(&vs_native);
976 		}
977 		if (error != 0)
978 			break;
979 
980 		if (cmd == NFS4ATTR_GETIT) {
981 			na->acl.fattr4_acl_len = vs_ace4.vsa_aclcnt;
982 			/* see case NFS4ATTR_FREEIT for this being freed */
983 			na->acl.fattr4_acl_val = vs_ace4.vsa_aclentp;
984 		} else {
985 			if (na->acl.fattr4_acl_len != vs_ace4.vsa_aclcnt)
986 				error = -1; /* no match */
987 			else if (ln_ace4_cmp(na->acl.fattr4_acl_val,
988 			    vs_ace4.vsa_aclentp,
989 			    vs_ace4.vsa_aclcnt) != 0)
990 				error = -1; /* no match */
991 		}
992 
993 		break;
994 
995 	case NFS4ATTR_SETIT:
996 		if (sarg->rdattr_error && (vp == NULL)) {
997 			return (-1);
998 		}
999 		ASSERT(vp != NULL);
1000 
1001 		/* prepare vs_ace4 from fattr4 data */
1002 		bzero(&vs_ace4, sizeof (vs_ace4));
1003 		vs_ace4.vsa_mask = VSA_ACE | VSA_ACECNT;
1004 		vs_ace4.vsa_aclcnt = na->acl.fattr4_acl_len;
1005 		vs_ace4.vsa_aclentp = na->acl.fattr4_acl_val;
1006 		vs_ace4.vsa_aclentsz = vs_ace4.vsa_aclcnt * sizeof (ace_t);
1007 		/* make sure we have correct owner/group */
1008 		if ((vap->va_mask & (AT_UID | AT_GID)) !=
1009 		    (AT_UID | AT_GID)) {
1010 			vap = &va;
1011 			vap->va_mask = AT_UID | AT_GID;
1012 			status = rfs4_vop_getattr(vp,
1013 			    vap, 0, sarg->cs->cr);
1014 			if (status != NFS4_OK)
1015 				return (geterrno4(status));
1016 		}
1017 
1018 		/* see which ACLs the fs supports */
1019 		error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl,
1020 		    sarg->cs->cr, NULL);
1021 		if (error != 0) {
1022 			/*
1023 			 * If we got an error, then the filesystem
1024 			 * likely does not understand the _PC_ACL_ENABLED
1025 			 * pathconf.  In this case, we fall back to trying
1026 			 * POSIX-draft (aka UFS-style) ACLs, since that's
1027 			 * the behavior used by earlier version of NFS.
1028 			 */
1029 			error = 0;
1030 			whichacl = _ACL_ACLENT_ENABLED;
1031 		}
1032 
1033 		if (!(whichacl & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED))) {
1034 			/*
1035 			 * If the file system supports neither ACE nor
1036 			 * ACLENT ACLs we will fall back to UFS-style ACLs
1037 			 * like we did above if there was an error upon
1038 			 * calling VOP_PATHCONF.
1039 			 *
1040 			 * ACE and ACLENT type ACLs are the only interfaces
1041 			 * supported thus far.  If any other bits are set on
1042 			 * 'whichacl' upon return from VOP_PATHCONF, we will
1043 			 * ignore them.
1044 			 */
1045 			whichacl = _ACL_ACLENT_ENABLED;
1046 		}
1047 
1048 		if (whichacl & _ACL_ACE_ENABLED) {
1049 			error = vs_ace4_to_acet(&vs_ace4, &vs_native,
1050 			    vap->va_uid, vap->va_gid, TRUE);
1051 			if (error != 0)
1052 				break;
1053 			(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
1054 			error = VOP_SETSECATTR(vp, &vs_native,
1055 			    0, sarg->cs->cr, NULL);
1056 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1057 			vs_acet_destroy(&vs_native);
1058 		} else if (whichacl & _ACL_ACLENT_ENABLED) {
1059 			error = vs_ace4_to_aent(&vs_ace4, &vs_native,
1060 			    vap->va_uid, vap->va_gid, vp->v_type == VDIR, TRUE);
1061 			if (error != 0)
1062 				break;
1063 			(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
1064 			error = VOP_SETSECATTR(vp, &vs_native,
1065 			    0, sarg->cs->cr, NULL);
1066 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1067 			vs_aent_destroy(&vs_native);
1068 		}
1069 		break;
1070 
1071 	case NFS4ATTR_FREEIT:
1072 		if (sarg->op == NFS4ATTR_GETIT) {
1073 			vs_ace4.vsa_mask = VSA_ACE | VSA_ACECNT;
1074 			vs_ace4.vsa_aclcnt = na->acl.fattr4_acl_len;
1075 			vs_ace4.vsa_aclentp = na->acl.fattr4_acl_val;
1076 			vs_ace4_destroy(&vs_ace4);
1077 		}
1078 		break;
1079 	}
1080 
1081 	return (error);
1082 }
1083 
1084 /* ARGSUSED */
1085 static int
1086 rfs4_fattr4_aclsupport(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1087     union nfs4_attr_u *na)
1088 {
1089 	int error = 0;
1090 
1091 	if (RFS4_MANDATTR_ONLY)
1092 		return (ENOTSUP);
1093 
1094 	switch (cmd) {
1095 	case NFS4ATTR_SUPPORTED:
1096 		if (sarg->op == NFS4ATTR_SETIT)
1097 			error = EINVAL;
1098 		break;	/* supported */
1099 	case NFS4ATTR_GETIT:
1100 		na->aclsupport = ACL4_SUPPORT_ALLOW_ACL |
1101 		    ACL4_SUPPORT_DENY_ACL;
1102 		break;
1103 	case NFS4ATTR_SETIT:
1104 		error = EINVAL;
1105 		break;
1106 	case NFS4ATTR_VERIT:
1107 		if (na->aclsupport != (ACL4_SUPPORT_ALLOW_ACL |
1108 		    ACL4_SUPPORT_DENY_ACL))
1109 			error = -1;	/* no match */
1110 		break;
1111 	}
1112 
1113 	return (error);
1114 }
1115 
1116 /* ARGSUSED */
1117 static int
1118 rfs4_fattr4_archive(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1119     union nfs4_attr_u *na)
1120 {
1121 	return (ENOTSUP);
1122 }
1123 
1124 /* ARGSUSED */
1125 static int
1126 rfs4_fattr4_cansettime(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1127     union nfs4_attr_u *na)
1128 {
1129 	int error = 0;
1130 
1131 	if (RFS4_MANDATTR_ONLY)
1132 		return (ENOTSUP);
1133 
1134 	switch (cmd) {
1135 	case NFS4ATTR_SUPPORTED:
1136 		if (sarg->op == NFS4ATTR_SETIT)
1137 			error = EINVAL;
1138 		break;		/* this attr is supported */
1139 	case NFS4ATTR_GETIT:
1140 		na->cansettime = TRUE;
1141 		break;
1142 	case NFS4ATTR_SETIT:
1143 		/*
1144 		 * read-only attr
1145 		 */
1146 		error = EINVAL;
1147 		break;
1148 	case NFS4ATTR_VERIT:
1149 		if (!na->cansettime)
1150 			error = -1;	/* no match */
1151 		break;
1152 	case NFS4ATTR_FREEIT:
1153 		break;
1154 	}
1155 	return (error);
1156 }
1157 
1158 /*
1159  * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports
1160  * case insensitive.
1161  */
1162 /* ARGSUSED */
1163 static int
1164 rfs4_fattr4_case_insensitive(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1165     union nfs4_attr_u *na)
1166 {
1167 	int error = 0;
1168 
1169 	if (RFS4_MANDATTR_ONLY)
1170 		return (ENOTSUP);
1171 
1172 	switch (cmd) {
1173 	case NFS4ATTR_SUPPORTED:
1174 		if (sarg->op == NFS4ATTR_SETIT)
1175 			error = EINVAL;
1176 		break;		/* this attr is supported */
1177 	case NFS4ATTR_GETIT:
1178 		na->case_insensitive = FALSE;
1179 		break;
1180 	case NFS4ATTR_SETIT:
1181 		/*
1182 		 * read-only attr
1183 		 */
1184 		error = EINVAL;
1185 		break;
1186 	case NFS4ATTR_VERIT:
1187 		if (!na->case_insensitive)
1188 			error = -1;	/* no match */
1189 		break;
1190 	case NFS4ATTR_FREEIT:
1191 		break;
1192 	}
1193 	return (error);
1194 }
1195 
1196 /* ARGSUSED */
1197 static int
1198 rfs4_fattr4_case_preserving(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1199     union nfs4_attr_u *na)
1200 {
1201 	int error = 0;
1202 
1203 	if (RFS4_MANDATTR_ONLY)
1204 		return (ENOTSUP);
1205 
1206 	switch (cmd) {
1207 	case NFS4ATTR_SUPPORTED:
1208 		if (sarg->op == NFS4ATTR_SETIT)
1209 			error = EINVAL;
1210 		break;		/* this attr is supported */
1211 	case NFS4ATTR_GETIT:
1212 		na->case_preserving = TRUE;
1213 		break;
1214 	case NFS4ATTR_SETIT:
1215 		/*
1216 		 * read-only attr
1217 		 */
1218 		error = EINVAL;
1219 		break;
1220 	case NFS4ATTR_VERIT:
1221 		if (!na->case_preserving)
1222 			error = -1;	/* no match */
1223 		break;
1224 	case NFS4ATTR_FREEIT:
1225 		break;
1226 	}
1227 	return (error);
1228 }
1229 
1230 /* fattr4_chown_restricted should reall be fattr4_chown_allowed */
1231 /* ARGSUSED */
1232 static int
1233 rfs4_fattr4_chown_restricted(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1234     union nfs4_attr_u *na)
1235 {
1236 	int error = 0;
1237 	ulong_t val;
1238 
1239 	if (RFS4_MANDATTR_ONLY)
1240 		return (ENOTSUP);
1241 
1242 	switch (cmd) {
1243 	case NFS4ATTR_SUPPORTED:
1244 		if (sarg->op == NFS4ATTR_SETIT)
1245 			error = EINVAL;
1246 		break;		/* this attr is supported */
1247 	case NFS4ATTR_GETIT:
1248 		if (sarg->rdattr_error && (sarg->cs->vp == NULL)) {
1249 			error = -1;	/* may be okay if rdattr_error */
1250 			break;
1251 		}
1252 		ASSERT(sarg->cs->vp != NULL);
1253 		error = VOP_PATHCONF(sarg->cs->vp,
1254 		    _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr, NULL);
1255 		if (error)
1256 			break;
1257 
1258 		na->chown_restricted = (val == 1);
1259 		break;
1260 	case NFS4ATTR_SETIT:
1261 		/*
1262 		 * read-only attr
1263 		 */
1264 		error = EINVAL;
1265 		break;
1266 	case NFS4ATTR_VERIT:
1267 		ASSERT(sarg->cs->vp != NULL);
1268 		error = VOP_PATHCONF(sarg->cs->vp,
1269 		    _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr, NULL);
1270 		if (error)
1271 			break;
1272 		if (na->chown_restricted != (val == 1))
1273 			error = -1;	/* no match */
1274 		break;
1275 	case NFS4ATTR_FREEIT:
1276 		break;
1277 	}
1278 	return (error);
1279 }
1280 
1281 /* ARGSUSED */
1282 static int
1283 rfs4_fattr4_fileid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1284     union nfs4_attr_u *na)
1285 {
1286 	int	error = 0;
1287 
1288 	if (RFS4_MANDATTR_ONLY)
1289 		return (ENOTSUP);
1290 
1291 	switch (cmd) {
1292 	case NFS4ATTR_SUPPORTED:
1293 		if (sarg->op == NFS4ATTR_SETIT)
1294 			error = EINVAL;
1295 		break;		/* this attr is supported */
1296 	case NFS4ATTR_GETIT:
1297 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_NODEID)) {
1298 			error = -1;	/* may be okay if rdattr_error */
1299 			break;
1300 		}
1301 		ASSERT(sarg->vap->va_mask & AT_NODEID);
1302 		na->fileid = sarg->vap->va_nodeid;
1303 		break;
1304 	case NFS4ATTR_SETIT:
1305 		/*
1306 		 * read-only attr
1307 		 */
1308 		error = EINVAL;
1309 		break;
1310 	case NFS4ATTR_VERIT:
1311 		ASSERT(sarg->vap->va_mask & AT_NODEID);
1312 		if (sarg->vap->va_nodeid != na->fileid)
1313 			error = -1;	/* no match */
1314 		break;
1315 	case NFS4ATTR_FREEIT:
1316 		break;
1317 	}
1318 	return (error);
1319 }
1320 
1321 /* ARGSUSED */
1322 static int
1323 rfs4_get_mntdfileid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg)
1324 {
1325 	int error = 0;
1326 	vattr_t	*vap, va;
1327 	vnode_t *stubvp = NULL, *vp;
1328 
1329 	vp = sarg->cs->vp;
1330 	sarg->mntdfid_set = FALSE;
1331 
1332 	/*
1333 	 * VROOT object or zone's root, must untraverse.
1334 	 *
1335 	 * NOTE: Not doing reality checks on curzone vs. compound
1336 	 * state vnode because it will mismatch once at initialization
1337 	 * if a non-global-zone triggers the module load, BUT in that case
1338 	 * the vp is literally "/" which has VROOT set.
1339 	 */
1340 	if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
1341 
1342 		/* extra hold for vp since untraverse might rele */
1343 		VN_HOLD(vp);
1344 		stubvp = untraverse(vp, ZONE_ROOTVP());
1345 
1346 		/*
1347 		 * If vp/stubvp are same, we must be at system-or-zone
1348 		 * root because untraverse returned same vp
1349 		 * for a VROOT object.  sarg->vap was setup
1350 		 * before we got here, so there's no need to do
1351 		 * another getattr -- just use the one in sarg.
1352 		 */
1353 		if (VN_CMP(vp, stubvp)) {
1354 			ASSERT(VN_IS_CURZONEROOT(vp));
1355 			vap = sarg->vap;
1356 		} else {
1357 			va.va_mask = AT_NODEID;
1358 			vap = &va;
1359 			error = rfs4_vop_getattr(stubvp, vap, 0, sarg->cs->cr);
1360 		}
1361 
1362 		/*
1363 		 * Done with stub, time to rele.  If vp and stubvp
1364 		 * were the same, then we need to rele either vp or
1365 		 * stubvp.  If they weren't the same, then untraverse()
1366 		 * already took case of the extra hold on vp, and only
1367 		 * the stub needs to be rele'd.  Both cases are handled
1368 		 * by unconditionally rele'ing the stub.
1369 		 */
1370 		VN_RELE(stubvp);
1371 	} else
1372 		vap = sarg->vap;
1373 
1374 	/*
1375 	 * At this point, vap should contain "correct" AT_NODEID --
1376 	 * (for V_ROOT case, nodeid of stub, for non-VROOT case,
1377 	 * nodeid of vp).  If error or AT_NODEID not available, then
1378 	 * make the obligatory (yet mysterious) rdattr_error
1379 	 * check that is so common in the attr code.
1380 	 */
1381 	if (!error && (vap->va_mask & AT_NODEID)) {
1382 		sarg->mounted_on_fileid = vap->va_nodeid;
1383 		sarg->mntdfid_set = TRUE;
1384 	} else if (sarg->rdattr_error)
1385 		error = -1;
1386 
1387 	/*
1388 	 * error describes these cases:
1389 	 *	0 : success
1390 	 *	-1: failure due to previous attr processing error (rddir only).
1391 	 *	* : new attr failure  (if rddir, caller will set rdattr_error)
1392 	 */
1393 	return (error);
1394 }
1395 
1396 /* ARGSUSED */
1397 static int
1398 rfs4_fattr4_mounted_on_fileid(nfs4_attr_cmd_t cmd,
1399     struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na)
1400 {
1401 	int	error = 0;
1402 
1403 	if (RFS4_MANDATTR_ONLY)
1404 		return (ENOTSUP);
1405 
1406 	switch (cmd) {
1407 	case NFS4ATTR_SUPPORTED:
1408 		if (sarg->op == NFS4ATTR_SETIT)
1409 			error = EINVAL;
1410 		break;		/* this attr is supported */
1411 	case NFS4ATTR_GETIT:
1412 	case NFS4ATTR_VERIT:
1413 		if (!sarg->mntdfid_set)
1414 			error = rfs4_get_mntdfileid(cmd, sarg);
1415 
1416 		if (!error && sarg->mntdfid_set) {
1417 			if (cmd == NFS4ATTR_GETIT)
1418 				na->mounted_on_fileid = sarg->mounted_on_fileid;
1419 			else
1420 				if (na->mounted_on_fileid !=
1421 				    sarg->mounted_on_fileid)
1422 					error = -1;
1423 		}
1424 		break;
1425 	case NFS4ATTR_SETIT:
1426 		/* read-only attr */
1427 		error = EINVAL;
1428 		break;
1429 	case NFS4ATTR_FREEIT:
1430 		break;
1431 	}
1432 	return (error);
1433 }
1434 
1435 /* ARGSUSED */
1436 static int
1437 rfs4_fattr4_files_avail(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1438     union nfs4_attr_u *na)
1439 {
1440 	int	error = 0;
1441 
1442 	if (RFS4_MANDATTR_ONLY)
1443 		return (ENOTSUP);
1444 
1445 	switch (cmd) {
1446 	case NFS4ATTR_SUPPORTED:
1447 		if (sarg->op == NFS4ATTR_SETIT)
1448 			error = EINVAL;
1449 		break;		/* this attr is supported */
1450 	case NFS4ATTR_GETIT:
1451 		if (sarg->rdattr_error && (sarg->sbp == NULL)) {
1452 			error = -1;	/* may be okay if rdattr_error */
1453 			break;
1454 		}
1455 		ASSERT(sarg->sbp != NULL);
1456 		na->files_avail = sarg->sbp->f_favail;
1457 		break;
1458 	case NFS4ATTR_SETIT:
1459 		/*
1460 		 * read-only attr
1461 		 */
1462 		error = EINVAL;
1463 		break;
1464 	case NFS4ATTR_VERIT:
1465 		ASSERT(sarg->sbp != NULL);
1466 		if (sarg->sbp->f_favail != na->files_avail)
1467 			error = -1;	/* no match */
1468 		break;
1469 	case NFS4ATTR_FREEIT:
1470 		break;
1471 	}
1472 	return (error);
1473 }
1474 
1475 /* ARGSUSED */
1476 static int
1477 rfs4_fattr4_files_free(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1478     union nfs4_attr_u *na)
1479 {
1480 	int	error = 0;
1481 
1482 	if (RFS4_MANDATTR_ONLY)
1483 		return (ENOTSUP);
1484 
1485 	switch (cmd) {
1486 	case NFS4ATTR_SUPPORTED:
1487 		if (sarg->op == NFS4ATTR_SETIT)
1488 			error = EINVAL;
1489 		break;		/* this attr is supported */
1490 	case NFS4ATTR_GETIT:
1491 		if (sarg->rdattr_error && (sarg->sbp == NULL)) {
1492 			error = -1;	/* may be okay if rdattr_error */
1493 			break;
1494 		}
1495 		ASSERT(sarg->sbp != NULL);
1496 		na->files_free = sarg->sbp->f_ffree;
1497 		break;
1498 	case NFS4ATTR_SETIT:
1499 		/*
1500 		 * read-only attr
1501 		 */
1502 		error = EINVAL;
1503 		break;
1504 	case NFS4ATTR_VERIT:
1505 		ASSERT(sarg->sbp != NULL);
1506 		if (sarg->sbp->f_ffree != na->files_free)
1507 			error = -1;	/* no match */
1508 		break;
1509 	case NFS4ATTR_FREEIT:
1510 		break;
1511 	}
1512 	return (error);
1513 }
1514 
1515 /* ARGSUSED */
1516 static int
1517 rfs4_fattr4_files_total(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1518     union nfs4_attr_u *na)
1519 {
1520 	int	error = 0;
1521 
1522 	if (RFS4_MANDATTR_ONLY)
1523 		return (ENOTSUP);
1524 
1525 	switch (cmd) {
1526 	case NFS4ATTR_SUPPORTED:
1527 		if (sarg->op == NFS4ATTR_SETIT)
1528 			error = EINVAL;
1529 		break;		/* this attr is supported */
1530 	case NFS4ATTR_GETIT:
1531 		if (sarg->rdattr_error && (sarg->sbp == NULL)) {
1532 			error = -1;	/* may be okay if rdattr_error */
1533 			break;
1534 		}
1535 		ASSERT(sarg->sbp != NULL);
1536 		na->files_total = sarg->sbp->f_files;
1537 		break;
1538 	case NFS4ATTR_SETIT:
1539 		/*
1540 		 * read-only attr
1541 		 */
1542 		error = EINVAL;
1543 		break;
1544 	case NFS4ATTR_VERIT:
1545 		ASSERT(sarg->sbp != NULL);
1546 		if (sarg->sbp->f_files != na->files_total)
1547 			error = -1;	/* no match */
1548 		break;
1549 	case NFS4ATTR_FREEIT:
1550 		break;
1551 	}
1552 	return (error);
1553 }
1554 
1555 static void
1556 rfs4_free_pathname4(pathname4 *pn4)
1557 {
1558 	int i, len;
1559 	utf8string *utf8s;
1560 
1561 	if (pn4 == NULL || (len = pn4->pathname4_len) == 0 ||
1562 	    (utf8s = pn4->pathname4_val) == NULL)
1563 		return;
1564 
1565 	for (i = 0; i < len; i++, utf8s++) {
1566 		if (utf8s->utf8string_val == NULL ||
1567 		    utf8s->utf8string_len == 0)
1568 			continue;
1569 
1570 		kmem_free(utf8s->utf8string_val, utf8s->utf8string_len);
1571 		utf8s->utf8string_val = NULL;
1572 	}
1573 
1574 	kmem_free(pn4->pathname4_val,
1575 	    sizeof (utf8string) * pn4->pathname4_len);
1576 	pn4->pathname4_val = 0;
1577 }
1578 
1579 static void
1580 rfs4_free_fs_location4(fs_location4 *fsl4)
1581 {
1582 	if (fsl4 == NULL)
1583 		return;
1584 
1585 	rfs4_free_pathname4((pathname4 *)&fsl4->server_len);
1586 	rfs4_free_pathname4(&fsl4->rootpath);
1587 }
1588 
1589 void
1590 rfs4_free_fs_locations4(fs_locations4 *fsls4)
1591 {
1592 	int i, len;
1593 	fs_location4 *fsl4;
1594 
1595 	if (fsls4 == NULL)
1596 		return;
1597 
1598 	/* free fs_root */
1599 	rfs4_free_pathname4(&fsls4->fs_root);
1600 
1601 	if ((len = fsls4->locations_len) == 0 ||
1602 	    (fsl4 = fsls4->locations_val) == NULL)
1603 		return;
1604 
1605 	/* free fs_location4 */
1606 	for (i = 0; i < len; i++) {
1607 		rfs4_free_fs_location4(fsl4);
1608 		fsl4++;
1609 	}
1610 
1611 	kmem_free(fsls4->locations_val, sizeof (fs_location4) * len);
1612 	fsls4->locations_val = NULL;
1613 }
1614 
1615 /* ARGSUSED */
1616 static int
1617 rfs4_fattr4_fs_locations(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1618     union nfs4_attr_u *na)
1619 {
1620 	int error = 0;
1621 	fs_locations4 *fsl;
1622 
1623 	if (RFS4_MANDATTR_ONLY)
1624 		return (ENOTSUP);
1625 
1626 	switch (cmd) {
1627 	case NFS4ATTR_SUPPORTED:
1628 		if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT)
1629 			error = EINVAL;
1630 		break;  /* this attr is supported */
1631 
1632 	case NFS4ATTR_GETIT:
1633 	{
1634 		kstat_named_t *stat =
1635 		    sarg->cs->exi->exi_ne->ne_globals->svstat[NFS_V4];
1636 
1637 		fsl = fetch_referral(sarg->cs->vp, sarg->cs->cr);
1638 		if (fsl == NULL)
1639 			(void) memset(&(na->fs_locations), 0,
1640 			    sizeof (fs_locations4));
1641 		else {
1642 			na->fs_locations = *fsl;
1643 			kmem_free(fsl, sizeof (fs_locations4));
1644 		}
1645 		stat[NFS_REFERRALS].value.ui64++;
1646 		break;
1647 	}
1648 	case NFS4ATTR_FREEIT:
1649 		if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT)
1650 			error = EINVAL;
1651 		rfs4_free_fs_locations4(&na->fs_locations);
1652 		break;
1653 
1654 	case NFS4ATTR_SETIT:
1655 	case NFS4ATTR_VERIT:
1656 		/*
1657 		 * read-only attr
1658 		 */
1659 		error = EINVAL;
1660 		break;
1661 	}
1662 	return (error);
1663 }
1664 
1665 /* ARGSUSED */
1666 static int
1667 rfs4_fattr4_hidden(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1668     union nfs4_attr_u *na)
1669 {
1670 	return (ENOTSUP);
1671 }
1672 
1673 /* ARGSUSED */
1674 static int
1675 rfs4_fattr4_homogeneous(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1676     union nfs4_attr_u *na)
1677 {
1678 	int error = 0;
1679 
1680 	if (RFS4_MANDATTR_ONLY)
1681 		return (ENOTSUP);
1682 
1683 	switch (cmd) {
1684 	case NFS4ATTR_SUPPORTED:
1685 		if (sarg->op == NFS4ATTR_SETIT)
1686 			error = EINVAL;
1687 		break;		/* this attr is supported */
1688 	case NFS4ATTR_GETIT:
1689 		na->homogeneous = TRUE; /* XXX - need a VOP extension */
1690 		break;
1691 	case NFS4ATTR_SETIT:
1692 		/*
1693 		 * read-only attr
1694 		 */
1695 		error = EINVAL;
1696 		break;
1697 	case NFS4ATTR_VERIT:
1698 		if (!na->homogeneous)
1699 			error = -1;	/* no match */
1700 		break;
1701 	case NFS4ATTR_FREEIT:
1702 		break;
1703 	}
1704 	return (error);
1705 }
1706 
1707 /* ARGSUSED */
1708 static int
1709 rfs4_fattr4_maxfilesize(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1710     union nfs4_attr_u *na)
1711 {
1712 	int error = 0;
1713 	ulong_t val;
1714 	fattr4_maxfilesize maxfilesize;
1715 
1716 	if (RFS4_MANDATTR_ONLY)
1717 		return (ENOTSUP);
1718 
1719 	switch (cmd) {
1720 	case NFS4ATTR_SUPPORTED:
1721 		if (sarg->op == NFS4ATTR_SETIT)
1722 			error = EINVAL;
1723 		break;		/* this attr is supported */
1724 	case NFS4ATTR_GETIT:
1725 		if (sarg->rdattr_error && (sarg->cs->vp == NULL)) {
1726 			error = -1;	/* may be okay if rdattr_error */
1727 			break;
1728 		}
1729 		ASSERT(sarg->cs->vp != NULL);
1730 		error = VOP_PATHCONF(sarg->cs->vp, _PC_FILESIZEBITS, &val,
1731 		    sarg->cs->cr, NULL);
1732 		if (error)
1733 			break;
1734 
1735 		/*
1736 		 * If the underlying file system does not support
1737 		 * _PC_FILESIZEBITS, return a reasonable default. Note that
1738 		 * error code on VOP_PATHCONF will be 0, even if the underlying
1739 		 * file system does not support _PC_FILESIZEBITS.
1740 		 */
1741 		if (val == (ulong_t)-1) {
1742 			na->maxfilesize = MAXOFF32_T;
1743 		} else {
1744 			if (val >= (sizeof (uint64_t) * 8))
1745 				na->maxfilesize = INT64_MAX;
1746 			else
1747 				na->maxfilesize = ((1LL << (val - 1)) - 1);
1748 		}
1749 		break;
1750 	case NFS4ATTR_SETIT:
1751 		/*
1752 		 * read-only attr
1753 		 */
1754 		error = EINVAL;
1755 		break;
1756 	case NFS4ATTR_VERIT:
1757 		ASSERT(sarg->cs->vp != NULL);
1758 		error = VOP_PATHCONF(sarg->cs->vp, _PC_FILESIZEBITS, &val,
1759 		    sarg->cs->cr, NULL);
1760 		if (error)
1761 			break;
1762 		/*
1763 		 * If the underlying file system does not support
1764 		 * _PC_FILESIZEBITS, return a reasonable default. Note that
1765 		 * error code on VOP_PATHCONF will be 0, even if the underlying
1766 		 * file system does not support _PC_FILESIZEBITS.
1767 		 */
1768 		if (val == (ulong_t)-1) {
1769 			maxfilesize = MAXOFF32_T;
1770 		} else {
1771 			if (val >= (sizeof (uint64_t) * 8))
1772 				maxfilesize = INT64_MAX;
1773 			else
1774 				maxfilesize = ((1LL << (val - 1)) - 1);
1775 		}
1776 		if (na->maxfilesize != maxfilesize)
1777 			error = -1;	/* no match */
1778 		break;
1779 	case NFS4ATTR_FREEIT:
1780 		break;
1781 	}
1782 	return (error);
1783 }
1784 
1785 /* ARGSUSED */
1786 static int
1787 rfs4_fattr4_maxlink(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1788     union nfs4_attr_u *na)
1789 {
1790 	int error = 0;
1791 	ulong_t val;
1792 
1793 	if (RFS4_MANDATTR_ONLY)
1794 		return (ENOTSUP);
1795 
1796 	switch (cmd) {
1797 	case NFS4ATTR_SUPPORTED:
1798 		if (sarg->op == NFS4ATTR_SETIT)
1799 			error = EINVAL;
1800 		break;		/* this attr is supported */
1801 	case NFS4ATTR_GETIT:
1802 		if (sarg->rdattr_error && (sarg->cs->vp == NULL)) {
1803 			error = -1;	/* may be okay if rdattr_error */
1804 			break;
1805 		}
1806 		ASSERT(sarg->cs->vp != NULL);
1807 		error = VOP_PATHCONF(sarg->cs->vp, _PC_LINK_MAX, &val,
1808 		    sarg->cs->cr, NULL);
1809 		if (error == 0) {
1810 			na->maxlink = val;
1811 		}
1812 		break;
1813 	case NFS4ATTR_SETIT:
1814 		/*
1815 		 * read-only attr
1816 		 */
1817 		error = EINVAL;
1818 		break;
1819 	case NFS4ATTR_VERIT:
1820 		ASSERT(sarg->cs->vp != NULL);
1821 		error = VOP_PATHCONF(sarg->cs->vp, _PC_LINK_MAX, &val,
1822 		    sarg->cs->cr, NULL);
1823 		if (!error && (na->maxlink != (uint32_t)val))
1824 			error = -1;	/* no match */
1825 		break;
1826 	case NFS4ATTR_FREEIT:
1827 		break;
1828 	}
1829 	return (error);
1830 }
1831 
1832 /* ARGSUSED */
1833 static int
1834 rfs4_fattr4_maxname(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1835     union nfs4_attr_u *na)
1836 {
1837 	int error = 0;
1838 	ulong_t val;
1839 
1840 	if (RFS4_MANDATTR_ONLY)
1841 		return (ENOTSUP);
1842 
1843 	switch (cmd) {
1844 	case NFS4ATTR_SUPPORTED:
1845 		if (sarg->op == NFS4ATTR_SETIT)
1846 			error = EINVAL;
1847 		break;		/* this attr is supported */
1848 	case NFS4ATTR_GETIT:
1849 		if (sarg->rdattr_error && (sarg->cs->vp == NULL)) {
1850 			error = -1;	/* may be okay if rdattr_error */
1851 			break;
1852 		}
1853 		ASSERT(sarg->cs->vp != NULL);
1854 		error = VOP_PATHCONF(sarg->cs->vp, _PC_NAME_MAX, &val,
1855 		    sarg->cs->cr, NULL);
1856 		if (error == 0) {
1857 			na->maxname = val;
1858 		}
1859 		break;
1860 	case NFS4ATTR_SETIT:
1861 		/*
1862 		 * read-only attr
1863 		 */
1864 		error = EINVAL;
1865 		break;
1866 	case NFS4ATTR_VERIT:
1867 		ASSERT(sarg->cs->vp != NULL);
1868 		error = VOP_PATHCONF(sarg->cs->vp, _PC_NAME_MAX, &val,
1869 		    sarg->cs->cr, NULL);
1870 		if (!error && (na->maxname != val))
1871 			error = -1;	/* no match */
1872 		break;
1873 	case NFS4ATTR_FREEIT:
1874 		break;
1875 	}
1876 	return (error);
1877 }
1878 
1879 /* ARGSUSED */
1880 static int
1881 rfs4_fattr4_maxread(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1882     union nfs4_attr_u *na)
1883 {
1884 	int error = 0;
1885 
1886 	if (RFS4_MANDATTR_ONLY)
1887 		return (ENOTSUP);
1888 
1889 	switch (cmd) {
1890 	case NFS4ATTR_SUPPORTED:
1891 		if (sarg->op == NFS4ATTR_SETIT)
1892 			error = EINVAL;
1893 		break;		/* this attr is supported */
1894 	case NFS4ATTR_GETIT:
1895 		na->maxread = rfs4_tsize(sarg->cs->req);
1896 		break;
1897 	case NFS4ATTR_SETIT:
1898 		/*
1899 		 * read-only attr
1900 		 */
1901 		error = EINVAL;
1902 		break;
1903 	case NFS4ATTR_VERIT:
1904 		if (na->maxread != rfs4_tsize(sarg->cs->req))
1905 			error = -1;	/* no match */
1906 		break;
1907 	case NFS4ATTR_FREEIT:
1908 		break;
1909 	}
1910 	return (error);
1911 }
1912 
1913 /* ARGSUSED */
1914 static int
1915 rfs4_fattr4_maxwrite(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1916     union nfs4_attr_u *na)
1917 {
1918 	int error = 0;
1919 
1920 	if (RFS4_MANDATTR_ONLY)
1921 		return (ENOTSUP);
1922 
1923 	switch (cmd) {
1924 	case NFS4ATTR_SUPPORTED:
1925 		if (sarg->op == NFS4ATTR_SETIT)
1926 			error = EINVAL;
1927 		break;		/* this attr is supported */
1928 	case NFS4ATTR_GETIT:
1929 		na->maxwrite = rfs4_tsize(sarg->cs->req);
1930 		break;
1931 	case NFS4ATTR_SETIT:
1932 		/*
1933 		 * read-only attr
1934 		 */
1935 		error = EINVAL;
1936 		break;
1937 	case NFS4ATTR_VERIT:
1938 		if (na->maxwrite != rfs4_tsize(sarg->cs->req))
1939 			error = -1;	/* no match */
1940 		break;
1941 	case NFS4ATTR_FREEIT:
1942 		break;
1943 	}
1944 	return (error);
1945 }
1946 
1947 /* ARGSUSED */
1948 static int
1949 rfs4_fattr4_mimetype(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1950     union nfs4_attr_u *na)
1951 {
1952 	return (ENOTSUP);
1953 }
1954 
1955 /* ARGSUSED */
1956 static int
1957 rfs4_fattr4_mode(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
1958     union nfs4_attr_u *na)
1959 {
1960 	int	error = 0;
1961 
1962 	if (RFS4_MANDATTR_ONLY)
1963 		return (ENOTSUP);
1964 
1965 	switch (cmd) {
1966 	case NFS4ATTR_SUPPORTED:
1967 		break;		/* this attr is supported */
1968 	case NFS4ATTR_GETIT:
1969 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_MODE)) {
1970 			error = -1;	/* may be okay if rdattr_error */
1971 			break;
1972 		}
1973 		ASSERT(sarg->vap->va_mask & AT_MODE);
1974 		na->mode = sarg->vap->va_mode;
1975 		break;
1976 	case NFS4ATTR_SETIT:
1977 		ASSERT(sarg->vap->va_mask & AT_MODE);
1978 		sarg->vap->va_mode = na->mode;
1979 		/*
1980 		 * If the filesystem is exported with nosuid, then mask off
1981 		 * the setuid and setgid bits.
1982 		 */
1983 		if (sarg->cs->vp->v_type == VREG &&
1984 		    (sarg->cs->exi->exi_export.ex_flags & EX_NOSUID))
1985 			sarg->vap->va_mode &= ~(VSUID | VSGID);
1986 		break;
1987 	case NFS4ATTR_VERIT:
1988 		ASSERT(sarg->vap->va_mask & AT_MODE);
1989 		if (sarg->vap->va_mode != na->mode)
1990 			error = -1;	/* no match */
1991 		break;
1992 	case NFS4ATTR_FREEIT:
1993 		break;
1994 	}
1995 	return (error);
1996 }
1997 
1998 /* ARGSUSED */
1999 static int
2000 rfs4_fattr4_no_trunc(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2001     union nfs4_attr_u *na)
2002 {
2003 	int error = 0;
2004 
2005 	if (RFS4_MANDATTR_ONLY)
2006 		return (ENOTSUP);
2007 
2008 	switch (cmd) {
2009 	case NFS4ATTR_SUPPORTED:
2010 		if (sarg->op == NFS4ATTR_SETIT)
2011 			error = EINVAL;
2012 		break;		/* this attr is supported */
2013 	case NFS4ATTR_GETIT:
2014 		na->no_trunc = TRUE;
2015 		break;
2016 	case NFS4ATTR_SETIT:
2017 		/*
2018 		 * read-only attr
2019 		 */
2020 		error = EINVAL;
2021 		break;
2022 	case NFS4ATTR_VERIT:
2023 		if (!na->no_trunc)
2024 			error = -1;	/* no match */
2025 		break;
2026 	case NFS4ATTR_FREEIT:
2027 		break;
2028 	}
2029 	return (error);
2030 }
2031 
2032 /* ARGSUSED */
2033 static int
2034 rfs4_fattr4_numlinks(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2035     union nfs4_attr_u *na)
2036 {
2037 	int	error = 0;
2038 
2039 	if (RFS4_MANDATTR_ONLY)
2040 		return (ENOTSUP);
2041 
2042 	switch (cmd) {
2043 	case NFS4ATTR_SUPPORTED:
2044 		if (sarg->op == NFS4ATTR_SETIT)
2045 			error = EINVAL;
2046 		break;		/* this attr is supported */
2047 	case NFS4ATTR_GETIT:
2048 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_NLINK)) {
2049 			error = -1;	/* may be okay if rdattr_error */
2050 			break;
2051 		}
2052 		ASSERT(sarg->vap->va_mask & AT_NLINK);
2053 		na->numlinks = sarg->vap->va_nlink;
2054 		break;
2055 	case NFS4ATTR_SETIT:
2056 		/*
2057 		 * read-only attr
2058 		 */
2059 		error = EINVAL;
2060 		break;
2061 	case NFS4ATTR_VERIT:
2062 		ASSERT(sarg->vap->va_mask & AT_NLINK);
2063 		if (sarg->vap->va_nlink != na->numlinks)
2064 			error = -1;	/* no match */
2065 		break;
2066 	case NFS4ATTR_FREEIT:
2067 		break;
2068 	}
2069 	return (error);
2070 }
2071 
2072 /* ARGSUSED */
2073 static int
2074 rfs4_fattr4_owner(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2075     union nfs4_attr_u *na)
2076 {
2077 	int	error = 0;
2078 	uid_t	uid;
2079 
2080 	if (RFS4_MANDATTR_ONLY)
2081 		return (ENOTSUP);
2082 
2083 	switch (cmd) {
2084 	case NFS4ATTR_SUPPORTED:
2085 		break;		/* this attr is supported */
2086 	case NFS4ATTR_GETIT:
2087 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_UID)) {
2088 			error = -1;	/* may be okay if rdattr_error */
2089 			break;
2090 		}
2091 		ASSERT(sarg->vap->va_mask & AT_UID);
2092 
2093 		/*
2094 		 * There are well defined polices for what happens on server-
2095 		 * side GETATTR when uid to attribute string conversion cannot
2096 		 * occur. Please refer to nfs4_idmap.c for details.
2097 		 */
2098 		error = nfs_idmap_uid_str(sarg->vap->va_uid, &na->owner, TRUE);
2099 		switch (error) {
2100 		case ECONNREFUSED:
2101 			error = NFS4ERR_DELAY;
2102 			break;
2103 		default:
2104 			break;
2105 		}
2106 		break;
2107 
2108 	case NFS4ATTR_SETIT:
2109 		ASSERT(sarg->vap->va_mask & AT_UID);
2110 
2111 		/*
2112 		 * There are well defined policies for what happens on server-
2113 		 * side SETATTR of 'owner' when a "user@domain" mapping cannot
2114 		 * occur. Please refer to nfs4_idmap.c for details.
2115 		 *
2116 		 * Any other errors, such as the mapping not being found by
2117 		 * nfsmapid(8), and interrupted clnt_call, etc, will result
2118 		 * in NFS4ERR_BADOWNER.
2119 		 *
2120 		 * XXX need to return consistent errors, perhaps all
2121 		 * server side attribute routines should return NFS4ERR*.
2122 		 */
2123 		error = nfs_idmap_str_uid(&na->owner, &sarg->vap->va_uid, TRUE);
2124 		switch (error) {
2125 		case NFS4_OK:
2126 		case ENOTSUP:
2127 			/*
2128 			 * Ignore warning that we are the
2129 			 * nfsmapid (can't happen on srv)
2130 			 */
2131 			error = 0;
2132 			MSG_PRT_DEBUG = FALSE;
2133 			break;
2134 
2135 		case ECOMM:
2136 		case ECONNREFUSED:
2137 			if (!MSG_PRT_DEBUG) {
2138 				/*
2139 				 * printed just once per daemon death,
2140 				 * inform the user and then stay silent
2141 				 */
2142 				cmn_err(CE_WARN, "!Unable to contact "
2143 				    "nfsmapid");
2144 				MSG_PRT_DEBUG = TRUE;
2145 			}
2146 			error = NFS4ERR_DELAY;
2147 			break;
2148 
2149 		case EINVAL:
2150 			error = NFS4ERR_INVAL;
2151 			break;
2152 
2153 		default:
2154 			error = NFS4ERR_BADOWNER;
2155 			break;
2156 		}
2157 		break;
2158 
2159 	case NFS4ATTR_VERIT:
2160 		ASSERT(sarg->vap->va_mask & AT_UID);
2161 		error = nfs_idmap_str_uid(&na->owner, &uid, TRUE);
2162 		/*
2163 		 * Ignore warning that we are the nfsmapid (can't happen on srv)
2164 		 */
2165 		if (error == ENOTSUP)
2166 			error = 0;
2167 		if (error)
2168 			error = -1;	/* no match */
2169 		else if (sarg->vap->va_uid != uid)
2170 			error = -1;	/* no match */
2171 		break;
2172 	case NFS4ATTR_FREEIT:
2173 		if (sarg->op == NFS4ATTR_GETIT) {
2174 			if (na->owner.utf8string_val) {
2175 				UTF8STRING_FREE(na->owner)
2176 				bzero(&na->owner, sizeof (na->owner));
2177 			}
2178 		}
2179 		break;
2180 	}
2181 	return (error);
2182 }
2183 
2184 /* ARGSUSED */
2185 static int
2186 rfs4_fattr4_owner_group(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2187     union nfs4_attr_u *na)
2188 {
2189 	int	error = 0;
2190 	gid_t	gid;
2191 
2192 	if (RFS4_MANDATTR_ONLY)
2193 		return (ENOTSUP);
2194 
2195 	switch (cmd) {
2196 	case NFS4ATTR_SUPPORTED:
2197 		break;		/* this attr is supported */
2198 	case NFS4ATTR_GETIT:
2199 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_GID)) {
2200 			error = -1;	/* may be okay if rdattr_error */
2201 			break;
2202 		}
2203 		ASSERT(sarg->vap->va_mask & AT_GID);
2204 
2205 		/*
2206 		 * There are well defined polices for what happens on server-
2207 		 * side GETATTR when gid to attribute string conversion cannot
2208 		 * occur. Please refer to nfs4_idmap.c for details.
2209 		 */
2210 		error = nfs_idmap_gid_str(sarg->vap->va_gid, &na->owner_group,
2211 		    TRUE);
2212 		switch (error) {
2213 		case ECONNREFUSED:
2214 			error = NFS4ERR_DELAY;
2215 			break;
2216 		default:
2217 			break;
2218 		}
2219 		break;
2220 
2221 	case NFS4ATTR_SETIT:
2222 		ASSERT(sarg->vap->va_mask & AT_GID);
2223 
2224 		/*
2225 		 * There are well defined policies for what happens on server-
2226 		 * side SETATTR of 'owner_group' when a "group@domain" mapping
2227 		 * cannot occur. Please refer to nfs4_idmap.c for details.
2228 		 *
2229 		 * Any other errors, such as the mapping not being found by
2230 		 * nfsmapid(8), and interrupted clnt_call, etc, will result
2231 		 * in NFS4ERR_BADOWNER.
2232 		 *
2233 		 * XXX need to return consistent errors, perhaps all
2234 		 * server side attribute routines should return NFS4ERR*.
2235 		 */
2236 		error = nfs_idmap_str_gid(&na->owner_group, &sarg->vap->va_gid,
2237 		    TRUE);
2238 		switch (error) {
2239 		case NFS4_OK:
2240 		case ENOTSUP:
2241 			/*
2242 			 * Ignore warning that we are the
2243 			 * nfsmapid (can't happen on srv)
2244 			 */
2245 			error = 0;
2246 			MSG_PRT_DEBUG = FALSE;
2247 			break;
2248 
2249 		case ECOMM:
2250 		case ECONNREFUSED:
2251 			if (!MSG_PRT_DEBUG) {
2252 				/*
2253 				 * printed just once per daemon death,
2254 				 * inform the user and then stay silent
2255 				 */
2256 				cmn_err(CE_WARN, "!Unable to contact "
2257 				    "nfsmapid");
2258 				MSG_PRT_DEBUG = TRUE;
2259 			}
2260 			error = NFS4ERR_DELAY;
2261 			break;
2262 
2263 		case EINVAL:
2264 			error = NFS4ERR_INVAL;
2265 			break;
2266 
2267 		default:
2268 			error = NFS4ERR_BADOWNER;
2269 			break;
2270 		}
2271 		break;
2272 
2273 	case NFS4ATTR_VERIT:
2274 		ASSERT(sarg->vap->va_mask & AT_GID);
2275 		error = nfs_idmap_str_gid(&na->owner_group, &gid, TRUE);
2276 		/*
2277 		 * Ignore warning that we are the nfsmapid (can't happen on srv)
2278 		 */
2279 		if (error == ENOTSUP)
2280 			error = 0;
2281 		if (error)
2282 			error = -1;	/* no match */
2283 		else if (sarg->vap->va_gid != gid)
2284 			error = -1;	/* no match */
2285 		break;
2286 	case NFS4ATTR_FREEIT:
2287 		if (sarg->op == NFS4ATTR_GETIT) {
2288 			if (na->owner_group.utf8string_val) {
2289 				UTF8STRING_FREE(na->owner_group)
2290 				bzero(&na->owner_group,
2291 				    sizeof (na->owner_group));
2292 			}
2293 		}
2294 		break;
2295 	}
2296 	return (error);
2297 }
2298 
2299 /* XXX - quota attributes should be supportable on Solaris 2 */
2300 /* ARGSUSED */
2301 static int
2302 rfs4_fattr4_quota_avail_hard(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2303     union nfs4_attr_u *na)
2304 {
2305 	return (ENOTSUP);
2306 }
2307 
2308 /* ARGSUSED */
2309 static int
2310 rfs4_fattr4_quota_avail_soft(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2311     union nfs4_attr_u *na)
2312 {
2313 	return (ENOTSUP);
2314 }
2315 
2316 /* ARGSUSED */
2317 static int
2318 rfs4_fattr4_quota_used(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2319     union nfs4_attr_u *na)
2320 {
2321 	return (ENOTSUP);
2322 }
2323 
2324 /* ARGSUSED */
2325 static int
2326 rfs4_fattr4_rawdev(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2327     union nfs4_attr_u *na)
2328 {
2329 	int	error = 0;
2330 
2331 	if (RFS4_MANDATTR_ONLY)
2332 		return (ENOTSUP);
2333 
2334 	switch (cmd) {
2335 	case NFS4ATTR_SUPPORTED:
2336 		if (sarg->op == NFS4ATTR_SETIT)
2337 			error = EINVAL;
2338 		break;		/* this attr is supported */
2339 	case NFS4ATTR_GETIT:
2340 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_RDEV)) {
2341 			error = -1;	/* may be okay if rdattr_error */
2342 			break;
2343 		}
2344 		ASSERT(sarg->vap->va_mask & AT_RDEV);
2345 		na->rawdev.specdata1 =  (uint32)getmajor(sarg->vap->va_rdev);
2346 		na->rawdev.specdata2 =  (uint32)getminor(sarg->vap->va_rdev);
2347 		break;
2348 	case NFS4ATTR_SETIT:
2349 		/*
2350 		 * read-only attr
2351 		 */
2352 		error = EINVAL;
2353 		break;
2354 	case NFS4ATTR_VERIT:
2355 		ASSERT(sarg->vap->va_mask & AT_RDEV);
2356 		if ((na->rawdev.specdata1 !=
2357 		    (uint32)getmajor(sarg->vap->va_rdev)) ||
2358 		    (na->rawdev.specdata2 !=
2359 		    (uint32)getminor(sarg->vap->va_rdev)))
2360 			error = -1;	/* no match */
2361 		break;
2362 	case NFS4ATTR_FREEIT:
2363 		break;
2364 	}
2365 	return (error);
2366 }
2367 
2368 /* ARGSUSED */
2369 static int
2370 rfs4_fattr4_space_avail(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2371     union nfs4_attr_u *na)
2372 {
2373 	int	error = 0;
2374 
2375 	if (RFS4_MANDATTR_ONLY)
2376 		return (ENOTSUP);
2377 
2378 	switch (cmd) {
2379 	case NFS4ATTR_SUPPORTED:
2380 		if (sarg->op == NFS4ATTR_SETIT)
2381 			error = EINVAL;
2382 		break;		/* this attr is supported */
2383 	case NFS4ATTR_GETIT:
2384 		if (sarg->rdattr_error && (sarg->sbp == NULL)) {
2385 			error = -1;	/* may be okay if rdattr_error */
2386 			break;
2387 		}
2388 		ASSERT(sarg->sbp != NULL);
2389 		if (sarg->sbp->f_bavail != (fsblkcnt64_t)-1) {
2390 			na->space_avail =
2391 			    (fattr4_space_avail) sarg->sbp->f_frsize *
2392 			    (fattr4_space_avail) sarg->sbp->f_bavail;
2393 		} else {
2394 			na->space_avail =
2395 			    (fattr4_space_avail) sarg->sbp->f_bavail;
2396 		}
2397 		break;
2398 	case NFS4ATTR_SETIT:
2399 		/*
2400 		 * read-only attr
2401 		 */
2402 		error = EINVAL;
2403 		break;
2404 	case NFS4ATTR_VERIT:
2405 		ASSERT(sarg->sbp != NULL);
2406 		if (sarg->sbp->f_bavail != na->space_avail)
2407 			error = -1;	/* no match */
2408 		break;
2409 	case NFS4ATTR_FREEIT:
2410 		break;
2411 	}
2412 	return (error);
2413 }
2414 
2415 /* ARGSUSED */
2416 static int
2417 rfs4_fattr4_space_free(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2418     union nfs4_attr_u *na)
2419 {
2420 	int	error = 0;
2421 
2422 	if (RFS4_MANDATTR_ONLY)
2423 		return (ENOTSUP);
2424 
2425 	switch (cmd) {
2426 	case NFS4ATTR_SUPPORTED:
2427 		if (sarg->op == NFS4ATTR_SETIT)
2428 			error = EINVAL;
2429 		break;		/* this attr is supported */
2430 	case NFS4ATTR_GETIT:
2431 		if (sarg->rdattr_error && (sarg->sbp == NULL)) {
2432 			error = -1;	/* may be okay if rdattr_error */
2433 			break;
2434 		}
2435 		ASSERT(sarg->sbp != NULL);
2436 		if (sarg->sbp->f_bfree != (fsblkcnt64_t)-1) {
2437 			na->space_free =
2438 			    (fattr4_space_free) sarg->sbp->f_frsize *
2439 			    (fattr4_space_free) sarg->sbp->f_bfree;
2440 		} else {
2441 			na->space_free =
2442 			    (fattr4_space_free) sarg->sbp->f_bfree;
2443 		}
2444 		break;
2445 	case NFS4ATTR_SETIT:
2446 		/*
2447 		 * read-only attr
2448 		 */
2449 		error = EINVAL;
2450 		break;
2451 	case NFS4ATTR_VERIT:
2452 		ASSERT(sarg->sbp != NULL);
2453 		if (sarg->sbp->f_bfree != na->space_free)
2454 			error = -1;	/* no match */
2455 		break;
2456 	case NFS4ATTR_FREEIT:
2457 		break;
2458 	}
2459 	return (error);
2460 }
2461 
2462 /* ARGSUSED */
2463 static int
2464 rfs4_fattr4_space_total(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2465     union nfs4_attr_u *na)
2466 {
2467 	int	error = 0;
2468 
2469 	if (RFS4_MANDATTR_ONLY)
2470 		return (ENOTSUP);
2471 
2472 	switch (cmd) {
2473 	case NFS4ATTR_SUPPORTED:
2474 		if (sarg->op == NFS4ATTR_SETIT)
2475 			error = EINVAL;
2476 		break;		/* this attr is supported */
2477 	case NFS4ATTR_GETIT:
2478 		if (sarg->rdattr_error_req && (sarg->sbp == NULL)) {
2479 			error = -1;	/* may be okay if rdattr_error */
2480 			break;
2481 		}
2482 		ASSERT(sarg->sbp != NULL);
2483 		if (sarg->sbp->f_blocks != (fsblkcnt64_t)-1) {
2484 			na->space_total =
2485 			    (fattr4_space_total) sarg->sbp->f_frsize *
2486 			    (fattr4_space_total) sarg->sbp->f_blocks;
2487 		} else {
2488 			na->space_total =
2489 			    (fattr4_space_total) sarg->sbp->f_blocks;
2490 		}
2491 		break;
2492 	case NFS4ATTR_SETIT:
2493 		/*
2494 		 * read-only attr
2495 		 */
2496 		error = EINVAL;
2497 		break;
2498 	case NFS4ATTR_VERIT:
2499 		ASSERT(sarg->sbp != NULL);
2500 		if (sarg->sbp->f_blocks != na->space_total)
2501 			error = -1;	/* no match */
2502 		break;
2503 	case NFS4ATTR_FREEIT:
2504 		break;
2505 	}
2506 	return (error);
2507 }
2508 
2509 /* ARGSUSED */
2510 static int
2511 rfs4_fattr4_space_used(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2512     union nfs4_attr_u *na)
2513 {
2514 	int	error = 0;
2515 
2516 	if (RFS4_MANDATTR_ONLY)
2517 		return (ENOTSUP);
2518 
2519 	switch (cmd) {
2520 	case NFS4ATTR_SUPPORTED:
2521 		if (sarg->op == NFS4ATTR_SETIT)
2522 			error = EINVAL;
2523 		break;		/* this attr is supported */
2524 	case NFS4ATTR_GETIT:
2525 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_NBLOCKS)) {
2526 			error = -1;	/* may be okay if rdattr_error */
2527 			break;
2528 		}
2529 		ASSERT(sarg->vap->va_mask & AT_NBLOCKS);
2530 		na->space_used =  (fattr4_space_used) DEV_BSIZE *
2531 		    (fattr4_space_used) sarg->vap->va_nblocks;
2532 		break;
2533 	case NFS4ATTR_SETIT:
2534 		/*
2535 		 * read-only attr
2536 		 */
2537 		error = EINVAL;
2538 		break;
2539 	case NFS4ATTR_VERIT:
2540 		ASSERT(sarg->vap->va_mask & AT_NBLOCKS);
2541 		if (sarg->vap->va_nblocks != na->space_used)
2542 			error = -1;	/* no match */
2543 		break;
2544 	case NFS4ATTR_FREEIT:
2545 		break;
2546 	}
2547 	return (error);
2548 }
2549 
2550 /* ARGSUSED */
2551 static int
2552 rfs4_fattr4_system(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2553     union nfs4_attr_u *na)
2554 {
2555 	return (ENOTSUP);
2556 }
2557 
2558 /* ARGSUSED */
2559 static int
2560 rfs4_fattr4_time_access(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2561     union nfs4_attr_u *na)
2562 {
2563 	int	error = 0;
2564 	timestruc_t atime;
2565 
2566 	if (RFS4_MANDATTR_ONLY)
2567 		return (ENOTSUP);
2568 
2569 	switch (cmd) {
2570 	case NFS4ATTR_SUPPORTED:
2571 		if (sarg->op == NFS4ATTR_SETIT)
2572 			error = EINVAL;
2573 		break;		/* this attr is supported */
2574 	case NFS4ATTR_GETIT:
2575 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_ATIME)) {
2576 			error = -1;	/* may be okay if rdattr_error */
2577 			break;
2578 		}
2579 		ASSERT(sarg->vap->va_mask & AT_ATIME);
2580 		error = nfs4_time_vton(&sarg->vap->va_atime, &na->time_access);
2581 		break;
2582 	case NFS4ATTR_SETIT:
2583 		/*
2584 		 * read-only attr
2585 		 */
2586 		error = EINVAL;
2587 		break;
2588 	case NFS4ATTR_VERIT:
2589 		ASSERT(sarg->vap->va_mask & AT_ATIME);
2590 		error = nfs4_time_ntov(&na->time_access, &atime);
2591 		if (error)
2592 			break;
2593 		if (bcmp(&atime, &sarg->vap->va_atime, sizeof (atime)))
2594 			error = -1;	/* no match */
2595 		break;
2596 	case NFS4ATTR_FREEIT:
2597 		break;
2598 	}
2599 	return (error);
2600 }
2601 
2602 /*
2603  * XXX - need to support the setting of access time
2604  */
2605 /* ARGSUSED */
2606 static int
2607 rfs4_fattr4_time_access_set(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2608     union nfs4_attr_u *na)
2609 {
2610 	int	error = 0;
2611 	settime4 *ta;
2612 
2613 	if (RFS4_MANDATTR_ONLY)
2614 		return (ENOTSUP);
2615 
2616 	switch (cmd) {
2617 	case NFS4ATTR_SUPPORTED:
2618 		if ((sarg->op == NFS4ATTR_GETIT) ||
2619 		    (sarg->op == NFS4ATTR_VERIT))
2620 			error = EINVAL;
2621 		break;		/* this attr is supported */
2622 	case NFS4ATTR_GETIT:
2623 	case NFS4ATTR_VERIT:
2624 		/*
2625 		 * write only attr
2626 		 */
2627 		error = EINVAL;
2628 		break;
2629 	case NFS4ATTR_SETIT:
2630 		ASSERT(sarg->vap->va_mask & AT_ATIME);
2631 		/*
2632 		 * Set access time (by server or by client)
2633 		 */
2634 		ta = &na->time_access_set;
2635 		if (ta->set_it == SET_TO_CLIENT_TIME4) {
2636 			error = nfs4_time_ntov(&ta->time, &sarg->vap->va_atime);
2637 		} else if (ta->set_it == SET_TO_SERVER_TIME4) {
2638 			gethrestime(&sarg->vap->va_atime);
2639 		} else {
2640 			error = EINVAL;
2641 		}
2642 		break;
2643 	case NFS4ATTR_FREEIT:
2644 		break;
2645 	}
2646 	return (error);
2647 }
2648 
2649 /* ARGSUSED */
2650 static int
2651 rfs4_fattr4_time_backup(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2652     union nfs4_attr_u *na)
2653 {
2654 	return (ENOTSUP);
2655 }
2656 
2657 /* ARGSUSED */
2658 static int
2659 rfs4_fattr4_time_create(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2660     union nfs4_attr_u *na)
2661 {
2662 	return (ENOTSUP);
2663 }
2664 
2665 /* ARGSUSED */
2666 static int
2667 rfs4_fattr4_time_delta(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2668     union nfs4_attr_u *na)
2669 {
2670 	int error = 0;
2671 
2672 	if (RFS4_MANDATTR_ONLY)
2673 		return (ENOTSUP);
2674 
2675 	switch (cmd) {
2676 	case NFS4ATTR_SUPPORTED:
2677 		if (sarg->op == NFS4ATTR_SETIT)
2678 			error = EINVAL;
2679 		break;		/* this attr is supported */
2680 	case NFS4ATTR_GETIT:
2681 		na->time_delta.seconds = 0;
2682 		na->time_delta.nseconds = 1000;
2683 		break;
2684 	case NFS4ATTR_SETIT:
2685 		/*
2686 		 * write only attr
2687 		 */
2688 		error = EINVAL;
2689 		break;
2690 	case NFS4ATTR_VERIT:
2691 		if ((na->time_delta.seconds != 0) ||
2692 		    (na->time_delta.nseconds != 1000))
2693 			error = -1;	/* no match */
2694 		break;
2695 	case NFS4ATTR_FREEIT:
2696 		break;
2697 	}
2698 	return (error);
2699 }
2700 
2701 /* ARGSUSED */
2702 static int
2703 rfs4_fattr4_time_metadata(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2704     union nfs4_attr_u *na)
2705 {
2706 	int	error = 0;
2707 	timestruc_t ctime;
2708 
2709 	if (RFS4_MANDATTR_ONLY)
2710 		return (ENOTSUP);
2711 
2712 	switch (cmd) {
2713 	case NFS4ATTR_SUPPORTED:
2714 		if (sarg->op == NFS4ATTR_SETIT)
2715 			error = EINVAL;
2716 		break;		/* this attr is supported */
2717 	case NFS4ATTR_GETIT:
2718 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_CTIME)) {
2719 			error = -1;	/* may be okay if rdattr_error */
2720 			break;
2721 		}
2722 		ASSERT(sarg->vap->va_mask & AT_CTIME);
2723 		error = nfs4_time_vton(&sarg->vap->va_ctime,
2724 		    &na->time_metadata);
2725 		break;
2726 	case NFS4ATTR_SETIT:
2727 		/*
2728 		 * read-only attr
2729 		 */
2730 		error = EINVAL;
2731 		break;
2732 	case NFS4ATTR_VERIT:
2733 		ASSERT(sarg->vap->va_mask & AT_CTIME);
2734 		error = nfs4_time_ntov(&na->time_metadata, &ctime);
2735 		if (error)
2736 			break;
2737 		if (bcmp(&ctime, &sarg->vap->va_ctime, sizeof (ctime)))
2738 			error = -1;	/* no match */
2739 		break;
2740 	case NFS4ATTR_FREEIT:
2741 		break;
2742 	}
2743 	return (error);
2744 }
2745 
2746 /* ARGSUSED */
2747 static int
2748 rfs4_fattr4_time_modify(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2749     union nfs4_attr_u *na)
2750 {
2751 	int	error = 0;
2752 	timestruc_t mtime;
2753 
2754 	if (RFS4_MANDATTR_ONLY)
2755 		return (ENOTSUP);
2756 
2757 	switch (cmd) {
2758 	case NFS4ATTR_SUPPORTED:
2759 		if (sarg->op == NFS4ATTR_SETIT)
2760 			error = EINVAL;
2761 		break;		/* this attr is supported */
2762 	case NFS4ATTR_GETIT:
2763 		if (sarg->rdattr_error && !(sarg->vap->va_mask & AT_MTIME)) {
2764 			error = -1;	/* may be okay if rdattr_error */
2765 			break;
2766 		}
2767 		ASSERT(sarg->vap->va_mask & AT_MTIME);
2768 		error = nfs4_time_vton(&sarg->vap->va_mtime, &na->time_modify);
2769 		break;
2770 	case NFS4ATTR_SETIT:
2771 		/*
2772 		 * read-only attr
2773 		 */
2774 		error = EINVAL;
2775 		break;
2776 	case NFS4ATTR_VERIT:
2777 		ASSERT(sarg->vap->va_mask & AT_MTIME);
2778 		error = nfs4_time_ntov(&na->time_modify, &mtime);
2779 		if (error)
2780 			break;
2781 		if (bcmp(&mtime, &sarg->vap->va_mtime, sizeof (mtime)))
2782 			error = -1;	/* no match */
2783 		break;
2784 	case NFS4ATTR_FREEIT:
2785 		break;
2786 	}
2787 	return (error);
2788 }
2789 
2790 /*
2791  * XXX - need to add support for setting modify time
2792  */
2793 /* ARGSUSED */
2794 static int
2795 rfs4_fattr4_time_modify_set(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg,
2796     union nfs4_attr_u *na)
2797 {
2798 	int	error = 0;
2799 	settime4 *tm;
2800 
2801 	if (RFS4_MANDATTR_ONLY)
2802 		return (ENOTSUP);
2803 
2804 	switch (cmd) {
2805 	case NFS4ATTR_SUPPORTED:
2806 		if ((sarg->op == NFS4ATTR_GETIT) ||
2807 		    (sarg->op == NFS4ATTR_VERIT))
2808 			error = EINVAL;
2809 		break;		/* this attr is supported */
2810 	case NFS4ATTR_GETIT:
2811 	case NFS4ATTR_VERIT:
2812 		/*
2813 		 * write only attr
2814 		 */
2815 		error = EINVAL;
2816 		break;
2817 	case NFS4ATTR_SETIT:
2818 		ASSERT(sarg->vap->va_mask & AT_MTIME);
2819 		/*
2820 		 * Set modify time (by server or by client)
2821 		 */
2822 		tm = &na->time_modify_set;
2823 		if (tm->set_it == SET_TO_CLIENT_TIME4) {
2824 			error = nfs4_time_ntov(&tm->time, &sarg->vap->va_mtime);
2825 			sarg->flag = ATTR_UTIME;
2826 		} else if (tm->set_it == SET_TO_SERVER_TIME4) {
2827 			gethrestime(&sarg->vap->va_mtime);
2828 		} else {
2829 			error = EINVAL;
2830 		}
2831 		break;
2832 	case NFS4ATTR_FREEIT:
2833 		break;
2834 	}
2835 	return (error);
2836 }
2837 
2838 /* ARGSUSED */
2839 static int
2840 rfs4_fattr4_suppattr_exclcreat(nfs4_attr_cmd_t cmd,
2841     struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na)
2842 {
2843 	int error = 0;
2844 
2845 	/* Not supported for nfs4.0 */
2846 	if (sarg->cs->minorversion == 0)
2847 		return (ENOTSUP);
2848 
2849 	switch (cmd) {
2850 	case NFS4ATTR_SUPPORTED:
2851 		if (sarg->op == NFS4ATTR_SETIT)
2852 			error = EINVAL;
2853 		break;		/* this attr is supported */
2854 	case NFS4ATTR_GETIT:
2855 		na->supp_exclcreat = RFS4_SUPPATTR_EXCLCREAT;
2856 		break;
2857 	case NFS4ATTR_SETIT:
2858 		/*
2859 		 * read-only attr
2860 		 */
2861 		error = EINVAL;
2862 		break;
2863 	case NFS4ATTR_VERIT:
2864 		if (na->supp_exclcreat != RFS4_SUPPATTR_EXCLCREAT)
2865 			error = -1; /* no match */
2866 		break;
2867 	case NFS4ATTR_FREEIT:
2868 		break;
2869 	}
2870 	return (error);
2871 }
2872 
2873 static void
2874 rfs4_ntov_init(void)
2875 {
2876 	/* index must be same as corresponding FATTR4_* define */
2877 	nfs4_ntov_map[0].sv_getit = rfs4_fattr4_supported_attrs;
2878 	nfs4_ntov_map[1].sv_getit = rfs4_fattr4_type;
2879 	nfs4_ntov_map[2].sv_getit = rfs4_fattr4_fh_expire_type;
2880 	nfs4_ntov_map[3].sv_getit = rfs4_fattr4_change;
2881 	nfs4_ntov_map[4].sv_getit = rfs4_fattr4_size;
2882 	nfs4_ntov_map[5].sv_getit = rfs4_fattr4_link_support;
2883 	nfs4_ntov_map[6].sv_getit = rfs4_fattr4_symlink_support;
2884 	nfs4_ntov_map[7].sv_getit = rfs4_fattr4_named_attr;
2885 	nfs4_ntov_map[8].sv_getit = rfs4_fattr4_fsid;
2886 	nfs4_ntov_map[9].sv_getit = rfs4_fattr4_unique_handles;
2887 	nfs4_ntov_map[10].sv_getit = rfs4_fattr4_lease_time;
2888 	nfs4_ntov_map[11].sv_getit = rfs4_fattr4_rdattr_error;
2889 	nfs4_ntov_map[12].sv_getit = rfs4_fattr4_acl;
2890 	nfs4_ntov_map[13].sv_getit = rfs4_fattr4_aclsupport;
2891 	nfs4_ntov_map[14].sv_getit = rfs4_fattr4_archive;
2892 	nfs4_ntov_map[15].sv_getit = rfs4_fattr4_cansettime;
2893 	nfs4_ntov_map[16].sv_getit = rfs4_fattr4_case_insensitive;
2894 	nfs4_ntov_map[17].sv_getit = rfs4_fattr4_case_preserving;
2895 	nfs4_ntov_map[18].sv_getit = rfs4_fattr4_chown_restricted;
2896 	nfs4_ntov_map[19].sv_getit = rfs4_fattr4_filehandle;
2897 	nfs4_ntov_map[20].sv_getit = rfs4_fattr4_fileid;
2898 	nfs4_ntov_map[21].sv_getit = rfs4_fattr4_files_avail;
2899 	nfs4_ntov_map[22].sv_getit = rfs4_fattr4_files_free;
2900 	nfs4_ntov_map[23].sv_getit = rfs4_fattr4_files_total;
2901 	nfs4_ntov_map[24].sv_getit = rfs4_fattr4_fs_locations;
2902 	nfs4_ntov_map[25].sv_getit = rfs4_fattr4_hidden;
2903 	nfs4_ntov_map[26].sv_getit = rfs4_fattr4_homogeneous;
2904 	nfs4_ntov_map[27].sv_getit = rfs4_fattr4_maxfilesize;
2905 	nfs4_ntov_map[28].sv_getit = rfs4_fattr4_maxlink;
2906 	nfs4_ntov_map[29].sv_getit = rfs4_fattr4_maxname;
2907 	nfs4_ntov_map[30].sv_getit = rfs4_fattr4_maxread;
2908 	nfs4_ntov_map[31].sv_getit = rfs4_fattr4_maxwrite;
2909 	nfs4_ntov_map[32].sv_getit = rfs4_fattr4_mimetype;
2910 	nfs4_ntov_map[33].sv_getit = rfs4_fattr4_mode;
2911 	nfs4_ntov_map[34].sv_getit = rfs4_fattr4_no_trunc;
2912 	nfs4_ntov_map[35].sv_getit = rfs4_fattr4_numlinks;
2913 	nfs4_ntov_map[36].sv_getit = rfs4_fattr4_owner;
2914 	nfs4_ntov_map[37].sv_getit = rfs4_fattr4_owner_group;
2915 	nfs4_ntov_map[38].sv_getit = rfs4_fattr4_quota_avail_hard;
2916 	nfs4_ntov_map[39].sv_getit = rfs4_fattr4_quota_avail_soft;
2917 	nfs4_ntov_map[40].sv_getit = rfs4_fattr4_quota_used;
2918 	nfs4_ntov_map[41].sv_getit = rfs4_fattr4_rawdev;
2919 	nfs4_ntov_map[42].sv_getit = rfs4_fattr4_space_avail;
2920 	nfs4_ntov_map[43].sv_getit = rfs4_fattr4_space_free;
2921 	nfs4_ntov_map[44].sv_getit = rfs4_fattr4_space_total;
2922 	nfs4_ntov_map[45].sv_getit = rfs4_fattr4_space_used;
2923 	nfs4_ntov_map[46].sv_getit = rfs4_fattr4_system;
2924 	nfs4_ntov_map[47].sv_getit = rfs4_fattr4_time_access;
2925 	nfs4_ntov_map[48].sv_getit = rfs4_fattr4_time_access_set;
2926 	nfs4_ntov_map[49].sv_getit = rfs4_fattr4_time_backup;
2927 	nfs4_ntov_map[50].sv_getit = rfs4_fattr4_time_create;
2928 	nfs4_ntov_map[51].sv_getit = rfs4_fattr4_time_delta;
2929 	nfs4_ntov_map[52].sv_getit = rfs4_fattr4_time_metadata;
2930 	nfs4_ntov_map[53].sv_getit = rfs4_fattr4_time_modify;
2931 	nfs4_ntov_map[54].sv_getit = rfs4_fattr4_time_modify_set;
2932 	nfs4_ntov_map[55].sv_getit = rfs4_fattr4_mounted_on_fileid;
2933 	nfs4_ntov_map[56].sv_getit = rfs4_fattr4_suppattr_exclcreat;
2934 }
2935