patch-2.4.20 linux-2.4.20/fs/nfs/nfs2xdr.c

Next file: linux-2.4.20/fs/nfs/nfs3proc.c
Previous file: linux-2.4.20/fs/nfs/mount_clnt.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/fs/nfs/nfs2xdr.c linux-2.4.20/fs/nfs/nfs2xdr.c
@@ -24,9 +24,6 @@
 #include <linux/nfs2.h>
 #include <linux/nfs_fs.h>
 
-/* Uncomment this to support servers requiring longword lengths */
-#define NFS_PAD_WRITES 1
-
 #define NFSDBG_FACILITY		NFSDBG_XDR
 /* #define NFS_PARANOIA 1 */
 
@@ -90,17 +87,6 @@
 	return p + XDR_QUADLEN(NFS2_FHSIZE);
 }
 
-static inline u32 *
-xdr_decode_string2(u32 *p, char **string, unsigned int *len,
-			unsigned int maxlen)
-{
-	*len = ntohl(*p++);
-	if (*len > maxlen)
-		return NULL;
-	*string = (char *) p;
-	return p + XDR_QUADLEN(*len);
-}
-
 static inline u32*
 xdr_decode_time(u32 *p, u64 *timep)
 {
@@ -109,7 +95,7 @@
 	return p;
 }
 
-static inline u32 *
+static u32 *
 xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
 {
 	fattr->type = (enum nfs_ftype) ntohl(*p++);
@@ -223,35 +209,20 @@
 nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 {
 	struct rpc_auth	*auth = req->rq_task->tk_auth;
-	int		buflen, replen;
-	unsigned int	nr;
+	unsigned int replen;
+	u32 offset = (u32)args->offset;
+	u32 count = args->count;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
-	*p++ = htonl(args->count);
+	*p++ = htonl(offset);
+	*p++ = htonl(count);
+	*p++ = htonl(count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	/* Get the number of buffers in the receive iovec */
-        nr = args->nriov;
-
-        if (nr+2 > MAX_IOVEC) {
-                printk(KERN_ERR "NFS: Bad number of iov's in xdr_readargs\n");
-                return -EINVAL;
-        }
-
-	/* set up reply iovec */
+	/* Inline the page array */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
-	req->rq_rvec[0].iov_len  = replen;
-        /* Copy the iovec */
-        memcpy(req->rq_rvec + 1, args->iov, nr * sizeof(struct iovec));
-
-	req->rq_rvec[nr+1].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[nr+1].iov_len  = buflen - replen;
-	req->rq_rlen = args->count + buflen;
-	req->rq_rnr += nr+1;
-
+	xdr_inline_pages(&req->rq_rcv_buf, replen,
+			 args->pages, args->pgbase, count);
 	return 0;
 }
 
@@ -261,6 +232,7 @@
 static int
 nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
+	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
 	struct iovec *iov = req->rq_rvec;
 	int	status, count, recvd, hdrlen;
 
@@ -269,26 +241,33 @@
 	p = xdr_decode_fattr(p, res->fattr);
 
 	count = ntohl(*p++);
+	res->eof = 0;
+	if (rcvbuf->page_len) {
+		u32 end = page_offset(rcvbuf->pages[0]) + rcvbuf->page_base + count;
+		if (end >= res->fattr->size)
+			res->eof = 1;
+	}
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	if (iov->iov_len > hdrlen) {
+	if (iov->iov_len < hdrlen) {
+		printk(KERN_WARNING "NFS: READ reply header overflowed:"
+				"length %d > %Zu\n", hdrlen, iov->iov_len);
+		return -errno_NFSERR_IO;
+	} else if (iov->iov_len != hdrlen) {
 		dprintk("NFS: READ header is short. iovec will be shifted.\n");
-		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+		xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
 	}
 
-	recvd = req->rq_rlen - hdrlen;
+	recvd = req->rq_received - hdrlen;
 	if (count > recvd) {
 		printk(KERN_WARNING "NFS: server cheating in read reply: "
 			"count %d > recvd %d\n", count, recvd);
 		count = recvd;
+		res->eof = 0;
 	}
 
 	dprintk("RPC:      readres OK count %d\n", count);
-	if (count < res->count) {
-		xdr_zero_iovec(iov+1, req->rq_rnr-2, res->count - count);
+	if (count < res->count)
 		res->count = count;
-		res->eof = 1;  /* Silly NFSv3ism which can't be helped */
-	} else
-		res->eof = 0;
 
 	return count;
 }
@@ -300,46 +279,19 @@
 static int
 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
-	unsigned int nr;
+	struct xdr_buf *sndbuf = &req->rq_snd_buf;
+	u32 offset = (u32)args->offset;
 	u32 count = args->count;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->offset);
-	*p++ = htonl(args->offset);
+	*p++ = htonl(offset);
+	*p++ = htonl(offset);
 	*p++ = htonl(count);
 	*p++ = htonl(count);
-	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-
-	/* Get the number of buffers in the send iovec */
-	nr = args->nriov;
-
-	if (nr+2 > MAX_IOVEC) {
-                printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs "
-                        "(nr %d max %d)\n", nr, MAX_IOVEC);
-                return -EINVAL;
-        }
-
-	/* Copy the iovec */
-        memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
-
-#ifdef NFS_PAD_WRITES
-	/*
-	 * Some old servers require that the message length
-	 * be a multiple of 4, so we pad it here if needed.
-	 */
-	if (count & 3) {
-		struct iovec	*iov = req->rq_svec + nr + 1;
-		int		pad = 4 - (count & 3);
-
-		iov->iov_base = (void *) "\0\0\0";
-		iov->iov_len  = pad;
-		count += pad;
-		nr++;
-	}
-#endif
-	req->rq_slen += count;
-	req->rq_snr += nr;
+	sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
 
+	/* Copy the page array */
+	xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
 	return 0;
 }
 
@@ -406,32 +358,24 @@
 {
 	struct rpc_task	*task = req->rq_task;
 	struct rpc_auth	*auth = task->tk_auth;
-	u32		bufsiz = args->bufsiz;
-	int		buflen, replen;
+	unsigned int replen;
+	u32 count = args->count;
 
 	/*
 	 * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
 	 * to be in longwords ... check whether to convert the size.
 	 */
 	if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
-		bufsiz = bufsiz >> 2;
+		count = count >> 2;
 
 	p = xdr_encode_fhandle(p, args->fh);
 	*p++ = htonl(args->cookie);
-	*p++ = htonl(bufsiz); /* see above */
+	*p++ = htonl(count); /* see above */
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-	/* set up reply iovec */
+	/* Inline the page array */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
-	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->bufsiz;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = buflen + args->bufsiz;
-	req->rq_rnr += 2;
-
+	xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
 	return 0;
 }
 
@@ -443,28 +387,38 @@
  * from nfs_readdir for each entry.
  */
 static int
-nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
+nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, void *dummy)
 {
-	struct iovec		*iov = req->rq_rvec;
-	int			 hdrlen;
-	int			 status, nr;
-	u32			*end, *entry, len;
+	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+	struct iovec *iov = rcvbuf->head;
+	struct page **page;
+	int hdrlen, recvd;
+	int status, nr;
+	unsigned int len, pglen;
+	u32 *end, *entry;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	if (iov->iov_len > hdrlen) {
+	if (iov->iov_len < hdrlen) {
+		printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
+				"length %d > %Zu\n", hdrlen, iov->iov_len);
+		return -errno_NFSERR_IO;
+	} else if (iov->iov_len != hdrlen) {
 		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
-		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
 	}
 
-
-	/* Get start and end address of XDR data */
-	p   = (u32 *) iov[1].iov_base;
-	end = (u32 *) ((u8 *) p + iov[1].iov_len);
+	pglen = rcvbuf->page_len;
+	recvd = req->rq_received - hdrlen;
+	if (pglen > recvd)
+		pglen = recvd;
+	page = rcvbuf->pages;
+	p = kmap(*page);
+	entry = p;
+	end = (u32 *)((char *)p + pglen);
 	for (nr = 0; *p++; nr++) {
-		entry = p - 1;
 		if (p + 2 > end)
 			goto short_pkt;
 		p++; /* fileid */
@@ -473,16 +427,28 @@
 		if (len > NFS2_MAXNAMLEN) {
 			printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
 						len);
-			return -errno_NFSERR_IO;
+			goto err_unmap;
 		}
 		if (p + 2 > end)
 			goto short_pkt;
+		entry = p;
 	}
+	if (!nr && (entry[0] != 0 || entry[1] == 0))
+		goto short_pkt;
+ out:
+	kunmap(*page);
 	return nr;
  short_pkt:
-	printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
 	entry[0] = entry[1] = 0;
-	return nr;
+	/* truncate listing ? */
+	if (!nr) {
+		printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
+		entry[1] = 1;
+	}
+	goto out;
+err_unmap:
+	kunmap(*page);
+	return -errno_NFSERR_IO;
 }
 
 u32 *
@@ -568,21 +534,16 @@
 static int
 nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
 {
-	struct rpc_task *task = req->rq_task;
-	struct rpc_auth *auth = task->tk_auth;
-	int		buflen, replen;
+	struct rpc_auth *auth = req->rq_task->tk_auth;
+	unsigned int replen;
+	u32 count = args->count - 4;
 
 	p = xdr_encode_fhandle(p, args->fh);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+	/* Inline the page array */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
-	buflen = req->rq_rvec[0].iov_len;
-	req->rq_rvec[0].iov_len  = replen;
-	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->bufsiz;
-	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
-	req->rq_rvec[2].iov_len  = buflen - replen;
-	req->rq_rlen = buflen + args->bufsiz;
-	req->rq_rnr += 2;
+	xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
 	return 0;
 }
 
@@ -590,32 +551,33 @@
  * Decode READLINK reply
  */
 static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
 {
-	struct iovec *iov = req->rq_rvec;
-	u32	*strlen;
+	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+	struct iovec *iov = rcvbuf->head;
+	unsigned int hdrlen;
+	u32	*strlen, len;
 	char	*string;
-	int	hdrlen;
 	int	status;
-	unsigned int len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
 	if (iov->iov_len > hdrlen) {
 		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
-		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
 	}
 
-	strlen = (u32*)res->buffer;
+	strlen = (u32*)kmap(rcvbuf->pages[0]);
 	/* Convert length of symlink */
 	len = ntohl(*strlen);
-	if (len > res->bufsiz - 5)
-		len = res->bufsiz - 5;
+	if (len > rcvbuf->page_len)
+		len = rcvbuf->page_len;
 	*strlen = len;
 	/* NULL terminate the string we got */
 	string = (char *)(strlen + 1);
 	string[len] = 0;
+	kunmap(rcvbuf->pages[0]);
 	return 0;
 }
 
@@ -732,33 +694,32 @@
 # define MAX(a, b)	(((a) > (b))? (a) : (b))
 #endif
 
-#define PROC(proc, argtype, restype)	\
-    { "nfs_" #proc,					\
-      (kxdrproc_t) nfs_xdr_##argtype,			\
-      (kxdrproc_t) nfs_xdr_##restype,			\
-      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2,	\
-      0							\
+#define PROC(proc, argtype, restype, timer)				\
+    { .p_procname =  "nfs_" #proc,					\
+      .p_encode   =  (kxdrproc_t) nfs_xdr_##argtype,			\
+      .p_decode   =  (kxdrproc_t) nfs_xdr_##restype,			\
+      .p_bufsiz   =  MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2,	\
+      .p_timer    =  timer						\
     }
-
 static struct rpc_procinfo	nfs_procedures[18] = {
-    PROC(null,		enc_void,	dec_void),
-    PROC(getattr,	fhandle,	attrstat),
-    PROC(setattr,	sattrargs,	attrstat),
-    PROC(root,		enc_void,	dec_void),
-    PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	readlinkargs,	readlinkres),
-    PROC(read,		readargs,	readres),
-    PROC(writecache,	enc_void,	dec_void),
-    PROC(write,		writeargs,	writeres),
-    PROC(create,	createargs,	diropres),
-    PROC(remove,	diropargs,	stat),
-    PROC(rename,	renameargs,	stat),
-    PROC(link,		linkargs,	stat),
-    PROC(symlink,	symlinkargs,	stat),
-    PROC(mkdir,		createargs,	diropres),
-    PROC(rmdir,		diropargs,	stat),
-    PROC(readdir,	readdirargs,	readdirres),
-    PROC(statfs,	fhandle,	statfsres),
+    PROC(null,		enc_void,	dec_void, 0),
+    PROC(getattr,	fhandle,	attrstat, 1),
+    PROC(setattr,	sattrargs,	attrstat, 0),
+    PROC(root,		enc_void,	dec_void, 0),
+    PROC(lookup,	diropargs,	diropres, 2),
+    PROC(readlink,	readlinkargs,	readlinkres, 3),
+    PROC(read,		readargs,	readres, 3),
+    PROC(writecache,	enc_void,	dec_void, 0),
+    PROC(write,		writeargs,	writeres, 4),
+    PROC(create,	createargs,	diropres, 0),
+    PROC(remove,	diropargs,	stat, 0),
+    PROC(rename,	renameargs,	stat, 0),
+    PROC(link,		linkargs,	stat, 0),
+    PROC(symlink,	symlinkargs,	stat, 0),
+    PROC(mkdir,		createargs,	diropres, 0),
+    PROC(rmdir,		diropargs,	stat, 0),
+    PROC(readdir,	readdirargs,	readdirres, 3),
+    PROC(statfs,	fhandle,	statfsres, 0),
 };
 
 struct rpc_version		nfs_version2 = {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)