diff options
Diffstat (limited to 'net/sunrpc/xdr.c')
-rw-r--r-- | net/sunrpc/xdr.c | 255 |
1 files changed, 162 insertions, 93 deletions
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 9022eb8b37ed..a0af250ca319 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
@@ -640,41 +640,30 @@ xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf) | |||
640 | buf->buflen = buf->len = iov->iov_len; | 640 | buf->buflen = buf->len = iov->iov_len; |
641 | } | 641 | } |
642 | 642 | ||
643 | /* Sets subiov to the intersection of iov with the buffer of length len | ||
644 | * starting base bytes after iov. Indicates empty intersection by setting | ||
645 | * length of subiov to zero. Decrements len by length of subiov, sets base | ||
646 | * to zero (or decrements it by length of iov if subiov is empty). */ | ||
647 | static void | ||
648 | iov_subsegment(struct kvec *iov, struct kvec *subiov, int *base, int *len) | ||
649 | { | ||
650 | if (*base > iov->iov_len) { | ||
651 | subiov->iov_base = NULL; | ||
652 | subiov->iov_len = 0; | ||
653 | *base -= iov->iov_len; | ||
654 | } else { | ||
655 | subiov->iov_base = iov->iov_base + *base; | ||
656 | subiov->iov_len = min(*len, (int)iov->iov_len - *base); | ||
657 | *base = 0; | ||
658 | } | ||
659 | *len -= subiov->iov_len; | ||
660 | } | ||
661 | |||
662 | /* Sets subbuf to the portion of buf of length len beginning base bytes | 643 | /* Sets subbuf to the portion of buf of length len beginning base bytes |
663 | * from the start of buf. Returns -1 if base of length are out of bounds. */ | 644 | * from the start of buf. Returns -1 if base of length are out of bounds. */ |
664 | int | 645 | int |
665 | xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, | 646 | xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, |
666 | int base, int len) | 647 | unsigned int base, unsigned int len) |
667 | { | 648 | { |
668 | int i; | ||
669 | |||
670 | subbuf->buflen = subbuf->len = len; | 649 | subbuf->buflen = subbuf->len = len; |
671 | iov_subsegment(buf->head, subbuf->head, &base, &len); | 650 | if (base < buf->head[0].iov_len) { |
651 | subbuf->head[0].iov_base = buf->head[0].iov_base + base; | ||
652 | subbuf->head[0].iov_len = min_t(unsigned int, len, | ||
653 | buf->head[0].iov_len - base); | ||
654 | len -= subbuf->head[0].iov_len; | ||
655 | base = 0; | ||
656 | } else { | ||
657 | subbuf->head[0].iov_base = NULL; | ||
658 | subbuf->head[0].iov_len = 0; | ||
659 | base -= buf->head[0].iov_len; | ||
660 | } | ||
672 | 661 | ||
673 | if (base < buf->page_len) { | 662 | if (base < buf->page_len) { |
674 | i = (base + buf->page_base) >> PAGE_CACHE_SHIFT; | 663 | subbuf->page_len = min(buf->page_len - base, len); |
675 | subbuf->pages = &buf->pages[i]; | 664 | base += buf->page_base; |
676 | subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK; | 665 | subbuf->page_base = base & ~PAGE_CACHE_MASK; |
677 | subbuf->page_len = min((int)buf->page_len - base, len); | 666 | subbuf->pages = &buf->pages[base >> PAGE_CACHE_SHIFT]; |
678 | len -= subbuf->page_len; | 667 | len -= subbuf->page_len; |
679 | base = 0; | 668 | base = 0; |
680 | } else { | 669 | } else { |
@@ -682,66 +671,85 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, | |||
682 | subbuf->page_len = 0; | 671 | subbuf->page_len = 0; |
683 | } | 672 | } |
684 | 673 | ||
685 | iov_subsegment(buf->tail, subbuf->tail, &base, &len); | 674 | if (base < buf->tail[0].iov_len) { |
675 | subbuf->tail[0].iov_base = buf->tail[0].iov_base + base; | ||
676 | subbuf->tail[0].iov_len = min_t(unsigned int, len, | ||
677 | buf->tail[0].iov_len - base); | ||
678 | len -= subbuf->tail[0].iov_len; | ||
679 | base = 0; | ||
680 | } else { | ||
681 | subbuf->tail[0].iov_base = NULL; | ||
682 | subbuf->tail[0].iov_len = 0; | ||
683 | base -= buf->tail[0].iov_len; | ||
684 | } | ||
685 | |||
686 | if (base || len) | 686 | if (base || len) |
687 | return -1; | 687 | return -1; |
688 | return 0; | 688 | return 0; |
689 | } | 689 | } |
690 | 690 | ||
691 | /* obj is assumed to point to allocated memory of size at least len: */ | 691 | static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) |
692 | int | ||
693 | read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len) | ||
694 | { | 692 | { |
695 | struct xdr_buf subbuf; | 693 | unsigned int this_len; |
696 | int this_len; | ||
697 | int status; | ||
698 | 694 | ||
699 | status = xdr_buf_subsegment(buf, &subbuf, base, len); | 695 | this_len = min_t(unsigned int, len, subbuf->head[0].iov_len); |
700 | if (status) | 696 | memcpy(obj, subbuf->head[0].iov_base, this_len); |
701 | goto out; | ||
702 | this_len = min(len, (int)subbuf.head[0].iov_len); | ||
703 | memcpy(obj, subbuf.head[0].iov_base, this_len); | ||
704 | len -= this_len; | 697 | len -= this_len; |
705 | obj += this_len; | 698 | obj += this_len; |
706 | this_len = min(len, (int)subbuf.page_len); | 699 | this_len = min_t(unsigned int, len, subbuf->page_len); |
707 | if (this_len) | 700 | if (this_len) |
708 | _copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len); | 701 | _copy_from_pages(obj, subbuf->pages, subbuf->page_base, this_len); |
709 | len -= this_len; | 702 | len -= this_len; |
710 | obj += this_len; | 703 | obj += this_len; |
711 | this_len = min(len, (int)subbuf.tail[0].iov_len); | 704 | this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len); |
712 | memcpy(obj, subbuf.tail[0].iov_base, this_len); | 705 | memcpy(obj, subbuf->tail[0].iov_base, this_len); |
713 | out: | ||
714 | return status; | ||
715 | } | 706 | } |
716 | 707 | ||
717 | /* obj is assumed to point to allocated memory of size at least len: */ | 708 | /* obj is assumed to point to allocated memory of size at least len: */ |
718 | int | 709 | int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len) |
719 | write_bytes_to_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len) | ||
720 | { | 710 | { |
721 | struct xdr_buf subbuf; | 711 | struct xdr_buf subbuf; |
722 | int this_len; | ||
723 | int status; | 712 | int status; |
724 | 713 | ||
725 | status = xdr_buf_subsegment(buf, &subbuf, base, len); | 714 | status = xdr_buf_subsegment(buf, &subbuf, base, len); |
726 | if (status) | 715 | if (status != 0) |
727 | goto out; | 716 | return status; |
728 | this_len = min(len, (int)subbuf.head[0].iov_len); | 717 | __read_bytes_from_xdr_buf(&subbuf, obj, len); |
729 | memcpy(subbuf.head[0].iov_base, obj, this_len); | 718 | return 0; |
719 | } | ||
720 | |||
721 | static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) | ||
722 | { | ||
723 | unsigned int this_len; | ||
724 | |||
725 | this_len = min_t(unsigned int, len, subbuf->head[0].iov_len); | ||
726 | memcpy(subbuf->head[0].iov_base, obj, this_len); | ||
730 | len -= this_len; | 727 | len -= this_len; |
731 | obj += this_len; | 728 | obj += this_len; |
732 | this_len = min(len, (int)subbuf.page_len); | 729 | this_len = min_t(unsigned int, len, subbuf->page_len); |
733 | if (this_len) | 730 | if (this_len) |
734 | _copy_to_pages(subbuf.pages, subbuf.page_base, obj, this_len); | 731 | _copy_to_pages(subbuf->pages, subbuf->page_base, obj, this_len); |
735 | len -= this_len; | 732 | len -= this_len; |
736 | obj += this_len; | 733 | obj += this_len; |
737 | this_len = min(len, (int)subbuf.tail[0].iov_len); | 734 | this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len); |
738 | memcpy(subbuf.tail[0].iov_base, obj, this_len); | 735 | memcpy(subbuf->tail[0].iov_base, obj, this_len); |
739 | out: | 736 | } |
740 | return status; | 737 | |
738 | /* obj is assumed to point to allocated memory of size at least len: */ | ||
739 | int write_bytes_to_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len) | ||
740 | { | ||
741 | struct xdr_buf subbuf; | ||
742 | int status; | ||
743 | |||
744 | status = xdr_buf_subsegment(buf, &subbuf, base, len); | ||
745 | if (status != 0) | ||
746 | return status; | ||
747 | __write_bytes_to_xdr_buf(&subbuf, obj, len); | ||
748 | return 0; | ||
741 | } | 749 | } |
742 | 750 | ||
743 | int | 751 | int |
744 | xdr_decode_word(struct xdr_buf *buf, int base, u32 *obj) | 752 | xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj) |
745 | { | 753 | { |
746 | __be32 raw; | 754 | __be32 raw; |
747 | int status; | 755 | int status; |
@@ -754,7 +762,7 @@ xdr_decode_word(struct xdr_buf *buf, int base, u32 *obj) | |||
754 | } | 762 | } |
755 | 763 | ||
756 | int | 764 | int |
757 | xdr_encode_word(struct xdr_buf *buf, int base, u32 obj) | 765 | xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj) |
758 | { | 766 | { |
759 | __be32 raw = htonl(obj); | 767 | __be32 raw = htonl(obj); |
760 | 768 | ||
@@ -765,44 +773,37 @@ xdr_encode_word(struct xdr_buf *buf, int base, u32 obj) | |||
765 | * entirely in the head or the tail, set object to point to it; otherwise | 773 | * entirely in the head or the tail, set object to point to it; otherwise |
766 | * try to find space for it at the end of the tail, copy it there, and | 774 | * try to find space for it at the end of the tail, copy it there, and |
767 | * set obj to point to it. */ | 775 | * set obj to point to it. */ |
768 | int | 776 | int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned int offset) |
769 | xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) | ||
770 | { | 777 | { |
771 | u32 tail_offset = buf->head[0].iov_len + buf->page_len; | 778 | struct xdr_buf subbuf; |
772 | u32 obj_end_offset; | ||
773 | 779 | ||
774 | if (xdr_decode_word(buf, offset, &obj->len)) | 780 | if (xdr_decode_word(buf, offset, &obj->len)) |
775 | goto out; | 781 | return -EFAULT; |
776 | obj_end_offset = offset + 4 + obj->len; | 782 | if (xdr_buf_subsegment(buf, &subbuf, offset + 4, obj->len)) |
777 | 783 | return -EFAULT; | |
778 | if (obj_end_offset <= buf->head[0].iov_len) { | ||
779 | /* The obj is contained entirely in the head: */ | ||
780 | obj->data = buf->head[0].iov_base + offset + 4; | ||
781 | } else if (offset + 4 >= tail_offset) { | ||
782 | if (obj_end_offset - tail_offset | ||
783 | > buf->tail[0].iov_len) | ||
784 | goto out; | ||
785 | /* The obj is contained entirely in the tail: */ | ||
786 | obj->data = buf->tail[0].iov_base | ||
787 | + offset - tail_offset + 4; | ||
788 | } else { | ||
789 | /* use end of tail as storage for obj: | ||
790 | * (We don't copy to the beginning because then we'd have | ||
791 | * to worry about doing a potentially overlapping copy. | ||
792 | * This assumes the object is at most half the length of the | ||
793 | * tail.) */ | ||
794 | if (obj->len > buf->tail[0].iov_len) | ||
795 | goto out; | ||
796 | obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len - | ||
797 | obj->len; | ||
798 | if (read_bytes_from_xdr_buf(buf, offset + 4, | ||
799 | obj->data, obj->len)) | ||
800 | goto out; | ||
801 | 784 | ||
802 | } | 785 | /* Is the obj contained entirely in the head? */ |
786 | obj->data = subbuf.head[0].iov_base; | ||
787 | if (subbuf.head[0].iov_len == obj->len) | ||
788 | return 0; | ||
789 | /* ..or is the obj contained entirely in the tail? */ | ||
790 | obj->data = subbuf.tail[0].iov_base; | ||
791 | if (subbuf.tail[0].iov_len == obj->len) | ||
792 | return 0; | ||
793 | |||
794 | /* use end of tail as storage for obj: | ||
795 | * (We don't copy to the beginning because then we'd have | ||
796 | * to worry about doing a potentially overlapping copy. | ||
797 | * This assumes the object is at most half the length of the | ||
798 | * tail.) */ | ||
799 | if (obj->len > buf->buflen - buf->len) | ||
800 | return -ENOMEM; | ||
801 | if (buf->tail[0].iov_len != 0) | ||
802 | obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len; | ||
803 | else | ||
804 | obj->data = buf->head[0].iov_base + buf->head[0].iov_len; | ||
805 | __read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len); | ||
803 | return 0; | 806 | return 0; |
804 | out: | ||
805 | return -1; | ||
806 | } | 807 | } |
807 | 808 | ||
808 | /* Returns 0 on success, or else a negative error code. */ | 809 | /* Returns 0 on success, or else a negative error code. */ |
@@ -1020,3 +1021,71 @@ xdr_encode_array2(struct xdr_buf *buf, unsigned int base, | |||
1020 | 1021 | ||
1021 | return xdr_xcode_array2(buf, base, desc, 1); | 1022 | return xdr_xcode_array2(buf, base, desc, 1); |
1022 | } | 1023 | } |
1024 | |||
1025 | int | ||
1026 | xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, | ||
1027 | int (*actor)(struct scatterlist *, void *), void *data) | ||
1028 | { | ||
1029 | int i, ret = 0; | ||
1030 | unsigned page_len, thislen, page_offset; | ||
1031 | struct scatterlist sg[1]; | ||
1032 | |||
1033 | if (offset >= buf->head[0].iov_len) { | ||
1034 | offset -= buf->head[0].iov_len; | ||
1035 | } else { | ||
1036 | thislen = buf->head[0].iov_len - offset; | ||
1037 | if (thislen > len) | ||
1038 | thislen = len; | ||
1039 | sg_set_buf(sg, buf->head[0].iov_base + offset, thislen); | ||
1040 | ret = actor(sg, data); | ||
1041 | if (ret) | ||
1042 | goto out; | ||
1043 | offset = 0; | ||
1044 | len -= thislen; | ||
1045 | } | ||
1046 | if (len == 0) | ||
1047 | goto out; | ||
1048 | |||
1049 | if (offset >= buf->page_len) { | ||
1050 | offset -= buf->page_len; | ||
1051 | } else { | ||
1052 | page_len = buf->page_len - offset; | ||
1053 | if (page_len > len) | ||
1054 | page_len = len; | ||
1055 | len -= page_len; | ||
1056 | page_offset = (offset + buf->page_base) & (PAGE_CACHE_SIZE - 1); | ||
1057 | i = (offset + buf->page_base) >> PAGE_CACHE_SHIFT; | ||
1058 | thislen = PAGE_CACHE_SIZE - page_offset; | ||
1059 | do { | ||
1060 | if (thislen > page_len) | ||
1061 | thislen = page_len; | ||
1062 | sg->page = buf->pages[i]; | ||
1063 | sg->offset = page_offset; | ||
1064 | sg->length = thislen; | ||
1065 | ret = actor(sg, data); | ||
1066 | if (ret) | ||
1067 | goto out; | ||
1068 | page_len -= thislen; | ||
1069 | i++; | ||
1070 | page_offset = 0; | ||
1071 | thislen = PAGE_CACHE_SIZE; | ||
1072 | } while (page_len != 0); | ||
1073 | offset = 0; | ||
1074 | } | ||
1075 | if (len == 0) | ||
1076 | goto out; | ||
1077 | if (offset < buf->tail[0].iov_len) { | ||
1078 | thislen = buf->tail[0].iov_len - offset; | ||
1079 | if (thislen > len) | ||
1080 | thislen = len; | ||
1081 | sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen); | ||
1082 | ret = actor(sg, data); | ||
1083 | len -= thislen; | ||
1084 | } | ||
1085 | if (len != 0) | ||
1086 | ret = -EINVAL; | ||
1087 | out: | ||
1088 | return ret; | ||
1089 | } | ||
1090 | EXPORT_SYMBOL(xdr_process_buf); | ||
1091 | |||