diff options
Diffstat (limited to 'fs/nfs/callback_xdr.c')
-rw-r--r-- | fs/nfs/callback_xdr.c | 137 |
1 files changed, 106 insertions, 31 deletions
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 76b0aa0f73bf..05af212f0edf 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/sunrpc/svc.h> | 9 | #include <linux/sunrpc/svc.h> |
10 | #include <linux/nfs4.h> | 10 | #include <linux/nfs4.h> |
11 | #include <linux/nfs_fs.h> | 11 | #include <linux/nfs_fs.h> |
12 | #include <linux/slab.h> | ||
12 | #include "nfs4_fs.h" | 13 | #include "nfs4_fs.h" |
13 | #include "callback.h" | 14 | #include "callback.h" |
14 | 15 | ||
@@ -23,10 +24,15 @@ | |||
23 | #if defined(CONFIG_NFS_V4_1) | 24 | #if defined(CONFIG_NFS_V4_1) |
24 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ | 25 | #define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \ |
25 | 4 + 1 + 3) | 26 | 4 + 1 + 3) |
27 | #define CB_OP_RECALLANY_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | ||
28 | #define CB_OP_RECALLSLOT_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ) | ||
26 | #endif /* CONFIG_NFS_V4_1 */ | 29 | #endif /* CONFIG_NFS_V4_1 */ |
27 | 30 | ||
28 | #define NFSDBG_FACILITY NFSDBG_CALLBACK | 31 | #define NFSDBG_FACILITY NFSDBG_CALLBACK |
29 | 32 | ||
33 | /* Internal error code */ | ||
34 | #define NFS4ERR_RESOURCE_HDR 11050 | ||
35 | |||
30 | typedef __be32 (*callback_process_op_t)(void *, void *); | 36 | typedef __be32 (*callback_process_op_t)(void *, void *); |
31 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); | 37 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); |
32 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); | 38 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); |
@@ -172,7 +178,7 @@ static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op) | |||
172 | __be32 *p; | 178 | __be32 *p; |
173 | p = read_buf(xdr, 4); | 179 | p = read_buf(xdr, 4); |
174 | if (unlikely(p == NULL)) | 180 | if (unlikely(p == NULL)) |
175 | return htonl(NFS4ERR_RESOURCE); | 181 | return htonl(NFS4ERR_RESOURCE_HDR); |
176 | *op = ntohl(*p); | 182 | *op = ntohl(*p); |
177 | return 0; | 183 | return 0; |
178 | } | 184 | } |
@@ -214,10 +220,10 @@ out: | |||
214 | 220 | ||
215 | #if defined(CONFIG_NFS_V4_1) | 221 | #if defined(CONFIG_NFS_V4_1) |
216 | 222 | ||
217 | static unsigned decode_sessionid(struct xdr_stream *xdr, | 223 | static __be32 decode_sessionid(struct xdr_stream *xdr, |
218 | struct nfs4_sessionid *sid) | 224 | struct nfs4_sessionid *sid) |
219 | { | 225 | { |
220 | uint32_t *p; | 226 | __be32 *p; |
221 | int len = NFS4_MAX_SESSIONID_LEN; | 227 | int len = NFS4_MAX_SESSIONID_LEN; |
222 | 228 | ||
223 | p = read_buf(xdr, len); | 229 | p = read_buf(xdr, len); |
@@ -228,12 +234,12 @@ static unsigned decode_sessionid(struct xdr_stream *xdr, | |||
228 | return 0; | 234 | return 0; |
229 | } | 235 | } |
230 | 236 | ||
231 | static unsigned decode_rc_list(struct xdr_stream *xdr, | 237 | static __be32 decode_rc_list(struct xdr_stream *xdr, |
232 | struct referring_call_list *rc_list) | 238 | struct referring_call_list *rc_list) |
233 | { | 239 | { |
234 | uint32_t *p; | 240 | __be32 *p; |
235 | int i; | 241 | int i; |
236 | unsigned status; | 242 | __be32 status; |
237 | 243 | ||
238 | status = decode_sessionid(xdr, &rc_list->rcl_sessionid); | 244 | status = decode_sessionid(xdr, &rc_list->rcl_sessionid); |
239 | if (status) | 245 | if (status) |
@@ -266,13 +272,13 @@ out: | |||
266 | return status; | 272 | return status; |
267 | } | 273 | } |
268 | 274 | ||
269 | static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp, | 275 | static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, |
270 | struct xdr_stream *xdr, | 276 | struct xdr_stream *xdr, |
271 | struct cb_sequenceargs *args) | 277 | struct cb_sequenceargs *args) |
272 | { | 278 | { |
273 | uint32_t *p; | 279 | __be32 *p; |
274 | int i; | 280 | int i; |
275 | unsigned status; | 281 | __be32 status; |
276 | 282 | ||
277 | status = decode_sessionid(xdr, &args->csa_sessionid); | 283 | status = decode_sessionid(xdr, &args->csa_sessionid); |
278 | if (status) | 284 | if (status) |
@@ -326,6 +332,39 @@ out_free: | |||
326 | goto out; | 332 | goto out; |
327 | } | 333 | } |
328 | 334 | ||
335 | static __be32 decode_recallany_args(struct svc_rqst *rqstp, | ||
336 | struct xdr_stream *xdr, | ||
337 | struct cb_recallanyargs *args) | ||
338 | { | ||
339 | __be32 *p; | ||
340 | |||
341 | args->craa_addr = svc_addr(rqstp); | ||
342 | p = read_buf(xdr, 4); | ||
343 | if (unlikely(p == NULL)) | ||
344 | return htonl(NFS4ERR_BADXDR); | ||
345 | args->craa_objs_to_keep = ntohl(*p++); | ||
346 | p = read_buf(xdr, 4); | ||
347 | if (unlikely(p == NULL)) | ||
348 | return htonl(NFS4ERR_BADXDR); | ||
349 | args->craa_type_mask = ntohl(*p); | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static __be32 decode_recallslot_args(struct svc_rqst *rqstp, | ||
355 | struct xdr_stream *xdr, | ||
356 | struct cb_recallslotargs *args) | ||
357 | { | ||
358 | __be32 *p; | ||
359 | |||
360 | args->crsa_addr = svc_addr(rqstp); | ||
361 | p = read_buf(xdr, 4); | ||
362 | if (unlikely(p == NULL)) | ||
363 | return htonl(NFS4ERR_BADXDR); | ||
364 | args->crsa_target_max_slots = ntohl(*p++); | ||
365 | return 0; | ||
366 | } | ||
367 | |||
329 | #endif /* CONFIG_NFS_V4_1 */ | 368 | #endif /* CONFIG_NFS_V4_1 */ |
330 | 369 | ||
331 | static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) | 370 | static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) |
@@ -445,7 +484,7 @@ static __be32 encode_op_hdr(struct xdr_stream *xdr, uint32_t op, __be32 res) | |||
445 | 484 | ||
446 | p = xdr_reserve_space(xdr, 8); | 485 | p = xdr_reserve_space(xdr, 8); |
447 | if (unlikely(p == NULL)) | 486 | if (unlikely(p == NULL)) |
448 | return htonl(NFS4ERR_RESOURCE); | 487 | return htonl(NFS4ERR_RESOURCE_HDR); |
449 | *p++ = htonl(op); | 488 | *p++ = htonl(op); |
450 | *p = res; | 489 | *p = res; |
451 | return 0; | 490 | return 0; |
@@ -479,10 +518,10 @@ out: | |||
479 | 518 | ||
480 | #if defined(CONFIG_NFS_V4_1) | 519 | #if defined(CONFIG_NFS_V4_1) |
481 | 520 | ||
482 | static unsigned encode_sessionid(struct xdr_stream *xdr, | 521 | static __be32 encode_sessionid(struct xdr_stream *xdr, |
483 | const struct nfs4_sessionid *sid) | 522 | const struct nfs4_sessionid *sid) |
484 | { | 523 | { |
485 | uint32_t *p; | 524 | __be32 *p; |
486 | int len = NFS4_MAX_SESSIONID_LEN; | 525 | int len = NFS4_MAX_SESSIONID_LEN; |
487 | 526 | ||
488 | p = xdr_reserve_space(xdr, len); | 527 | p = xdr_reserve_space(xdr, len); |
@@ -493,11 +532,11 @@ static unsigned encode_sessionid(struct xdr_stream *xdr, | |||
493 | return 0; | 532 | return 0; |
494 | } | 533 | } |
495 | 534 | ||
496 | static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp, | 535 | static __be32 encode_cb_sequence_res(struct svc_rqst *rqstp, |
497 | struct xdr_stream *xdr, | 536 | struct xdr_stream *xdr, |
498 | const struct cb_sequenceres *res) | 537 | const struct cb_sequenceres *res) |
499 | { | 538 | { |
500 | uint32_t *p; | 539 | __be32 *p; |
501 | unsigned status = res->csr_status; | 540 | unsigned status = res->csr_status; |
502 | 541 | ||
503 | if (unlikely(status != 0)) | 542 | if (unlikely(status != 0)) |
@@ -533,6 +572,8 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
533 | case OP_CB_GETATTR: | 572 | case OP_CB_GETATTR: |
534 | case OP_CB_RECALL: | 573 | case OP_CB_RECALL: |
535 | case OP_CB_SEQUENCE: | 574 | case OP_CB_SEQUENCE: |
575 | case OP_CB_RECALL_ANY: | ||
576 | case OP_CB_RECALL_SLOT: | ||
536 | *op = &callback_ops[op_nr]; | 577 | *op = &callback_ops[op_nr]; |
537 | break; | 578 | break; |
538 | 579 | ||
@@ -540,9 +581,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
540 | case OP_CB_NOTIFY_DEVICEID: | 581 | case OP_CB_NOTIFY_DEVICEID: |
541 | case OP_CB_NOTIFY: | 582 | case OP_CB_NOTIFY: |
542 | case OP_CB_PUSH_DELEG: | 583 | case OP_CB_PUSH_DELEG: |
543 | case OP_CB_RECALL_ANY: | ||
544 | case OP_CB_RECALLABLE_OBJ_AVAIL: | 584 | case OP_CB_RECALLABLE_OBJ_AVAIL: |
545 | case OP_CB_RECALL_SLOT: | ||
546 | case OP_CB_WANTS_CANCELLED: | 585 | case OP_CB_WANTS_CANCELLED: |
547 | case OP_CB_NOTIFY_LOCK: | 586 | case OP_CB_NOTIFY_LOCK: |
548 | return htonl(NFS4ERR_NOTSUPP); | 587 | return htonl(NFS4ERR_NOTSUPP); |
@@ -582,20 +621,18 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) | |||
582 | static __be32 process_op(uint32_t minorversion, int nop, | 621 | static __be32 process_op(uint32_t minorversion, int nop, |
583 | struct svc_rqst *rqstp, | 622 | struct svc_rqst *rqstp, |
584 | struct xdr_stream *xdr_in, void *argp, | 623 | struct xdr_stream *xdr_in, void *argp, |
585 | struct xdr_stream *xdr_out, void *resp) | 624 | struct xdr_stream *xdr_out, void *resp, int* drc_status) |
586 | { | 625 | { |
587 | struct callback_op *op = &callback_ops[0]; | 626 | struct callback_op *op = &callback_ops[0]; |
588 | unsigned int op_nr = OP_CB_ILLEGAL; | 627 | unsigned int op_nr; |
589 | __be32 status; | 628 | __be32 status; |
590 | long maxlen; | 629 | long maxlen; |
591 | __be32 res; | 630 | __be32 res; |
592 | 631 | ||
593 | dprintk("%s: start\n", __func__); | 632 | dprintk("%s: start\n", __func__); |
594 | status = decode_op_hdr(xdr_in, &op_nr); | 633 | status = decode_op_hdr(xdr_in, &op_nr); |
595 | if (unlikely(status)) { | 634 | if (unlikely(status)) |
596 | status = htonl(NFS4ERR_OP_ILLEGAL); | 635 | return status; |
597 | goto out; | ||
598 | } | ||
599 | 636 | ||
600 | dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", | 637 | dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", |
601 | __func__, minorversion, nop, op_nr); | 638 | __func__, minorversion, nop, op_nr); |
@@ -604,19 +641,32 @@ static __be32 process_op(uint32_t minorversion, int nop, | |||
604 | preprocess_nfs4_op(op_nr, &op); | 641 | preprocess_nfs4_op(op_nr, &op); |
605 | if (status == htonl(NFS4ERR_OP_ILLEGAL)) | 642 | if (status == htonl(NFS4ERR_OP_ILLEGAL)) |
606 | op_nr = OP_CB_ILLEGAL; | 643 | op_nr = OP_CB_ILLEGAL; |
607 | out: | 644 | if (status) |
645 | goto encode_hdr; | ||
646 | |||
647 | if (*drc_status) { | ||
648 | status = *drc_status; | ||
649 | goto encode_hdr; | ||
650 | } | ||
651 | |||
608 | maxlen = xdr_out->end - xdr_out->p; | 652 | maxlen = xdr_out->end - xdr_out->p; |
609 | if (maxlen > 0 && maxlen < PAGE_SIZE) { | 653 | if (maxlen > 0 && maxlen < PAGE_SIZE) { |
610 | if (likely(status == 0 && op->decode_args != NULL)) | 654 | status = op->decode_args(rqstp, xdr_in, argp); |
611 | status = op->decode_args(rqstp, xdr_in, argp); | 655 | if (likely(status == 0)) |
612 | if (likely(status == 0 && op->process_op != NULL)) | ||
613 | status = op->process_op(argp, resp); | 656 | status = op->process_op(argp, resp); |
614 | } else | 657 | } else |
615 | status = htonl(NFS4ERR_RESOURCE); | 658 | status = htonl(NFS4ERR_RESOURCE); |
616 | 659 | ||
660 | /* Only set by OP_CB_SEQUENCE processing */ | ||
661 | if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) { | ||
662 | *drc_status = status; | ||
663 | status = 0; | ||
664 | } | ||
665 | |||
666 | encode_hdr: | ||
617 | res = encode_op_hdr(xdr_out, op_nr, status); | 667 | res = encode_op_hdr(xdr_out, op_nr, status); |
618 | if (status == 0) | 668 | if (unlikely(res)) |
619 | status = res; | 669 | return res; |
620 | if (op->encode_res != NULL && status == 0) | 670 | if (op->encode_res != NULL && status == 0) |
621 | status = op->encode_res(rqstp, xdr_out, resp); | 671 | status = op->encode_res(rqstp, xdr_out, resp); |
622 | dprintk("%s: done, status = %d\n", __func__, ntohl(status)); | 672 | dprintk("%s: done, status = %d\n", __func__, ntohl(status)); |
@@ -632,7 +682,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
632 | struct cb_compound_hdr_res hdr_res = { NULL }; | 682 | struct cb_compound_hdr_res hdr_res = { NULL }; |
633 | struct xdr_stream xdr_in, xdr_out; | 683 | struct xdr_stream xdr_in, xdr_out; |
634 | __be32 *p; | 684 | __be32 *p; |
635 | __be32 status; | 685 | __be32 status, drc_status = 0; |
636 | unsigned int nops = 0; | 686 | unsigned int nops = 0; |
637 | 687 | ||
638 | dprintk("%s: start\n", __func__); | 688 | dprintk("%s: start\n", __func__); |
@@ -652,11 +702,18 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
652 | return rpc_system_err; | 702 | return rpc_system_err; |
653 | 703 | ||
654 | while (status == 0 && nops != hdr_arg.nops) { | 704 | while (status == 0 && nops != hdr_arg.nops) { |
655 | status = process_op(hdr_arg.minorversion, nops, | 705 | status = process_op(hdr_arg.minorversion, nops, rqstp, |
656 | rqstp, &xdr_in, argp, &xdr_out, resp); | 706 | &xdr_in, argp, &xdr_out, resp, &drc_status); |
657 | nops++; | 707 | nops++; |
658 | } | 708 | } |
659 | 709 | ||
710 | /* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return | ||
711 | * resource error in cb_compound status without returning op */ | ||
712 | if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) { | ||
713 | status = htonl(NFS4ERR_RESOURCE); | ||
714 | nops--; | ||
715 | } | ||
716 | |||
660 | *hdr_res.status = status; | 717 | *hdr_res.status = status; |
661 | *hdr_res.nops = htonl(nops); | 718 | *hdr_res.nops = htonl(nops); |
662 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); | 719 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); |
@@ -688,6 +745,16 @@ static struct callback_op callback_ops[] = { | |||
688 | .encode_res = (callback_encode_res_t)encode_cb_sequence_res, | 745 | .encode_res = (callback_encode_res_t)encode_cb_sequence_res, |
689 | .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ, | 746 | .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ, |
690 | }, | 747 | }, |
748 | [OP_CB_RECALL_ANY] = { | ||
749 | .process_op = (callback_process_op_t)nfs4_callback_recallany, | ||
750 | .decode_args = (callback_decode_arg_t)decode_recallany_args, | ||
751 | .res_maxsize = CB_OP_RECALLANY_RES_MAXSZ, | ||
752 | }, | ||
753 | [OP_CB_RECALL_SLOT] = { | ||
754 | .process_op = (callback_process_op_t)nfs4_callback_recallslot, | ||
755 | .decode_args = (callback_decode_arg_t)decode_recallslot_args, | ||
756 | .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ, | ||
757 | }, | ||
691 | #endif /* CONFIG_NFS_V4_1 */ | 758 | #endif /* CONFIG_NFS_V4_1 */ |
692 | }; | 759 | }; |
693 | 760 | ||
@@ -716,5 +783,13 @@ struct svc_version nfs4_callback_version1 = { | |||
716 | .vs_proc = nfs4_callback_procedures1, | 783 | .vs_proc = nfs4_callback_procedures1, |
717 | .vs_xdrsize = NFS4_CALLBACK_XDRSIZE, | 784 | .vs_xdrsize = NFS4_CALLBACK_XDRSIZE, |
718 | .vs_dispatch = NULL, | 785 | .vs_dispatch = NULL, |
786 | .vs_hidden = 1, | ||
719 | }; | 787 | }; |
720 | 788 | ||
789 | struct svc_version nfs4_callback_version4 = { | ||
790 | .vs_vers = 4, | ||
791 | .vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1), | ||
792 | .vs_proc = nfs4_callback_procedures1, | ||
793 | .vs_xdrsize = NFS4_CALLBACK_XDRSIZE, | ||
794 | .vs_dispatch = NULL, | ||
795 | }; | ||