diff options
Diffstat (limited to 'fs/nfs/callback_xdr.c')
-rw-r--r-- | fs/nfs/callback_xdr.c | 105 |
1 files changed, 73 insertions, 32 deletions
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 8e1a2511c8be..db30c0b398b5 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c | |||
@@ -24,10 +24,14 @@ | |||
24 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ | 24 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ |
25 | 4 + 1 + 3) | 25 | 4 + 1 + 3) |
26 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | 26 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) |
27 | #define CB_OP_RECALLSLOT_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | ||
27 | #endif /* CONFIG_NFS_V4_1 */ | 28 | #endif /* CONFIG_NFS_V4_1 */ |
28 | 29 | ||
29 | #define NFSDBG_FACILITY NFSDBG_CALLBACK | 30 | #define NFSDBG_FACILITY NFSDBG_CALLBACK |
30 | 31 | ||
32 | /* Internal error code */ | ||
33 | #define NFS4ERR_RESOURCE_HDR 11050 | ||
34 | |||
31 | typedef __be32 (*callback_process_op_t)(void *, void *); | 35 | typedef __be32 (*callback_process_op_t)(void *, void *); |
32 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); | 36 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); |
33 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); | 37 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); |
@@ -173,7 +177,7 @@ static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op) | |||
173 | __be32 *p; | 177 | __be32 *p; |
174 | p = read_buf(xdr, 4); | 178 | p = read_buf(xdr, 4); |
175 | if (unlikely(p == NULL)) | 179 | if (unlikely(p == NULL)) |
176 | return htonl(NFS4ERR_RESOURCE); | 180 | return htonl(NFS4ERR_RESOURCE_HDR); |
177 | *op = ntohl(*p); | 181 | *op = ntohl(*p); |
178 | return 0; | 182 | return 0; |
179 | } | 183 | } |
@@ -215,10 +219,10 @@ out: | |||
215 | 219 | ||
216 | #if defined(CONFIG_NFS_V4_1) | 220 | #if defined(CONFIG_NFS_V4_1) |
217 | 221 | ||
218 | static unsigned decode_sessionid(struct xdr_stream *xdr, | 222 | static __be32 decode_sessionid(struct xdr_stream *xdr, |
219 | struct nfs4_sessionid *sid) | 223 | struct nfs4_sessionid *sid) |
220 | { | 224 | { |
221 | uint32_t *p; | 225 | __be32 *p; |
222 | int len = NFS4_MAX_SESSIONID_LEN; | 226 | int len = NFS4_MAX_SESSIONID_LEN; |
223 | 227 | ||
224 | p = read_buf(xdr, len); | 228 | p = read_buf(xdr, len); |
@@ -229,12 +233,12 @@ static unsigned decode_sessionid(struct xdr_stream *xdr, | |||
229 | return 0; | 233 | return 0; |
230 | } | 234 | } |
231 | 235 | ||
232 | static unsigned decode_rc_list(struct xdr_stream *xdr, | 236 | static __be32 decode_rc_list(struct xdr_stream *xdr, |
233 | struct referring_call_list *rc_list) | 237 | struct referring_call_list *rc_list) |
234 | { | 238 | { |
235 | uint32_t *p; | 239 | __be32 *p; |
236 | int i; | 240 | int i; |
237 | unsigned status; | 241 | __be32 status; |
238 | 242 | ||
239 | status = decode_sessionid(xdr, &rc_list->rcl_sessionid); | 243 | status = decode_sessionid(xdr, &rc_list->rcl_sessionid); |
240 | if (status) | 244 | if (status) |
@@ -267,13 +271,13 @@ out: | |||
267 | return status; | 271 | return status; |
268 | } | 272 | } |
269 | 273 | ||
270 | static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp, | 274 | static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, |
271 | struct xdr_stream *xdr, | 275 | struct xdr_stream *xdr, |
272 | struct cb_sequenceargs *args) | 276 | struct cb_sequenceargs *args) |
273 | { | 277 | { |
274 | uint32_t *p; | 278 | __be32 *p; |
275 | int i; | 279 | int i; |
276 | unsigned status; | 280 | __be32 status; |
277 | 281 | ||
278 | status = decode_sessionid(xdr, &args->csa_sessionid); | 282 | status = decode_sessionid(xdr, &args->csa_sessionid); |
279 | if (status) | 283 | if (status) |
@@ -327,11 +331,11 @@ out_free: | |||
327 | goto out; | 331 | goto out; |
328 | } | 332 | } |
329 | 333 | ||
330 | static unsigned decode_recallany_args(struct svc_rqst *rqstp, | 334 | static __be32 decode_recallany_args(struct svc_rqst *rqstp, |
331 | struct xdr_stream *xdr, | 335 | struct xdr_stream *xdr, |
332 | struct cb_recallanyargs *args) | 336 | struct cb_recallanyargs *args) |
333 | { | 337 | { |
334 | uint32_t *p; | 338 | __be32 *p; |
335 | 339 | ||
336 | args->craa_addr = svc_addr(rqstp); | 340 | args->craa_addr = svc_addr(rqstp); |
337 | p = read_buf(xdr, 4); | 341 | p = read_buf(xdr, 4); |
@@ -346,6 +350,20 @@ static unsigned decode_recallany_args(struct svc_rqst *rqstp, | |||
346 | return 0; | 350 | return 0; |
347 | } | 351 | } |
348 | 352 | ||
353 | static __be32 decode_recallslot_args(struct svc_rqst *rqstp, | ||
354 | struct xdr_stream *xdr, | ||
355 | struct cb_recallslotargs *args) | ||
356 | { | ||
357 | __be32 *p; | ||
358 | |||
359 | args->crsa_addr = svc_addr(rqstp); | ||
360 | p = read_buf(xdr, 4); | ||
361 | if (unlikely(p == NULL)) | ||
362 | return htonl(NFS4ERR_BADXDR); | ||
363 | args->crsa_target_max_slots = ntohl(*p++); | ||
364 | return 0; | ||
365 | } | ||
366 | |||
349 | #endif /* CONFIG_NFS_V4_1 */ | 367 | #endif /* CONFIG_NFS_V4_1 */ |
350 | 368 | ||
351 | static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) | 369 | static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) |
@@ -465,7 +483,7 @@ static __be32 encode_op_hdr(struct xdr_stream *xdr, uint32_t op, __be32 res) | |||
465 | 483 | ||
466 | p = xdr_reserve_space(xdr, 8); | 484 | p = xdr_reserve_space(xdr, 8); |
467 | if (unlikely(p == NULL)) | 485 | if (unlikely(p == NULL)) |
468 | return htonl(NFS4ERR_RESOURCE); | 486 | return htonl(NFS4ERR_RESOURCE_HDR); |
469 | *p++ = htonl(op); | 487 | *p++ = htonl(op); |
470 | *p = res; | 488 | *p = res; |
471 | return 0; | 489 | return 0; |
@@ -499,10 +517,10 @@ out: | |||
499 | 517 | ||
500 | #if defined(CONFIG_NFS_V4_1) | 518 | #if defined(CONFIG_NFS_V4_1) |
501 | 519 | ||
502 | static unsigned encode_sessionid(struct xdr_stream *xdr, | 520 | static __be32 encode_sessionid(struct xdr_stream *xdr, |
503 | const struct nfs4_sessionid *sid) | 521 | const struct nfs4_sessionid *sid) |
504 | { | 522 | { |
505 | uint32_t *p; | 523 | __be32 *p; |
506 | int len = NFS4_MAX_SESSIONID_LEN; | 524 | int len = NFS4_MAX_SESSIONID_LEN; |
507 | 525 | ||
508 | p = xdr_reserve_space(xdr, len); | 526 | p = xdr_reserve_space(xdr, len); |
@@ -513,11 +531,11 @@ static unsigned encode_sessionid(struct xdr_stream *xdr, | |||
513 | return 0; | 531 | return 0; |
514 | } | 532 | } |
515 | 533 | ||
516 | static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp, | 534 | static __be32 encode_cb_sequence_res(struct svc_rqst *rqstp, |
517 | struct xdr_stream *xdr, | 535 | struct xdr_stream *xdr, |
518 | const struct cb_sequenceres *res) | 536 | const struct cb_sequenceres *res) |
519 | { | 537 | { |
520 | uint32_t *p; | 538 | __be32 *p; |
521 | unsigned status = res->csr_status; | 539 | unsigned status = res->csr_status; |
522 | 540 | ||
523 | if (unlikely(status != 0)) | 541 | if (unlikely(status != 0)) |
@@ -554,6 +572,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
554 | case OP_CB_RECALL: | 572 | case OP_CB_RECALL: |
555 | case OP_CB_SEQUENCE: | 573 | case OP_CB_SEQUENCE: |
556 | case OP_CB_RECALL_ANY: | 574 | case OP_CB_RECALL_ANY: |
575 | case OP_CB_RECALL_SLOT: | ||
557 | *op = &callback_ops[op_nr]; | 576 | *op = &callback_ops[op_nr]; |
558 | break; | 577 | break; |
559 | 578 | ||
@@ -562,7 +581,6 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
562 | case OP_CB_NOTIFY: | 581 | case OP_CB_NOTIFY: |
563 | case OP_CB_PUSH_DELEG: | 582 | case OP_CB_PUSH_DELEG: |
564 | case OP_CB_RECALLABLE_OBJ_AVAIL: | 583 | case OP_CB_RECALLABLE_OBJ_AVAIL: |
565 | case OP_CB_RECALL_SLOT: | ||
566 | case OP_CB_WANTS_CANCELLED: | 584 | case OP_CB_WANTS_CANCELLED: |
567 | case OP_CB_NOTIFY_LOCK: | 585 | case OP_CB_NOTIFY_LOCK: |
568 | return htonl(NFS4ERR_NOTSUPP); | 586 | return htonl(NFS4ERR_NOTSUPP); |
@@ -602,20 +620,18 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) | |||
602 | static __be32 process_op(uint32_t minorversion, int nop, | 620 | static __be32 process_op(uint32_t minorversion, int nop, |
603 | struct svc_rqst *rqstp, | 621 | struct svc_rqst *rqstp, |
604 | struct xdr_stream *xdr_in, void *argp, | 622 | struct xdr_stream *xdr_in, void *argp, |
605 | struct xdr_stream *xdr_out, void *resp) | 623 | struct xdr_stream *xdr_out, void *resp, int* drc_status) |
606 | { | 624 | { |
607 | struct callback_op *op = &callback_ops[0]; | 625 | struct callback_op *op = &callback_ops[0]; |
608 | unsigned int op_nr = OP_CB_ILLEGAL; | 626 | unsigned int op_nr; |
609 | __be32 status; | 627 | __be32 status; |
610 | long maxlen; | 628 | long maxlen; |
611 | __be32 res; | 629 | __be32 res; |
612 | 630 | ||
613 | dprintk("%s: start\n", __func__); | 631 | dprintk("%s: start\n", __func__); |
614 | status = decode_op_hdr(xdr_in, &op_nr); | 632 | status = decode_op_hdr(xdr_in, &op_nr); |
615 | if (unlikely(status)) { | 633 | if (unlikely(status)) |
616 | status = htonl(NFS4ERR_OP_ILLEGAL); | 634 | return status; |
617 | goto out; | ||
618 | } | ||
619 | 635 | ||
620 | dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", | 636 | dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", |
621 | __func__, minorversion, nop, op_nr); | 637 | __func__, minorversion, nop, op_nr); |
@@ -624,19 +640,32 @@ static __be32 process_op(uint32_t minorversion, int nop, | |||
624 | preprocess_nfs4_op(op_nr, &op); | 640 | preprocess_nfs4_op(op_nr, &op); |
625 | if (status == htonl(NFS4ERR_OP_ILLEGAL)) | 641 | if (status == htonl(NFS4ERR_OP_ILLEGAL)) |
626 | op_nr = OP_CB_ILLEGAL; | 642 | op_nr = OP_CB_ILLEGAL; |
627 | out: | 643 | if (status) |
644 | goto encode_hdr; | ||
645 | |||
646 | if (*drc_status) { | ||
647 | status = *drc_status; | ||
648 | goto encode_hdr; | ||
649 | } | ||
650 | |||
628 | maxlen = xdr_out->end - xdr_out->p; | 651 | maxlen = xdr_out->end - xdr_out->p; |
629 | if (maxlen > 0 && maxlen < PAGE_SIZE) { | 652 | if (maxlen > 0 && maxlen < PAGE_SIZE) { |
630 | if (likely(status == 0 && op->decode_args != NULL)) | 653 | status = op->decode_args(rqstp, xdr_in, argp); |
631 | status = op->decode_args(rqstp, xdr_in, argp); | 654 | if (likely(status == 0)) |
632 | if (likely(status == 0 && op->process_op != NULL)) | ||
633 | status = op->process_op(argp, resp); | 655 | status = op->process_op(argp, resp); |
634 | } else | 656 | } else |
635 | status = htonl(NFS4ERR_RESOURCE); | 657 | status = htonl(NFS4ERR_RESOURCE); |
636 | 658 | ||
659 | /* Only set by OP_CB_SEQUENCE processing */ | ||
660 | if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) { | ||
661 | *drc_status = status; | ||
662 | status = 0; | ||
663 | } | ||
664 | |||
665 | encode_hdr: | ||
637 | res = encode_op_hdr(xdr_out, op_nr, status); | 666 | res = encode_op_hdr(xdr_out, op_nr, status); |
638 | if (status == 0) | 667 | if (unlikely(res)) |
639 | status = res; | 668 | return res; |
640 | if (op->encode_res != NULL && status == 0) | 669 | if (op->encode_res != NULL && status == 0) |
641 | status = op->encode_res(rqstp, xdr_out, resp); | 670 | status = op->encode_res(rqstp, xdr_out, resp); |
642 | dprintk("%s: done, status = %d\n", __func__, ntohl(status)); | 671 | dprintk("%s: done, status = %d\n", __func__, ntohl(status)); |
@@ -652,7 +681,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
652 | struct cb_compound_hdr_res hdr_res = { NULL }; | 681 | struct cb_compound_hdr_res hdr_res = { NULL }; |
653 | struct xdr_stream xdr_in, xdr_out; | 682 | struct xdr_stream xdr_in, xdr_out; |
654 | __be32 *p; | 683 | __be32 *p; |
655 | __be32 status; | 684 | __be32 status, drc_status = 0; |
656 | unsigned int nops = 0; | 685 | unsigned int nops = 0; |
657 | 686 | ||
658 | dprintk("%s: start\n", __func__); | 687 | dprintk("%s: start\n", __func__); |
@@ -672,11 +701,18 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
672 | return rpc_system_err; | 701 | return rpc_system_err; |
673 | 702 | ||
674 | while (status == 0 && nops != hdr_arg.nops) { | 703 | while (status == 0 && nops != hdr_arg.nops) { |
675 | status = process_op(hdr_arg.minorversion, nops, | 704 | status = process_op(hdr_arg.minorversion, nops, rqstp, |
676 | rqstp, &xdr_in, argp, &xdr_out, resp); | 705 | &xdr_in, argp, &xdr_out, resp, &drc_status); |
677 | nops++; | 706 | nops++; |
678 | } | 707 | } |
679 | 708 | ||
709 | /* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return | ||
710 | * resource error in cb_compound status without returning op */ | ||
711 | if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) { | ||
712 | status = htonl(NFS4ERR_RESOURCE); | ||
713 | nops--; | ||
714 | } | ||
715 | |||
680 | *hdr_res.status = status; | 716 | *hdr_res.status = status; |
681 | *hdr_res.nops = htonl(nops); | 717 | *hdr_res.nops = htonl(nops); |
682 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); | 718 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); |
@@ -713,6 +749,11 @@ static struct callback_op callback_ops[] = { | |||
713 | .decode_args = (callback_decode_arg_t)decode_recallany_args, | 749 | .decode_args = (callback_decode_arg_t)decode_recallany_args, |
714 | .res_maxsize = CB_OP_RECALLANY_RES_MAXSZ, | 750 | .res_maxsize = CB_OP_RECALLANY_RES_MAXSZ, |
715 | }, | 751 | }, |
752 | [OP_CB_RECALL_SLOT] = { | ||
753 | .process_op = (callback_process_op_t)nfs4_callback_recallslot, | ||
754 | .decode_args = (callback_decode_arg_t)decode_recallslot_args, | ||
755 | .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ, | ||
756 | }, | ||
716 | #endif /* CONFIG_NFS_V4_1 */ | 757 | #endif /* CONFIG_NFS_V4_1 */ |
717 | }; | 758 | }; |
718 | 759 | ||