/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.  Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/time.h>
#include <sys/systm.h>

#include <nfs/nfs.h>
#include <nfs/nfs4.h>
#include <nfs/rnode4.h>
#include <nfs/nfs4_clnt.h>
#include <sys/cmn_err.h>

static int
timestruc_to_settime4(timestruc_t *tt, settime4 *tt4, int flags)
{
	int	error = 0;

	if (flags & ATTR_UTIME) {
		tt4->set_it = SET_TO_CLIENT_TIME4;
		error = nfs4_time_vton(tt, &tt4->time);
	} else {
		tt4->set_it = SET_TO_SERVER_TIME4;
	}
	return (error);
}


/*
 * nfs4_ver_fattr4_attr translates a vattr attribute into a fattr4 attribute
 * for use by nfsv4 verify.  For setting atime or mtime use the entry for
 * time_XX (XX == access or modify).
 * Return TRUE if arg was set (even if there was an error) and FALSE
 * otherwise. Also set error code. The caller should not continue
 * if error was set, whether or not the return is TRUE or FALSE. Returning
 * FALSE does not mean there was an error, only that the attr was not set.
 *
 * Note: For now we only have the options used by setattr. In the future
 * the switch statement below should cover all vattr attrs and possibly
 * sys attrs as well.
 */
/* ARGSUSED */
static bool_t
nfs4_ver_fattr4_attr(vattr_t *vap, struct nfs4_ntov_map *ntovp,
	union nfs4_attr_u *nap, int flags, int *errorp)
{
	bool_t	retval = TRUE;

	/*
	 * Special case for time set: if setting the
	 * time, ignore entry for time access/modify set (setattr)
	 * and instead use that of time access/modify.
	 */
	*errorp = 0;
	/*
	 * Bit matches the mask
	 */
	switch (ntovp->vbit & vap->va_mask) {
	case AT_SIZE:
		nap->size = vap->va_size;
		break;
	case AT_MODE:
		nap->mode = vap->va_mode;
		break;
	case AT_UID:
		/*
		 * if no mapping, uid could be mapped to a numeric string,
		 * e.g. 12345->"12345"
		 */
		if (*errorp = nfs_idmap_uid_str(vap->va_uid, &nap->owner,
		    FALSE))
			retval = FALSE;
		break;
	case AT_GID:
		/*
		 * if no mapping, gid will be mapped to a number string,
		 * e.g. "12345"
		 */
		if (*errorp = nfs_idmap_gid_str(vap->va_gid, &nap->owner_group,
		    FALSE))
			retval = FALSE;
		break;
	case AT_ATIME:
		if ((ntovp->nval != FATTR4_TIME_ACCESS) ||
		    (*errorp = nfs4_time_vton(&vap->va_ctime,
					&nap->time_access))) {
			/*
			 * either asked for FATTR4_TIME_ACCESS_SET -
			 *	not used for setattr
			 * or system time invalid for otw transfers
			 */
			retval = FALSE;
		}
		break;
	case AT_MTIME:
		if ((ntovp->nval != FATTR4_TIME_MODIFY) ||
		    (*errorp = nfs4_time_vton(&vap->va_mtime,
					&nap->time_modify))) {
			/*
			 * either asked for FATTR4_TIME_MODIFY_SET -
			 *	not used for setattr
			 * or system time invalid for otw transfers
			 */
			retval = FALSE;
		}
		break;
	case AT_CTIME:
		if (*errorp = nfs4_time_vton(&vap->va_ctime,
					&nap->time_metadata)) {
			/*
			 * system time invalid for otw transfers
			 */
			retval = FALSE;
		}
		break;
	default:
		retval = FALSE;
	}
	return (retval);
}

/*
 * nfs4_set_fattr4_attr translates a vattr attribute into a fattr4 attribute
 * for use by nfs4_setattr.  For setting atime or mtime use the entry for
 * time_XX_set rather than time_XX (XX == access or modify).
 * Return TRUE if arg was set (even if there was an error) and FALSE
 * otherwise. Also set error code. The caller should not continue
 * if error was set, whether or not the return is TRUE or FALSE. Returning
 * FALSE does not mean there was an error, only that the attr was not set.
 */
static bool_t
nfs4_set_fattr4_attr(vattr_t *vap, vsecattr_t *vsap,
	struct nfs4_ntov_map *ntovp, union nfs4_attr_u *nap, int flags,
	int *errorp)
{
	bool_t	retval = TRUE;

	/*
	 * Special case for time set: if setting the
	 * time, ignore entry for time access/modify
	 * and instead use that of time access/modify set.
	 */
	*errorp = 0;
	/*
	 * Bit matches the mask
	 */
	switch (ntovp->vbit & vap->va_mask) {
	case AT_SIZE:
		nap->size = vap->va_size;
		break;
	case AT_MODE:
		nap->mode = vap->va_mode;
		break;
	case AT_UID:
		/*
		 * if no mapping, uid will be mapped to a number string,
		 * e.g. "12345"
		 */
		if (*errorp = nfs_idmap_uid_str(vap->va_uid, &nap->owner,
		    FALSE))
			retval = FALSE;
		break;
	case AT_GID:
		/*
		 * if no mapping, gid will be mapped to a number string,
		 * e.g. "12345"
		 */
		if (*errorp = nfs_idmap_gid_str(vap->va_gid, &nap->owner_group,
		    FALSE))
			retval = FALSE;
		break;
	case AT_ATIME:
		if ((ntovp->nval != FATTR4_TIME_ACCESS_SET) ||
		    (*errorp = timestruc_to_settime4(&vap->va_atime,
				&nap->time_access_set, flags))) {
			/* FATTR4_TIME_ACCESS - not used for verify */
			retval = FALSE;
		}
		break;
	case AT_MTIME:
		if ((ntovp->nval != FATTR4_TIME_MODIFY_SET) ||
		    (*errorp = timestruc_to_settime4(&vap->va_mtime,
				&nap->time_modify_set, flags))) {
			/* FATTR4_TIME_MODIFY - not used for verify */
			retval = FALSE;
		}
		break;
	default:
		/*
		 * If the ntovp->vbit == 0 this is most likely the ACL.
		 */
		if (ntovp->vbit == 0 && ntovp->fbit == FATTR4_ACL_MASK) {
			ASSERT(vsap->vsa_mask == (VSA_ACE | VSA_ACECNT));
			nap->acl.fattr4_acl_len = vsap->vsa_aclcnt;
			nap->acl.fattr4_acl_val = vsap->vsa_aclentp;
		} else
			retval = FALSE;
	}

	return (retval);
}

/*
 * XXX - This is a shorter version of vattr_to_fattr4 which only takes care
 * of setattr args - size, mode, uid/gid, times. Eventually we should generalize
 * by using nfs4_ntov_map and the same functions used by the server.
 * Here we just hardcoded the setattr attributes. Note that the order is
 * important - it should follow the order of the bits in the mask.
 */
int
vattr_to_fattr4(vattr_t *vap, vsecattr_t *vsap, fattr4 *fattrp, int flags,
		enum nfs_opnum4 op, bitmap4 supp)
{
	int i, j;
	union nfs4_attr_u *na = NULL;
	int attrcnt;
	int uid_attr = -1;
	int gid_attr = -1;
	int acl_attr = -1;
	XDR xdr;
	ulong_t xdr_size;
	char *xdr_attrs;
	int error = 0;
	uint8_t amap[NFS4_MAXNUM_ATTRS];
	uint_t va_mask = vap->va_mask;
	bool_t (*attrfunc)();

#ifndef lint
	/*
	 * Make sure that maximum attribute number can be expressed as an
	 * 8 bit quantity.
	 */
	ASSERT(NFS4_MAXNUM_ATTRS <= (UINT8_MAX + 1));
#endif
	fattrp->attrmask = 0;
	fattrp->attrlist4_len = 0;
	fattrp->attrlist4 = NULL;
	na = kmem_zalloc(sizeof (union nfs4_attr_u) * nfs4_ntov_map_size,
			KM_SLEEP);

	if (op == OP_SETATTR || op == OP_CREATE || op == OP_OPEN) {
		/*
		 * Note we need to set the attrmask for set operations.
		 * In particular mtime and atime will be set to the
		 * servers time.
		 */
		nfs4_vmask_to_nmask_set(va_mask, &fattrp->attrmask);
		if (vsap != NULL)
			fattrp->attrmask |= FATTR4_ACL_MASK;
		attrfunc = nfs4_set_fattr4_attr;
	} else {	/* verify/nverify */
		/*
		 * Verfy/nverify use the "normal vmask_to_nmask
		 * this routine knows how to handle all vmask bits
		 */
		nfs4_vmask_to_nmask(va_mask, &fattrp->attrmask);
		/*
		 * XXX verify/nverify only works for a subset of attrs that
		 * directly map to vattr_t attrs.  So, verify/nverify is
		 * broken for servers that only support mandatory attrs.
		 * Mask out change attr for now and fix verify op to
		 * work with mandonly servers later.  nfs4_vmask_to_nmask
		 * sets change whenever it sees request for ctime/mtime,
		 * so we must turn off change because nfs4_ver_fattr4_attr
		 * will not generate args for change.  This is a bug
		 * that will be fixed later.
		 * XXX
		 */
		fattrp->attrmask &= ~FATTR4_CHANGE_MASK;
		attrfunc = nfs4_ver_fattr4_attr;
	}

	/* Mask out any rec attrs unsupported by server */
	fattrp->attrmask &= supp;

	attrcnt = 0;
	xdr_size = 0;
	for (i = 0; i < nfs4_ntov_map_size; i++) {
		/*
		 * In the case of FATTR4_ACL_MASK, the vbit will be 0 (zero)
		 * so we must also check if the fbit is FATTR4_ACL_MASK before
		 * skipping over this attribute.
		 */
		if (!(nfs4_ntov_map[i].vbit & vap->va_mask)) {
			if (nfs4_ntov_map[i].fbit != FATTR4_ACL_MASK)
				continue;
			if (vsap == NULL)
				continue;
		}

		if (attrfunc == nfs4_set_fattr4_attr) {
			if (!(*attrfunc)(vap, vsap, &nfs4_ntov_map[i],
			    &na[attrcnt], flags, &error))
				continue;
		} else if (attrfunc == nfs4_ver_fattr4_attr) {
			if (!(*attrfunc)(vap, &nfs4_ntov_map[i], &na[attrcnt],
			    flags, &error))
				continue;
		}

		if (error)
			goto done;	/* Exit! */

		/*
		 * Calculate XDR size
		 */
		if (nfs4_ntov_map[i].xdr_size != 0) {
			/*
			 * If we are setting attributes (attrfunc is
			 * nfs4_set_fattr4_attr) and are setting the
			 * mtime or atime, adjust the xdr size down by
			 * 3 words, since we are using the server's
			 * time as the current time.  Exception: if
			 * ATTR_UTIME is set, the client sends the
			 * time, so leave the xdr size alone.
			 */
			xdr_size += nfs4_ntov_map[i].xdr_size;
			if ((nfs4_ntov_map[i].nval == FATTR4_TIME_ACCESS_SET ||
			    nfs4_ntov_map[i].nval == FATTR4_TIME_MODIFY_SET) &&
				attrfunc == nfs4_set_fattr4_attr &&
				!(flags & ATTR_UTIME)) {
				xdr_size -= 3 * BYTES_PER_XDR_UNIT;
			}
		} else {
			/*
			 * The only zero xdr_sizes we should see
			 * are AT_UID, AT_GID and FATTR4_ACL_MASK
			 */
			ASSERT(nfs4_ntov_map[i].vbit == AT_UID ||
				nfs4_ntov_map[i].vbit == AT_GID ||
				nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK);
			if (nfs4_ntov_map[i].vbit == AT_UID) {
				uid_attr = attrcnt;
				xdr_size += BYTES_PER_XDR_UNIT;	/* length */
				xdr_size +=
					RNDUP(na[attrcnt].owner.utf8string_len);
			} else if (nfs4_ntov_map[i].vbit == AT_GID) {
				gid_attr = attrcnt;
				xdr_size += BYTES_PER_XDR_UNIT;	/* length */
				xdr_size +=
				    RNDUP(
					na[attrcnt].owner_group.utf8string_len);
			} else if (nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK) {
				nfsace4 *tmpacl = (nfsace4 *)vsap->vsa_aclentp;

				acl_attr = attrcnt;
				/* fattr4_acl_len */
				xdr_size += BYTES_PER_XDR_UNIT;
				/* fattr4_acl_val */
				xdr_size += RNDUP((vsap->vsa_aclcnt *
				    (sizeof (acetype4) + sizeof (aceflag4)
				    + sizeof (acemask4))));

				for (j = 0; j < vsap->vsa_aclcnt; j++) {
					/* who - utf8string_len */
					xdr_size += BYTES_PER_XDR_UNIT;
					/* who - utf8string_val */
					xdr_size +=
					    RNDUP(tmpacl[j].who.utf8string_len);
				}
			}
		}

		/*
		 * This attr is going otw
		 */
		amap[attrcnt] = (uint8_t)nfs4_ntov_map[i].nval;
		attrcnt++;

		/*
		 * Clear this bit from test mask so we stop
		 * as soon as all requested attrs are done.
		 */
		va_mask &= ~nfs4_ntov_map[i].vbit;
		if (va_mask == 0 &&
		    (vsap == NULL || (vsap != NULL && acl_attr != -1)))
			break;
	}

	if (attrcnt == 0) {
		goto done;
	}

	fattrp->attrlist4 = xdr_attrs = kmem_alloc(xdr_size, KM_SLEEP);
	fattrp->attrlist4_len = xdr_size;
	xdrmem_create(&xdr, xdr_attrs, xdr_size, XDR_ENCODE);
	for (i = 0; i < attrcnt; i++) {
		if ((*nfs4_ntov_map[amap[i]].xfunc)(&xdr, &na[i]) == FALSE) {
			cmn_err(CE_WARN, "vattr_to_fattr4: xdr encode of "
				"attribute failed\n");
			error = EINVAL;
			break;
		}
	}
done:
	/*
	 * Free any malloc'd attrs, can only be uid or gid
	 */
	if (uid_attr != -1 && na[uid_attr].owner.utf8string_val != NULL) {
		kmem_free(na[uid_attr].owner.utf8string_val,
				na[uid_attr].owner.utf8string_len);
	}
	if (gid_attr != -1 && na[gid_attr].owner_group.utf8string_val != NULL) {
		kmem_free(na[gid_attr].owner_group.utf8string_val,
				na[gid_attr].owner_group.utf8string_len);
	}

	/* xdrmem_destroy(&xdrs); */	/* NO-OP */
	kmem_free(na, sizeof (union nfs4_attr_u) * nfs4_ntov_map_size);
	if (error)
		nfs4_fattr4_free(fattrp);
	return (error);
}

void
nfs4_fattr4_free(fattr4 *attrp)
{
	/*
	 * set attrlist4val/len to 0 because...
	 *
	 * op_readdir resfree function could call us again
	 * for last entry4 if it was able to encode the name
	 * and cookie but couldn't encode the attrs because
	 * of maxcount violation (from rddir args).  In that
	 * case, the last/partial entry4's fattr4 has already
	 * been free'd, but the entry4 remains on the end of
	 * the list.
	 */
	attrp->attrmask = 0;

	if (attrp->attrlist4) {
		kmem_free(attrp->attrlist4, attrp->attrlist4_len);
		attrp->attrlist4 = NULL;
		attrp->attrlist4_len = 0;
	}
}

/*
 * Translate a vattr_t mask to a fattr4 type bitmap, caller is
 * responsible for zeroing bitsval if needed.
 */
void
nfs4_vmask_to_nmask(uint_t vmask, bitmap4 *bitsval)
{
	if (vmask == AT_ALL || vmask == NFS4_VTON_ATTR_MASK) {
		*bitsval |= NFS4_NTOV_ATTR_MASK;
		return;
	}

	vmask &= NFS4_VTON_ATTR_MASK;
	if (vmask == 0) {
		return;
	}

	if (vmask & AT_TYPE)
		*bitsval |= FATTR4_TYPE_MASK;
	if (vmask & AT_MODE)
		*bitsval |= FATTR4_MODE_MASK;
	if (vmask & AT_UID)
		*bitsval |= FATTR4_OWNER_MASK;
	if (vmask & AT_GID)
		*bitsval |= FATTR4_OWNER_GROUP_MASK;
	if (vmask & AT_FSID)
		*bitsval |= FATTR4_FSID_MASK;
	/* set mounted_on_fileid when AT_NODEID requested */
	if (vmask & AT_NODEID)
		*bitsval |= FATTR4_FILEID_MASK | FATTR4_MOUNTED_ON_FILEID_MASK;
	if (vmask & AT_NLINK)
		*bitsval |= FATTR4_NUMLINKS_MASK;
	if (vmask & AT_SIZE)
		*bitsval |= FATTR4_SIZE_MASK;
	if (vmask & AT_ATIME)
		*bitsval |= FATTR4_TIME_ACCESS_MASK;
	if (vmask & AT_MTIME)
		*bitsval |= FATTR4_TIME_MODIFY_MASK;
	/* also set CHANGE whenever AT_CTIME requested */
	if (vmask & AT_CTIME)
		*bitsval |= FATTR4_TIME_METADATA_MASK | FATTR4_CHANGE_MASK;
	if (vmask & AT_NBLOCKS)
		*bitsval |= FATTR4_SPACE_USED_MASK;
	if (vmask & AT_RDEV)
		*bitsval |= FATTR4_RAWDEV_MASK;
}

/*
 * nfs4_vmask_to_nmask_set is used for setattr. A separate function needed
 * because of special treatment to timeset.
 */
void
nfs4_vmask_to_nmask_set(uint_t vmask, bitmap4 *bitsval)
{
	vmask &= NFS4_VTON_ATTR_MASK_SET;

	if (vmask == 0) {
		return;
	}

	if (vmask & AT_MODE)
		*bitsval |= FATTR4_MODE_MASK;
	if (vmask & AT_UID)
		*bitsval |= FATTR4_OWNER_MASK;
	if (vmask & AT_GID)
		*bitsval |= FATTR4_OWNER_GROUP_MASK;
	if (vmask & AT_SIZE)
		*bitsval |= FATTR4_SIZE_MASK;
	if (vmask & AT_ATIME)
		*bitsval |= FATTR4_TIME_ACCESS_SET_MASK;
	if (vmask & AT_MTIME)
		*bitsval |= FATTR4_TIME_MODIFY_SET_MASK;
}

/*
 * Convert NFS Version 4 over the network attributes to the local
 * virtual attributes.
 */
vtype_t nf4_to_vt[] = {
	VBAD, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VDIR, VREG
};


/*
 *	{ fbit, vbit, vfsstat, mandatory,
 *		nval, xdr_size, xfunc,
 *		sv_getit, prtstr },
 */
struct nfs4_ntov_map nfs4_ntov_map[] = {
	{ FATTR4_SUPPORTED_ATTRS_MASK, 0, FALSE, TRUE,
		FATTR4_SUPPORTED_ATTRS, 2 * BYTES_PER_XDR_UNIT, xdr_bitmap4,
		NULL, "fattr4_supported_attrs" },

	{ FATTR4_TYPE_MASK, AT_TYPE, FALSE, TRUE,
		FATTR4_TYPE, BYTES_PER_XDR_UNIT, xdr_int,
		NULL, "fattr4_type" },

	{ FATTR4_FH_EXPIRE_TYPE_MASK, 0, FALSE, TRUE,
		FATTR4_FH_EXPIRE_TYPE, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_fh_expire_type" },

	{ FATTR4_CHANGE_MASK, 0, FALSE, TRUE,
		FATTR4_CHANGE, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_change" },

	{ FATTR4_SIZE_MASK, AT_SIZE, FALSE, TRUE,
		FATTR4_SIZE,  2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_size" },

	{ FATTR4_LINK_SUPPORT_MASK, 0, FALSE, TRUE,
		FATTR4_LINK_SUPPORT, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_link_support" },

	{ FATTR4_SYMLINK_SUPPORT_MASK, 0, FALSE, TRUE,
		FATTR4_SYMLINK_SUPPORT, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_symlink_support" },

	{ FATTR4_NAMED_ATTR_MASK, 0, FALSE, TRUE,
		FATTR4_NAMED_ATTR, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_named_attr" },

	{ FATTR4_FSID_MASK, AT_FSID, FALSE, TRUE,
		FATTR4_FSID, 4 * BYTES_PER_XDR_UNIT, xdr_fattr4_fsid,
		NULL, "fattr4_fsid" },

	{ FATTR4_UNIQUE_HANDLES_MASK, 0, FALSE, TRUE,
		FATTR4_UNIQUE_HANDLES, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_unique_handles" },

	{ FATTR4_LEASE_TIME_MASK, 0, FALSE, TRUE,
		FATTR4_LEASE_TIME, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_lease_time" },

	{ FATTR4_RDATTR_ERROR_MASK, 0, FALSE, TRUE,
		FATTR4_RDATTR_ERROR, BYTES_PER_XDR_UNIT, xdr_int,
		NULL, "fattr4_rdattr_error" },

	{ FATTR4_ACL_MASK, 0, FALSE, FALSE,
		FATTR4_ACL, 0, xdr_fattr4_acl,
		NULL, "fattr4_acl" },

	{ FATTR4_ACLSUPPORT_MASK, 0, FALSE, FALSE,
		FATTR4_ACLSUPPORT, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_aclsupport" },

	{ FATTR4_ARCHIVE_MASK, 0, FALSE, FALSE,
		FATTR4_ARCHIVE, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_archive" },

	{ FATTR4_CANSETTIME_MASK, 0, FALSE, FALSE,
		FATTR4_CANSETTIME, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_cansettime" },

	{ FATTR4_CASE_INSENSITIVE_MASK, 0, FALSE, FALSE,
		FATTR4_CASE_INSENSITIVE, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_case_insensitive" },

	{ FATTR4_CASE_PRESERVING_MASK, 0, FALSE, FALSE,
		FATTR4_CASE_PRESERVING, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_case_preserving" },

	{ FATTR4_CHOWN_RESTRICTED_MASK, 0, FALSE, FALSE,
		FATTR4_CHOWN_RESTRICTED, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_chown_restricted" },

	{ FATTR4_FILEHANDLE_MASK, 0, FALSE, TRUE,
		FATTR4_FILEHANDLE, 0, xdr_nfs_fh4,
		NULL, "fattr4_filehandle" },

	{ FATTR4_FILEID_MASK, AT_NODEID, FALSE, FALSE,
		FATTR4_FILEID, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_fileid" },

	{ FATTR4_FILES_AVAIL_MASK, 0, TRUE, FALSE,
		FATTR4_FILES_AVAIL, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_files_avail" },

	{ FATTR4_FILES_FREE_MASK, 0, TRUE, FALSE,
		FATTR4_FILES_FREE, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_files_free" },

	{ FATTR4_FILES_TOTAL_MASK, 0, TRUE, FALSE,
		FATTR4_FILES_TOTAL, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_files_total" },

	{ FATTR4_FS_LOCATIONS_MASK, 0, FALSE, FALSE,
		FATTR4_FS_LOCATIONS, 0, xdr_fattr4_fs_locations,
		NULL, "fattr4_fs_locations" },

	{ FATTR4_HIDDEN_MASK, 0, FALSE, FALSE,
		FATTR4_HIDDEN, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_hidden" },

	{ FATTR4_HOMOGENEOUS_MASK, 0, FALSE, FALSE,
		FATTR4_HOMOGENEOUS, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_homogeneous" },

	{ FATTR4_MAXFILESIZE_MASK, 0, FALSE, FALSE,
		FATTR4_MAXFILESIZE, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_maxfilesize" },

	{ FATTR4_MAXLINK_MASK, 0, FALSE, FALSE,
		FATTR4_MAXLINK, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_maxlink" },

	{ FATTR4_MAXNAME_MASK, 0, FALSE, FALSE,
		FATTR4_MAXNAME, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_maxname" },

	{ FATTR4_MAXREAD_MASK, 0, FALSE, FALSE,
		FATTR4_MAXREAD, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_maxread" },

	{ FATTR4_MAXWRITE_MASK, 0, FALSE, FALSE,
		FATTR4_MAXWRITE, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_maxwrite" },

	{ FATTR4_MIMETYPE_MASK, 0, FALSE, FALSE,
		FATTR4_MIMETYPE, 0, xdr_utf8string,
		NULL, "fattr4_mimetype" },

	{ FATTR4_MODE_MASK, AT_MODE, FALSE, FALSE,
		FATTR4_MODE, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_mode" },

	{ FATTR4_NO_TRUNC_MASK, 0, FALSE, FALSE,
		FATTR4_NO_TRUNC, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_no_trunc" },

	{ FATTR4_NUMLINKS_MASK, AT_NLINK, FALSE, FALSE,
		FATTR4_NUMLINKS, BYTES_PER_XDR_UNIT, xdr_u_int,
		NULL, "fattr4_numlinks" },

	{ FATTR4_OWNER_MASK, AT_UID, FALSE, FALSE,
		FATTR4_OWNER, 0, xdr_utf8string,
		NULL, "fattr4_owner" },

	{ FATTR4_OWNER_GROUP_MASK, AT_GID, FALSE, FALSE,
		FATTR4_OWNER_GROUP, 0, xdr_utf8string,
		NULL, "fattr4_owner_group" },

	{ FATTR4_QUOTA_AVAIL_HARD_MASK, 0, FALSE, FALSE,
		FATTR4_QUOTA_AVAIL_HARD, 2 * BYTES_PER_XDR_UNIT,
		xdr_u_longlong_t,
		NULL, "fattr4_quota_avail_hard" },

	{ FATTR4_QUOTA_AVAIL_SOFT_MASK, 0, FALSE, FALSE,
		FATTR4_QUOTA_AVAIL_SOFT, 2 * BYTES_PER_XDR_UNIT,
		xdr_u_longlong_t,
		NULL, "fattr4_quota_avail_soft" },

	{ FATTR4_QUOTA_USED_MASK, 0, FALSE, FALSE,
		FATTR4_QUOTA_USED, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_quota_used" },

	{ FATTR4_RAWDEV_MASK, AT_RDEV, FALSE, FALSE,
		FATTR4_RAWDEV, 2 * BYTES_PER_XDR_UNIT, xdr_fattr4_rawdev,
		NULL, "fattr4_rawdev" },

	{ FATTR4_SPACE_AVAIL_MASK, 0, TRUE, FALSE,
		FATTR4_SPACE_AVAIL, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_space_avail" },

	{ FATTR4_SPACE_FREE_MASK, 0, TRUE, FALSE,
		FATTR4_SPACE_FREE, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_space_free" },

	{ FATTR4_SPACE_TOTAL_MASK, 0, TRUE, FALSE,
		FATTR4_SPACE_TOTAL, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_space_total" },

	{ FATTR4_SPACE_USED_MASK, AT_NBLOCKS, FALSE, FALSE,
		FATTR4_SPACE_USED, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t,
		NULL, "fattr4_space_used" },

	{ FATTR4_SYSTEM_MASK, 0, FALSE, FALSE,
		FATTR4_SYSTEM, BYTES_PER_XDR_UNIT, xdr_bool,
		NULL, "fattr4_system" },

	{ FATTR4_TIME_ACCESS_MASK, AT_ATIME, FALSE, FALSE,
		FATTR4_TIME_ACCESS, 3 * BYTES_PER_XDR_UNIT, xdr_nfstime4,
		NULL, "fattr4_time_access" },

	{ FATTR4_TIME_ACCESS_SET_MASK, AT_ATIME, FALSE, FALSE,
		FATTR4_TIME_ACCESS_SET, 4 * BYTES_PER_XDR_UNIT, xdr_settime4,
		NULL, "fattr4_time_access_set" },

	{ FATTR4_TIME_BACKUP_MASK, 0, FALSE, FALSE,
		FATTR4_TIME_BACKUP, 3 * BYTES_PER_XDR_UNIT, xdr_nfstime4,
		NULL, "fattr4_time_backup" },

	{ FATTR4_TIME_CREATE_MASK, 0, FALSE, FALSE,
		FATTR4_TIME_CREATE, 3 * BYTES_PER_XDR_UNIT, xdr_nfstime4,
		NULL, "fattr4_time_create" },

	{ FATTR4_TIME_DELTA_MASK, 0, FALSE, FALSE,
		FATTR4_TIME_DELTA, 3 * BYTES_PER_XDR_UNIT, xdr_nfstime4,
		NULL, "fattr4_time_delta" },

	{ FATTR4_TIME_METADATA_MASK, AT_CTIME, FALSE, FALSE,
		FATTR4_TIME_METADATA, 3 * BYTES_PER_XDR_UNIT, xdr_nfstime4,
		NULL, "fattr4_time_metadata" },

	{ FATTR4_TIME_MODIFY_MASK, AT_MTIME, FALSE, FALSE,
		FATTR4_TIME_MODIFY, 3 * BYTES_PER_XDR_UNIT, xdr_nfstime4,
		NULL, "fattr4_time_modify" },

	{ FATTR4_TIME_MODIFY_SET_MASK, AT_MTIME, FALSE, FALSE,
		FATTR4_TIME_MODIFY_SET, 4 * BYTES_PER_XDR_UNIT, xdr_settime4,
		NULL, "fattr4_time_modify_set" },

	{ FATTR4_MOUNTED_ON_FILEID_MASK, 0, FALSE, FALSE,
		FATTR4_MOUNTED_ON_FILEID, 2 * BYTES_PER_XDR_UNIT,
		xdr_u_longlong_t,
		NULL, "fattr4_mounted_on_fileid" },

};

uint_t nfs4_ntov_map_size = sizeof (nfs4_ntov_map) /
	sizeof (struct nfs4_ntov_map);