Commit 0b1d57cf authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] kNFSd: Fix nfs3 dentry encoding

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: default avatarNeil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c6453d0a
...@@ -436,7 +436,6 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, ...@@ -436,7 +436,6 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
resp->buflen = count; resp->buflen = count;
resp->common.err = nfs_ok; resp->common.err = nfs_ok;
resp->buffer = argp->buffer; resp->buffer = argp->buffer;
resp->offset = NULL;
resp->rqstp = rqstp; resp->rqstp = rqstp;
nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie,
&resp->common, nfs3svc_encode_entry); &resp->common, nfs3svc_encode_entry);
......
...@@ -847,8 +847,18 @@ encode_entry(struct readdir_cd *ccd, const char *name, ...@@ -847,8 +847,18 @@ encode_entry(struct readdir_cd *ccd, const char *name,
int elen; /* estimated entry length in words */ int elen; /* estimated entry length in words */
int num_entry_words = 0; /* actual number of words */ int num_entry_words = 0; /* actual number of words */
if (cd->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); xdr_encode_hyper(cd->offset, (u64) offset);
}
}
/* /*
dprintk("encode_entry(%.*s @%ld%s)\n", dprintk("encode_entry(%.*s @%ld%s)\n",
...@@ -929,17 +939,32 @@ encode_entry(struct readdir_cd *ccd, const char *name, ...@@ -929,17 +939,32 @@ encode_entry(struct readdir_cd *ccd, const char *name,
/* update offset */ /* update offset */
cd->offset = cd->buffer + (cd->offset - tmp); cd->offset = cd->buffer + (cd->offset - tmp);
} else { } 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; len2 = (num_entry_words << 2) - len1;
/* move from temp page to current and next pages */ /* move from temp page to current and next pages */
memmove(p, tmp, len1); memmove(p, tmp, len1);
memmove(tmp, (caddr_t)tmp+len1, len2); 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); p = tmp + (len2 >> 2);
} }
} }
......
...@@ -170,6 +170,7 @@ struct nfsd3_readdirres { ...@@ -170,6 +170,7 @@ struct nfsd3_readdirres {
u32 * buffer; u32 * buffer;
int buflen; int buflen;
u32 * offset; u32 * offset;
u32 * offset1;
struct svc_rqst * rqstp; struct svc_rqst * rqstp;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment