diff options
Diffstat (limited to 'net/sunrpc/xdr.c')
-rw-r--r-- | net/sunrpc/xdr.c | 298 |
1 files changed, 288 insertions, 10 deletions
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 67b9f035ba86..8a4d9c106af1 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
@@ -176,21 +176,23 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, | |||
176 | xdr->buflen += len; | 176 | xdr->buflen += len; |
177 | } | 177 | } |
178 | 178 | ||
179 | void | 179 | ssize_t |
180 | xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | 180 | xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, |
181 | skb_reader_t *desc, | 181 | skb_reader_t *desc, |
182 | skb_read_actor_t copy_actor) | 182 | skb_read_actor_t copy_actor) |
183 | { | 183 | { |
184 | struct page **ppage = xdr->pages; | 184 | struct page **ppage = xdr->pages; |
185 | unsigned int len, pglen = xdr->page_len; | 185 | unsigned int len, pglen = xdr->page_len; |
186 | ssize_t copied = 0; | ||
186 | int ret; | 187 | int ret; |
187 | 188 | ||
188 | len = xdr->head[0].iov_len; | 189 | len = xdr->head[0].iov_len; |
189 | if (base < len) { | 190 | if (base < len) { |
190 | len -= base; | 191 | len -= base; |
191 | ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); | 192 | ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); |
193 | copied += ret; | ||
192 | if (ret != len || !desc->count) | 194 | if (ret != len || !desc->count) |
193 | return; | 195 | goto out; |
194 | base = 0; | 196 | base = 0; |
195 | } else | 197 | } else |
196 | base -= len; | 198 | base -= len; |
@@ -210,6 +212,17 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
210 | do { | 212 | do { |
211 | char *kaddr; | 213 | char *kaddr; |
212 | 214 | ||
215 | /* ACL likes to be lazy in allocating pages - ACLs | ||
216 | * are small by default but can get huge. */ | ||
217 | if (unlikely(*ppage == NULL)) { | ||
218 | *ppage = alloc_page(GFP_ATOMIC); | ||
219 | if (unlikely(*ppage == NULL)) { | ||
220 | if (copied == 0) | ||
221 | copied = -ENOMEM; | ||
222 | goto out; | ||
223 | } | ||
224 | } | ||
225 | |||
213 | len = PAGE_CACHE_SIZE; | 226 | len = PAGE_CACHE_SIZE; |
214 | kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); | 227 | kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); |
215 | if (base) { | 228 | if (base) { |
@@ -225,14 +238,17 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
225 | } | 238 | } |
226 | flush_dcache_page(*ppage); | 239 | flush_dcache_page(*ppage); |
227 | kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); | 240 | kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); |
241 | copied += ret; | ||
228 | if (ret != len || !desc->count) | 242 | if (ret != len || !desc->count) |
229 | return; | 243 | goto out; |
230 | ppage++; | 244 | ppage++; |
231 | } while ((pglen -= len) != 0); | 245 | } while ((pglen -= len) != 0); |
232 | copy_tail: | 246 | copy_tail: |
233 | len = xdr->tail[0].iov_len; | 247 | len = xdr->tail[0].iov_len; |
234 | if (base < len) | 248 | if (base < len) |
235 | copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); | 249 | copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); |
250 | out: | ||
251 | return copied; | ||
236 | } | 252 | } |
237 | 253 | ||
238 | 254 | ||
@@ -616,12 +632,24 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len) | |||
616 | void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) | 632 | void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) |
617 | { | 633 | { |
618 | struct kvec *iov = buf->head; | 634 | struct kvec *iov = buf->head; |
635 | int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len; | ||
619 | 636 | ||
637 | BUG_ON(scratch_len < 0); | ||
620 | xdr->buf = buf; | 638 | xdr->buf = buf; |
621 | xdr->iov = iov; | 639 | xdr->iov = iov; |
622 | xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); | 640 | xdr->p = (uint32_t *)((char *)iov->iov_base + iov->iov_len); |
623 | buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base; | 641 | xdr->end = (uint32_t *)((char *)iov->iov_base + scratch_len); |
624 | xdr->p = p; | 642 | BUG_ON(iov->iov_len > scratch_len); |
643 | |||
644 | if (p != xdr->p && p != NULL) { | ||
645 | size_t len; | ||
646 | |||
647 | BUG_ON(p < xdr->p || p > xdr->end); | ||
648 | len = (char *)p - (char *)xdr->p; | ||
649 | xdr->p = p; | ||
650 | buf->len += len; | ||
651 | iov->iov_len += len; | ||
652 | } | ||
625 | } | 653 | } |
626 | EXPORT_SYMBOL(xdr_init_encode); | 654 | EXPORT_SYMBOL(xdr_init_encode); |
627 | 655 | ||
@@ -859,8 +887,34 @@ out: | |||
859 | return status; | 887 | return status; |
860 | } | 888 | } |
861 | 889 | ||
862 | static int | 890 | /* obj is assumed to point to allocated memory of size at least len: */ |
863 | read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) | 891 | int |
892 | write_bytes_to_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len) | ||
893 | { | ||
894 | struct xdr_buf subbuf; | ||
895 | int this_len; | ||
896 | int status; | ||
897 | |||
898 | status = xdr_buf_subsegment(buf, &subbuf, base, len); | ||
899 | if (status) | ||
900 | goto out; | ||
901 | this_len = min(len, (int)subbuf.head[0].iov_len); | ||
902 | memcpy(subbuf.head[0].iov_base, obj, this_len); | ||
903 | len -= this_len; | ||
904 | obj += this_len; | ||
905 | this_len = min(len, (int)subbuf.page_len); | ||
906 | if (this_len) | ||
907 | _copy_to_pages(subbuf.pages, subbuf.page_base, obj, this_len); | ||
908 | len -= this_len; | ||
909 | obj += this_len; | ||
910 | this_len = min(len, (int)subbuf.tail[0].iov_len); | ||
911 | memcpy(subbuf.tail[0].iov_base, obj, this_len); | ||
912 | out: | ||
913 | return status; | ||
914 | } | ||
915 | |||
916 | int | ||
917 | xdr_decode_word(struct xdr_buf *buf, int base, u32 *obj) | ||
864 | { | 918 | { |
865 | u32 raw; | 919 | u32 raw; |
866 | int status; | 920 | int status; |
@@ -872,6 +926,14 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) | |||
872 | return 0; | 926 | return 0; |
873 | } | 927 | } |
874 | 928 | ||
929 | int | ||
930 | xdr_encode_word(struct xdr_buf *buf, int base, u32 obj) | ||
931 | { | ||
932 | u32 raw = htonl(obj); | ||
933 | |||
934 | return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj)); | ||
935 | } | ||
936 | |||
875 | /* If the netobj starting offset bytes from the start of xdr_buf is contained | 937 | /* If the netobj starting offset bytes from the start of xdr_buf is contained |
876 | * entirely in the head or the tail, set object to point to it; otherwise | 938 | * entirely in the head or the tail, set object to point to it; otherwise |
877 | * try to find space for it at the end of the tail, copy it there, and | 939 | * try to find space for it at the end of the tail, copy it there, and |
@@ -882,7 +944,7 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) | |||
882 | u32 tail_offset = buf->head[0].iov_len + buf->page_len; | 944 | u32 tail_offset = buf->head[0].iov_len + buf->page_len; |
883 | u32 obj_end_offset; | 945 | u32 obj_end_offset; |
884 | 946 | ||
885 | if (read_u32_from_xdr_buf(buf, offset, &obj->len)) | 947 | if (xdr_decode_word(buf, offset, &obj->len)) |
886 | goto out; | 948 | goto out; |
887 | obj_end_offset = offset + 4 + obj->len; | 949 | obj_end_offset = offset + 4 + obj->len; |
888 | 950 | ||
@@ -915,3 +977,219 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) | |||
915 | out: | 977 | out: |
916 | return -1; | 978 | return -1; |
917 | } | 979 | } |
980 | |||
981 | /* Returns 0 on success, or else a negative error code. */ | ||
982 | static int | ||
983 | xdr_xcode_array2(struct xdr_buf *buf, unsigned int base, | ||
984 | struct xdr_array2_desc *desc, int encode) | ||
985 | { | ||
986 | char *elem = NULL, *c; | ||
987 | unsigned int copied = 0, todo, avail_here; | ||
988 | struct page **ppages = NULL; | ||
989 | int err; | ||
990 | |||
991 | if (encode) { | ||
992 | if (xdr_encode_word(buf, base, desc->array_len) != 0) | ||
993 | return -EINVAL; | ||
994 | } else { | ||
995 | if (xdr_decode_word(buf, base, &desc->array_len) != 0 || | ||
996 | (unsigned long) base + 4 + desc->array_len * | ||
997 | desc->elem_size > buf->len) | ||
998 | return -EINVAL; | ||
999 | } | ||
1000 | base += 4; | ||
1001 | |||
1002 | if (!desc->xcode) | ||
1003 | return 0; | ||
1004 | |||
1005 | todo = desc->array_len * desc->elem_size; | ||
1006 | |||
1007 | /* process head */ | ||
1008 | if (todo && base < buf->head->iov_len) { | ||
1009 | c = buf->head->iov_base + base; | ||
1010 | avail_here = min_t(unsigned int, todo, | ||
1011 | buf->head->iov_len - base); | ||
1012 | todo -= avail_here; | ||
1013 | |||
1014 | while (avail_here >= desc->elem_size) { | ||
1015 | err = desc->xcode(desc, c); | ||
1016 | if (err) | ||
1017 | goto out; | ||
1018 | c += desc->elem_size; | ||
1019 | avail_here -= desc->elem_size; | ||
1020 | } | ||
1021 | if (avail_here) { | ||
1022 | if (!elem) { | ||
1023 | elem = kmalloc(desc->elem_size, GFP_KERNEL); | ||
1024 | err = -ENOMEM; | ||
1025 | if (!elem) | ||
1026 | goto out; | ||
1027 | } | ||
1028 | if (encode) { | ||
1029 | err = desc->xcode(desc, elem); | ||
1030 | if (err) | ||
1031 | goto out; | ||
1032 | memcpy(c, elem, avail_here); | ||
1033 | } else | ||
1034 | memcpy(elem, c, avail_here); | ||
1035 | copied = avail_here; | ||
1036 | } | ||
1037 | base = buf->head->iov_len; /* align to start of pages */ | ||
1038 | } | ||
1039 | |||
1040 | /* process pages array */ | ||
1041 | base -= buf->head->iov_len; | ||
1042 | if (todo && base < buf->page_len) { | ||
1043 | unsigned int avail_page; | ||
1044 | |||
1045 | avail_here = min(todo, buf->page_len - base); | ||
1046 | todo -= avail_here; | ||
1047 | |||
1048 | base += buf->page_base; | ||
1049 | ppages = buf->pages + (base >> PAGE_CACHE_SHIFT); | ||
1050 | base &= ~PAGE_CACHE_MASK; | ||
1051 | avail_page = min_t(unsigned int, PAGE_CACHE_SIZE - base, | ||
1052 | avail_here); | ||
1053 | c = kmap(*ppages) + base; | ||
1054 | |||
1055 | while (avail_here) { | ||
1056 | avail_here -= avail_page; | ||
1057 | if (copied || avail_page < desc->elem_size) { | ||
1058 | unsigned int l = min(avail_page, | ||
1059 | desc->elem_size - copied); | ||
1060 | if (!elem) { | ||
1061 | elem = kmalloc(desc->elem_size, | ||
1062 | GFP_KERNEL); | ||
1063 | err = -ENOMEM; | ||
1064 | if (!elem) | ||
1065 | goto out; | ||
1066 | } | ||
1067 | if (encode) { | ||
1068 | if (!copied) { | ||
1069 | err = desc->xcode(desc, elem); | ||
1070 | if (err) | ||
1071 | goto out; | ||
1072 | } | ||
1073 | memcpy(c, elem + copied, l); | ||
1074 | copied += l; | ||
1075 | if (copied == desc->elem_size) | ||
1076 | copied = 0; | ||
1077 | } else { | ||
1078 | memcpy(elem + copied, c, l); | ||
1079 | copied += l; | ||
1080 | if (copied == desc->elem_size) { | ||
1081 | err = desc->xcode(desc, elem); | ||
1082 | if (err) | ||
1083 | goto out; | ||
1084 | copied = 0; | ||
1085 | } | ||
1086 | } | ||
1087 | avail_page -= l; | ||
1088 | c += l; | ||
1089 | } | ||
1090 | while (avail_page >= desc->elem_size) { | ||
1091 | err = desc->xcode(desc, c); | ||
1092 | if (err) | ||
1093 | goto out; | ||
1094 | c += desc->elem_size; | ||
1095 | avail_page -= desc->elem_size; | ||
1096 | } | ||
1097 | if (avail_page) { | ||
1098 | unsigned int l = min(avail_page, | ||
1099 | desc->elem_size - copied); | ||
1100 | if (!elem) { | ||
1101 | elem = kmalloc(desc->elem_size, | ||
1102 | GFP_KERNEL); | ||
1103 | err = -ENOMEM; | ||
1104 | if (!elem) | ||
1105 | goto out; | ||
1106 | } | ||
1107 | if (encode) { | ||
1108 | if (!copied) { | ||
1109 | err = desc->xcode(desc, elem); | ||
1110 | if (err) | ||
1111 | goto out; | ||
1112 | } | ||
1113 | memcpy(c, elem + copied, l); | ||
1114 | copied += l; | ||
1115 | if (copied == desc->elem_size) | ||
1116 | copied = 0; | ||
1117 | } else { | ||
1118 | memcpy(elem + copied, c, l); | ||
1119 | copied += l; | ||
1120 | if (copied == desc->elem_size) { | ||
1121 | err = desc->xcode(desc, elem); | ||
1122 | if (err) | ||
1123 | goto out; | ||
1124 | copied = 0; | ||
1125 | } | ||
1126 | } | ||
1127 | } | ||
1128 | if (avail_here) { | ||
1129 | kunmap(*ppages); | ||
1130 | ppages++; | ||
1131 | c = kmap(*ppages); | ||
1132 | } | ||
1133 | |||
1134 | avail_page = min(avail_here, | ||
1135 | (unsigned int) PAGE_CACHE_SIZE); | ||
1136 | } | ||
1137 | base = buf->page_len; /* align to start of tail */ | ||
1138 | } | ||
1139 | |||
1140 | /* process tail */ | ||
1141 | base -= buf->page_len; | ||
1142 | if (todo) { | ||
1143 | c = buf->tail->iov_base + base; | ||
1144 | if (copied) { | ||
1145 | unsigned int l = desc->elem_size - copied; | ||
1146 | |||
1147 | if (encode) | ||
1148 | memcpy(c, elem + copied, l); | ||
1149 | else { | ||
1150 | memcpy(elem + copied, c, l); | ||
1151 | err = desc->xcode(desc, elem); | ||
1152 | if (err) | ||
1153 | goto out; | ||
1154 | } | ||
1155 | todo -= l; | ||
1156 | c += l; | ||
1157 | } | ||
1158 | while (todo) { | ||
1159 | err = desc->xcode(desc, c); | ||
1160 | if (err) | ||
1161 | goto out; | ||
1162 | c += desc->elem_size; | ||
1163 | todo -= desc->elem_size; | ||
1164 | } | ||
1165 | } | ||
1166 | err = 0; | ||
1167 | |||
1168 | out: | ||
1169 | if (elem) | ||
1170 | kfree(elem); | ||
1171 | if (ppages) | ||
1172 | kunmap(*ppages); | ||
1173 | return err; | ||
1174 | } | ||
1175 | |||
1176 | int | ||
1177 | xdr_decode_array2(struct xdr_buf *buf, unsigned int base, | ||
1178 | struct xdr_array2_desc *desc) | ||
1179 | { | ||
1180 | if (base >= buf->len) | ||
1181 | return -EINVAL; | ||
1182 | |||
1183 | return xdr_xcode_array2(buf, base, desc, 0); | ||
1184 | } | ||
1185 | |||
1186 | int | ||
1187 | xdr_encode_array2(struct xdr_buf *buf, unsigned int base, | ||
1188 | struct xdr_array2_desc *desc) | ||
1189 | { | ||
1190 | if ((unsigned long) base + 4 + desc->array_len * desc->elem_size > | ||
1191 | buf->head->iov_len + buf->page_len + buf->tail->iov_len) | ||
1192 | return -EINVAL; | ||
1193 | |||
1194 | return xdr_xcode_array2(buf, base, desc, 1); | ||
1195 | } | ||