cleanup
[linux-2.4.21-pre4.git] / net / sunrpc / xdr.c
1 /*
2  * linux/net/sunrpc/xdr.c
3  *
4  * Generic XDR support.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include <linux/types.h>
10 #include <linux/socket.h>
11 #include <linux/string.h>
12 #include <linux/kernel.h>
13 #include <linux/pagemap.h>
14 #include <linux/errno.h>
15 #include <linux/in.h>
16 #include <linux/sunrpc/xdr.h>
17 #include <linux/sunrpc/msg_prot.h>
18
19 /*
20  * XDR functions for basic NFS types
21  */
22 u32 *
23 xdr_encode_netobj(u32 *p, const struct xdr_netobj *obj)
24 {
25         unsigned int    quadlen = XDR_QUADLEN(obj->len);
26
27         p[quadlen] = 0;         /* zero trailing bytes */
28         *p++ = htonl(obj->len);
29         memcpy(p, obj->data, obj->len);
30         return p + XDR_QUADLEN(obj->len);
31 }
32
33 u32 *
34 xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len)
35 {
36         if (ntohl(*p++) != len)
37                 return NULL;
38         memcpy(obj, p, len);
39         return p + XDR_QUADLEN(len);
40 }
41
42 u32 *
43 xdr_decode_netobj(u32 *p, struct xdr_netobj *obj)
44 {
45         unsigned int    len;
46
47         if ((len = ntohl(*p++)) > XDR_MAX_NETOBJ)
48                 return NULL;
49         obj->len  = len;
50         obj->data = (u8 *) p;
51         return p + XDR_QUADLEN(len);
52 }
53
54 u32 *
55 xdr_encode_array(u32 *p, const char *array, unsigned int len)
56 {
57         int quadlen = XDR_QUADLEN(len);
58
59         p[quadlen] = 0;
60         *p++ = htonl(len);
61         memcpy(p, array, len);
62         return p + quadlen;
63 }
64
65 u32 *
66 xdr_encode_string(u32 *p, const char *string)
67 {
68         return xdr_encode_array(p, string, strlen(string));
69 }
70
71 u32 *
72 xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen)
73 {
74         unsigned int    len;
75         char            *string;
76
77         if ((len = ntohl(*p++)) > maxlen)
78                 return NULL;
79         if (lenp)
80                 *lenp = len;
81         if ((len % 4) != 0) {
82                 string = (char *) p;
83         } else {
84                 string = (char *) (p - 1);
85                 memmove(string, p, len);
86         }
87         string[len] = '\0';
88         *sp = string;
89         return p + XDR_QUADLEN(len);
90 }
91
92 u32 *
93 xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen)
94 {
95         unsigned int    len;
96
97         if ((len = ntohl(*p++)) > maxlen)
98                 return NULL;
99         *lenp = len;
100         *sp = (char *) p;
101         return p + XDR_QUADLEN(len);
102 }
103
104
105 void
106 xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
107                  unsigned int len)
108 {
109         xdr->pages = pages;
110         xdr->page_base = base;
111         xdr->page_len = len;
112
113         if (len & 3) {
114                 struct iovec *iov = xdr->tail;
115                 unsigned int pad = 4 - (len & 3);
116
117                 iov->iov_base = (void *) "\0\0\0";
118                 iov->iov_len  = pad;
119                 len += pad;
120         }
121         xdr->len += len;
122 }
123
124 void
125 xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
126                  struct page **pages, unsigned int base, unsigned int len)
127 {
128         struct iovec *head = xdr->head;
129         struct iovec *tail = xdr->tail;
130         char *buf = (char *)head->iov_base;
131         unsigned int buflen = head->iov_len;
132
133         head->iov_len  = offset;
134
135         xdr->pages = pages;
136         xdr->page_base = base;
137         xdr->page_len = len;
138
139         tail->iov_base = buf + offset;
140         tail->iov_len = buflen - offset;
141
142         xdr->len += len;
143 }
144
145 /*
146  * Realign the iovec if the server missed out some reply elements
147  * (such as post-op attributes,...)
148  * Note: This is a simple implementation that assumes that
149  *            len <= iov->iov_len !!!
150  *       The RPC header (assumed to be the 1st element in the iov array)
151  *            is not shifted.
152  */
153 void xdr_shift_iovec(struct iovec *iov, int nr, size_t len)
154 {
155         struct iovec *pvec;
156
157         for (pvec = iov + nr - 1; nr > 1; nr--, pvec--) {
158                 struct iovec *svec = pvec - 1;
159
160                 if (len > pvec->iov_len) {
161                         printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
162                         return;
163                 }
164                 memmove((char *)pvec->iov_base + len, pvec->iov_base,
165                         pvec->iov_len - len);
166
167                 if (len > svec->iov_len) {
168                         printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
169                         return;
170                 }
171                 memcpy(pvec->iov_base,
172                        (char *)svec->iov_base + svec->iov_len - len, len);
173         }
174 }
175
176 /*
177  * Map a struct xdr_buf into an iovec array.
178  */
179 int xdr_kmap(struct iovec *iov_base, struct xdr_buf *xdr, unsigned int base)
180 {
181         struct iovec    *iov = iov_base;
182         struct page     **ppage = xdr->pages;
183         unsigned int    len, pglen = xdr->page_len;
184
185         len = xdr->head[0].iov_len;
186         if (base < len) {
187                 iov->iov_len = len - base;
188                 iov->iov_base = (char *)xdr->head[0].iov_base + base;
189                 iov++;
190                 base = 0;
191         } else
192                 base -= len;
193
194         if (pglen == 0)
195                 goto map_tail;
196         if (base >= pglen) {
197                 base -= pglen;
198                 goto map_tail;
199         }
200         if (base || xdr->page_base) {
201                 pglen -= base;
202                 base  += xdr->page_base;
203                 ppage += base >> PAGE_CACHE_SHIFT;
204                 base &= ~PAGE_CACHE_MASK;
205         }
206         do {
207                 len = PAGE_CACHE_SIZE;
208                 iov->iov_base = kmap(*ppage);
209                 if (base) {
210                         iov->iov_base += base;
211                         len -= base;
212                         base = 0;
213                 }
214                 if (pglen < len)
215                         len = pglen;
216                 iov->iov_len = len;
217                 iov++;
218                 ppage++;
219         } while ((pglen -= len) != 0);
220 map_tail:
221         if (xdr->tail[0].iov_len) {
222                 iov->iov_len = xdr->tail[0].iov_len - base;
223                 iov->iov_base = (char *)xdr->tail[0].iov_base + base;
224                 iov++;
225         }
226         return (iov - iov_base);
227 }
228
229 void xdr_kunmap(struct xdr_buf *xdr, unsigned int base)
230 {
231         struct page     **ppage = xdr->pages;
232         unsigned int    pglen = xdr->page_len;
233
234         if (!pglen)
235                 return;
236         if (base > xdr->head[0].iov_len)
237                 base -= xdr->head[0].iov_len;
238         else
239                 base = 0;
240
241         if (base >= pglen)
242                 return;
243         if (base || xdr->page_base) {
244                 pglen -= base;
245                 base  += xdr->page_base;
246                 ppage += base >> PAGE_CACHE_SHIFT;
247                 /* Note: The offset means that the length of the first
248                  * page is really (PAGE_CACHE_SIZE - (base & ~PAGE_CACHE_MASK)).
249                  * In order to avoid an extra test inside the loop,
250                  * we bump pglen here, and just subtract PAGE_CACHE_SIZE... */
251                 pglen += base & ~PAGE_CACHE_MASK;
252         }
253         for (;;) {
254                 flush_dcache_page(*ppage);
255                 kunmap(*ppage);
256                 if (pglen <= PAGE_CACHE_SIZE)
257                         break;
258                 pglen -= PAGE_CACHE_SIZE;
259                 ppage++;
260         }
261 }
262
263 void
264 xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
265                           skb_reader_t *desc,
266                           skb_read_actor_t copy_actor)
267 {
268         struct page     **ppage = xdr->pages;
269         unsigned int    len, pglen = xdr->page_len;
270         int             ret;
271
272         len = xdr->head[0].iov_len;
273         if (base < len) {
274                 len -= base;
275                 ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len);
276                 if (ret != len || !desc->count)
277                         return;
278                 base = 0;
279         } else
280                 base -= len;
281
282         if (pglen == 0)
283                 goto copy_tail;
284         if (base >= pglen) {
285                 base -= pglen;
286                 goto copy_tail;
287         }
288         if (base || xdr->page_base) {
289                 pglen -= base;
290                 base  += xdr->page_base;
291                 ppage += base >> PAGE_CACHE_SHIFT;
292                 base &= ~PAGE_CACHE_MASK;
293         }
294         do {
295                 char *kaddr;
296
297                 len = PAGE_CACHE_SIZE;
298                 kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA);
299                 if (base) {
300                         len -= base;
301                         if (pglen < len)
302                                 len = pglen;
303                         ret = copy_actor(desc, kaddr + base, len);
304                         base = 0;
305                 } else {
306                         if (pglen < len)
307                                 len = pglen;
308                         ret = copy_actor(desc, kaddr, len);
309                 }
310                 kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA);
311                 if (ret != len || !desc->count)
312                         return;
313                 ppage++;
314         } while ((pglen -= len) != 0);
315 copy_tail:
316         len = xdr->tail[0].iov_len;
317         if (len)
318                 copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len);
319 }
320
321 void
322 xdr_shift_buf(struct xdr_buf *xdr, size_t len)
323 {
324         struct iovec iov[MAX_IOVEC];
325         unsigned int nr;
326
327         nr = xdr_kmap(iov, xdr, 0);
328         xdr_shift_iovec(iov, nr, len);
329         xdr_kunmap(xdr, 0);
330 }