aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc/xdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sunrpc/xdr.c')
-rw-r--r--net/sunrpc/xdr.c255
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). */
647static void
648iov_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. */
664int 645int
665xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, 646xdr_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: */ 691static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
692int
693read_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);
713out:
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: */
718int 709int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
719write_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
721static 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);
739out: 736}
740 return status; 737
738/* obj is assumed to point to allocated memory of size at least len: */
739int 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
743int 751int
744xdr_decode_word(struct xdr_buf *buf, int base, u32 *obj) 752xdr_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
756int 764int
757xdr_encode_word(struct xdr_buf *buf, int base, u32 obj) 765xdr_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. */
768int 776int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned int offset)
769xdr_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;
804out:
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
1025int
1026xdr_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;
1087out:
1088 return ret;
1089}
1090EXPORT_SYMBOL(xdr_process_buf);
1091