Skip to content

Commit 0b1d57c

Browse files
Neil BrownLinus Torvalds
authored andcommitted
[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: Neil Brown <neilb@cse.unsw.edu.au> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
1 parent c6453d0 commit 0b1d57c

File tree

3 files changed

+33
-8
lines changed

3 files changed

+33
-8
lines changed

fs/nfsd/nfs3proc.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,6 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
436436
resp->buflen = count;
437437
resp->common.err = nfs_ok;
438438
resp->buffer = argp->buffer;
439-
resp->offset = NULL;
440439
resp->rqstp = rqstp;
441440
nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie,
442441
&resp->common, nfs3svc_encode_entry);

fs/nfsd/nfs3xdr.c

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -847,8 +847,18 @@ encode_entry(struct readdir_cd *ccd, const char *name,
847847
int elen; /* estimated entry length in words */
848848
int num_entry_words = 0; /* actual number of words */
849849

850-
if (cd->offset)
851-
xdr_encode_hyper(cd->offset, (u64) offset);
850+
if (cd->offset) {
851+
u64 offset64 = offset;
852+
853+
if (unlikely(cd->offset1)) {
854+
/* we ended up with offset on a page boundary */
855+
*cd->offset = htonl(offset64 >> 32);
856+
*cd->offset1 = htonl(offset64 & 0xffffffff);
857+
cd->offset1 = NULL;
858+
} else {
859+
xdr_encode_hyper(cd->offset, (u64) offset);
860+
}
861+
}
852862

853863
/*
854864
dprintk("encode_entry(%.*s @%ld%s)\n",
@@ -929,17 +939,32 @@ encode_entry(struct readdir_cd *ccd, const char *name,
929939
/* update offset */
930940
cd->offset = cd->buffer + (cd->offset - tmp);
931941
} else {
942+
unsigned int offset_r = (cd->offset - tmp) << 2;
943+
944+
/* update pointer to offset location.
945+
* This is a 64bit quantity, so we need to
946+
* deal with 3 cases:
947+
* - entirely in first page
948+
* - entirely in second page
949+
* - 4 bytes in each page
950+
*/
951+
if (offset_r + 8 <= len1) {
952+
cd->offset = p + (cd->offset - tmp);
953+
} else if (offset_r >= len1) {
954+
cd->offset -= len1 >> 2;
955+
} else {
956+
/* sitting on the fence */
957+
BUG_ON(offset_r != len1 - 4);
958+
cd->offset = p + (cd->offset - tmp);
959+
cd->offset1 = tmp;
960+
}
961+
932962
len2 = (num_entry_words << 2) - len1;
933963

934964
/* move from temp page to current and next pages */
935965
memmove(p, tmp, len1);
936966
memmove(tmp, (caddr_t)tmp+len1, len2);
937967

938-
/* update offset */
939-
if (((cd->offset - tmp) << 2) < len1)
940-
cd->offset = p + (cd->offset - tmp);
941-
else
942-
cd->offset -= len1 >> 2;
943968
p = tmp + (len2 >> 2);
944969
}
945970
}

include/linux/nfsd/xdr3.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ struct nfsd3_readdirres {
170170
u32 * buffer;
171171
int buflen;
172172
u32 * offset;
173+
u32 * offset1;
173174
struct svc_rqst * rqstp;
174175

175176
};

0 commit comments

Comments
 (0)