From: Peter Staubach <staubach@redhat.com>
To: NFS List <nfs@lists.sourceforge.net>
Cc: Neil Brown <neilb@suse.de>, Andrew Morton <akpm@linux-foundation.org>
Subject: [PATCH] the NFSv2/NFSv3 server does not handle zero length WRITE requests correctly
Date: Thu, 15 Feb 2007 11:41:26 -0500 [thread overview]
Message-ID: <45D48D36.4050508@redhat.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 773 bytes --]
Hi.
The NFSv2 and NFSv3 servers do not handle WRITE requests for 0 bytes
correctly. The specifications indicate that the server should accept
the request, but it should mostly turn into a no-op. Currently, the
server will return an XDR decode error, which it should not.
Attached is a patch which addresses this issue. It also adds some
boundary checking to ensure that the request contains as much data
as was requested to be written. It also correctly handles an NFSv3
request which requests to write more data than the server has stated
that it is prepared to handle. Previously, there was some support
which looked like it should work, but wasn't quite right.
Questions or comments?
Thanx...
ps
Signed-off-by: Peter Staubach <staubach@redhat.com>
[-- Attachment #2: nfsxdr.devel --]
[-- Type: text/plain, Size: 4570 bytes --]
--- linux-2.6.20.i686/fs/nfsd/nfsxdr.c.org
+++ linux-2.6.20.i686/fs/nfsd/nfsxdr.c
@@ -271,8 +271,9 @@ int
nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_writeargs *args)
{
- unsigned int len;
+ unsigned int len, hdr, dlen;
int v;
+
if (!(p = decode_fh(p, &args->fh)))
return 0;
@@ -280,11 +281,42 @@ nfssvc_decode_writeargs(struct svc_rqst
args->offset = ntohl(*p++); /* offset */
p++; /* totalcount */
len = args->len = ntohl(*p++);
- rqstp->rq_vec[0].iov_base = (void*)p;
- rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len -
- (((void*)p) - rqstp->rq_arg.head[0].iov_base);
+ /*
+ * The protocol specifies a maximum of 8192 bytes.
+ */
if (len > NFSSVC_MAXBLKSIZE_V2)
- len = NFSSVC_MAXBLKSIZE_V2;
+ return 0;
+
+ /*
+ * Check to make sure that we got the right number of
+ * bytes.
+ *
+ * If more than one page was used, then compute the length
+ * of the data in the request as the total size of the
+ * request minus the transport protocol headers minus the
+ * RPC protocol headers minus the NFS protocol fields
+ * already consumed. If the request fits into a single
+ * page, then compete the length of the data as the size
+ * of the NFS portion of the request minus the NFS
+ * protocol fields already consumed.
+ */
+ hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
+ if (rqstp->rq_respages != rqstp->rq_pages + 1) {
+ dlen = rqstp->rq_arg.len -
+ (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr;
+ } else {
+ dlen = rqstp->rq_arg.head[0].iov_len - hdr;
+ }
+ /*
+ * Round the length of the data which was specified up to
+ * the next multiple of XDR units and then compare that
+ * against the length which was actually received.
+ */
+ if (dlen != ((len + 3) & ~0x3))
+ return 0;
+
+ rqstp->rq_vec[0].iov_base = (void*)p;
+ rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
v = 0;
while (len > rqstp->rq_vec[v].iov_len) {
len -= rqstp->rq_vec[v].iov_len;
@@ -293,8 +325,8 @@ nfssvc_decode_writeargs(struct svc_rqst
rqstp->rq_vec[v].iov_len = PAGE_SIZE;
}
rqstp->rq_vec[v].iov_len = len;
- args->vlen = v+1;
- return rqstp->rq_vec[0].iov_len > 0;
+ args->vlen = v + 1;
+ return 1;
}
int
--- linux-2.6.20.i686/fs/nfsd/nfs3xdr.c.org
+++ linux-2.6.20.i686/fs/nfsd/nfs3xdr.c
@@ -354,7 +354,7 @@ int
nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_writeargs *args)
{
- unsigned int len, v, hdr;
+ unsigned int len, v, hdr, dlen;
u32 max_blocksize = svc_max_payload(rqstp);
if (!(p = decode_fh(p, &args->fh))
@@ -364,18 +364,47 @@ nfs3svc_decode_writeargs(struct svc_rqst
args->count = ntohl(*p++);
args->stable = ntohl(*p++);
len = args->len = ntohl(*p++);
+ /*
+ * The count must equal the amount of data passed.
+ */
+ if (args->count != args->len)
+ return 0;
+ /*
+ * Check to make sure that we got the right number of
+ * bytes.
+ *
+ * If more than one page was used, then compute the length
+ * of the data in the request as the total size of the
+ * request minus the transport protocol headers minus the
+ * RPC protocol headers minus the NFS protocol fields
+ * already consumed. If the request fits into a single
+ * page, then compete the length of the data as the size
+ * of the NFS portion of the request minus the NFS
+ * protocol fields already consumed.
+ */
hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
- if (rqstp->rq_arg.len < hdr ||
- rqstp->rq_arg.len - hdr < len)
+ if (rqstp->rq_respages != rqstp->rq_pages + 1) {
+ dlen = rqstp->rq_arg.len -
+ (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr;
+ } else {
+ dlen = rqstp->rq_arg.head[0].iov_len - hdr;
+ }
+ /*
+ * Round the length of the data which was specified up to
+ * the next multiple of XDR units and then compare that
+ * against the length which was actually received.
+ */
+ if (dlen != ((len + 3) & ~0x3))
return 0;
+ if (args->count > max_blocksize) {
+ args->count = max_blocksize;
+ len = args->len = max_blocksize;
+ }
rqstp->rq_vec[0].iov_base = (void*)p;
rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
-
- if (len > max_blocksize)
- len = max_blocksize;
- v= 0;
+ v = 0;
while (len > rqstp->rq_vec[v].iov_len) {
len -= rqstp->rq_vec[v].iov_len;
v++;
@@ -383,9 +412,8 @@ nfs3svc_decode_writeargs(struct svc_rqst
rqstp->rq_vec[v].iov_len = PAGE_SIZE;
}
rqstp->rq_vec[v].iov_len = len;
- args->vlen = v+1;
-
- return args->count == args->len && rqstp->rq_vec[0].iov_len > 0;
+ args->vlen = v + 1;
+ return 1;
}
int
[-- Attachment #3: Type: text/plain, Size: 345 bytes --]
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
[-- Attachment #4: Type: text/plain, Size: 140 bytes --]
_______________________________________________
NFS maillist - NFS@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/nfs
reply other threads:[~2007-02-15 16:41 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=45D48D36.4050508@redhat.com \
--to=staubach@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=neilb@suse.de \
--cc=nfs@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox