Skip to content

Commit bd8100e

Browse files
Andreas GruenbacherTrond Myklebust
authored andcommitted
[PATCH] RPC: Encode and decode arbitrary XDR arrays
Signed-off-by: Andreas Gruenbacher <[email protected]> Acked-by: Olaf Kirch <[email protected]> Signed-off-by: Trond Myklebust <[email protected]>
1 parent 7e06b53 commit bd8100e

File tree

3 files changed

+275
-4
lines changed

3 files changed

+275
-4
lines changed

include/linux/sunrpc/xdr.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ extern void xdr_shift_buf(struct xdr_buf *, size_t);
146146
extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *);
147147
extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int);
148148
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);
150151

151152
/*
152153
* Helper structure for copying from an sk_buff.
@@ -168,6 +169,22 @@ struct sockaddr;
168169
extern int xdr_sendpages(struct socket *, struct sockaddr *, int,
169170
struct xdr_buf *, unsigned int, int);
170171

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+
171188
/*
172189
* Provide some simple tools for XDR buffer overflow-checking etc.
173190
*/

net/sunrpc/sunrpc_syms.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ EXPORT_SYMBOL(xdr_encode_netobj);
129129
EXPORT_SYMBOL(xdr_encode_pages);
130130
EXPORT_SYMBOL(xdr_inline_pages);
131131
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);
132136
EXPORT_SYMBOL(xdr_buf_from_iov);
133137
EXPORT_SYMBOL(xdr_buf_subsegment);
134138
EXPORT_SYMBOL(xdr_buf_read_netobj);

net/sunrpc/xdr.c

Lines changed: 253 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -887,8 +887,34 @@ read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len)
887887
return status;
888888
}
889889

890-
static int
891-
read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
890+
/* obj is assumed to point to allocated memory of size at least len: */
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)
892918
{
893919
u32 raw;
894920
int status;
@@ -900,6 +926,14 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
900926
return 0;
901927
}
902928

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+
903937
/* If the netobj starting offset bytes from the start of xdr_buf is contained
904938
* entirely in the head or the tail, set object to point to it; otherwise
905939
* 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)
910944
u32 tail_offset = buf->head[0].iov_len + buf->page_len;
911945
u32 obj_end_offset;
912946

913-
if (read_u32_from_xdr_buf(buf, offset, &obj->len))
947+
if (xdr_decode_word(buf, offset, &obj->len))
914948
goto out;
915949
obj_end_offset = offset + 4 + obj->len;
916950

@@ -943,3 +977,219 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset)
943977
out:
944978
return -1;
945979
}
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+
}

0 commit comments

Comments
 (0)