Skip to contentSkip to conversation

nfsd4: fix rd_dircount enforcement

PATCH In review J. Bruce Fields 2 participants 3 messages Aug 29, 2014

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 nfsd4: fix rd_dircount enforcement

From: "J. Bruce Fields" <[email protected]>

Commit 3b299709091b "nfsd4: enforce rd_dircount" totally misunderstood rd_dircount; it refers to total non-attribute bytes returned, not number of directory entries returned.

Bring the code into agreement with RFC 3530 section 14.2.24.

---
 fs/nfsd/nfs4xdr.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
fs/nfsd/nfs4xdr.c +13 -1
index f9821ce6658a..e94457c33ad6 100644
@@ -2657,6 +2657,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
 	struct xdr_stream *xdr = cd->xdr;
 	int start_offset = xdr->buf->len;
 	int cookie_offset;
+	u32 name_and_cookie;
 	int entry_bytes;
 	__be32 nfserr = nfserr_toosmall;
 	__be64 wire_offset;
@@ -2718,7 +2719,14 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
 	cd->rd_maxcount -= entry_bytes;
 	if (!cd->rd_dircount)
 		goto fail;
-	cd->rd_dircount--;
+	/*
+	 * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so
+	 * let's always let through the first entry, at least:
+	 */
+	name_and_cookie = 4 * XDR_QUADLEN(namlen) + 8;
+	if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
+		goto fail;
+	cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
 	cd->cookie_offset = cookie_offset;
 skip_entry:
 	cd->common.err = nfs_ok;
@@ -3321,6 +3329,10 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
 	}
 	maxcount = min_t(int, maxcount-16, bytes_left);
 
+	/* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
+	if (!readdir->rd_dircount)
+		readdir->rd_dircount = INT_MAX;
+
 	readdir->xdr = xdr;
 	readdir->rd_maxcount = maxcount;
 	readdir->common.err = 0;
Signature · 1 line
1.9.3
#1 lore raw
Quoted 43 lines · J. Bruce Fields

From: "J. Bruce Fields" <[email protected]>

Commit 3b299709091b "nfsd4: enforce rd_dircount" totally misunderstood rd_dircount; it refers to total non-attribute bytes returned, not number of directory entries returned.

Bring the code into agreement with RFC 3530 section 14.2.24.

---
 fs/nfsd/nfs4xdr.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
fs/nfsd/nfs4xdr.c +9 -1
index f9821ce6658a..e94457c33ad6 100644
@@ -2657,6 +2657,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
        struct xdr_stream *xdr = cd->xdr;
        int start_offset = xdr->buf->len;
        int cookie_offset;
+       u32 name_and_cookie;
        int entry_bytes;
        __be32 nfserr = nfserr_toosmall;
        __be64 wire_offset;
@@ -2718,7 +2719,14 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
        cd->rd_maxcount -= entry_bytes;
        if (!cd->rd_dircount)
                goto fail;
-       cd->rd_dircount--;
+       /*
+        * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so
+        * let's always let through the first entry, at least:
+        */
+       name_and_cookie = 4 * XDR_QUADLEN(namlen) + 8;
+       if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
+               goto fail;
+       cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
        cd->cookie_offset = cookie_offset;
 skip_entry:
        cd->common.err = nfs_ok;

fs/nfsd/nfs4xdr.c nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 · line 3335

Quoted 7 lines
fs/nfsd/nfs4xdr.c +4 -0
@@ -3321,6 +3329,10 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
        }
        maxcount = min_t(int, maxcount-16, bytes_left);
 
+       /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
+       if (!readdir->rd_dircount)
+               readdir->rd_dircount = INT_MAX;
+

Ah... Time to change the Linux client to always set this value to 0. It really is useless in our case.

Signature · 5 lines
Trond Myklebust

Linux NFS client maintainer, PrimaryData

[email protected]
#2 lore raw
Quoted 47 lines · Trond Myklebust
Quoted 46 lines · J. Bruce Fields

From: "J. Bruce Fields" <[email protected]>

Commit 3b299709091b "nfsd4: enforce rd_dircount" totally misunderstood rd_dircount; it refers to total non-attribute bytes returned, not number of directory entries returned.

Bring the code into agreement with RFC 3530 section 14.2.24.

---
 fs/nfsd/nfs4xdr.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
fs/nfsd/nfs4xdr.c +9 -1
index f9821ce6658a..e94457c33ad6 100644
@@ -2657,6 +2657,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
        struct xdr_stream *xdr = cd->xdr;
        int start_offset = xdr->buf->len;
        int cookie_offset;
+       u32 name_and_cookie;
        int entry_bytes;
        __be32 nfserr = nfserr_toosmall;
        __be64 wire_offset;
@@ -2718,7 +2719,14 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
        cd->rd_maxcount -= entry_bytes;
        if (!cd->rd_dircount)
                goto fail;
-       cd->rd_dircount--;
+       /*
+        * RFC 3530 14.2.24 describes rd_dircount as only a "hint", so
+        * let's always let through the first entry, at least:
+        */
+       name_and_cookie = 4 * XDR_QUADLEN(namlen) + 8;
+       if (name_and_cookie > cd->rd_dircount && cd->cookie_offset)
+               goto fail;
+       cd->rd_dircount -= min(cd->rd_dircount, name_and_cookie);
        cd->cookie_offset = cookie_offset;
 skip_entry:
        cd->common.err = nfs_ok;
@@ -3321,6 +3329,10 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
        }
        maxcount = min_t(int, maxcount-16, bytes_left);
 
Quoted 7 lines

fs/nfsd/nfs4xdr.c nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 · line 3335

Quoted 4 lines
fs/nfsd/nfs4xdr.c +4 -0
+       /* RFC 3530 14.2.24 allows us to ignore dircount when it's 0: */
+       if (!readdir->rd_dircount)
+               readdir->rd_dircount = INT_MAX;
+

Ah... Time to change the Linux client to always set this value to 0. It really is useless in our case.

I didn't look at the client. Hm:

	uint32_t dircount = readdir->count >> 1
	...
	*p++ = cpu_to_be32(dircount);
	*p++ = cpu_to_be32(readdir->count);

Yeah, that's kind of arbitrary. This dircount thing is just inherently weird. We could just revert the server back to ignoring it completely like it used to--the spec only says it's a "hint", whatever that means. But presumably somebody must have thought they had a use for it.

--b.