Introduce a new set of XDR encoding/decoding functions for NFSv2 basic types that operate on xdr_streams. Use coding style similar to what's in fs/nfs/nfs4xdr.c. Signed-off-by: Chuck Lever <chuck.lever@xxxxxxxxxx> --- fs/nfs/nfs2xdr.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 342 insertions(+), 0 deletions(-) diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 279883c..9d5159f 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -3,6 +3,11 @@ * * XDR functions to encode/decode NFS RPC arguments and results. * + * NFSv2 argument and result types are defined in section 2.2 of + * RFC 1094: "NFS: Network File System Protocol Specification". + * Basic data types are defined in section 2.3 of RFC 1094. + * + * * Copyright (C) 1992, 1993, 1994 Rick Sladkey * Copyright (C) 1996 Olaf Kirch * 04 Aug 1998 Ion Badulescu <ionut@xxxxxxxxxxxxxxx> @@ -61,6 +66,343 @@ #define NFS_readdirres_sz (1) #define NFS_statfsres_sz (1+NFS_info_sz) +static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) +{ + dprintk("NFS: %s: prematurely hit end of receive buffer. " + "Remaining buffer length is %tu workds.\n", + func, xdr->end - xdr->p); +} + +/* + * Encode/decode NFSv2 basic data types + * + * Not all basic data types have their own encoding and decoding + * functions. For run-time efficiency, some data types are encoded + * or decoded inline. + */ + +/* + * 2.3.1. stat + * + * enum stat { + * NFS_OK = 0, + * ... + * } + */ +static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) +{ + __be32 *p; + + p = xdr_inline_decode(xdr, 4); + if (unlikely(p == NULL)) { + print_overflow_msg(__func__, xdr); + return -EIO; + } + *status = be32_to_cpup(p); + return 0; +} + +/* + * 2.3.3. fhandle + * + * typedef opaque fhandle[FHSIZE]; + */ +static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) +{ + __be32 *p; + + BUG_ON(unlikely(fh->size != NFS2_FHSIZE)); + p = xdr_reserve_space(xdr, NFS2_FHSIZE); + BUG_ON(unlikely(p == NULL)); + xdr_encode_opaque_fixed(p, &fh->data, NFS2_FHSIZE); +} + +static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) +{ + __be32 *p; + + p = xdr_inline_decode(xdr, NFS2_FHSIZE); + if (unlikely(p == NULL)) { + print_overflow_msg(__func__, xdr); + return -EIO; + } + memcpy(&fh->data, p, NFS2_FHSIZE); + fh->size = NFS2_FHSIZE; + return 0; +} + +/* + * 2.3.4. timeval + * + * struct timeval { + * unsigned int seconds; + * unsigned int useconds; + * }; + */ +static void encode_timeval(struct xdr_stream *xdr, const struct timespec *timep) +{ + __be32 *p; + u32 usec; + + p = xdr_reserve_space(xdr, 8); + BUG_ON(unlikely(p == NULL)); + usec = timep->tv_nsec ? (timep->tv_nsec / NSEC_PER_USEC) : 0; + *p++ = cpu_to_be32(timep->tv_sec); + *p = cpu_to_be32(usec); +} + +/* + * Passing the invalid value useconds=1000000 is a Sun convention + * for "set to current server time". It's needed to make + * permissions checks for the "touch" program across v2 mounts to + * Solaris and Irix servers work correctly. Note that this is not + * discussed in RFC 1094; see description of sattr in section 6.1 + * of "NFS Illustrated" by Brent Callaghan, Addison-Wesley, + * ISBN 0-201-32750-5 + */ +static void encode_current_server_timeval(struct xdr_stream *xdr, + const struct timespec *timep) +{ + __be32 *p; + + p = xdr_reserve_space(xdr, 8); + BUG_ON(unlikely(p == NULL)); + *p++ = cpu_to_be32(timep->tv_sec); + *p = cpu_to_be32(1000000); +} + +/* + * 2.3.5. fattr + * + * struct fattr { + * ftype type; + * unsigned int mode; + * unsigned int nlink; + * unsigned int uid; + * unsigned int gid; + * unsigned int size; + * unsigned int blocksize; + * unsigned int rdev; + * unsigned int blocks; + * unsigned int fsid; + * unsigned int fileid; + * timeval atime; + * timeval mtime; + * timeval ctime; + * }; + * + */ +static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) +{ + u32 rdev, type; + __be32 *p; + + p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); + if (unlikely(p == NULL)) { + print_overflow_msg(__func__, xdr); + return -EIO; + } + + fattr->valid |= NFS_ATTR_FATTR; + + type = be32_to_cpup(p++); /* 2.3.2. ftype */ + if (unlikely(type > NF2FIFO)) + type = NFBAD; + + fattr->mode = be32_to_cpup(p++); + fattr->nlink = be32_to_cpup(p++); + fattr->uid = be32_to_cpup(p++); + fattr->gid = be32_to_cpup(p++); + fattr->size = be32_to_cpup(p++); + fattr->du.nfs2.blocksize = be32_to_cpup(p++); + rdev = be32_to_cpup(p++); + fattr->rdev = new_decode_dev(rdev); + fattr->du.nfs2.blocks = be32_to_cpup(p++); + fattr->fsid.major = be32_to_cpup(p++); + fattr->fsid.minor = 0; + fattr->fileid = be32_to_cpup(p++); + + /* 2.3.4. timeval */ + fattr->atime.tv_sec = be32_to_cpup(p++); + fattr->atime.tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; + + /* 2.3.4. timeval */ + fattr->mtime.tv_sec = be32_to_cpup(p++); + fattr->mtime.tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; + + /* 2.3.4. timeval */ + fattr->ctime.tv_sec = be32_to_cpup(p++); + fattr->ctime.tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; + + /* + * RFC 1094 section 2.3.2 suggests that NF2SOCK and NF2FIFO + * are not supported by version 2 of the protocol. + */ + if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) { + fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; + fattr->rdev = 0; + } + + return 0; +} + +/* + * 2.3.6. sattr + * + * struct sattr { + * unsigned int mode; + * unsigned int uid; + * unsigned int gid; + * unsigned int size; + * timeval atime; + * timeval mtime; + * }; + */ + +#define NFS2_SATTR_NOT_SET (0xffffffff) + +static void encode_time_not_set(struct xdr_stream *xdr) +{ + __be32 *p; + + p = xdr_reserve_space(xdr, 8); + BUG_ON(unlikely(p == NULL)); + *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); + *p = cpu_to_be32(NFS2_SATTR_NOT_SET); +} + +static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) +{ + unsigned int valid = attr->ia_valid; + __be32 *p; + + p = xdr_reserve_space(xdr, 16); + BUG_ON(unlikely(p == NULL)); + if (valid & ATTR_MODE) + *p++ = cpu_to_be32(attr->ia_mode); + else + *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); + if (valid & ATTR_UID) + *p++ = cpu_to_be32(attr->ia_uid); + else + *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); + if (valid & ATTR_GID) + *p++ = cpu_to_be32(attr->ia_gid); + else + *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); + if (valid & ATTR_SIZE) + *p++ = cpu_to_be32((u32)attr->ia_size); + else + *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); + + if (valid & ATTR_ATIME_SET) + encode_timeval(xdr, &attr->ia_atime); + else if (valid & ATTR_ATIME) + encode_current_server_timeval(xdr, &attr->ia_atime); + else + encode_time_not_set(xdr); + if (valid & ATTR_MTIME_SET) + encode_timeval(xdr, &attr->ia_mtime); + else if (valid & ATTR_MTIME) + encode_current_server_timeval(xdr, &attr->ia_mtime); + else + encode_time_not_set(xdr); +} + +/* + * 2.3.7. filename + * + * typedef string filename<MAXNAMLEN>; + */ +static void encode_filename(struct xdr_stream *xdr, const char *name, u32 count) +{ + __be32 *p; + + BUG_ON(unlikely(count > NFS2_MAXNAMLEN)); + p = xdr_reserve_space(xdr, 4 + count); + BUG_ON(unlikely(p == NULL)); + *p++ = cpu_to_be32(count); + xdr_encode_opaque_fixed(p, name, count); +} + +/* + * 2.3.8. path + * + * typedef string path<MAXPATHLEN>; + */ +static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 count) +{ + __be32 *p; + + BUG_ON(count > NFS_MAXPATHLEN); + p = xdr_reserve_space(xdr, 4); + BUG_ON(unlikely(p == NULL)); + *p = cpu_to_be32(count); + xdr_write_pages(xdr, pages, 0, count); +} + +static int decode_path(struct xdr_stream *xdr) +{ + u32 recvd, count; + size_t hdrlen; + __be32 *p; + + p = xdr_inline_decode(xdr, 4); + if (unlikely(p == NULL)) { + print_overflow_msg(__func__, xdr); + return -EIO; + } + count = be32_to_cpup(p); + + if (count >= xdr->buf->page_len || count > NFS_MAXPATHLEN) { + dprintk("NFS: server returned giant pathname!\n"); + return -ENAMETOOLONG; + } + hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; + recvd = xdr->buf->len - hdrlen; + if (count > recvd) { + dprintk("NFS: server cheating in pathname result: " + "count %u > recvd %u\n", count, recvd); + return -EIO; + } + + xdr_read_pages(xdr, count); + xdr_terminate_string(xdr->buf, count); + return 0; +} + +/* + * 2.3.10. diropargs + * + * struct diropargs { + * fhandle dir; + * filename name; + * }; + */ +static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh, + const char *name, u32 count) +{ + encode_fhandle(xdr, fh); + encode_filename(xdr, name, count); +} + +/* + * 2.3.11. diropok + * + * struct { + * fhandle file; + * fattr attributes; + * } diropok; + */ +static int decode_diropok(struct xdr_stream *xdr, struct nfs_fh *fh, + struct nfs_fattr *fattr) +{ + if (decode_fhandle(xdr, fh) != 0) + return -EIO; + return decode_fattr(xdr, fattr); +} + + /* * Common NFS XDR functions as inlines */ -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html