diff options
Diffstat (limited to 'fs/nfsd/nfs3xdr.c')
-rw-r--r-- | fs/nfsd/nfs3xdr.c | 48 |
1 files changed, 38 insertions, 10 deletions
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 7e4bb0af24d7..43fb360784b7 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c | |||
@@ -369,7 +369,7 @@ int | |||
369 | nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, | 369 | nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, |
370 | struct nfsd3_writeargs *args) | 370 | struct nfsd3_writeargs *args) |
371 | { | 371 | { |
372 | unsigned int len, v, hdr; | 372 | unsigned int len, v, hdr, dlen; |
373 | u32 max_blocksize = svc_max_payload(rqstp); | 373 | u32 max_blocksize = svc_max_payload(rqstp); |
374 | 374 | ||
375 | if (!(p = decode_fh(p, &args->fh)) | 375 | if (!(p = decode_fh(p, &args->fh)) |
@@ -379,18 +379,47 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, | |||
379 | args->count = ntohl(*p++); | 379 | args->count = ntohl(*p++); |
380 | args->stable = ntohl(*p++); | 380 | args->stable = ntohl(*p++); |
381 | len = args->len = ntohl(*p++); | 381 | len = args->len = ntohl(*p++); |
382 | /* | ||
383 | * The count must equal the amount of data passed. | ||
384 | */ | ||
385 | if (args->count != args->len) | ||
386 | return 0; | ||
382 | 387 | ||
388 | /* | ||
389 | * Check to make sure that we got the right number of | ||
390 | * bytes. | ||
391 | * | ||
392 | * If more than one page was used, then compute the length | ||
393 | * of the data in the request as the total size of the | ||
394 | * request minus the transport protocol headers minus the | ||
395 | * RPC protocol headers minus the NFS protocol fields | ||
396 | * already consumed. If the request fits into a single | ||
397 | * page, then compete the length of the data as the size | ||
398 | * of the NFS portion of the request minus the NFS | ||
399 | * protocol fields already consumed. | ||
400 | */ | ||
383 | hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; | 401 | hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; |
384 | if (rqstp->rq_arg.len < hdr || | 402 | if (rqstp->rq_respages != rqstp->rq_pages + 1) { |
385 | rqstp->rq_arg.len - hdr < len) | 403 | dlen = rqstp->rq_arg.len - |
404 | (PAGE_SIZE - rqstp->rq_arg.head[0].iov_len) - hdr; | ||
405 | } else { | ||
406 | dlen = rqstp->rq_arg.head[0].iov_len - hdr; | ||
407 | } | ||
408 | /* | ||
409 | * Round the length of the data which was specified up to | ||
410 | * the next multiple of XDR units and then compare that | ||
411 | * against the length which was actually received. | ||
412 | */ | ||
413 | if (dlen != ((len + 3) & ~0x3)) | ||
386 | return 0; | 414 | return 0; |
387 | 415 | ||
416 | if (args->count > max_blocksize) { | ||
417 | args->count = max_blocksize; | ||
418 | len = args->len = max_blocksize; | ||
419 | } | ||
388 | rqstp->rq_vec[0].iov_base = (void*)p; | 420 | rqstp->rq_vec[0].iov_base = (void*)p; |
389 | rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; | 421 | rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; |
390 | 422 | v = 0; | |
391 | if (len > max_blocksize) | ||
392 | len = max_blocksize; | ||
393 | v= 0; | ||
394 | while (len > rqstp->rq_vec[v].iov_len) { | 423 | while (len > rqstp->rq_vec[v].iov_len) { |
395 | len -= rqstp->rq_vec[v].iov_len; | 424 | len -= rqstp->rq_vec[v].iov_len; |
396 | v++; | 425 | v++; |
@@ -398,9 +427,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, | |||
398 | rqstp->rq_vec[v].iov_len = PAGE_SIZE; | 427 | rqstp->rq_vec[v].iov_len = PAGE_SIZE; |
399 | } | 428 | } |
400 | rqstp->rq_vec[v].iov_len = len; | 429 | rqstp->rq_vec[v].iov_len = len; |
401 | args->vlen = v+1; | 430 | args->vlen = v + 1; |
402 | 431 | return 1; | |
403 | return args->count == args->len && rqstp->rq_vec[0].iov_len > 0; | ||
404 | } | 432 | } |
405 | 433 | ||
406 | int | 434 | int |