/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * These are the XDR routines used to serialize and deserialize * the various structures passed as parameters across the network * between NFS clients and servers. */ /* * XDR null terminated ASCII strings * xdr_string3 deals with "C strings" - arrays of bytes that are * terminated by a NULL character. The parameter cpp references a * pointer to storage; If the pointer is null, then the necessary * storage is allocated. The last parameter is the max allowed length * of the string as allowed by the system. The NFS Version 3 protocol * does not place limits on strings, but the implementation needs to * place a reasonable limit to avoid problems. */ bool_t xdr_string3(XDR *xdrs, char **cpp, uint_t maxsize) { char *sp; uint_t size; uint_t nodesize; bool_t mem_alloced = FALSE; /* * first deal with the length since xdr strings are counted-strings */ sp = *cpp; switch (xdrs->x_op) { case XDR_FREE: if (sp == NULL || sp == nfs3nametoolong) return (TRUE); /* already free */ /* FALLTHROUGH */ case XDR_ENCODE: size = (uint_t)strlen(sp); break; case XDR_DECODE: break; } if (!xdr_u_int(xdrs, &size)) return (FALSE); /* * now deal with the actual bytes */ switch (xdrs->x_op) { case XDR_DECODE: if (size >= maxsize) { *cpp = nfs3nametoolong; if (!XDR_CONTROL(xdrs, XDR_SKIPBYTES, &size)) return (FALSE); return (TRUE); } nodesize = size + 1; if (nodesize == 0) return (TRUE); if (sp == NULL) { sp = kmem_alloc(nodesize, KM_NOSLEEP); *cpp = sp; if (sp == NULL) return (FALSE); mem_alloced = TRUE; } sp[size] = 0; if (xdr_opaque(xdrs, sp, size)) { if (strlen(sp) != size) { if (mem_alloced) kmem_free(sp, nodesize); *cpp = NULL; return (FALSE); } } else { if (mem_alloced) kmem_free(sp, nodesize); *cpp = NULL; return (FALSE); } return (TRUE); case XDR_ENCODE: return (xdr_opaque(xdrs, sp, size)); case XDR_FREE: nodesize = size + 1; kmem_free(sp, nodesize); *cpp = NULL; return (TRUE); } return (FALSE); } /* * XDR_INLINE decode a filehandle. */ bool_t xdr_inline_decode_nfs_fh3(uint32_t *ptr, nfs_fh3 *fhp, uint32_t fhsize) { uchar_t *bp = (uchar_t *)ptr; uchar_t *cp; uint32_t dsize; uintptr_t resid; /* * Check to see if what the client sent us is bigger or smaller * than what we can ever possibly send out. NFS_FHMAXDATA is * unfortunately badly named as it is no longer the max and is * really the min of what is sent over the wire. */ if (fhsize > sizeof (fhandle3_t) || fhsize < (sizeof (fsid_t) + sizeof (ushort_t) + NFS_FHMAXDATA + sizeof (ushort_t) + NFS_FHMAXDATA)) { return (FALSE); } /* * All internal parts of a filehandle are in native byte order. * * Decode what should be fh3_fsid, it is aligned. */ fhp->fh3_fsid.val[0] = *(uint32_t *)bp; bp += BYTES_PER_XDR_UNIT; fhp->fh3_fsid.val[1] = *(uint32_t *)bp; bp += BYTES_PER_XDR_UNIT; /* * Decode what should be fh3_len. fh3_len is two bytes, so we're * unaligned now. */ cp = (uchar_t *)&fhp->fh3_len; *cp++ = *bp++; *cp++ = *bp++; fhsize -= 2 * BYTES_PER_XDR_UNIT + sizeof (ushort_t); /* * For backwards compatability, the fid length may be less than * NFS_FHMAXDATA, but it was always encoded as NFS_FHMAXDATA bytes. */ dsize = fhp->fh3_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_len; /* * Make sure the client isn't sending us a bogus length for fh3x_data. */ if (fhsize < dsize) return (FALSE); bcopy(bp, fhp->fh3_data, dsize); bp += dsize; fhsize -= dsize; if (fhsize < sizeof (ushort_t)) return (FALSE); cp = (uchar_t *)&fhp->fh3_xlen; *cp++ = *bp++; *cp++ = *bp++; fhsize -= sizeof (ushort_t); dsize = fhp->fh3_xlen < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_xlen; /* * Make sure the client isn't sending us a bogus length for fh3x_xdata. */ if (fhsize < dsize) return (FALSE); bcopy(bp, fhp->fh3_xdata, dsize); fhsize -= dsize; bp += dsize; /* * We realign things on purpose, so skip any padding */ resid = (uintptr_t)bp % BYTES_PER_XDR_UNIT; if (resid != 0) { if (fhsize < (BYTES_PER_XDR_UNIT - resid)) return (FALSE); bp += BYTES_PER_XDR_UNIT - resid; fhsize -= BYTES_PER_XDR_UNIT - resid; } /* * Make sure client didn't send extra bytes */ if (fhsize != 0) return (FALSE); return (TRUE); } static bool_t xdr_decode_nfs_fh3(XDR *xdrs, nfs_fh3 *objp) { uint32_t fhsize; /* filehandle size */ uint32_t bufsize; rpc_inline_t *ptr; uchar_t *bp; ASSERT(xdrs->x_op == XDR_DECODE); /* * Retrieve the filehandle length. */ if (!XDR_GETINT32(xdrs, (int32_t *)&fhsize)) return (FALSE); bzero(objp->fh3_u.data, sizeof (objp->fh3_u.data)); objp->fh3_length = 0; /* * Check to see if what the client sent us is bigger or smaller * than what we can ever possibly send out. NFS_FHMAXDATA is * unfortunately badly named as it is no longer the max and is * really the min of what is sent over the wire. */ if (fhsize > sizeof (fhandle3_t) || fhsize < (sizeof (fsid_t) + sizeof (ushort_t) + NFS_FHMAXDATA + sizeof (ushort_t) + NFS_FHMAXDATA)) { if (!XDR_CONTROL(xdrs, XDR_SKIPBYTES, &fhsize)) return (FALSE); return (TRUE); } /* * bring in fhsize plus any padding */ bufsize = RNDUP(fhsize); ptr = XDR_INLINE(xdrs, bufsize); bp = (uchar_t *)ptr; if (ptr == NULL) { bp = kmem_alloc(bufsize, KM_SLEEP); if (!xdr_opaque(xdrs, (char *)bp, bufsize)) { kmem_free(bp, bufsize); return (FALSE); } } objp->fh3_length = sizeof (fhandle3_t); if (xdr_inline_decode_nfs_fh3((uint32_t *)bp, objp, fhsize) == FALSE) { /* * If in the process of decoding we find the file handle * is not correctly formed, we need to continue decoding * and trigger an NFS layer error. Set the nfs_fh3_len to * zero so it gets caught as a bad length. */ bzero(objp->fh3_u.data, sizeof (objp->fh3_u.data)); objp->fh3_length = 0; } if (ptr == NULL) kmem_free(bp, bufsize); return (TRUE); } /* * XDR_INLINE encode a filehandle. */ bool_t xdr_inline_encode_nfs_fh3(uint32_t **ptrp, uint32_t *ptr_redzone, nfs_fh3 *fhp) { uint32_t *ptr = *ptrp; uchar_t *cp; uint_t otw_len, fsize, xsize; /* otw, file, and export sizes */ uint32_t padword; fsize = fhp->fh3_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_len; xsize = fhp->fh3_xlen < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh3_xlen; /* * First get the initial and variable sized part of the filehandle. */ otw_len = sizeof (fhp->fh3_fsid) + sizeof (fhp->fh3_len) + fsize + sizeof (fhp->fh3_xlen) + xsize; /* * Round out to a full word. */ otw_len = RNDUP(otw_len); padword = (otw_len / BYTES_PER_XDR_UNIT); /* includes fhlen */ /* * Make sure we don't exceed our buffer. */ if ((ptr + (otw_len / BYTES_PER_XDR_UNIT) + 1) > ptr_redzone) return (FALSE); /* * Zero out the pading. */ ptr[padword] = 0; IXDR_PUT_U_INT32(ptr, otw_len); /* * The rest of the filehandle is in native byteorder */ /* fh3_fsid */ *ptr++ = (uint32_t)fhp->fh3_fsid.val[0]; *ptr++ = (uint32_t)fhp->fh3_fsid.val[1]; /* * Since the next pieces are unaligned, we need to * do bytewise copies. */ cp = (uchar_t *)ptr; /* fh3_len + fh3_data */ bcopy(&fhp->fh3_len, cp, sizeof (fhp->fh3_len) + fsize); cp += sizeof (fhp->fh3_len) + fsize; /* fh3_xlen + fh3_xdata */ bcopy(&fhp->fh3_xlen, cp, sizeof (fhp->fh3_xlen) + xsize); cp += sizeof (fhp->fh3_xlen) + xsize; /* do necessary rounding/padding */ cp = (uchar_t *)RNDUP((uintptr_t)cp); ptr = (uint32_t *)cp; /* * With the above padding, we're word aligned again. */ ASSERT(((uintptr_t)ptr % BYTES_PER_XDR_UNIT) == 0); *ptrp = ptr; return (TRUE); } static bool_t xdr_encode_nfs_fh3(XDR *xdrs, nfs_fh3 *objp) { uint_t otw_len, fsize, xsize; /* otw, file, and export sizes */ bool_t ret; rpc_inline_t *ptr; rpc_inline_t *buf = NULL; uint32_t *ptr_redzone; ASSERT(xdrs->x_op == XDR_ENCODE); fsize = objp->fh3_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : objp->fh3_len; xsize = objp->fh3_xlen < NFS_FHMAXDATA ? NFS_FHMAXDATA : objp->fh3_xlen; /* * First get the over the wire size, it is the 4 bytes * for the length, plus the combined size of the * file handle components. */ otw_len = BYTES_PER_XDR_UNIT + sizeof (objp->fh3_fsid) + sizeof (objp->fh3_len) + fsize + sizeof (objp->fh3_xlen) + xsize; /* * Round out to a full word. */ otw_len = RNDUP(otw_len); /* * Next try to inline the XDR stream, if that fails (rare) * allocate a buffer to encode the file handle and then * copy it using xdr_opaque and free the buffer. */ ptr = XDR_INLINE(xdrs, otw_len); if (ptr == NULL) ptr = buf = kmem_alloc(otw_len, KM_SLEEP); ptr_redzone = (uint32_t *)(ptr + (otw_len / BYTES_PER_XDR_UNIT)); ret = xdr_inline_encode_nfs_fh3((uint32_t **)&ptr, ptr_redzone, objp); if (buf != NULL) { if (ret == TRUE) ret = xdr_opaque(xdrs, (char *)buf, otw_len); kmem_free(buf, otw_len); } return (ret); } /* * XDR a NFSv3 filehandle the naive way. */ bool_t xdr_nfs_fh3(XDR *xdrs, nfs_fh3 *objp) { if (xdrs->x_op == XDR_FREE) return (TRUE); if (!xdr_u_int(xdrs, &objp->fh3_length)) return (FALSE); if (objp->fh3_length > NFS3_FHSIZE) return (FALSE); return (xdr_opaque(xdrs, objp->fh3_u.data, objp->fh3_length)); } /* * XDR a NFSv3 filehandle with intelligence on the server. * Encoding goes from our in-memory structure to wire format. * Decoding goes from wire format to our in-memory structure. */ bool_t xdr_nfs_fh3_server(XDR *xdrs, nfs_fh3 *objp) { switch (xdrs->x_op) { case XDR_ENCODE: return (xdr_encode_nfs_fh3(xdrs, objp)); case XDR_DECODE: return (xdr_decode_nfs_fh3(xdrs, objp)); case XDR_FREE: if (objp->fh3_u.data != NULL) bzero(objp->fh3_u.data, sizeof (objp->fh3_u.data)); return (TRUE); } return (FALSE); } bool_t xdr_diropargs3(XDR *xdrs, diropargs3 *objp) { switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, objp->dirp)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->dir)) return (FALSE); break; } return (xdr_string3(xdrs, &objp->name, MAXNAMELEN)); } static bool_t xdr_fattr3(XDR *xdrs, fattr3 *na) { int32_t *ptr; if (xdrs->x_op == XDR_FREE) return (TRUE); ptr = XDR_INLINE(xdrs, NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT); if (ptr != NULL) { if (xdrs->x_op == XDR_DECODE) { na->type = IXDR_GET_ENUM(ptr, enum ftype3); na->mode = IXDR_GET_U_INT32(ptr); na->nlink = IXDR_GET_U_INT32(ptr); na->uid = IXDR_GET_U_INT32(ptr); na->gid = IXDR_GET_U_INT32(ptr); IXDR_GET_U_HYPER(ptr, na->size); IXDR_GET_U_HYPER(ptr, na->used); na->rdev.specdata1 = IXDR_GET_U_INT32(ptr); na->rdev.specdata2 = IXDR_GET_U_INT32(ptr); IXDR_GET_U_HYPER(ptr, na->fsid); IXDR_GET_U_HYPER(ptr, na->fileid); na->atime.seconds = IXDR_GET_U_INT32(ptr); na->atime.nseconds = IXDR_GET_U_INT32(ptr); na->mtime.seconds = IXDR_GET_U_INT32(ptr); na->mtime.nseconds = IXDR_GET_U_INT32(ptr); na->ctime.seconds = IXDR_GET_U_INT32(ptr); na->ctime.nseconds = IXDR_GET_U_INT32(ptr); } else { IXDR_PUT_ENUM(ptr, na->type); IXDR_PUT_U_INT32(ptr, na->mode); IXDR_PUT_U_INT32(ptr, na->nlink); IXDR_PUT_U_INT32(ptr, na->uid); IXDR_PUT_U_INT32(ptr, na->gid); IXDR_PUT_U_HYPER(ptr, na->size); IXDR_PUT_U_HYPER(ptr, na->used); IXDR_PUT_U_INT32(ptr, na->rdev.specdata1); IXDR_PUT_U_INT32(ptr, na->rdev.specdata2); IXDR_PUT_U_HYPER(ptr, na->fsid); IXDR_PUT_U_HYPER(ptr, na->fileid); IXDR_PUT_U_INT32(ptr, na->atime.seconds); IXDR_PUT_U_INT32(ptr, na->atime.nseconds); IXDR_PUT_U_INT32(ptr, na->mtime.seconds); IXDR_PUT_U_INT32(ptr, na->mtime.nseconds); IXDR_PUT_U_INT32(ptr, na->ctime.seconds); IXDR_PUT_U_INT32(ptr, na->ctime.nseconds); } return (TRUE); } if (!(xdr_enum(xdrs, (enum_t *)&na->type) && xdr_u_int(xdrs, &na->mode) && xdr_u_int(xdrs, &na->nlink) && xdr_u_int(xdrs, &na->uid) && xdr_u_int(xdrs, &na->gid) && xdr_u_longlong_t(xdrs, &na->size) && xdr_u_longlong_t(xdrs, &na->used) && xdr_u_int(xdrs, &na->rdev.specdata1) && xdr_u_int(xdrs, &na->rdev.specdata2) && xdr_u_longlong_t(xdrs, &na->fsid) && xdr_u_longlong_t(xdrs, &na->fileid) && xdr_u_int(xdrs, &na->atime.seconds) && xdr_u_int(xdrs, &na->atime.nseconds) && xdr_u_int(xdrs, &na->mtime.seconds) && xdr_u_int(xdrs, &na->mtime.nseconds) && xdr_u_int(xdrs, &na->ctime.seconds) && xdr_u_int(xdrs, &na->ctime.nseconds))) return (FALSE); return (TRUE); } /* * Fast decode of an fattr3 to a vattr * Only return FALSE on decode error, all other fattr to vattr translation * failures set status. * * Callers must catch the following errors: * EFBIG - file size will not fit in va_size * EOVERFLOW - time will not fit in va_*time */ static bool_t xdr_fattr3_to_vattr(XDR *xdrs, fattr3_res *objp) { int32_t *ptr; size3 used; specdata3 rdev; uint32_t ntime; vattr_t *vap = objp->vap; /* * DECODE only */ ASSERT(xdrs->x_op == XDR_DECODE); objp->status = 0; ptr = XDR_INLINE(xdrs, NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT); if (ptr != NULL) { /* * Common case */ vap->va_type = IXDR_GET_ENUM(ptr, enum vtype); if (vap->va_type < NF3REG || vap->va_type > NF3FIFO) vap->va_type = VBAD; else vap->va_type = nf3_to_vt[vap->va_type]; vap->va_mode = IXDR_GET_U_INT32(ptr); vap->va_nlink = IXDR_GET_U_INT32(ptr); vap->va_uid = (uid_t)IXDR_GET_U_INT32(ptr); if (vap->va_uid == NFS_UID_NOBODY) vap->va_uid = UID_NOBODY; vap->va_gid = (gid_t)IXDR_GET_U_INT32(ptr); if (vap->va_gid == NFS_GID_NOBODY) vap->va_gid = GID_NOBODY; IXDR_GET_U_HYPER(ptr, vap->va_size); /* * If invalid size, stop decode, set status, and * return TRUE, x_handy will be correct, caller must ignore vap. */ if (!NFS3_SIZE_OK(vap->va_size)) { objp->status = EFBIG; return (TRUE); } IXDR_GET_U_HYPER(ptr, used); rdev.specdata1 = IXDR_GET_U_INT32(ptr); rdev.specdata2 = IXDR_GET_U_INT32(ptr); /* fsid is ignored */ ptr += 2; IXDR_GET_U_HYPER(ptr, vap->va_nodeid); /* * nfs protocol defines times as unsigned so don't * extend sign, unless sysadmin set nfs_allow_preepoch_time. * The inline macros do the equivilant of NFS_TIME_T_CONVERT */ if (nfs_allow_preepoch_time) { vap->va_atime.tv_sec = IXDR_GET_INT32(ptr); vap->va_atime.tv_nsec = IXDR_GET_U_INT32(ptr); vap->va_mtime.tv_sec = IXDR_GET_INT32(ptr); vap->va_mtime.tv_nsec = IXDR_GET_U_INT32(ptr); vap->va_ctime.tv_sec = IXDR_GET_INT32(ptr); vap->va_ctime.tv_nsec = IXDR_GET_U_INT32(ptr); } else { /* * Check if the time would overflow on 32-bit */ ntime = IXDR_GET_U_INT32(ptr); /*CONSTCOND*/ if (NFS3_TIME_OVERFLOW(ntime)) { objp->status = EOVERFLOW; return (TRUE); } vap->va_atime.tv_sec = ntime; vap->va_atime.tv_nsec = IXDR_GET_U_INT32(ptr); ntime = IXDR_GET_U_INT32(ptr); /*CONSTCOND*/ if (NFS3_TIME_OVERFLOW(ntime)) { objp->status = EOVERFLOW; return (TRUE); } vap->va_mtime.tv_sec = ntime; vap->va_mtime.tv_nsec = IXDR_GET_U_INT32(ptr); ntime = IXDR_GET_U_INT32(ptr); /*CONSTCOND*/ if (NFS3_TIME_OVERFLOW(ntime)) { objp->status = EOVERFLOW; return (TRUE); } vap->va_ctime.tv_sec = ntime; vap->va_ctime.tv_nsec = IXDR_GET_U_INT32(ptr); } } else { uint64 fsid; /* * Slow path */ if (!(xdr_enum(xdrs, (enum_t *)&vap->va_type) && xdr_u_int(xdrs, &vap->va_mode) && xdr_u_int(xdrs, &vap->va_nlink) && xdr_u_int(xdrs, (uint_t *)&vap->va_uid) && xdr_u_int(xdrs, (uint_t *)&vap->va_gid) && xdr_u_longlong_t(xdrs, &vap->va_size) && xdr_u_longlong_t(xdrs, &used) && xdr_u_int(xdrs, &rdev.specdata1) && xdr_u_int(xdrs, &rdev.specdata2) && xdr_u_longlong_t(xdrs, &fsid) && /* ignored */ xdr_u_longlong_t(xdrs, &vap->va_nodeid))) return (FALSE); if (nfs_allow_preepoch_time) { if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_atime.tv_sec = (int32_t)ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_atime.tv_nsec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_mtime.tv_sec = (int32_t)ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_mtime.tv_nsec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_ctime.tv_sec = (int32_t)ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_ctime.tv_nsec = ntime; } else { /* * Check if the time would overflow on 32-bit * Set status and keep decoding stream. */ if (!xdr_u_int(xdrs, &ntime)) return (FALSE); /*CONSTCOND*/ if (NFS3_TIME_OVERFLOW(ntime)) { objp->status = EOVERFLOW; } vap->va_atime.tv_sec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_atime.tv_nsec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); /*CONSTCOND*/ if (NFS3_TIME_OVERFLOW(ntime)) { objp->status = EOVERFLOW; } vap->va_mtime.tv_sec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_mtime.tv_nsec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); /*CONSTCOND*/ if (NFS3_TIME_OVERFLOW(ntime)) { objp->status = EOVERFLOW; } vap->va_ctime.tv_sec = ntime; if (!xdr_u_int(xdrs, &ntime)) return (FALSE); vap->va_ctime.tv_nsec = ntime; } /* * Fixup as needed */ if (vap->va_type < NF3REG || vap->va_type > NF3FIFO) vap->va_type = VBAD; else vap->va_type = nf3_to_vt[vap->va_type]; if (vap->va_uid == NFS_UID_NOBODY) vap->va_uid = UID_NOBODY; if (vap->va_gid == NFS_GID_NOBODY) vap->va_gid = GID_NOBODY; /* * If invalid size, set status, and * return TRUE, caller must ignore vap. */ if (!NFS3_SIZE_OK(vap->va_size)) { objp->status = EFBIG; return (TRUE); } } /* * Fill in derived fields */ vap->va_fsid = objp->vp->v_vfsp->vfs_dev; vap->va_seq = 0; /* * Common case values */ vap->va_rdev = 0; vap->va_blksize = MAXBSIZE; vap->va_nblocks = 0; switch (vap->va_type) { case VREG: case VDIR: case VLNK: vap->va_nblocks = (u_longlong_t) ((used + (size3)DEV_BSIZE - (size3)1) / (size3)DEV_BSIZE); break; case VBLK: vap->va_blksize = DEV_BSIZE; /* FALLTHRU */ case VCHR: vap->va_rdev = makedevice(rdev.specdata1, rdev.specdata2); break; case VSOCK: case VFIFO: default: break; } return (TRUE); } static bool_t xdr_post_op_vattr(XDR *xdrs, post_op_vattr *objp) { /* * DECODE only */ ASSERT(xdrs->x_op == XDR_DECODE); if (!xdr_bool(xdrs, &objp->attributes)) return (FALSE); if (objp->attributes == FALSE) return (TRUE); if (objp->attributes != TRUE) return (FALSE); if (!xdr_fattr3_to_vattr(xdrs, &objp->fres)) return (FALSE); /* * The file size may cause an EFBIG or the time values * may cause EOVERFLOW, if so simply drop the attributes. */ if (objp->fres.status != NFS3_OK) objp->attributes = FALSE; return (TRUE); } bool_t xdr_post_op_attr(XDR *xdrs, post_op_attr *objp) { if (!xdr_bool(xdrs, &objp->attributes)) return (FALSE); if (objp->attributes == FALSE) return (TRUE); if (objp->attributes != TRUE) return (FALSE); if (!xdr_fattr3(xdrs, &objp->attr)) return (FALSE); /* * Check that we don't get a file we can't handle through * existing interfaces (especially stat64()). * Decode only check since on encode the data has * been dealt with in the above call to xdr_fattr3(). */ if (xdrs->x_op == XDR_DECODE) { /* Set attrs to false if invalid size or time */ if (!NFS3_SIZE_OK(objp->attr.size)) { objp->attributes = FALSE; return (TRUE); } #ifndef _LP64 if (!NFS3_FATTR_TIME_OK(&objp->attr)) objp->attributes = FALSE; #endif } return (TRUE); } static bool_t xdr_wcc_data(XDR *xdrs, wcc_data *objp) { int32_t *ptr; wcc_attr *attrp; if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op == XDR_DECODE) { /* pre_op_attr */ if (!xdr_bool(xdrs, &objp->before.attributes)) return (FALSE); switch (objp->before.attributes) { case TRUE: attrp = &objp->before.attr; ptr = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT); if (ptr != NULL) { IXDR_GET_U_HYPER(ptr, attrp->size); attrp->mtime.seconds = IXDR_GET_U_INT32(ptr); attrp->mtime.nseconds = IXDR_GET_U_INT32(ptr); attrp->ctime.seconds = IXDR_GET_U_INT32(ptr); attrp->ctime.nseconds = IXDR_GET_U_INT32(ptr); } else { if (!xdr_u_longlong_t(xdrs, &attrp->size)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->mtime.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->mtime.nseconds)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->ctime.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->ctime.nseconds)) return (FALSE); } #ifndef _LP64 /* * check time overflow. */ if (!NFS3_TIME_OK(attrp->mtime.seconds) || !NFS3_TIME_OK(attrp->ctime.seconds)) objp->before.attributes = FALSE; #endif break; case FALSE: break; default: return (FALSE); } } if (xdrs->x_op == XDR_ENCODE) { /* pre_op_attr */ if (!xdr_bool(xdrs, &objp->before.attributes)) return (FALSE); switch (objp->before.attributes) { case TRUE: attrp = &objp->before.attr; ptr = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT); if (ptr != NULL) { IXDR_PUT_U_HYPER(ptr, attrp->size); IXDR_PUT_U_INT32(ptr, attrp->mtime.seconds); IXDR_PUT_U_INT32(ptr, attrp->mtime.nseconds); IXDR_PUT_U_INT32(ptr, attrp->ctime.seconds); IXDR_PUT_U_INT32(ptr, attrp->ctime.nseconds); } else { if (!xdr_u_longlong_t(xdrs, &attrp->size)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->mtime.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->mtime.nseconds)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->ctime.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &attrp->ctime.nseconds)) return (FALSE); } break; case FALSE: break; default: return (FALSE); } } return (xdr_post_op_attr(xdrs, &objp->after)); } bool_t xdr_post_op_fh3(XDR *xdrs, post_op_fh3 *objp) { if (!xdr_bool(xdrs, &objp->handle_follows)) return (FALSE); switch (objp->handle_follows) { case TRUE: switch (xdrs->x_op) { case XDR_ENCODE: if (!xdr_nfs_fh3_server(xdrs, &objp->handle)) return (FALSE); break; case XDR_FREE: case XDR_DECODE: if (!xdr_nfs_fh3(xdrs, &objp->handle)) return (FALSE); break; } return (TRUE); case FALSE: return (TRUE); default: return (FALSE); } } static bool_t xdr_sattr3(XDR *xdrs, sattr3 *objp) { /* set_mode3 */ if (!xdr_bool(xdrs, &objp->mode.set_it)) return (FALSE); if (objp->mode.set_it) if (!xdr_u_int(xdrs, &objp->mode.mode)) return (FALSE); /* set_uid3 */ if (!xdr_bool(xdrs, &objp->uid.set_it)) return (FALSE); if (objp->uid.set_it) if (!xdr_u_int(xdrs, &objp->uid.uid)) return (FALSE); /* set_gid3 */ if (!xdr_bool(xdrs, &objp->gid.set_it)) return (FALSE); if (objp->gid.set_it) if (!xdr_u_int(xdrs, &objp->gid.gid)) return (FALSE); /* set_size3 */ if (!xdr_bool(xdrs, &objp->size.set_it)) return (FALSE); if (objp->size.set_it) if (!xdr_u_longlong_t(xdrs, &objp->size.size)) return (FALSE); /* set_atime */ if (!xdr_enum(xdrs, (enum_t *)&objp->atime.set_it)) return (FALSE); if (objp->atime.set_it == SET_TO_CLIENT_TIME) { if (!xdr_u_int(xdrs, &objp->atime.atime.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &objp->atime.atime.nseconds)) return (FALSE); } /* set_mtime */ if (!xdr_enum(xdrs, (enum_t *)&objp->mtime.set_it)) return (FALSE); if (objp->mtime.set_it == SET_TO_CLIENT_TIME) { if (!xdr_u_int(xdrs, &objp->mtime.mtime.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &objp->mtime.mtime.nseconds)) return (FALSE); } return (TRUE); } bool_t xdr_GETATTR3res(XDR *xdrs, GETATTR3res *objp) { if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (TRUE); /* xdr_GETATTR3resok */ return (xdr_fattr3(xdrs, &objp->resok.obj_attributes)); } bool_t xdr_GETATTR3vres(XDR *xdrs, GETATTR3vres *objp) { /* * DECODE or FREE only */ if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op != XDR_DECODE) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (TRUE); return (xdr_fattr3_to_vattr(xdrs, &objp->fres)); } bool_t xdr_SETATTR3args(XDR *xdrs, SETATTR3args *objp) { switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->object)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->object)) return (FALSE); break; } if (!xdr_sattr3(xdrs, &objp->new_attributes)) return (FALSE); /* sattrguard3 */ if (!xdr_bool(xdrs, &objp->guard.check)) return (FALSE); switch (objp->guard.check) { case TRUE: if (!xdr_u_int(xdrs, &objp->guard.obj_ctime.seconds)) return (FALSE); return (xdr_u_int(xdrs, &objp->guard.obj_ctime.nseconds)); case FALSE: return (TRUE); default: return (FALSE); } } bool_t xdr_SETATTR3res(XDR *xdrs, SETATTR3res *objp) { if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: return (xdr_wcc_data(xdrs, &objp->resok.obj_wcc)); default: return (xdr_wcc_data(xdrs, &objp->resfail.obj_wcc)); } } bool_t xdr_LOOKUP3res(XDR *xdrs, LOOKUP3res *objp) { LOOKUP3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.dir_attributes)); /* xdr_LOOKUP3resok */ resokp = &objp->resok; switch (xdrs->x_op) { case XDR_ENCODE: if (!xdr_nfs_fh3_server(xdrs, &resokp->object)) return (FALSE); break; case XDR_FREE: case XDR_DECODE: if (!xdr_nfs_fh3(xdrs, &resokp->object)) return (FALSE); break; } if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); return (xdr_post_op_attr(xdrs, &resokp->dir_attributes)); } bool_t xdr_LOOKUP3vres(XDR *xdrs, LOOKUP3vres *objp) { /* * DECODE or FREE only */ if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op != XDR_DECODE) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_vattr(xdrs, &objp->dir_attributes)); if (!xdr_nfs_fh3(xdrs, &objp->object)) return (FALSE); if (!xdr_post_op_vattr(xdrs, &objp->obj_attributes)) return (FALSE); return (xdr_post_op_vattr(xdrs, &objp->dir_attributes)); } bool_t xdr_ACCESS3args(XDR *xdrs, ACCESS3args *objp) { switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->object)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->object)) return (FALSE); break; } return (xdr_u_int(xdrs, &objp->access)); } bool_t xdr_ACCESS3res(XDR *xdrs, ACCESS3res *objp) { ACCESS3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes)); /* xdr_ACCESS3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); return (xdr_u_int(xdrs, &resokp->access)); } bool_t xdr_READLINK3res(XDR *xdrs, READLINK3res *objp) { READLINK3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.symlink_attributes)); /* xdr_READLINK3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->symlink_attributes)) return (FALSE); return (xdr_string3(xdrs, &resokp->data, MAXPATHLEN)); } bool_t xdr_READ3args(XDR *xdrs, READ3args *objp) { switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->file)) return (FALSE); break; } if (!xdr_u_longlong_t(xdrs, &objp->offset)) return (FALSE); return (xdr_u_int(xdrs, &objp->count)); } bool_t xdr_READ3res(XDR *xdrs, READ3res *objp) { READ3resok *resokp; bool_t ret; mblk_t *mp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.file_attributes)); resokp = &objp->resok; if (xdr_post_op_attr(xdrs, &resokp->file_attributes) == FALSE || xdr_u_int(xdrs, &resokp->count) == FALSE || xdr_bool(xdrs, &resokp->eof) == FALSE) { return (FALSE); } if (xdrs->x_op == XDR_ENCODE) { int i, rndup; mp = resokp->data.mp; if (mp != NULL && xdrs->x_ops == &xdrmblk_ops) { mp->b_wptr += resokp->count; rndup = BYTES_PER_XDR_UNIT - (resokp->data.data_len % BYTES_PER_XDR_UNIT); if (rndup != BYTES_PER_XDR_UNIT) for (i = 0; i < rndup; i++) *mp->b_wptr++ = '\0'; if (xdrmblk_putmblk(xdrs, mp, resokp->count) == TRUE) { resokp->data.mp = NULL; return (TRUE); } } /* * Fall thru for the xdr_bytes() * * note: the mblk will be freed in * rfs3_read_free. */ } ret = xdr_bytes(xdrs, (char **)&resokp->data.data_val, &resokp->data.data_len, nfs3tsize()); return (ret); } bool_t xdr_READ3vres(XDR *xdrs, READ3vres *objp) { /* * DECODE or FREE only */ if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op != XDR_DECODE) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (!xdr_post_op_vattr(xdrs, &objp->pov)) return (FALSE); if (objp->status != NFS3_OK) return (TRUE); if (!xdr_u_int(xdrs, &objp->count)) return (FALSE); if (!xdr_bool(xdrs, &objp->eof)) return (FALSE); return (xdr_bytes(xdrs, (char **)&objp->data.data_val, &objp->data.data_len, nfs3tsize())); } bool_t xdr_READ3uiores(XDR *xdrs, READ3uiores *objp) { bool_t attributes; mblk_t *mp; size_t n; int error; int size = (int)objp->size; struct uio *uiop = objp->uiop; int32_t fattr3_len = NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT; int32_t *ptr; /* * DECODE or FREE only */ if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op != XDR_DECODE) return (FALSE); if (!XDR_GETINT32(xdrs, (int32_t *)&objp->status)) return (FALSE); if (!XDR_GETINT32(xdrs, (int32_t *)&attributes)) return (FALSE); /* * For directio we just skip over attributes if present */ switch (attributes) { case TRUE: if (!XDR_CONTROL(xdrs, XDR_SKIPBYTES, &fattr3_len)) return (FALSE); break; case FALSE: break; default: return (FALSE); } if (objp->status != NFS3_OK) return (TRUE); if (!XDR_GETINT32(xdrs, (int32_t *)&objp->count)) return (FALSE); if (!XDR_GETINT32(xdrs, (int32_t *)&objp->eof)) return (FALSE); if (xdrs->x_ops == &xdrmblk_ops) { if (!xdrmblk_getmblk(xdrs, &mp, &objp->size)) return (FALSE); if (objp->size == 0) return (TRUE); if (objp->size > size) return (FALSE); size = (int)objp->size; do { n = MIN(size, mp->b_wptr - mp->b_rptr); if ((n = MIN(uiop->uio_resid, n)) != 0) { error = uiomove((char *)mp->b_rptr, n, UIO_READ, uiop); if (error) return (FALSE); mp->b_rptr += n; size -= n; } while (mp && (mp->b_rptr >= mp->b_wptr)) mp = mp->b_cont; } while (mp && size > 0 && uiop->uio_resid > 0); return (TRUE); } /* * This isn't an xdrmblk stream. Handle the likely * case that it can be inlined (ex. xdrmem). */ if (!XDR_GETINT32(xdrs, (int32_t *)&objp->size)) return (FALSE); if (objp->size == 0) return (TRUE); if (objp->size > size) return (FALSE); size = (int)objp->size; if ((ptr = XDR_INLINE(xdrs, size)) != NULL) return (uiomove(ptr, size, UIO_READ, uiop) ? FALSE : TRUE); /* * Handle some other (unlikely) stream type that will need a copy. */ if ((ptr = kmem_alloc(size, KM_NOSLEEP)) == NULL) return (FALSE); if (!XDR_GETBYTES(xdrs, (caddr_t)ptr, size)) { kmem_free(ptr, size); return (FALSE); } error = uiomove(ptr, size, UIO_READ, uiop); kmem_free(ptr, size); return (error ? FALSE : TRUE); } bool_t xdr_WRITE3args(XDR *xdrs, WRITE3args *objp) { switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->file)) return (FALSE); break; } if (!xdr_u_longlong_t(xdrs, &objp->offset)) return (FALSE); if (!xdr_u_int(xdrs, &objp->count)) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->stable)) return (FALSE); if (xdrs->x_op == XDR_DECODE) { if (xdrs->x_ops == &xdrmblk_ops) { if (xdrmblk_getmblk(xdrs, &objp->mblk, &objp->data.data_len) == TRUE) { objp->data.data_val = NULL; return (TRUE); } } objp->mblk = NULL; /* Else fall thru for the xdr_bytes(). */ } return (xdr_bytes(xdrs, (char **)&objp->data.data_val, &objp->data.data_len, nfs3tsize())); } bool_t xdr_WRITE3res(XDR *xdrs, WRITE3res *objp) { WRITE3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) /* xdr_WRITE3resfail */ return (xdr_wcc_data(xdrs, &objp->resfail.file_wcc)); /* xdr_WRITE3resok */ resokp = &objp->resok; if (!xdr_wcc_data(xdrs, &resokp->file_wcc)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->count)) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&resokp->committed)) return (FALSE); /* * writeverf3 is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ return (xdr_u_longlong_t(xdrs, &resokp->verf)); } bool_t xdr_CREATE3args(XDR *xdrs, CREATE3args *objp) { createhow3 *howp; if (!xdr_diropargs3(xdrs, &objp->where)) return (FALSE); /* xdr_createhow3 */ howp = &objp->how; if (!xdr_enum(xdrs, (enum_t *)&howp->mode)) return (FALSE); switch (howp->mode) { case UNCHECKED: case GUARDED: return (xdr_sattr3(xdrs, &howp->createhow3_u.obj_attributes)); case EXCLUSIVE: /* * createverf3 is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ return (xdr_u_longlong_t(xdrs, &howp->createhow3_u.verf)); default: return (FALSE); } } bool_t xdr_CREATE3res(XDR *xdrs, CREATE3res *objp) { CREATE3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: /* xdr_CREATE3resok */ resokp = &objp->resok; if (!xdr_post_op_fh3(xdrs, &resokp->obj)) return (FALSE); if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); return (xdr_wcc_data(xdrs, &resokp->dir_wcc)); default: /* xdr_CREATE3resfail */ return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc)); } } bool_t xdr_MKDIR3args(XDR *xdrs, MKDIR3args *objp) { if (!xdr_diropargs3(xdrs, &objp->where)) return (FALSE); return (xdr_sattr3(xdrs, &objp->attributes)); } bool_t xdr_MKDIR3res(XDR *xdrs, MKDIR3res *objp) { MKDIR3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: /* xdr_MKDIR3resok */ resokp = &objp->resok; if (!xdr_post_op_fh3(xdrs, &resokp->obj)) return (FALSE); if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); return (xdr_wcc_data(xdrs, &resokp->dir_wcc)); default: return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc)); } } bool_t xdr_SYMLINK3args(XDR *xdrs, SYMLINK3args *objp) { if (!xdr_diropargs3(xdrs, &objp->where)) return (FALSE); if (!xdr_sattr3(xdrs, &objp->symlink.symlink_attributes)) return (FALSE); return (xdr_string3(xdrs, &objp->symlink.symlink_data, MAXPATHLEN)); } bool_t xdr_SYMLINK3res(XDR *xdrs, SYMLINK3res *objp) { SYMLINK3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: resokp = &objp->resok; /* xdr_SYMLINK3resok */ if (!xdr_post_op_fh3(xdrs, &resokp->obj)) return (FALSE); if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); return (xdr_wcc_data(xdrs, &resokp->dir_wcc)); default: return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc)); } } bool_t xdr_MKNOD3args(XDR *xdrs, MKNOD3args *objp) { mknoddata3 *whatp; devicedata3 *nod_objp; if (!xdr_diropargs3(xdrs, &objp->where)) return (FALSE); whatp = &objp->what; if (!xdr_enum(xdrs, (enum_t *)&whatp->type)) return (FALSE); switch (whatp->type) { case NF3CHR: case NF3BLK: /* xdr_devicedata3 */ nod_objp = &whatp->mknoddata3_u.device; if (!xdr_sattr3(xdrs, &nod_objp->dev_attributes)) return (FALSE); if (!xdr_u_int(xdrs, &nod_objp->spec.specdata1)) return (FALSE); return (xdr_u_int(xdrs, &nod_objp->spec.specdata2)); case NF3SOCK: case NF3FIFO: return (xdr_sattr3(xdrs, &whatp->mknoddata3_u.pipe_attributes)); default: break; } return (TRUE); } bool_t xdr_MKNOD3res(XDR *xdrs, MKNOD3res *objp) { MKNOD3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: /* xdr_MKNOD3resok */ resokp = &objp->resok; if (!xdr_post_op_fh3(xdrs, &resokp->obj)) return (FALSE); if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); return (xdr_wcc_data(xdrs, &resokp->dir_wcc)); default: return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc)); } } bool_t xdr_REMOVE3res(XDR *xdrs, REMOVE3res *objp) { if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: return (xdr_wcc_data(xdrs, &objp->resok.dir_wcc)); default: return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc)); } } bool_t xdr_RMDIR3res(XDR *xdrs, RMDIR3res *objp) { if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: return (xdr_wcc_data(xdrs, &objp->resok.dir_wcc)); default: return (xdr_wcc_data(xdrs, &objp->resfail.dir_wcc)); } } bool_t xdr_RENAME3args(XDR *xdrs, RENAME3args *objp) { if (!xdr_diropargs3(xdrs, &objp->from)) return (FALSE); return (xdr_diropargs3(xdrs, &objp->to)); } bool_t xdr_RENAME3res(XDR *xdrs, RENAME3res *objp) { RENAME3resok *resokp; RENAME3resfail *resfailp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: /* xdr_RENAME3resok */ resokp = &objp->resok; if (!xdr_wcc_data(xdrs, &resokp->fromdir_wcc)) return (FALSE); return (xdr_wcc_data(xdrs, &resokp->todir_wcc)); default: /* xdr_RENAME3resfail */ resfailp = &objp->resfail; if (!xdr_wcc_data(xdrs, &resfailp->fromdir_wcc)) return (FALSE); return (xdr_wcc_data(xdrs, &resfailp->todir_wcc)); } } bool_t xdr_LINK3args(XDR *xdrs, LINK3args *objp) { switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->file)) return (FALSE); break; } return (xdr_diropargs3(xdrs, &objp->link)); } bool_t xdr_LINK3res(XDR *xdrs, LINK3res *objp) { LINK3resok *resokp; LINK3resfail *resfailp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: /* xdr_LINK3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->file_attributes)) return (FALSE); return (xdr_wcc_data(xdrs, &resokp->linkdir_wcc)); default: /* xdr_LINK3resfail */ resfailp = &objp->resfail; if (!xdr_post_op_attr(xdrs, &resfailp->file_attributes)) return (FALSE); return (xdr_wcc_data(xdrs, &resfailp->linkdir_wcc)); } } bool_t xdr_READDIR3args(XDR *xdrs, READDIR3args *objp) { if (xdrs->x_op == XDR_FREE) return (TRUE); switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->dir)) return (FALSE); break; } if (!xdr_u_longlong_t(xdrs, &objp->cookie)) return (FALSE); /* * cookieverf is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ if (!xdr_u_longlong_t(xdrs, &objp->cookieverf)) return (FALSE); return (xdr_u_int(xdrs, &objp->count)); } #ifdef nextdp #undef nextdp #endif #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) #ifdef roundup #undef roundup #endif #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) /* * ENCODE ONLY */ static bool_t xdr_putdirlist(XDR *xdrs, READDIR3resok *objp) { struct dirent64 *dp; char *name; int size; int bufsize; uint_t namlen; bool_t true = TRUE; bool_t false = FALSE; int entrysz; int tofit; fileid3 fileid; cookie3 cookie; if (xdrs->x_op != XDR_ENCODE) return (FALSE); /* * bufsize is used to keep track of the size of the response. * It is primed with: * 1 for the status + * 1 for the dir_attributes.attributes boolean + * 2 for the cookie verifier * all times BYTES_PER_XDR_UNIT to convert from XDR units * to bytes. If there are directory attributes to be * returned, then: * NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3 * time BYTES_PER_XDR_UNIT is added to account for them. */ bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT; if (objp->dir_attributes.attributes) bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT; for (size = objp->size, dp = (struct dirent64 *)objp->reply.entries; size > 0; size -= dp->d_reclen, dp = nextdp(dp)) { if (dp->d_reclen == 0) return (FALSE); if (dp->d_ino == 0) continue; name = dp->d_name; namlen = (uint_t)strlen(dp->d_name); /* * An entry is composed of: * 1 for the true/false list indicator + * 2 for the fileid + * 1 for the length of the name + * 2 for the cookie + * all times BYTES_PER_XDR_UNIT to convert from * XDR units to bytes, plus the length of the name * rounded up to the nearest BYTES_PER_XDR_UNIT. */ entrysz = (1 + 2 + 1 + 2) * BYTES_PER_XDR_UNIT + roundup(namlen, BYTES_PER_XDR_UNIT); /* * We need to check to see if the number of bytes left * to go into the buffer will actually fit into the * buffer. This is calculated as the size of this * entry plus: * 1 for the true/false list indicator + * 1 for the eof indicator * times BYTES_PER_XDR_UNIT to convert from from * XDR units to bytes. */ tofit = entrysz + (1 + 1) * BYTES_PER_XDR_UNIT; if (bufsize + tofit > objp->count) { objp->reply.eof = FALSE; break; } fileid = (fileid3)(dp->d_ino); cookie = (cookie3)(dp->d_off); if (!xdr_bool(xdrs, &true) || !xdr_u_longlong_t(xdrs, &fileid) || !xdr_bytes(xdrs, &name, &namlen, ~0) || !xdr_u_longlong_t(xdrs, &cookie)) { return (FALSE); } bufsize += entrysz; } if (!xdr_bool(xdrs, &false)) return (FALSE); if (!xdr_bool(xdrs, &objp->reply.eof)) return (FALSE); return (TRUE); } bool_t xdr_READDIR3res(XDR *xdrs, READDIR3res *objp) { READDIR3resok *resokp; /* * ENCODE or FREE only */ if (xdrs->x_op == XDR_DECODE) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.dir_attributes)); /* xdr_READDIR3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->dir_attributes)) return (FALSE); if (xdrs->x_op != XDR_ENCODE) return (TRUE); /* * cookieverf is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ if (!xdr_u_longlong_t(xdrs, &resokp->cookieverf)) return (FALSE); return (xdr_putdirlist(xdrs, resokp)); } bool_t xdr_READDIR3vres(XDR *xdrs, READDIR3vres *objp) { dirent64_t *dp; uint_t entries_size; int outcount = 0; /* * DECODE or FREE only */ if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op != XDR_DECODE) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (!xdr_post_op_vattr(xdrs, &objp->dir_attributes)) return (FALSE); if (objp->status != NFS3_OK) return (TRUE); /* * cookieverf is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ if (!xdr_u_longlong_t(xdrs, &objp->cookieverf)) return (FALSE); entries_size = objp->entries_size; dp = objp->entries; for (;;) { uint_t this_reclen; bool_t valid; uint_t namlen; ino64_t fileid; if (!XDR_GETINT32(xdrs, (int32_t *)&valid)) return (FALSE); if (!valid) { /* * We have run out of entries, decode eof. */ if (!XDR_GETINT32(xdrs, (int32_t *)&objp->eof)) return (FALSE); break; } /* * fileid3 fileid */ if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&fileid)) return (FALSE); /* * filename3 name */ if (!XDR_GETINT32(xdrs, (int32_t *)&namlen)) return (FALSE); this_reclen = DIRENT64_RECLEN(namlen); /* * If this will overflow buffer, stop decoding */ if ((outcount + this_reclen) > entries_size) { objp->eof = FALSE; break; } dp->d_reclen = this_reclen; dp->d_ino = fileid; if (!xdr_opaque(xdrs, dp->d_name, namlen)) return (FALSE); bzero(&dp->d_name[namlen], DIRENT64_NAMELEN(this_reclen) - namlen); /* * cookie3 cookie */ if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&dp->d_off)) return (FALSE); objp->loff = dp->d_off; outcount += this_reclen; dp = (dirent64_t *)((intptr_t)dp + this_reclen); } objp->size = outcount; return (TRUE); } bool_t xdr_READDIRPLUS3args(XDR *xdrs, READDIRPLUS3args *objp) { if (xdrs->x_op == XDR_FREE) return (TRUE); switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->dir)) return (FALSE); break; } if (!xdr_u_longlong_t(xdrs, &objp->cookie)) return (FALSE); /* * cookieverf is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ if (!xdr_u_longlong_t(xdrs, &objp->cookieverf)) return (FALSE); if (!xdr_u_int(xdrs, &objp->dircount)) return (FALSE); return (xdr_u_int(xdrs, &objp->maxcount)); } /* * ENCODE ONLY */ static bool_t xdr_putdirpluslist(XDR *xdrs, READDIRPLUS3resok *objp) { struct dirent64 *dp; char *name; int nents; bool_t true = TRUE; bool_t false = FALSE; fileid3 fileid; cookie3 cookie; entryplus3_info *infop; if (xdrs->x_op != XDR_ENCODE) return (FALSE); dp = (struct dirent64 *)objp->reply.entries; nents = objp->size; infop = objp->infop; while (nents > 0) { if (dp->d_reclen == 0) return (FALSE); if (dp->d_ino != 0) { name = dp->d_name; fileid = (fileid3)(dp->d_ino); cookie = (cookie3)(dp->d_off); if (!xdr_bool(xdrs, &true) || !xdr_u_longlong_t(xdrs, &fileid) || !xdr_bytes(xdrs, &name, &infop->namelen, ~0) || !xdr_u_longlong_t(xdrs, &cookie) || !xdr_post_op_attr(xdrs, &infop->attr) || !xdr_post_op_fh3(xdrs, &infop->fh)) { return (FALSE); } } dp = nextdp(dp); infop++; nents--; } if (!xdr_bool(xdrs, &false)) return (FALSE); if (!xdr_bool(xdrs, &objp->reply.eof)) return (FALSE); return (TRUE); } bool_t xdr_READDIRPLUS3res(XDR *xdrs, READDIRPLUS3res *objp) { READDIRPLUS3resok *resokp; /* * ENCODE or FREE only */ if (xdrs->x_op == XDR_DECODE) return (FALSE); if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); switch (objp->status) { case NFS3_OK: /* xdr_READDIRPLUS3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->dir_attributes)) return (FALSE); /* * cookieverf is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ if (!xdr_u_longlong_t(xdrs, &resokp->cookieverf)) return (FALSE); if (xdrs->x_op == XDR_ENCODE) { if (!xdr_putdirpluslist(xdrs, resokp)) return (FALSE); } break; default: return (xdr_post_op_attr(xdrs, &objp->resfail.dir_attributes)); } return (TRUE); } /* * Decode readdirplus directly into a dirent64_t and do the DNLC caching. */ bool_t xdr_READDIRPLUS3vres(XDR *xdrs, READDIRPLUS3vres *objp) { dirent64_t *dp; vnode_t *dvp; uint_t entries_size; int outcount = 0; vnode_t *nvp; rnode_t *rp; post_op_vattr pov; vattr_t va; /* * DECODE or FREE only */ if (xdrs->x_op == XDR_FREE) return (TRUE); if (xdrs->x_op != XDR_DECODE) return (FALSE); if (!XDR_GETINT32(xdrs, (int32_t *)&objp->status)) return (FALSE); if (!xdr_post_op_vattr(xdrs, &objp->dir_attributes)) return (FALSE); if (objp->status != NFS3_OK) return (TRUE); /* * cookieverf is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ if (!xdr_u_longlong_t(xdrs, &objp->cookieverf)) return (FALSE); dvp = objp->dir_attributes.fres.vp; rp = VTOR(dvp); pov.fres.vap = &va; pov.fres.vp = dvp; entries_size = objp->entries_size; dp = objp->entries; for (;;) { uint_t this_reclen; bool_t valid; uint_t namlen; nfs_fh3 fh; int va_valid; int fh_valid; ino64_t fileid; if (!XDR_GETINT32(xdrs, (int32_t *)&valid)) return (FALSE); if (!valid) { /* * We have run out of entries, decode eof. */ if (!XDR_GETINT32(xdrs, (int32_t *)&objp->eof)) return (FALSE); break; } /* * fileid3 fileid */ if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&fileid)) return (FALSE); /* * filename3 name */ if (!XDR_GETINT32(xdrs, (int32_t *)&namlen)) return (FALSE); this_reclen = DIRENT64_RECLEN(namlen); /* * If this will overflow buffer, stop decoding */ if ((outcount + this_reclen) > entries_size) { objp->eof = FALSE; break; } dp->d_reclen = this_reclen; dp->d_ino = fileid; if (!xdr_opaque(xdrs, dp->d_name, namlen)) return (FALSE); bzero(&dp->d_name[namlen], DIRENT64_NAMELEN(this_reclen) - namlen); /* * cookie3 cookie */ if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&dp->d_off)) return (FALSE); objp->loff = dp->d_off; /* * post_op_attr name_attributes */ if (!xdr_post_op_vattr(xdrs, &pov)) return (FALSE); if (pov.attributes == TRUE && pov.fres.status == NFS3_OK) va_valid = TRUE; else va_valid = FALSE; /* * post_op_fh3 name_handle */ if (!XDR_GETINT32(xdrs, (int32_t *)&fh_valid)) return (FALSE); /* * By definition of the standard fh_valid can be 0 (FALSE) or * 1 (TRUE), but we have to account for it being anything else * in case some other system didn't follow the standard. Note * that this is why the else checks if the fh_valid variable * is != FALSE. */ if (fh_valid == TRUE) { if (!xdr_nfs_fh3(xdrs, &fh)) return (FALSE); } else { if (fh_valid != FALSE) return (FALSE); } /* * If the name is "." or there are no attributes, * don't polute the DNLC with "." entries or files * we cannot determine the type for. */ if (!(namlen == 1 && dp->d_name[0] == '.') && va_valid && fh_valid) { /* * Do the DNLC caching */ nvp = makenfs3node_va(&fh, &va, dvp->v_vfsp, objp->time, objp->credentials, rp->r_path, dp->d_name); dnlc_update(dvp, dp->d_name, nvp); VN_RELE(nvp); } outcount += this_reclen; dp = (dirent64_t *)((intptr_t)dp + this_reclen); } objp->size = outcount; return (TRUE); } bool_t xdr_FSSTAT3res(XDR *xdrs, FSSTAT3res *objp) { FSSTAT3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes)); /* xdr_FSSTAT3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->tbytes)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->fbytes)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->abytes)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->tfiles)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->ffiles)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->afiles)) return (FALSE); return (xdr_u_int(xdrs, &resokp->invarsec)); } bool_t xdr_FSINFO3res(XDR *xdrs, FSINFO3res *objp) { FSINFO3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) /* xdr_FSSTAT3resfail */ return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes)); /* xdr_FSINFO3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->rtmax)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->rtpref)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->rtmult)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->wtmax)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->wtpref)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->wtmult)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->dtpref)) return (FALSE); if (!xdr_u_longlong_t(xdrs, &resokp->maxfilesize)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->time_delta.seconds)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->time_delta.nseconds)) return (FALSE); return (xdr_u_int(xdrs, &resokp->properties)); } bool_t xdr_PATHCONF3res(XDR *xdrs, PATHCONF3res *objp) { PATHCONF3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_post_op_attr(xdrs, &objp->resfail.obj_attributes)); /* xdr_PATHCONF3resok */ resokp = &objp->resok; if (!xdr_post_op_attr(xdrs, &resokp->obj_attributes)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->info.link_max)) return (FALSE); if (!xdr_u_int(xdrs, &resokp->info.name_max)) return (FALSE); if (!xdr_bool(xdrs, &resokp->info.no_trunc)) return (FALSE); if (!xdr_bool(xdrs, &resokp->info.chown_restricted)) return (FALSE); if (!xdr_bool(xdrs, &resokp->info.case_insensitive)) return (FALSE); return (xdr_bool(xdrs, &resokp->info.case_preserving)); } bool_t xdr_COMMIT3args(XDR *xdrs, COMMIT3args *objp) { if (xdrs->x_op == XDR_FREE) return (TRUE); switch (xdrs->x_op) { case XDR_FREE: case XDR_ENCODE: if (!xdr_nfs_fh3(xdrs, &objp->file)) return (FALSE); break; case XDR_DECODE: if (!xdr_nfs_fh3_server(xdrs, &objp->file)) return (FALSE); break; } if (!xdr_u_longlong_t(xdrs, &objp->offset)) return (FALSE); return (xdr_u_int(xdrs, &objp->count)); } bool_t xdr_COMMIT3res(XDR *xdrs, COMMIT3res *objp) { COMMIT3resok *resokp; if (!xdr_enum(xdrs, (enum_t *)&objp->status)) return (FALSE); if (objp->status != NFS3_OK) return (xdr_wcc_data(xdrs, &objp->resfail.file_wcc)); /* xdr_COMMIT3resok */ resokp = &objp->resok; if (!xdr_wcc_data(xdrs, &resokp->file_wcc)) return (FALSE); /* * writeverf3 is really an opaque 8 byte * quantity, but we will treat it as a * hyper for efficiency, the cost of * a byteswap here saves bcopys elsewhere */ return (xdr_u_longlong_t(xdrs, &resokp->verf)); }