Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / sunrpc / xdr.c
index 67b9f03..ca4bfa5 100644 (file)
@@ -6,15 +6,12 @@
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  */
 
+#include <linux/module.h>
 #include <linux/types.h>
-#include <linux/socket.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/pagemap.h>
 #include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/net.h>
-#include <net/sock.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/msg_prot.h>
 
@@ -95,27 +92,6 @@ xdr_encode_string(u32 *p, const char *string)
        return xdr_encode_array(p, string, strlen(string));
 }
 
-u32 *
-xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen)
-{
-       unsigned int    len;
-       char            *string;
-
-       if ((len = ntohl(*p++)) > maxlen)
-               return NULL;
-       if (lenp)
-               *lenp = len;
-       if ((len % 4) != 0) {
-               string = (char *) p;
-       } else {
-               string = (char *) (p - 1);
-               memmove(string, p, len);
-       }
-       string[len] = '\0';
-       *sp = string;
-       return p + XDR_QUADLEN(len);
-}
-
 u32 *
 xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen)
 {
@@ -176,162 +152,6 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
        xdr->buflen += len;
 }
 
-void
-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;
-       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);
-               if (ret != len || !desc->count)
-                       return;
-               base = 0;
-       } else
-               base -= len;
-
-       if (pglen == 0)
-               goto copy_tail;
-       if (base >= pglen) {
-               base -= pglen;
-               goto copy_tail;
-       }
-       if (base || xdr->page_base) {
-               pglen -= base;
-               base  += xdr->page_base;
-               ppage += base >> PAGE_CACHE_SHIFT;
-               base &= ~PAGE_CACHE_MASK;
-       }
-       do {
-               char *kaddr;
-
-               len = PAGE_CACHE_SIZE;
-               kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA);
-               if (base) {
-                       len -= base;
-                       if (pglen < len)
-                               len = pglen;
-                       ret = copy_actor(desc, kaddr + base, len);
-                       base = 0;
-               } else {
-                       if (pglen < len)
-                               len = pglen;
-                       ret = copy_actor(desc, kaddr, len);
-               }
-               flush_dcache_page(*ppage);
-               kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA);
-               if (ret != len || !desc->count)
-                       return;
-               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);
-}
-
-
-int
-xdr_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
-               struct xdr_buf *xdr, unsigned int base, int msgflags)
-{
-       struct page **ppage = xdr->pages;
-       unsigned int len, pglen = xdr->page_len;
-       int err, ret = 0;
-       ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
-
-       len = xdr->head[0].iov_len;
-       if (base < len || (addr != NULL && base == 0)) {
-               struct kvec iov = {
-                       .iov_base = xdr->head[0].iov_base + base,
-                       .iov_len  = len - base,
-               };
-               struct msghdr msg = {
-                       .msg_name    = addr,
-                       .msg_namelen = addrlen,
-                       .msg_flags   = msgflags,
-               };
-               if (xdr->len > len)
-                       msg.msg_flags |= MSG_MORE;
-
-               if (iov.iov_len != 0)
-                       err = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
-               else
-                       err = kernel_sendmsg(sock, &msg, NULL, 0, 0);
-               if (ret == 0)
-                       ret = err;
-               else if (err > 0)
-                       ret += err;
-               if (err != iov.iov_len)
-                       goto out;
-               base = 0;
-       } else
-               base -= len;
-
-       if (pglen == 0)
-               goto copy_tail;
-       if (base >= pglen) {
-               base -= pglen;
-               goto copy_tail;
-       }
-       if (base || xdr->page_base) {
-               pglen -= base;
-               base  += xdr->page_base;
-               ppage += base >> PAGE_CACHE_SHIFT;
-               base &= ~PAGE_CACHE_MASK;
-       }
-
-       sendpage = sock->ops->sendpage ? : sock_no_sendpage;
-       do {
-               int flags = msgflags;
-
-               len = PAGE_CACHE_SIZE;
-               if (base)
-                       len -= base;
-               if (pglen < len)
-                       len = pglen;
-
-               if (pglen != len || xdr->tail[0].iov_len != 0)
-                       flags |= MSG_MORE;
-
-               /* Hmm... We might be dealing with highmem pages */
-               if (PageHighMem(*ppage))
-                       sendpage = sock_no_sendpage;
-               err = sendpage(sock, *ppage, base, len, flags);
-               if (ret == 0)
-                       ret = err;
-               else if (err > 0)
-                       ret += err;
-               if (err != len)
-                       goto out;
-               base = 0;
-               ppage++;
-       } while ((pglen -= len) != 0);
-copy_tail:
-       len = xdr->tail[0].iov_len;
-       if (base < len) {
-               struct kvec iov = {
-                       .iov_base = xdr->tail[0].iov_base + base,
-                       .iov_len  = len - base,
-               };
-               struct msghdr msg = {
-                       .msg_flags   = msgflags,
-               };
-               err = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
-               if (ret == 0)
-                       ret = err;
-               else if (err > 0)
-                       ret += err;
-       }
-out:
-       return ret;
-}
-
 
 /*
  * Helper routines for doing 'memmove' like operations on a struct xdr_buf
@@ -616,12 +436,24 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
 void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p)
 {
        struct kvec *iov = buf->head;
+       int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
 
+       BUG_ON(scratch_len < 0);
        xdr->buf = buf;
        xdr->iov = iov;
-       xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len);
-       buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base;
-       xdr->p = p;
+       xdr->p = (uint32_t *)((char *)iov->iov_base + iov->iov_len);
+       xdr->end = (uint32_t *)((char *)iov->iov_base + scratch_len);
+       BUG_ON(iov->iov_len > scratch_len);
+
+       if (p != xdr->p && p != NULL) {
+               size_t len;
+
+               BUG_ON(p < xdr->p || p > xdr->end);
+               len = (char *)p - (char *)xdr->p;
+               xdr->p = p;
+               buf->len += len;
+               iov->iov_len += len;
+       }
 }
 EXPORT_SYMBOL(xdr_init_encode);
 
@@ -859,8 +691,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;
@@ -872,6 +730,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
@@ -882,7 +748,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;
 
@@ -915,3 +781,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 ||
+                   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:
+       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);
+}