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