diff options
-rw-r--r-- | include/linux/sunrpc/xdr.h | 19 | ||||
-rw-r--r-- | net/sunrpc/sunrpc_syms.c | 4 | ||||
-rw-r--r-- | net/sunrpc/xdr.c | 256 |
3 files changed, 275 insertions, 4 deletions
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 5d1eed2b58a1..34ec3e8d99b3 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h | |||
@@ -146,7 +146,8 @@ extern void xdr_shift_buf(struct xdr_buf *, size_t); | |||
146 | extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *); | 146 | extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *); |
147 | extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int); | 147 | extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int); |
148 | extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int); | 148 | extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int); |
149 | extern int read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len); | 149 | extern int read_bytes_from_xdr_buf(struct xdr_buf *, int, void *, int); |
150 | extern int write_bytes_to_xdr_buf(struct xdr_buf *, int, void *, int); | ||
150 | 151 | ||
151 | /* | 152 | /* |
152 | * Helper structure for copying from an sk_buff. | 153 | * Helper structure for copying from an sk_buff. |
@@ -168,6 +169,22 @@ struct sockaddr; | |||
168 | extern int xdr_sendpages(struct socket *, struct sockaddr *, int, | 169 | extern int xdr_sendpages(struct socket *, struct sockaddr *, int, |
169 | struct xdr_buf *, unsigned int, int); | 170 | struct xdr_buf *, unsigned int, int); |
170 | 171 | ||
172 | extern int xdr_encode_word(struct xdr_buf *, int, u32); | ||
173 | extern int xdr_decode_word(struct xdr_buf *, int, u32 *); | ||
174 | |||
175 | struct xdr_array2_desc; | ||
176 | typedef int (*xdr_xcode_elem_t)(struct xdr_array2_desc *desc, void *elem); | ||
177 | struct xdr_array2_desc { | ||
178 | unsigned int elem_size; | ||
179 | unsigned int array_len; | ||
180 | xdr_xcode_elem_t xcode; | ||
181 | }; | ||
182 | |||
183 | extern int xdr_decode_array2(struct xdr_buf *buf, unsigned int base, | ||
184 | struct xdr_array2_desc *desc); | ||
185 | extern int xdr_encode_array2(struct xdr_buf *buf, unsigned int base, | ||
186 | struct xdr_array2_desc *desc); | ||
187 | |||
171 | /* | 188 | /* |
172 | * Provide some simple tools for XDR buffer overflow-checking etc. | 189 | * Provide some simple tools for XDR buffer overflow-checking etc. |
173 | */ | 190 | */ |
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index d8673f66acc3..32e8acbc60fe 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c | |||
@@ -129,6 +129,10 @@ EXPORT_SYMBOL(xdr_encode_netobj); | |||
129 | EXPORT_SYMBOL(xdr_encode_pages); | 129 | EXPORT_SYMBOL(xdr_encode_pages); |
130 | EXPORT_SYMBOL(xdr_inline_pages); | 130 | EXPORT_SYMBOL(xdr_inline_pages); |
131 | EXPORT_SYMBOL(xdr_shift_buf); | 131 | EXPORT_SYMBOL(xdr_shift_buf); |
132 | EXPORT_SYMBOL(xdr_encode_word); | ||
133 | EXPORT_SYMBOL(xdr_decode_word); | ||
134 | EXPORT_SYMBOL(xdr_encode_array2); | ||
135 | EXPORT_SYMBOL(xdr_decode_array2); | ||
132 | EXPORT_SYMBOL(xdr_buf_from_iov); | 136 | EXPORT_SYMBOL(xdr_buf_from_iov); |
133 | EXPORT_SYMBOL(xdr_buf_subsegment); | 137 | EXPORT_SYMBOL(xdr_buf_subsegment); |
134 | EXPORT_SYMBOL(xdr_buf_read_netobj); | 138 | EXPORT_SYMBOL(xdr_buf_read_netobj); |
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index b3ac3f72bf9c..8a4d9c106af1 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
@@ -887,8 +887,34 @@ out: | |||
887 | return status; | 887 | return status; |
888 | } | 888 | } |
889 | 889 | ||
890 | static int | 890 | /* obj is assumed to point to allocated memory of size at least len: */ |
891 | 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) | ||
892 | { | 918 | { |
893 | u32 raw; | 919 | u32 raw; |
894 | int status; | 920 | int status; |
@@ -900,6 +926,14 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) | |||
900 | return 0; | 926 | return 0; |
901 | } | 927 | } |
902 | 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 | |||
903 | /* 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 |
904 | * 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 |
905 | * 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 |
@@ -910,7 +944,7 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) | |||
910 | u32 tail_offset = buf->head[0].iov_len + buf->page_len; | 944 | u32 tail_offset = buf->head[0].iov_len + buf->page_len; |
911 | u32 obj_end_offset; | 945 | u32 obj_end_offset; |
912 | 946 | ||
913 | if (read_u32_from_xdr_buf(buf, offset, &obj->len)) | 947 | if (xdr_decode_word(buf, offset, &obj->len)) |
914 | goto out; | 948 | goto out; |
915 | obj_end_offset = offset + 4 + obj->len; | 949 | obj_end_offset = offset + 4 + obj->len; |
916 | 950 | ||
@@ -943,3 +977,219 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) | |||
943 | out: | 977 | out: |
944 | return -1; | 978 | return -1; |
945 | } | 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 | } | ||