aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfsxdr.c
diff options
context:
space:
mode:
authorPeter Staubach <staubach@redhat.com>2007-05-09 05:34:48 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-09 15:30:54 -0400
commitf34b95689d2ce001c157b1604289ff240b4bdee0 (patch)
treee249e166e3c66656ad1b5ac895da6e4c207830e1 /fs/nfsd/nfsxdr.c
parent8842c9655b2b7f0e8e6c50a773b649e5d8a57678 (diff)
The NFSv2/NFSv3 server does not handle zero length WRITE requests correctly
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. Signed-off-by: Peter Staubach <staubach@redhat.com> Acked-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nfsd/nfsxdr.c')
-rw-r--r--fs/nfsd/nfsxdr.c46
1 files changed, 39 insertions, 7 deletions
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index 0c24b9e24fe8..6035e03655c6 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -284,8 +284,9 @@ int
284nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, 284nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
285 struct nfsd_writeargs *args) 285 struct nfsd_writeargs *args)
286{ 286{
287 unsigned int len; 287 unsigned int len, hdr, dlen;
288 int v; 288 int v;
289
289 if (!(p = decode_fh(p, &args->fh))) 290 if (!(p = decode_fh(p, &args->fh)))
290 return 0; 291 return 0;
291 292
@@ -293,11 +294,42 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
293 args->offset = ntohl(*p++); /* offset */ 294 args->offset = ntohl(*p++); /* offset */
294 p++; /* totalcount */ 295 p++; /* totalcount */
295 len = args->len = ntohl(*p++); 296 len = args->len = ntohl(*p++);
296 rqstp->rq_vec[0].iov_base = (void*)p; 297 /*
297 rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - 298 * The protocol specifies a maximum of 8192 bytes.
298 (((void*)p) - rqstp->rq_arg.head[0].iov_base); 299 */
299 if (len > NFSSVC_MAXBLKSIZE_V2) 300 if (len > NFSSVC_MAXBLKSIZE_V2)
300 len = NFSSVC_MAXBLKSIZE_V2; 301 return 0;
302
303 /*
304 * Check to make sure that we got the right number of
305 * bytes.
306 *
307 * If more than one page was used, then compute the length
308 * of the data in the request as the total size of the
309 * request minus the transport protocol headers minus the
310 * RPC protocol headers minus the NFS protocol fields
311 * already consumed. If the request fits into a single
312 * page, then compete the length of the data as the size
313 * of the NFS portion of the request minus the NFS
314 * protocol fields already consumed.
315 */
316 hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
317 if (rqstp->rq_respages != rqstp->rq_pages + 1) {
318 dlen = rqstp->rq_arg.len -
319 (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr;
320 } else {
321 dlen = rqstp->rq_arg.head[0].iov_len - hdr;
322 }
323 /*
324 * Round the length of the data which was specified up to
325 * the next multiple of XDR units and then compare that
326 * against the length which was actually received.
327 */
328 if (dlen != ((len + 3) & ~0x3))
329 return 0;
330
331 rqstp->rq_vec[0].iov_base = (void*)p;
332 rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
301 v = 0; 333 v = 0;
302 while (len > rqstp->rq_vec[v].iov_len) { 334 while (len > rqstp->rq_vec[v].iov_len) {
303 len -= rqstp->rq_vec[v].iov_len; 335 len -= rqstp->rq_vec[v].iov_len;
@@ -306,8 +338,8 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
306 rqstp->rq_vec[v].iov_len = PAGE_SIZE; 338 rqstp->rq_vec[v].iov_len = PAGE_SIZE;
307 } 339 }
308 rqstp->rq_vec[v].iov_len = len; 340 rqstp->rq_vec[v].iov_len = len;
309 args->vlen = v+1; 341 args->vlen = v + 1;
310 return rqstp->rq_vec[0].iov_len > 0; 342 return 1;
311} 343}
312 344
313int 345int