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.c298
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
179void 179ssize_t
180xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, 180xdr_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);
232copy_tail: 246copy_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);
250out:
251 return copied;
236} 252}
237 253
238 254
@@ -616,12 +632,24 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
616void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p) 632void 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}
626EXPORT_SYMBOL(xdr_init_encode); 654EXPORT_SYMBOL(xdr_init_encode);
627 655
@@ -859,8 +887,34 @@ out:
859 return status; 887 return status;
860} 888}
861 889
862static int 890/* obj is assumed to point to allocated memory of size at least len: */
863read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) 891int
892write_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);
912out:
913 return status;
914}
915
916int
917xdr_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
929int
930xdr_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)
915out: 977out:
916 return -1; 978 return -1;
917} 979}
980
981/* Returns 0 on success, or else a negative error code. */
982static int
983xdr_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
1168out:
1169 if (elem)
1170 kfree(elem);
1171 if (ppages)
1172 kunmap(*ppages);
1173 return err;
1174}
1175
1176int
1177xdr_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
1186int
1187xdr_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}