From: NeilBrown <neilb@cse.unsw.edu.au>

The "offset" in an entry in an nfs3 readdir response is 64 bits long and as it
has only a 32 bit alignment, it fall half in one page of the response and half
in another.

This patch adds a second offset pointer (offset1) which points to the second
half in the unusual case of the offset being split between pages, and sets and
uses it accordingly.

From: Olaf Kirch <okir@suse.de>
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
---

 25-akpm/fs/nfsd/nfs3proc.c        |    1 
 25-akpm/fs/nfsd/nfs3xdr.c         |   39 +++++++++++++++++++++++++++++++-------
 25-akpm/include/linux/nfsd/xdr3.h |    1 
 3 files changed, 33 insertions(+), 8 deletions(-)

diff -puN fs/nfsd/nfs3proc.c~knfsd-1-of-11-fix-nfs3-dentry-encoding fs/nfsd/nfs3proc.c
--- 25/fs/nfsd/nfs3proc.c~knfsd-1-of-11-fix-nfs3-dentry-encoding	2004-05-28 00:07:57.078434328 -0700
+++ 25-akpm/fs/nfsd/nfs3proc.c	2004-05-28 00:07:57.085433264 -0700
@@ -436,7 +436,6 @@ nfsd3_proc_readdir(struct svc_rqst *rqst
 	resp->buflen = count;
 	resp->common.err = nfs_ok;
 	resp->buffer = argp->buffer;
-	resp->offset = NULL;
 	resp->rqstp = rqstp;
 	nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, 
 					&resp->common, nfs3svc_encode_entry);
diff -puN fs/nfsd/nfs3xdr.c~knfsd-1-of-11-fix-nfs3-dentry-encoding fs/nfsd/nfs3xdr.c
--- 25/fs/nfsd/nfs3xdr.c~knfsd-1-of-11-fix-nfs3-dentry-encoding	2004-05-28 00:07:57.080434024 -0700
+++ 25-akpm/fs/nfsd/nfs3xdr.c	2004-05-28 00:07:57.086433112 -0700
@@ -847,8 +847,18 @@ encode_entry(struct readdir_cd *ccd, con
 	int		elen;		/* estimated entry length in words */
 	int		num_entry_words = 0;	/* actual number of words */
 
-	if (cd->offset)
-		xdr_encode_hyper(cd->offset, (u64) offset);
+	if (cd->offset) {
+		u64 offset64 = offset;
+
+		if (unlikely(cd->offset1)) {
+			/* we ended up with offset on a page boundary */
+			*cd->offset = htonl(offset64 >> 32);
+			*cd->offset1 = htonl(offset64 & 0xffffffff);
+			cd->offset1 = NULL;
+		} else {
+			xdr_encode_hyper(cd->offset, (u64) offset);
+		}
+	}
 
 	/*
 	dprintk("encode_entry(%.*s @%ld%s)\n",
@@ -929,17 +939,32 @@ encode_entry(struct readdir_cd *ccd, con
 			/* update offset */
 			cd->offset = cd->buffer + (cd->offset - tmp);
 		} else {
+			unsigned int offset_r = (cd->offset - tmp) << 2;
+
+			/* update pointer to offset location.
+			 * This is a 64bit quantity, so we need to
+			 * deal with 3 cases:
+			 *  -	entirely in first page
+			 *  -	entirely in second page
+			 *  -	4 bytes in each page
+			 */
+			if (offset_r + 8 <= len1) {
+				cd->offset = p + (cd->offset - tmp);
+			} else if (offset_r >= len1) {
+				cd->offset -= len1 >> 2;
+			} else {
+				/* sitting on the fence */
+				BUG_ON(offset_r != len1 - 4);
+				cd->offset = p + (cd->offset - tmp);
+				cd->offset1 = tmp;
+			}
+
 			len2 = (num_entry_words << 2) - len1;
 
 			/* move from temp page to current and next pages */
 			memmove(p, tmp, len1);
 			memmove(tmp, (caddr_t)tmp+len1, len2);
 
-			/* update offset */
-			if (((cd->offset - tmp) << 2) < len1)
-				cd->offset = p + (cd->offset - tmp);
-			else
-				cd->offset -= len1 >> 2;
 			p = tmp + (len2 >> 2);
 		}
 	}
diff -puN include/linux/nfsd/xdr3.h~knfsd-1-of-11-fix-nfs3-dentry-encoding include/linux/nfsd/xdr3.h
--- 25/include/linux/nfsd/xdr3.h~knfsd-1-of-11-fix-nfs3-dentry-encoding	2004-05-28 00:07:57.081433872 -0700
+++ 25-akpm/include/linux/nfsd/xdr3.h	2004-05-28 00:07:57.087432960 -0700
@@ -170,6 +170,7 @@ struct nfsd3_readdirres {
 	u32 *			buffer;
 	int			buflen;
 	u32 *			offset;
+	u32 *			offset1;
 	struct svc_rqst *	rqstp;
 
 };
_