xdr.c revision a51482bde22f99c63fbbb57d5d46cc666384e379
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * linux/net/sunrpc/xdr.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Generic XDR support.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9a246b0105bbd9a70a698f69baae2042996f2a0e9Chuck Lever#include <linux/module.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pagemap.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sunrpc/xdr.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sunrpc/msg_prot.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * XDR functions for basic NFS types
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_encode_netobj(u32 *p, const struct xdr_netobj *obj)
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	quadlen = XDR_QUADLEN(obj->len);
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	p[quadlen] = 0;		/* zero trailing bytes */
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*p++ = htonl(obj->len);
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(p, obj->data, obj->len);
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p + XDR_QUADLEN(obj->len);
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_decode_netobj(u32 *p, struct xdr_netobj *obj)
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	len;
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((len = ntohl(*p++)) > XDR_MAX_NETOBJ)
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	obj->len  = len;
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	obj->data = (u8 *) p;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p + XDR_QUADLEN(len);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_encode_opaque_fixed - Encode fixed length opaque data
464dc3b16ba18c0f967ad100c52fa65b01a4f76ff0Pavel Pisa * @p: pointer to current position in XDR buffer.
474dc3b16ba18c0f967ad100c52fa65b01a4f76ff0Pavel Pisa * @ptr: pointer to data to encode (or NULL)
484dc3b16ba18c0f967ad100c52fa65b01a4f76ff0Pavel Pisa * @nbytes: size of data.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copy the array of data of length nbytes at ptr to the XDR buffer
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * at position p, then align to the next 32-bit boundary by padding
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * with zero bytes (see RFC1832).
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: if ptr is NULL, only the padding is performed.
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns the updated current XDR buffer position
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *xdr_encode_opaque_fixed(u32 *p, const void *ptr, unsigned int nbytes)
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (likely(nbytes != 0)) {
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int quadlen = XDR_QUADLEN(nbytes);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int padding = (quadlen << 2) - nbytes;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ptr != NULL)
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(p, ptr, nbytes);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (padding != 0)
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memset((char *)p + nbytes, 0, padding);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p += quadlen;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_encode_opaque_fixed);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_encode_opaque - Encode variable length opaque data
764dc3b16ba18c0f967ad100c52fa65b01a4f76ff0Pavel Pisa * @p: pointer to current position in XDR buffer.
774dc3b16ba18c0f967ad100c52fa65b01a4f76ff0Pavel Pisa * @ptr: pointer to data to encode (or NULL)
784dc3b16ba18c0f967ad100c52fa65b01a4f76ff0Pavel Pisa * @nbytes: size of data.
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Returns the updated current XDR buffer position
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *xdr_encode_opaque(u32 *p, const void *ptr, unsigned int nbytes)
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*p++ = htonl(nbytes);
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return xdr_encode_opaque_fixed(p, ptr, nbytes);
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_encode_opaque);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_encode_string(u32 *p, const char *string)
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return xdr_encode_array(p, string, strlen(string));
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen)
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	len;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char		*string;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((len = ntohl(*p++)) > maxlen)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (lenp)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*lenp = len;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((len % 4) != 0) {
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		string = (char *) p;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		string = (char *) (p - 1);
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memmove(string, p, len);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	string[len] = '\0';
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*sp = string;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p + XDR_QUADLEN(len);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsu32 *
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen)
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	len;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((len = ntohl(*p++)) > maxlen)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*lenp = len;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*sp = (char *) p;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p + XDR_QUADLEN(len);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 unsigned int len)
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *tail = xdr->tail;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 *p;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->pages = pages;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->page_base = base;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->page_len = len;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail->iov_base = p;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail->iov_len = 0;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len & 3) {
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int pad = 4 - (len & 3);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*p = 0;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tail->iov_base = (char *)p + (len & 3);
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tail->iov_len  = pad;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len += pad;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->buflen += len;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->len += len;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 struct page **pages, unsigned int base, unsigned int len)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *head = xdr->head;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *tail = xdr->tail;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *buf = (char *)head->iov_base;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int buflen = head->iov_len;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	head->iov_len  = offset;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->pages = pages;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->page_base = base;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->page_len = len;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail->iov_base = buf + offset;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail->iov_len = buflen - offset;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->buflen += len;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Helper routines for doing 'memmove' like operations on a struct xdr_buf
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * _shift_data_right_pages
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pages: vector of pages containing both the source and dest memory area.
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pgto_base: page vector address of destination
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pgfrom_base: page vector address of source
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: number of bytes to copy
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: the addresses pgto_base and pgfrom_base are both calculated in
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *       the same way:
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *            if a memory area starts at byte 'base' in page 'pages[i]',
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *            then its address is given as (i << PAGE_CACHE_SHIFT) + base
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Also note: pgfrom_base must be < pgto_base, but the memory areas
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	they point to may overlap.
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds_shift_data_right_pages(struct page **pages, size_t pgto_base,
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t pgfrom_base, size_t len)
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct page **pgfrom, **pgto;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *vfrom, *vto;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t copy;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	BUG_ON(pgto_base <= pgfrom_base);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgto_base += len;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgfrom_base += len;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgto = pages + (pgto_base >> PAGE_CACHE_SHIFT);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgfrom = pages + (pgfrom_base >> PAGE_CACHE_SHIFT);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgto_base &= ~PAGE_CACHE_MASK;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgfrom_base &= ~PAGE_CACHE_MASK;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Are any pointers crossing a page boundary? */
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pgto_base == 0) {
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			flush_dcache_page(*pgto);
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgto_base = PAGE_CACHE_SIZE;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgto--;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pgfrom_base == 0) {
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgfrom_base = PAGE_CACHE_SIZE;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgfrom--;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy = len;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy > pgto_base)
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = pgto_base;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy > pgfrom_base)
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = pgfrom_base;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgto_base -= copy;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgfrom_base -= copy;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vto = kmap_atomic(*pgto, KM_USER0);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vfrom = kmap_atomic(*pgfrom, KM_USER1);
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memmove(vto + pgto_base, vfrom + pgfrom_base, copy);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kunmap_atomic(vfrom, KM_USER1);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kunmap_atomic(vto, KM_USER0);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while ((len -= copy) != 0);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flush_dcache_page(*pgto);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * _copy_to_pages
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pages: array of pages
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pgbase: page vector address of destination
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @p: pointer to source data
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: length
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copies data from an arbitrary memory location into an array of pages
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The copy is assumed to be non-overlapping.
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds_copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct page **pgto;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *vto;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t copy;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgto = pages + (pgbase >> PAGE_CACHE_SHIFT);
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgbase &= ~PAGE_CACHE_MASK;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy = PAGE_CACHE_SIZE - pgbase;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy > len)
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = len;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vto = kmap_atomic(*pgto, KM_USER0);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(vto + pgbase, p, copy);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kunmap_atomic(vto, KM_USER0);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgbase += copy;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pgbase == PAGE_CACHE_SIZE) {
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			flush_dcache_page(*pgto);
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgbase = 0;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgto++;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p += copy;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while ((len -= copy) != 0);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flush_dcache_page(*pgto);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * _copy_from_pages
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @p: pointer to destination
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pages: array of pages
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pgbase: offset of source data
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: length
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copies data into an arbitrary memory location from an array of pages
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The copy is assumed to be non-overlapping.
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct page **pgfrom;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *vfrom;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t copy;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgfrom = pages + (pgbase >> PAGE_CACHE_SHIFT);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pgbase &= ~PAGE_CACHE_MASK;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy = PAGE_CACHE_SIZE - pgbase;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy > len)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = len;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vfrom = kmap_atomic(*pgfrom, KM_USER0);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(p, vfrom + pgbase, copy);
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kunmap_atomic(vfrom, KM_USER0);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgbase += copy;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pgbase == PAGE_CACHE_SIZE) {
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgbase = 0;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgfrom++;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p += copy;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while ((len -= copy) != 0);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_shrink_bufhead
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @buf: xdr_buf
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: bytes to remove from buf->head[0]
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Shrinks XDR buffer's header kvec buf->head[0] by
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 'len' bytes. The extra data is not lost, but is instead
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * moved into the inlined pages and/or the tail.
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *head, *tail;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t copy, offs;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int pglen = buf->page_len;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail = buf->tail;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	head = buf->head;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	BUG_ON (len > head->iov_len);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Shift the tail first */
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tail->iov_len != 0) {
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tail->iov_len > len) {
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = tail->iov_len - len;
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memmove((char *)tail->iov_base + len,
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					tail->iov_base, copy);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Copy from the inlined pages into the tail */
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy = len;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy > pglen)
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = pglen;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offs = len - copy;
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (offs >= tail->iov_len)
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = 0;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else if (copy > tail->iov_len - offs)
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = tail->iov_len - offs;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy != 0)
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			_copy_from_pages((char *)tail->iov_base + offs,
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					buf->pages,
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					buf->page_base + pglen + offs - len,
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					copy);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Do we also need to copy data from the head into the tail ? */
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len > pglen) {
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			offs = copy = len - pglen;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (copy > tail->iov_len)
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				copy = tail->iov_len;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(tail->iov_base,
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					(char *)head->iov_base +
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					head->iov_len - offs,
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					copy);
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Now handle pages */
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pglen != 0) {
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pglen > len)
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			_shift_data_right_pages(buf->pages,
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					buf->page_base + len,
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					buf->page_base,
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pglen - len);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy = len;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len > pglen)
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = pglen;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_copy_to_pages(buf->pages, buf->page_base,
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				(char *)head->iov_base + head->iov_len - len,
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				copy);
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	head->iov_len -= len;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->buflen -= len;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Have we truncated the message? */
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (buf->len > buf->buflen)
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf->len = buf->buflen;
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_shrink_pagelen
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @buf: xdr_buf
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: bytes to remove from buf->pages
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Shrinks XDR buffer's page array buf->pages by
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 'len' bytes. The extra data is not lost, but is instead
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * moved into the tail.
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *tail;
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t copy;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *p;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int pglen = buf->page_len;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail = buf->tail;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	BUG_ON (len > pglen);
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Shift the tail first */
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tail->iov_len != 0) {
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p = (char *)tail->iov_base + len;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tail->iov_len > len) {
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = tail->iov_len - len;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memmove(p, tail->iov_base, copy);
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buf->buflen -= len;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Copy from the inlined pages into the tail */
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy = len;
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy > tail->iov_len)
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			copy = tail->iov_len;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_copy_from_pages((char *)tail->iov_base,
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				buf->pages, buf->page_base + pglen - len,
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				copy);
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->page_len -= len;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->buflen -= len;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Have we truncated the message? */
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (buf->len > buf->buflen)
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf->len = buf->buflen;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_shift_buf(struct xdr_buf *buf, size_t len)
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr_shrink_bufhead(buf, len);
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_init_encode - Initialize a struct xdr_stream for sending data.
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @xdr: pointer to xdr_stream struct
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @buf: pointer to XDR buffer in which to encode data
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @p: current pointer inside XDR buffer
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: at the moment the RPC client only passes the length of our
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	 scratch buffer in the xdr_buf's header kvec. Previously this
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	 meant we needed to call xdr_adjust_iovec() after encoding the
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	 data. With the new scheme, the xdr_stream manages the details
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	 of the buffer length, and takes care of adjusting the kvec
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	 length for us.
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *iov = buf->head;
460334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
462334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	BUG_ON(scratch_len < 0);
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->buf = buf;
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->iov = iov;
465334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	xdr->p = (uint32_t *)((char *)iov->iov_base + iov->iov_len);
466334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	xdr->end = (uint32_t *)((char *)iov->iov_base + scratch_len);
467334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	BUG_ON(iov->iov_len > scratch_len);
468334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust
469334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	if (p != xdr->p && p != NULL) {
470334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust		size_t len;
471334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust
472334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust		BUG_ON(p < xdr->p || p > xdr->end);
473334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust		len = (char *)p - (char *)xdr->p;
474334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust		xdr->p = p;
475334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust		buf->len += len;
476334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust		iov->iov_len += len;
477334ccfd545bba9690515f2c5c167d5adb161989bTrond Myklebust	}
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_init_encode);
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_reserve_space - Reserve buffer space for sending
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @xdr: pointer to xdr_stream
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @nbytes: number of bytes to reserve
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Checks that we have enough buffer space to encode 'nbytes' more
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * bytes of data. If so, update the total xdr_buf length, and
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * adjust the length of the current kvec.
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsuint32_t * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t *p = xdr->p;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t *q;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* align nbytes on the next 32-bit boundary */
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nbytes += 3;
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nbytes &= ~3;
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	q = p + (nbytes >> 2);
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (unlikely(q > xdr->end || q < p))
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->p = q;
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->iov->iov_len += nbytes;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->buf->len += nbytes;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p;
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_reserve_space);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @xdr: pointer to xdr_stream
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @pages: list of pages
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @base: offset of first byte
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: length of data in bytes
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base,
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 unsigned int len)
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct xdr_buf *buf = xdr->buf;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *iov = buf->tail;
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->pages = pages;
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->page_base = base;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->page_len = len;
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iov->iov_base = (char *)xdr->p;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iov->iov_len  = 0;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->iov = iov;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len & 3) {
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int pad = 4 - (len & 3);
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		BUG_ON(xdr->p >= xdr->end);
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iov->iov_base = (char *)xdr->p + (len & 3);
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iov->iov_len  += pad;
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len += pad;
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*xdr->p++ = 0;
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->buflen += len;
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->len += len;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_write_pages);
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_init_decode - Initialize an xdr_stream for decoding data.
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @xdr: pointer to xdr_stream struct
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @buf: pointer to XDR buffer from which to decode data
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @p: current pointer inside XDR buffer
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p)
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *iov = buf->head;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int len = iov->iov_len;
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len > buf->len)
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len = buf->len;
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->buf = buf;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->iov = iov;
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->p = p;
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->end = (uint32_t *)((char *)iov->iov_base + len);
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_init_decode);
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_inline_decode - Retrieve non-page XDR data to decode
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @xdr: pointer to xdr_stream struct
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @nbytes: number of bytes of data to decode
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check if the input buffer is long enough to enable us to decode
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 'nbytes' more bytes of data starting at the current position.
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If so return the current pointer, then update the current
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * pointer position.
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsuint32_t * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t *p = xdr->p;
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint32_t *q = p + XDR_QUADLEN(nbytes);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (unlikely(q > xdr->end || q < p))
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->p = q;
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return p;
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_inline_decode);
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @xdr: pointer to xdr_stream struct
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @len: number of bytes of page data
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Moves data beyond the current pointer position from the XDR head[] buffer
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * into the page list. Any data that lies beyond current position + "len"
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * bytes is moved into the XDR tail[]. The current pointer is then
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * repositioned at the beginning of the XDR tail.
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct xdr_buf *buf = xdr->buf;
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct kvec *iov;
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t shift;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int end;
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int padding;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Realign pages to current pointer position */
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iov  = buf->head;
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (shift > 0)
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		xdr_shrink_bufhead(buf, shift);
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Truncate page data and move it into the tail */
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (buf->page_len > len)
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		xdr_shrink_pagelen(buf, buf->page_len - len);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	padding = (XDR_QUADLEN(len) << 2) - len;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->iov = iov = buf->tail;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Compute remaining message length.  */
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	end = iov->iov_len;
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	shift = buf->buflen - buf->len;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (shift < end)
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		end -= shift;
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (shift > 0)
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		end = 0;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Position current pointer at beginning of tail, and
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * set remaining message length.
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->p = (uint32_t *)((char *)iov->iov_base + padding);
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xdr->end = (uint32_t *)((char *)iov->iov_base + end);
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(xdr_read_pages);
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->head[0] = *iov;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->tail[0] = empty_iov;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->page_len = 0;
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf->buflen = buf->len = iov->iov_len;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Sets subiov to the intersection of iov with the buffer of length len
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * starting base bytes after iov.  Indicates empty intersection by setting
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * length of subiov to zero.  Decrements len by length of subiov, sets base
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to zero (or decrements it by length of iov if subiov is empty). */
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiov_subsegment(struct kvec *iov, struct kvec *subiov, int *base, int *len)
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (*base > iov->iov_len) {
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subiov->iov_base = NULL;
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subiov->iov_len = 0;
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*base -= iov->iov_len;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subiov->iov_base = iov->iov_base + *base;
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subiov->iov_len = min(*len, (int)iov->iov_len - *base);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*base = 0;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*len -= subiov->iov_len;
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Sets subbuf to the portion of buf of length len beginning base bytes
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * from the start of buf. Returns -1 if base of length are out of bounds. */
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int base, int len)
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	subbuf->buflen = subbuf->len = len;
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iov_subsegment(buf->head, subbuf->head, &base, &len);
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (base < buf->page_len) {
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i = (base + buf->page_base) >> PAGE_CACHE_SHIFT;
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subbuf->pages = &buf->pages[i];
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK;
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subbuf->page_len = min((int)buf->page_len - base, len);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len -= subbuf->page_len;
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		base = 0;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		base -= buf->page_len;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		subbuf->page_len = 0;
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iov_subsegment(buf->tail, subbuf->tail, &base, &len);
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (base || len)
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* obj is assumed to point to allocated memory of size at least len: */
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsread_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len)
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct xdr_buf subbuf;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_len;
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = xdr_buf_subsegment(buf, &subbuf, base, len);
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status)
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	this_len = min(len, (int)subbuf.head[0].iov_len);
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(obj, subbuf.head[0].iov_base, this_len);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len -= this_len;
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	obj += this_len;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	this_len = min(len, (int)subbuf.page_len);
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (this_len)
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len);
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len -= this_len;
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	obj += this_len;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	this_len = min(len, (int)subbuf.tail[0].iov_len);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(obj, subbuf.tail[0].iov_base, this_len);
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
715bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher/* obj is assumed to point to allocated memory of size at least len: */
716bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherint
717bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherwrite_bytes_to_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len)
718bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher{
719bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	struct xdr_buf subbuf;
720bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	int this_len;
721bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	int status;
722bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
723bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	status = xdr_buf_subsegment(buf, &subbuf, base, len);
724bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (status)
725bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		goto out;
726bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	this_len = min(len, (int)subbuf.head[0].iov_len);
727bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	memcpy(subbuf.head[0].iov_base, obj, this_len);
728bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	len -= this_len;
729bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	obj += this_len;
730bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	this_len = min(len, (int)subbuf.page_len);
731bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (this_len)
732bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		_copy_to_pages(subbuf.pages, subbuf.page_base, obj, this_len);
733bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	len -= this_len;
734bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	obj += this_len;
735bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	this_len = min(len, (int)subbuf.tail[0].iov_len);
736bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	memcpy(subbuf.tail[0].iov_base, obj, this_len);
737bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherout:
738bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	return status;
739bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher}
740bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
741bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherint
742bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherxdr_decode_word(struct xdr_buf *buf, int base, u32 *obj)
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32	raw;
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	status;
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status)
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*obj = ntohl(raw);
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
754bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherint
755bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherxdr_encode_word(struct xdr_buf *buf, int base, u32 obj)
756bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher{
757bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	u32	raw = htonl(obj);
758bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
759bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
760bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher}
761bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* If the netobj starting offset bytes from the start of xdr_buf is contained
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * entirely in the head or the tail, set object to point to it; otherwise
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * try to find space for it at the end of the tail, copy it there, and
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * set obj to point to it. */
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsxdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset)
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32	tail_offset = buf->head[0].iov_len + buf->page_len;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32	obj_end_offset;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
772bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (xdr_decode_word(buf, offset, &obj->len))
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	obj_end_offset = offset + 4 + obj->len;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (obj_end_offset <= buf->head[0].iov_len) {
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* The obj is contained entirely in the head: */
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		obj->data = buf->head[0].iov_base + offset + 4;
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (offset + 4 >= tail_offset) {
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (obj_end_offset - tail_offset
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				> buf->tail[0].iov_len)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* The obj is contained entirely in the tail: */
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		obj->data = buf->tail[0].iov_base
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			+ offset - tail_offset + 4;
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* use end of tail as storage for obj:
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * (We don't copy to the beginning because then we'd have
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * to worry about doing a potentially overlapping copy.
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * This assumes the object is at most half the length of the
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * tail.) */
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (obj->len > buf->tail[0].iov_len)
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len -
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				obj->len;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (read_bytes_from_xdr_buf(buf, offset + 4,
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					obj->data, obj->len))
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
805bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
806bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher/* Returns 0 on success, or else a negative error code. */
807bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherstatic int
808bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherxdr_xcode_array2(struct xdr_buf *buf, unsigned int base,
809bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		 struct xdr_array2_desc *desc, int encode)
810bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher{
811bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	char *elem = NULL, *c;
812bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	unsigned int copied = 0, todo, avail_here;
813bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	struct page **ppages = NULL;
814bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	int err;
815bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
816bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (encode) {
817bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		if (xdr_encode_word(buf, base, desc->array_len) != 0)
818bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			return -EINVAL;
819bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	} else {
820bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
82158fcb8df0bf663bb6b8f46cd3010bfe8d13d97cfTrond Myklebust		    desc->array_len > desc->array_maxlen ||
822bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		    (unsigned long) base + 4 + desc->array_len *
823bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				    desc->elem_size > buf->len)
824bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			return -EINVAL;
825bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	}
826bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	base += 4;
827bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
828bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (!desc->xcode)
829bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		return 0;
830bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
831bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	todo = desc->array_len * desc->elem_size;
832bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
833bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	/* process head */
834bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (todo && base < buf->head->iov_len) {
835bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		c = buf->head->iov_base + base;
836bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		avail_here = min_t(unsigned int, todo,
837bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				   buf->head->iov_len - base);
838bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		todo -= avail_here;
839bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
840bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		while (avail_here >= desc->elem_size) {
841bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			err = desc->xcode(desc, c);
842bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (err)
843bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				goto out;
844bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			c += desc->elem_size;
845bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			avail_here -= desc->elem_size;
846bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		}
847bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		if (avail_here) {
848bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (!elem) {
849bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				elem = kmalloc(desc->elem_size, GFP_KERNEL);
850bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				err = -ENOMEM;
851bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (!elem)
852bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					goto out;
853bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			}
854bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (encode) {
855bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				err = desc->xcode(desc, elem);
856bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (err)
857bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					goto out;
858bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				memcpy(c, elem, avail_here);
859bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			} else
860bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				memcpy(elem, c, avail_here);
861bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			copied = avail_here;
862bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		}
863bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		base = buf->head->iov_len;  /* align to start of pages */
864bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	}
865bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
866bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	/* process pages array */
867bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	base -= buf->head->iov_len;
868bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (todo && base < buf->page_len) {
869bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		unsigned int avail_page;
870bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
871bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		avail_here = min(todo, buf->page_len - base);
872bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		todo -= avail_here;
873bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
874bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		base += buf->page_base;
875bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		ppages = buf->pages + (base >> PAGE_CACHE_SHIFT);
876bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		base &= ~PAGE_CACHE_MASK;
877bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		avail_page = min_t(unsigned int, PAGE_CACHE_SIZE - base,
878bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					avail_here);
879bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		c = kmap(*ppages) + base;
880bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
881bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		while (avail_here) {
882bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			avail_here -= avail_page;
883bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (copied || avail_page < desc->elem_size) {
884bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				unsigned int l = min(avail_page,
885bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					desc->elem_size - copied);
886bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (!elem) {
887bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					elem = kmalloc(desc->elem_size,
888bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						       GFP_KERNEL);
889bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					err = -ENOMEM;
890bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (!elem)
891bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						goto out;
892bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				}
893bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (encode) {
894bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (!copied) {
895bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						err = desc->xcode(desc, elem);
896bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						if (err)
897bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher							goto out;
898bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					}
899bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					memcpy(c, elem + copied, l);
900bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					copied += l;
901bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (copied == desc->elem_size)
902bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						copied = 0;
903bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				} else {
904bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					memcpy(elem + copied, c, l);
905bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					copied += l;
906bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (copied == desc->elem_size) {
907bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						err = desc->xcode(desc, elem);
908bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						if (err)
909bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher							goto out;
910bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						copied = 0;
911bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					}
912bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				}
913bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				avail_page -= l;
914bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				c += l;
915bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			}
916bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			while (avail_page >= desc->elem_size) {
917bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				err = desc->xcode(desc, c);
918bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (err)
919bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					goto out;
920bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				c += desc->elem_size;
921bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				avail_page -= desc->elem_size;
922bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			}
923bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (avail_page) {
924bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				unsigned int l = min(avail_page,
925bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					    desc->elem_size - copied);
926bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (!elem) {
927bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					elem = kmalloc(desc->elem_size,
928bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						       GFP_KERNEL);
929bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					err = -ENOMEM;
930bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (!elem)
931bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						goto out;
932bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				}
933bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (encode) {
934bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (!copied) {
935bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						err = desc->xcode(desc, elem);
936bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						if (err)
937bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher							goto out;
938bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					}
939bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					memcpy(c, elem + copied, l);
940bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					copied += l;
941bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (copied == desc->elem_size)
942bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						copied = 0;
943bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				} else {
944bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					memcpy(elem + copied, c, l);
945bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					copied += l;
946bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					if (copied == desc->elem_size) {
947bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						err = desc->xcode(desc, elem);
948bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						if (err)
949bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher							goto out;
950bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher						copied = 0;
951bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					}
952bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				}
953bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			}
954bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (avail_here) {
955bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				kunmap(*ppages);
956bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				ppages++;
957bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				c = kmap(*ppages);
958bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			}
959bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
960bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			avail_page = min(avail_here,
961bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				 (unsigned int) PAGE_CACHE_SIZE);
962bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		}
963bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		base = buf->page_len;  /* align to start of tail */
964bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	}
965bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
966bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	/* process tail */
967bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	base -= buf->page_len;
968bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (todo) {
969bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		c = buf->tail->iov_base + base;
970bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		if (copied) {
971bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			unsigned int l = desc->elem_size - copied;
972bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
973bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (encode)
974bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				memcpy(c, elem + copied, l);
975bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			else {
976bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				memcpy(elem + copied, c, l);
977bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				err = desc->xcode(desc, elem);
978bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				if (err)
979bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher					goto out;
980bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			}
981bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			todo -= l;
982bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			c += l;
983bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		}
984bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		while (todo) {
985bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			err = desc->xcode(desc, c);
986bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			if (err)
987bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher				goto out;
988bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			c += desc->elem_size;
989bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher			todo -= desc->elem_size;
990bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		}
991bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	}
992bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	err = 0;
993bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
994bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherout:
995a51482bde22f99c63fbbb57d5d46cc666384e379Jesper Juhl	kfree(elem);
996bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (ppages)
997bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		kunmap(*ppages);
998bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	return err;
999bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher}
1000bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
1001bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherint
1002bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherxdr_decode_array2(struct xdr_buf *buf, unsigned int base,
1003bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		  struct xdr_array2_desc *desc)
1004bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher{
1005bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if (base >= buf->len)
1006bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		return -EINVAL;
1007bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
1008bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	return xdr_xcode_array2(buf, base, desc, 0);
1009bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher}
1010bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
1011bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherint
1012bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacherxdr_encode_array2(struct xdr_buf *buf, unsigned int base,
1013bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		  struct xdr_array2_desc *desc)
1014bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher{
1015bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
1016bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	    buf->head->iov_len + buf->page_len + buf->tail->iov_len)
1017bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher		return -EINVAL;
1018bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher
1019bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher	return xdr_xcode_array2(buf, base, desc, 1);
1020bd8100e7eda87507649c6ba4cb32173b34e49986Andreas Gruenbacher}
1021