]> err.no Git - linux-2.6/blobdiff - net/sunrpc/xdr.c
[NETFILTER]: CLUSTERIP: fix memcpy() length typo
[linux-2.6] / net / sunrpc / xdr.c
index 65b268d39782b8be87a6962776ddb0fe461b5bd4..fde16f40a581dfb64e725e694afb4df5090693ed 100644 (file)
@@ -176,21 +176,23 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
        xdr->buflen += len;
 }
 
-int
+ssize_t
 xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
                          skb_reader_t *desc,
                          skb_read_actor_t copy_actor)
 {
        struct page     **ppage = xdr->pages;
        unsigned int    len, pglen = xdr->page_len;
+       ssize_t         copied = 0;
        int             ret;
 
        len = xdr->head[0].iov_len;
        if (base < len) {
                len -= base;
                ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len);
+               copied += ret;
                if (ret != len || !desc->count)
-                       return 0;
+                       goto out;
                base = 0;
        } else
                base -= len;
@@ -214,8 +216,11 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
                 * are small by default but can get huge. */
                if (unlikely(*ppage == NULL)) {
                        *ppage = alloc_page(GFP_ATOMIC);
-                       if (unlikely(*ppage == NULL))
-                               return -ENOMEM;
+                       if (unlikely(*ppage == NULL)) {
+                               if (copied == 0)
+                                       copied = -ENOMEM;
+                               goto out;
+                       }
                }
 
                len = PAGE_CACHE_SIZE;
@@ -233,16 +238,17 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
                }
                flush_dcache_page(*ppage);
                kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA);
+               copied += ret;
                if (ret != len || !desc->count)
-                       return 0;
+                       goto out;
                ppage++;
        } while ((pglen -= len) != 0);
 copy_tail:
        len = xdr->tail[0].iov_len;
        if (base < len)
-               copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base);
-
-       return 0;
+               copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base);
+out:
+       return copied;
 }
 
 
@@ -881,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;
@@ -894,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
@@ -904,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;
 
@@ -937,3 +977,220 @@ 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 ||
+                   desc->array_len > desc->array_maxlen ||
+                   (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);
+}