From: NeilBrown 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 Signed-off-by: Neil Brown --- 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; }; _