[PATCH] RPC: Encode and decode arbitrary XDR arrays
authorAndreas Gruenbacher <agruen@suse.de>
Wed, 22 Jun 2005 17:16:24 +0000 (17:16 +0000)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 22 Jun 2005 20:07:20 +0000 (16:07 -0400)
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Acked-by: Olaf Kirch <okir@suse.de>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
include/linux/sunrpc/xdr.h
net/sunrpc/sunrpc_syms.c
net/sunrpc/xdr.c

index 5d1eed2b58a11b043dff1543701feb59429104c2..34ec3e8d99b3f04fb4e23631f84ce9d497529cf3 100644 (file)
@@ -146,7 +146,8 @@ extern void xdr_shift_buf(struct xdr_buf *, size_t);
 extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *);
 extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int);
 extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int);
-extern int read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len);
+extern int read_bytes_from_xdr_buf(struct xdr_buf *, int, void *, int);
+extern int write_bytes_to_xdr_buf(struct xdr_buf *, int, void *, int);
 
 /*
  * Helper structure for copying from an sk_buff.
@@ -168,6 +169,22 @@ struct sockaddr;
 extern int xdr_sendpages(struct socket *, struct sockaddr *, int,
                struct xdr_buf *, unsigned int, int);
 
+extern int xdr_encode_word(struct xdr_buf *, int, u32);
+extern int xdr_decode_word(struct xdr_buf *, int, u32 *);
+
+struct xdr_array2_desc;
+typedef int (*xdr_xcode_elem_t)(struct xdr_array2_desc *desc, void *elem);
+struct xdr_array2_desc {
+       unsigned int elem_size;
+       unsigned int array_len;
+       xdr_xcode_elem_t xcode;
+};
+
+extern int xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
+                             struct xdr_array2_desc *desc);
+extern int xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
+                            struct xdr_array2_desc *desc);
+
 /*
  * Provide some simple tools for XDR buffer overflow-checking etc.
  */
index d8673f66acc3f71364d10d31deca12b63efc6170..32e8acbc60feae1c2cc509c2ba009c2b09e9de4f 100644 (file)
@@ -129,6 +129,10 @@ EXPORT_SYMBOL(xdr_encode_netobj);
 EXPORT_SYMBOL(xdr_encode_pages);
 EXPORT_SYMBOL(xdr_inline_pages);
 EXPORT_SYMBOL(xdr_shift_buf);
+EXPORT_SYMBOL(xdr_encode_word);
+EXPORT_SYMBOL(xdr_decode_word);
+EXPORT_SYMBOL(xdr_encode_array2);
+EXPORT_SYMBOL(xdr_decode_array2);
 EXPORT_SYMBOL(xdr_buf_from_iov);
 EXPORT_SYMBOL(xdr_buf_subsegment);
 EXPORT_SYMBOL(xdr_buf_read_netobj);
index b3ac3f72bf9c3cc2dbba109f4e5e50af6b64de73..8a4d9c106af1b37b1120a2adb53e21f7fb3dc317 100644 (file)
@@ -887,8 +887,34 @@ out:
        return status;
 }
 
-static int
-read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
+/* obj is assumed to point to allocated memory of size at least len: */
+int
+write_bytes_to_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len)
+{
+       struct xdr_buf subbuf;
+       int this_len;
+       int status;
+
+       status = xdr_buf_subsegment(buf, &subbuf, base, len);
+       if (status)
+               goto out;
+       this_len = min(len, (int)subbuf.head[0].iov_len);
+       memcpy(subbuf.head[0].iov_base, obj, this_len);
+       len -= this_len;
+       obj += this_len;
+       this_len = min(len, (int)subbuf.page_len);
+       if (this_len)
+               _copy_to_pages(subbuf.pages, subbuf.page_base, obj, this_len);
+       len -= this_len;
+       obj += this_len;
+       this_len = min(len, (int)subbuf.tail[0].iov_len);
+       memcpy(subbuf.tail[0].iov_base, obj, this_len);
+out:
+       return status;
+}
+
+int
+xdr_decode_word(struct xdr_buf *buf, int base, u32 *obj)
 {
        u32     raw;
        int     status;
@@ -900,6 +926,14 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
        return 0;
 }
 
+int
+xdr_encode_word(struct xdr_buf *buf, int base, u32 obj)
+{
+       u32     raw = htonl(obj);
+
+       return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
+}
+
 /* If the netobj starting offset bytes from the start of xdr_buf is contained
  * entirely in the head or the tail, set object to point to it; otherwise
  * 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)
        u32     tail_offset = buf->head[0].iov_len + buf->page_len;
        u32     obj_end_offset;
 
-       if (read_u32_from_xdr_buf(buf, offset, &obj->len))
+       if (xdr_decode_word(buf, offset, &obj->len))
                goto out;
        obj_end_offset = offset + 4 + obj->len;
 
@@ -943,3 +977,219 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset)
 out:
        return -1;
 }
+
+/* Returns 0 on success, or else a negative error code. */
+static int
+xdr_xcode_array2(struct xdr_buf *buf, unsigned int base,
+                struct xdr_array2_desc *desc, int encode)
+{
+       char *elem = NULL, *c;
+       unsigned int copied = 0, todo, avail_here;
+       struct page **ppages = NULL;
+       int err;
+
+       if (encode) {
+               if (xdr_encode_word(buf, base, desc->array_len) != 0)
+                       return -EINVAL;
+       } else {
+               if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
+                   (unsigned long) base + 4 + desc->array_len *
+                                   desc->elem_size > buf->len)
+                       return -EINVAL;
+       }
+       base += 4;
+
+       if (!desc->xcode)
+               return 0;
+
+       todo = desc->array_len * desc->elem_size;
+
+       /* process head */
+       if (todo && base < buf->head->iov_len) {
+               c = buf->head->iov_base + base;
+               avail_here = min_t(unsigned int, todo,
+                                  buf->head->iov_len - base);
+               todo -= avail_here;
+
+               while (avail_here >= desc->elem_size) {
+                       err = desc->xcode(desc, c);
+                       if (err)
+                               goto out;
+                       c += desc->elem_size;
+                       avail_here -= desc->elem_size;
+               }
+               if (avail_here) {
+                       if (!elem) {
+                               elem = kmalloc(desc->elem_size, GFP_KERNEL);
+                               err = -ENOMEM;
+                               if (!elem)
+                                       goto out;
+                       }
+                       if (encode) {
+                               err = desc->xcode(desc, elem);
+                               if (err)
+                                       goto out;
+                               memcpy(c, elem, avail_here);
+                       } else
+                               memcpy(elem, c, avail_here);
+                       copied = avail_here;
+               }
+               base = buf->head->iov_len;  /* align to start of pages */
+       }
+
+       /* process pages array */
+       base -= buf->head->iov_len;
+       if (todo && base < buf->page_len) {
+               unsigned int avail_page;
+
+               avail_here = min(todo, buf->page_len - base);
+               todo -= avail_here;
+
+               base += buf->page_base;
+               ppages = buf->pages + (base >> PAGE_CACHE_SHIFT);
+               base &= ~PAGE_CACHE_MASK;
+               avail_page = min_t(unsigned int, PAGE_CACHE_SIZE - base,
+                                       avail_here);
+               c = kmap(*ppages) + base;
+
+               while (avail_here) {
+                       avail_here -= avail_page;
+                       if (copied || avail_page < desc->elem_size) {
+                               unsigned int l = min(avail_page,
+                                       desc->elem_size - copied);
+                               if (!elem) {
+                                       elem = kmalloc(desc->elem_size,
+                                                      GFP_KERNEL);
+                                       err = -ENOMEM;
+                                       if (!elem)
+                                               goto out;
+                               }
+                               if (encode) {
+                                       if (!copied) {
+                                               err = desc->xcode(desc, elem);
+                                               if (err)
+                                                       goto out;
+                                       }
+                                       memcpy(c, elem + copied, l);
+                                       copied += l;
+                                       if (copied == desc->elem_size)
+                                               copied = 0;
+                               } else {
+                                       memcpy(elem + copied, c, l);
+                                       copied += l;
+                                       if (copied == desc->elem_size) {
+                                               err = desc->xcode(desc, elem);
+                                               if (err)
+                                                       goto out;
+                                               copied = 0;
+                                       }
+                               }
+                               avail_page -= l;
+                               c += l;
+                       }
+                       while (avail_page >= desc->elem_size) {
+                               err = desc->xcode(desc, c);
+                               if (err)
+                                       goto out;
+                               c += desc->elem_size;
+                               avail_page -= desc->elem_size;
+                       }
+                       if (avail_page) {
+                               unsigned int l = min(avail_page,
+                                           desc->elem_size - copied);
+                               if (!elem) {
+                                       elem = kmalloc(desc->elem_size,
+                                                      GFP_KERNEL);
+                                       err = -ENOMEM;
+                                       if (!elem)
+                                               goto out;
+                               }
+                               if (encode) {
+                                       if (!copied) {
+                                               err = desc->xcode(desc, elem);
+                                               if (err)
+                                                       goto out;
+                                       }
+                                       memcpy(c, elem + copied, l);
+                                       copied += l;
+                                       if (copied == desc->elem_size)
+                                               copied = 0;
+                               } else {
+                                       memcpy(elem + copied, c, l);
+                                       copied += l;
+                                       if (copied == desc->elem_size) {
+                                               err = desc->xcode(desc, elem);
+                                               if (err)
+                                                       goto out;
+                                               copied = 0;
+                                       }
+                               }
+                       }
+                       if (avail_here) {
+                               kunmap(*ppages);
+                               ppages++;
+                               c = kmap(*ppages);
+                       }
+
+                       avail_page = min(avail_here,
+                                (unsigned int) PAGE_CACHE_SIZE);
+               }
+               base = buf->page_len;  /* align to start of tail */
+       }
+
+       /* process tail */
+       base -= buf->page_len;
+       if (todo) {
+               c = buf->tail->iov_base + base;
+               if (copied) {
+                       unsigned int l = desc->elem_size - copied;
+
+                       if (encode)
+                               memcpy(c, elem + copied, l);
+                       else {
+                               memcpy(elem + copied, c, l);
+                               err = desc->xcode(desc, elem);
+                               if (err)
+                                       goto out;
+                       }
+                       todo -= l;
+                       c += l;
+               }
+               while (todo) {
+                       err = desc->xcode(desc, c);
+                       if (err)
+                               goto out;
+                       c += desc->elem_size;
+                       todo -= desc->elem_size;
+               }
+       }
+       err = 0;
+
+out:
+       if (elem)
+               kfree(elem);
+       if (ppages)
+               kunmap(*ppages);
+       return err;
+}
+
+int
+xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
+                 struct xdr_array2_desc *desc)
+{
+       if (base >= buf->len)
+               return -EINVAL;
+
+       return xdr_xcode_array2(buf, base, desc, 0);
+}
+
+int
+xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
+                 struct xdr_array2_desc *desc)
+{
+       if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
+           buf->head->iov_len + buf->page_len + buf->tail->iov_len)
+               return -EINVAL;
+
+       return xdr_xcode_array2(buf, base, desc, 1);
+}