Index: linux-2.6.7/include/linux/sunrpc/svc.h =================================================================== --- linux-2.6.7.orig/include/linux/sunrpc/svc.h 2004-08-04 15:55:32.045100738 +0200 +++ linux-2.6.7/include/linux/sunrpc/svc.h 2004-08-04 15:58:29.293911972 +0200 @@ -177,6 +177,25 @@ return vec->iov_len <= PAGE_SIZE; } +static inline struct page * +svc_take_arg_page(struct svc_rqst *rqstp) +{ + if (rqstp->rq_arghi <= rqstp->rq_argused) + return NULL; + return rqstp->rq_argpages[rqstp->rq_argused++]; +} + +static inline struct page * +svc_take_res_page(struct svc_rqst *rqstp) +{ + if (rqstp->rq_arghi <= rqstp->rq_argused) + return NULL; + rqstp->rq_arghi--; + rqstp->rq_respages[rqstp->rq_resused] = + rqstp->rq_argpages[rqstp->rq_arghi]; + return rqstp->rq_respages[rqstp->rq_resused++]; +} + static inline int svc_take_page(struct svc_rqst *rqstp) { if (rqstp->rq_arghi <= rqstp->rq_argused) Index: linux-2.6.7/include/linux/sunrpc/xdr.h =================================================================== --- linux-2.6.7.orig/include/linux/sunrpc/xdr.h 2004-08-04 15:56:41.107403302 +0200 +++ linux-2.6.7/include/linux/sunrpc/xdr.h 2004-08-04 15:58:29.294911875 +0200 @@ -174,6 +174,22 @@ extern int xdr_sendpages(struct socket *, struct sockaddr *, int, struct xdr_buf *, unsigned int, int); +extern int xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 w); +extern int xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *w); + +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: linux-2.6.7/net/sunrpc/sunrpc_syms.c =================================================================== --- linux-2.6.7.orig/net/sunrpc/sunrpc_syms.c 2004-06-16 07:19:52.000000000 +0200 +++ linux-2.6.7/net/sunrpc/sunrpc_syms.c 2004-08-04 15:59:08.508109267 +0200 @@ -128,6 +128,10 @@ 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: linux-2.6.7/net/sunrpc/xdr.c =================================================================== --- linux-2.6.7.orig/net/sunrpc/xdr.c 2004-08-04 15:56:41.108403205 +0200 +++ linux-2.6.7/net/sunrpc/xdr.c 2004-08-04 15:58:29.296911681 +0200 @@ -1047,3 +1047,244 @@ out: return -1; } + +int +xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 w) +{ + if (base < buf->head->iov_len) { + ((u32 *) buf->head->iov_base)[base >> 2] = htonl(w); + return 0; + } + base -= buf->head->iov_len; + if (base < buf->page_len) { + base += buf->page_base; + struct page **ppages = buf->pages + (base >> PAGE_CACHE_SHIFT); + u32 *p = (u32 *) kmap(*ppages) + + ((base & ~PAGE_CACHE_MASK) >> 2); + *p = htonl(w); + kunmap(*ppages); + return 0; + } + base -= buf->page_len; + if (base < buf->tail->iov_len) { + ((u32 *) buf->tail->iov_base)[base >> 2] = htonl(w); + return 0; + } + return -EINVAL; +} + +int +xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *w) +{ + if (base < buf->head->iov_len) { + *w = ntohl(((u32 *) buf->head->iov_base)[base >> 2]); + return 0; + } + base -= buf->head->iov_len; + if (base < buf->page_len) { + base += buf->page_base; + struct page **ppages = buf->pages + (base >> PAGE_CACHE_SHIFT); + u32 *p = (u32 *) kmap(*ppages) + + ((base & ~PAGE_CACHE_MASK) >> 2); + *w = ntohl(*p); + kunmap(*ppages); + return 0; + } + base -= buf->page_len; + if (base < buf->tail->iov_len) { + *w = ntohl(((u32 *) buf->tail->iov_base)[base >> 2]); + return 0; + } + return -EINVAL; +} + +/* 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[desc->elem_size], *c; + unsigned int copied = 0, todo, avail_here; + struct page **ppages = NULL; + int err = 0; + + 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 (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) { + 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; + unsigned int 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 (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 (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; + } + } + +out: + 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); +}