blob: 71e03b930b70a3ac9e84d8a50d4cefb4d5ece69f [file] [log] [blame]
Thomas Gleixner457c8992019-05-19 13:08:55 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * linux/net/sunrpc/xdr.c
4 *
5 * Generic XDR support.
6 *
7 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
8 */
9
Chuck Levera246b012005-08-11 16:25:23 -040010#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090011#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/string.h>
14#include <linux/kernel.h>
15#include <linux/pagemap.h>
16#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/sunrpc/xdr.h>
18#include <linux/sunrpc/msg_prot.h>
Trond Myklebust9d96acb2018-09-13 12:22:04 -040019#include <linux/bvec.h>
Chuck Lever55828632019-02-11 11:24:10 -050020#include <trace/events/sunrpc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021
Anna Schumakere6ac0ac2020-04-21 11:27:00 -040022static void _copy_to_pages(struct page **, size_t, const char *, size_t);
23
24
Linus Torvalds1da177e2005-04-16 15:20:36 -070025/*
26 * XDR functions for basic NFS types
27 */
Alexey Dobriyand8ed0292006-09-26 22:29:38 -070028__be32 *
29xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj)
Linus Torvalds1da177e2005-04-16 15:20:36 -070030{
31 unsigned int quadlen = XDR_QUADLEN(obj->len);
32
33 p[quadlen] = 0; /* zero trailing bytes */
Benny Halevy9f162d22009-08-14 17:18:44 +030034 *p++ = cpu_to_be32(obj->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 memcpy(p, obj->data, obj->len);
36 return p + XDR_QUADLEN(obj->len);
37}
Trond Myklebust468039e2008-12-23 15:21:31 -050038EXPORT_SYMBOL_GPL(xdr_encode_netobj);
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Alexey Dobriyand8ed0292006-09-26 22:29:38 -070040__be32 *
41xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042{
43 unsigned int len;
44
Benny Halevy98866b52009-08-14 17:18:49 +030045 if ((len = be32_to_cpu(*p++)) > XDR_MAX_NETOBJ)
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 return NULL;
47 obj->len = len;
48 obj->data = (u8 *) p;
49 return p + XDR_QUADLEN(len);
50}
Trond Myklebust468039e2008-12-23 15:21:31 -050051EXPORT_SYMBOL_GPL(xdr_decode_netobj);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53/**
54 * xdr_encode_opaque_fixed - Encode fixed length opaque data
Pavel Pisa4dc3b162005-05-01 08:59:25 -070055 * @p: pointer to current position in XDR buffer.
56 * @ptr: pointer to data to encode (or NULL)
57 * @nbytes: size of data.
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 *
59 * Copy the array of data of length nbytes at ptr to the XDR buffer
60 * at position p, then align to the next 32-bit boundary by padding
61 * with zero bytes (see RFC1832).
62 * Note: if ptr is NULL, only the padding is performed.
63 *
64 * Returns the updated current XDR buffer position
65 *
66 */
Alexey Dobriyand8ed0292006-09-26 22:29:38 -070067__be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -070068{
69 if (likely(nbytes != 0)) {
70 unsigned int quadlen = XDR_QUADLEN(nbytes);
71 unsigned int padding = (quadlen << 2) - nbytes;
72
73 if (ptr != NULL)
74 memcpy(p, ptr, nbytes);
75 if (padding != 0)
76 memset((char *)p + nbytes, 0, padding);
77 p += quadlen;
78 }
79 return p;
80}
Trond Myklebust468039e2008-12-23 15:21:31 -050081EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83/**
84 * xdr_encode_opaque - Encode variable length opaque data
Pavel Pisa4dc3b162005-05-01 08:59:25 -070085 * @p: pointer to current position in XDR buffer.
86 * @ptr: pointer to data to encode (or NULL)
87 * @nbytes: size of data.
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 *
89 * Returns the updated current XDR buffer position
90 */
Alexey Dobriyand8ed0292006-09-26 22:29:38 -070091__be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
Benny Halevy9f162d22009-08-14 17:18:44 +030093 *p++ = cpu_to_be32(nbytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 return xdr_encode_opaque_fixed(p, ptr, nbytes);
95}
Trond Myklebust468039e2008-12-23 15:21:31 -050096EXPORT_SYMBOL_GPL(xdr_encode_opaque);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
Alexey Dobriyand8ed0292006-09-26 22:29:38 -070098__be32 *
99xdr_encode_string(__be32 *p, const char *string)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
101 return xdr_encode_array(p, string, strlen(string));
102}
Trond Myklebust468039e2008-12-23 15:21:31 -0500103EXPORT_SYMBOL_GPL(xdr_encode_string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700105__be32 *
Chuck Levere5cff482007-11-01 16:56:47 -0400106xdr_decode_string_inplace(__be32 *p, char **sp,
107 unsigned int *lenp, unsigned int maxlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108{
Chuck Levere5cff482007-11-01 16:56:47 -0400109 u32 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Benny Halevy98866b52009-08-14 17:18:49 +0300111 len = be32_to_cpu(*p++);
Chuck Levere5cff482007-11-01 16:56:47 -0400112 if (len > maxlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 return NULL;
114 *lenp = len;
115 *sp = (char *) p;
116 return p + XDR_QUADLEN(len);
117}
Trond Myklebust468039e2008-12-23 15:21:31 -0500118EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Chuck Leverb4687da2010-09-21 16:55:48 -0400120/**
121 * xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf
122 * @buf: XDR buffer where string resides
123 * @len: length of string, in bytes
124 *
125 */
126void
127xdr_terminate_string(struct xdr_buf *buf, const u32 len)
128{
129 char *kaddr;
130
Cong Wangb8541782011-11-25 23:14:40 +0800131 kaddr = kmap_atomic(buf->pages[0]);
Chuck Leverb4687da2010-09-21 16:55:48 -0400132 kaddr[buf->page_base + len] = '\0';
Cong Wangb8541782011-11-25 23:14:40 +0800133 kunmap_atomic(kaddr);
Chuck Leverb4687da2010-09-21 16:55:48 -0400134}
Trond Myklebust0d961aa2011-07-13 19:24:15 -0400135EXPORT_SYMBOL_GPL(xdr_terminate_string);
Chuck Leverb4687da2010-09-21 16:55:48 -0400136
Trond Myklebust9d96acb2018-09-13 12:22:04 -0400137size_t
138xdr_buf_pagecount(struct xdr_buf *buf)
139{
140 if (!buf->page_len)
141 return 0;
142 return (buf->page_base + buf->page_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
143}
144
145int
146xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp)
147{
148 size_t i, n = xdr_buf_pagecount(buf);
149
150 if (n != 0 && buf->bvec == NULL) {
151 buf->bvec = kmalloc_array(n, sizeof(buf->bvec[0]), gfp);
152 if (!buf->bvec)
153 return -ENOMEM;
154 for (i = 0; i < n; i++) {
155 buf->bvec[i].bv_page = buf->pages[i];
156 buf->bvec[i].bv_len = PAGE_SIZE;
157 buf->bvec[i].bv_offset = 0;
158 }
159 }
160 return 0;
161}
162
163void
164xdr_free_bvec(struct xdr_buf *buf)
165{
166 kfree(buf->bvec);
167 buf->bvec = NULL;
168}
169
Chuck Levercf500ba2019-02-11 11:25:20 -0500170/**
171 * xdr_inline_pages - Prepare receive buffer for a large reply
172 * @xdr: xdr_buf into which reply will be placed
173 * @offset: expected offset where data payload will start, in bytes
174 * @pages: vector of struct page pointers
175 * @base: offset in first page where receive should start, in bytes
176 * @len: expected size of the upper layer data payload, in bytes
177 *
178 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
181 struct page **pages, unsigned int base, unsigned int len)
182{
183 struct kvec *head = xdr->head;
184 struct kvec *tail = xdr->tail;
185 char *buf = (char *)head->iov_base;
186 unsigned int buflen = head->iov_len;
187
188 head->iov_len = offset;
189
190 xdr->pages = pages;
191 xdr->page_base = base;
192 xdr->page_len = len;
193
194 tail->iov_base = buf + offset;
195 tail->iov_len = buflen - offset;
Chuck Lever02ef04e2019-02-11 11:25:25 -0500196 if ((xdr->page_len & 3) == 0)
197 tail->iov_len -= sizeof(__be32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198
199 xdr->buflen += len;
200}
Trond Myklebust468039e2008-12-23 15:21:31 -0500201EXPORT_SYMBOL_GPL(xdr_inline_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203/*
204 * Helper routines for doing 'memmove' like operations on a struct xdr_buf
Ben Hutchings2c530402012-07-10 10:55:09 +0000205 */
206
207/**
Anna Schumakere6ac0ac2020-04-21 11:27:00 -0400208 * _shift_data_left_pages
209 * @pages: vector of pages containing both the source and dest memory area.
210 * @pgto_base: page vector address of destination
211 * @pgfrom_base: page vector address of source
212 * @len: number of bytes to copy
213 *
214 * Note: the addresses pgto_base and pgfrom_base are both calculated in
215 * the same way:
216 * if a memory area starts at byte 'base' in page 'pages[i]',
217 * then its address is given as (i << PAGE_CACHE_SHIFT) + base
218 * Alse note: pgto_base must be < pgfrom_base, but the memory areas
219 * they point to may overlap.
220 */
221static void
222_shift_data_left_pages(struct page **pages, size_t pgto_base,
223 size_t pgfrom_base, size_t len)
224{
225 struct page **pgfrom, **pgto;
226 char *vfrom, *vto;
227 size_t copy;
228
229 BUG_ON(pgfrom_base <= pgto_base);
230
231 pgto = pages + (pgto_base >> PAGE_SHIFT);
232 pgfrom = pages + (pgfrom_base >> PAGE_SHIFT);
233
234 pgto_base &= ~PAGE_MASK;
235 pgfrom_base &= ~PAGE_MASK;
236
237 do {
238 if (pgto_base >= PAGE_SIZE) {
239 pgto_base = 0;
240 pgto++;
241 }
242 if (pgfrom_base >= PAGE_SIZE){
243 pgfrom_base = 0;
244 pgfrom++;
245 }
246
247 copy = len;
248 if (copy > (PAGE_SIZE - pgto_base))
249 copy = PAGE_SIZE - pgto_base;
250 if (copy > (PAGE_SIZE - pgfrom_base))
251 copy = PAGE_SIZE - pgfrom_base;
252
253 vto = kmap_atomic(*pgto);
254 if (*pgto != *pgfrom) {
255 vfrom = kmap_atomic(*pgfrom);
256 memcpy(vto + pgto_base, vfrom + pgfrom_base, copy);
257 kunmap_atomic(vfrom);
258 } else
259 memmove(vto + pgto_base, vto + pgfrom_base, copy);
260 flush_dcache_page(*pgto);
261 kunmap_atomic(vto);
262
263 pgto_base += copy;
264 pgfrom_base += copy;
265
266 } while ((len -= copy) != 0);
267}
268
269static void
270_shift_data_left_tail(struct xdr_buf *buf, unsigned int pgto, size_t len)
271{
272 struct kvec *tail = buf->tail;
273
274 if (len > tail->iov_len)
275 len = tail->iov_len;
276
277 _copy_to_pages(buf->pages,
278 buf->page_base + pgto,
279 (char *)tail->iov_base,
280 len);
281 tail->iov_len -= len;
282
283 if (tail->iov_len > 0)
284 memmove((char *)tail->iov_base,
285 tail->iov_base + len,
286 tail->iov_len);
287}
288
289/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 * _shift_data_right_pages
291 * @pages: vector of pages containing both the source and dest memory area.
292 * @pgto_base: page vector address of destination
293 * @pgfrom_base: page vector address of source
294 * @len: number of bytes to copy
295 *
296 * Note: the addresses pgto_base and pgfrom_base are both calculated in
297 * the same way:
298 * if a memory area starts at byte 'base' in page 'pages[i]',
Kirill A. Shutemovea1754a2016-04-01 15:29:48 +0300299 * then its address is given as (i << PAGE_SHIFT) + base
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 * Also note: pgfrom_base must be < pgto_base, but the memory areas
301 * they point to may overlap.
302 */
303static void
304_shift_data_right_pages(struct page **pages, size_t pgto_base,
305 size_t pgfrom_base, size_t len)
306{
307 struct page **pgfrom, **pgto;
308 char *vfrom, *vto;
309 size_t copy;
310
311 BUG_ON(pgto_base <= pgfrom_base);
312
313 pgto_base += len;
314 pgfrom_base += len;
315
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300316 pgto = pages + (pgto_base >> PAGE_SHIFT);
317 pgfrom = pages + (pgfrom_base >> PAGE_SHIFT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300319 pgto_base &= ~PAGE_MASK;
320 pgfrom_base &= ~PAGE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
322 do {
323 /* Are any pointers crossing a page boundary? */
324 if (pgto_base == 0) {
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300325 pgto_base = PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 pgto--;
327 }
328 if (pgfrom_base == 0) {
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300329 pgfrom_base = PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 pgfrom--;
331 }
332
333 copy = len;
334 if (copy > pgto_base)
335 copy = pgto_base;
336 if (copy > pgfrom_base)
337 copy = pgfrom_base;
338 pgto_base -= copy;
339 pgfrom_base -= copy;
340
Cong Wangb8541782011-11-25 23:14:40 +0800341 vto = kmap_atomic(*pgto);
Trond Myklebust347e2232013-08-28 13:35:13 -0400342 if (*pgto != *pgfrom) {
343 vfrom = kmap_atomic(*pgfrom);
344 memcpy(vto + pgto_base, vfrom + pgfrom_base, copy);
345 kunmap_atomic(vfrom);
346 } else
347 memmove(vto + pgto_base, vto + pgfrom_base, copy);
Trond Myklebustbce34812006-07-05 13:17:12 -0400348 flush_dcache_page(*pgto);
Cong Wangb8541782011-11-25 23:14:40 +0800349 kunmap_atomic(vto);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 } while ((len -= copy) != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352}
353
Anna Schumaker43f0f082020-05-06 13:21:30 -0400354static unsigned int
355_shift_data_right_tail(struct xdr_buf *buf, unsigned int pgfrom, size_t len)
356{
357 struct kvec *tail = buf->tail;
358 unsigned int tailbuf_len;
359 unsigned int result = 0;
360 size_t copy;
361
362 tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len;
363
364 /* Shift the tail first */
365 if (tailbuf_len != 0) {
366 unsigned int free_space = tailbuf_len - tail->iov_len;
367
368 if (len < free_space)
369 free_space = len;
370 if (len > free_space)
371 len = free_space;
372
373 tail->iov_len += free_space;
374 copy = len;
375
376 if (tail->iov_len > len) {
377 char *p = (char *)tail->iov_base + len;
378 memmove(p, tail->iov_base, tail->iov_len - free_space);
379 result += tail->iov_len - free_space;
380 } else
381 copy = tail->iov_len;
382
383 /* Copy from the inlined pages into the tail */
384 _copy_from_pages((char *)tail->iov_base,
385 buf->pages,
386 buf->page_base + pgfrom,
387 copy);
388 result += copy;
389 }
390
391 return result;
392}
393
Ben Hutchings2c530402012-07-10 10:55:09 +0000394/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 * _copy_to_pages
396 * @pages: array of pages
397 * @pgbase: page vector address of destination
398 * @p: pointer to source data
399 * @len: length
400 *
401 * Copies data from an arbitrary memory location into an array of pages
402 * The copy is assumed to be non-overlapping.
403 */
404static void
405_copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
406{
407 struct page **pgto;
408 char *vto;
409 size_t copy;
410
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300411 pgto = pages + (pgbase >> PAGE_SHIFT);
412 pgbase &= ~PAGE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Trond Myklebustdaeba892008-03-31 17:02:02 -0400414 for (;;) {
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300415 copy = PAGE_SIZE - pgbase;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 if (copy > len)
417 copy = len;
418
Cong Wangb8541782011-11-25 23:14:40 +0800419 vto = kmap_atomic(*pgto);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 memcpy(vto + pgbase, p, copy);
Cong Wangb8541782011-11-25 23:14:40 +0800421 kunmap_atomic(vto);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
Trond Myklebustdaeba892008-03-31 17:02:02 -0400423 len -= copy;
424 if (len == 0)
425 break;
426
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 pgbase += copy;
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300428 if (pgbase == PAGE_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 flush_dcache_page(*pgto);
430 pgbase = 0;
431 pgto++;
432 }
433 p += copy;
Trond Myklebustdaeba892008-03-31 17:02:02 -0400434 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 flush_dcache_page(*pgto);
436}
437
Ben Hutchings2c530402012-07-10 10:55:09 +0000438/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 * _copy_from_pages
440 * @p: pointer to destination
441 * @pages: array of pages
442 * @pgbase: offset of source data
443 * @len: length
444 *
445 * Copies data into an arbitrary memory location from an array of pages
446 * The copy is assumed to be non-overlapping.
447 */
Andy Adamsonbf118a32011-12-07 11:55:27 -0500448void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
450{
451 struct page **pgfrom;
452 char *vfrom;
453 size_t copy;
454
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300455 pgfrom = pages + (pgbase >> PAGE_SHIFT);
456 pgbase &= ~PAGE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 do {
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300459 copy = PAGE_SIZE - pgbase;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 if (copy > len)
461 copy = len;
462
Cong Wangb8541782011-11-25 23:14:40 +0800463 vfrom = kmap_atomic(*pgfrom);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 memcpy(p, vfrom + pgbase, copy);
Cong Wangb8541782011-11-25 23:14:40 +0800465 kunmap_atomic(vfrom);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
467 pgbase += copy;
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300468 if (pgbase == PAGE_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 pgbase = 0;
470 pgfrom++;
471 }
472 p += copy;
473
474 } while ((len -= copy) != 0);
475}
Andy Adamsonbf118a32011-12-07 11:55:27 -0500476EXPORT_SYMBOL_GPL(_copy_from_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
Ben Hutchings2c530402012-07-10 10:55:09 +0000478/**
Anna Schumaker84ce1822014-05-28 13:38:53 -0400479 * _zero_pages
480 * @pages: array of pages
481 * @pgbase: beginning page vector address
482 * @len: length
483 */
484static void
485_zero_pages(struct page **pages, size_t pgbase, size_t len)
486{
487 struct page **page;
488 char *vpage;
489 size_t zero;
490
491 page = pages + (pgbase >> PAGE_SHIFT);
492 pgbase &= ~PAGE_MASK;
493
494 do {
495 zero = PAGE_SIZE - pgbase;
496 if (zero > len)
497 zero = len;
498
499 vpage = kmap_atomic(*page);
500 memset(vpage + pgbase, 0, zero);
501 kunmap_atomic(vpage);
502
503 flush_dcache_page(*page);
504 pgbase = 0;
505 page++;
506
507 } while ((len -= zero) != 0);
508}
509
510/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 * xdr_shrink_bufhead
512 * @buf: xdr_buf
513 * @len: bytes to remove from buf->head[0]
514 *
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800515 * Shrinks XDR buffer's header kvec buf->head[0] by
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 * 'len' bytes. The extra data is not lost, but is instead
517 * moved into the inlined pages and/or the tail.
518 */
Chuck Lever7be9cea32019-02-11 11:24:16 -0500519static unsigned int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
521{
522 struct kvec *head, *tail;
523 size_t copy, offs;
524 unsigned int pglen = buf->page_len;
Chuck Lever7be9cea32019-02-11 11:24:16 -0500525 unsigned int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Chuck Lever7be9cea32019-02-11 11:24:16 -0500527 result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 tail = buf->tail;
529 head = buf->head;
Weston Andros Adamson18e624a2012-10-23 10:43:42 -0400530
531 WARN_ON_ONCE(len > head->iov_len);
532 if (len > head->iov_len)
533 len = head->iov_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
535 /* Shift the tail first */
536 if (tail->iov_len != 0) {
537 if (tail->iov_len > len) {
538 copy = tail->iov_len - len;
539 memmove((char *)tail->iov_base + len,
540 tail->iov_base, copy);
Chuck Lever7be9cea32019-02-11 11:24:16 -0500541 result += copy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 }
543 /* Copy from the inlined pages into the tail */
544 copy = len;
545 if (copy > pglen)
546 copy = pglen;
547 offs = len - copy;
548 if (offs >= tail->iov_len)
549 copy = 0;
550 else if (copy > tail->iov_len - offs)
551 copy = tail->iov_len - offs;
Chuck Lever7be9cea32019-02-11 11:24:16 -0500552 if (copy != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 _copy_from_pages((char *)tail->iov_base + offs,
554 buf->pages,
555 buf->page_base + pglen + offs - len,
556 copy);
Chuck Lever7be9cea32019-02-11 11:24:16 -0500557 result += copy;
558 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 /* Do we also need to copy data from the head into the tail ? */
560 if (len > pglen) {
561 offs = copy = len - pglen;
562 if (copy > tail->iov_len)
563 copy = tail->iov_len;
564 memcpy(tail->iov_base,
565 (char *)head->iov_base +
566 head->iov_len - offs,
567 copy);
Chuck Lever7be9cea32019-02-11 11:24:16 -0500568 result += copy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 }
570 }
571 /* Now handle pages */
572 if (pglen != 0) {
573 if (pglen > len)
574 _shift_data_right_pages(buf->pages,
575 buf->page_base + len,
576 buf->page_base,
577 pglen - len);
578 copy = len;
579 if (len > pglen)
580 copy = pglen;
581 _copy_to_pages(buf->pages, buf->page_base,
582 (char *)head->iov_base + head->iov_len - len,
583 copy);
Chuck Lever7be9cea32019-02-11 11:24:16 -0500584 result += copy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 }
586 head->iov_len -= len;
587 buf->buflen -= len;
588 /* Have we truncated the message? */
589 if (buf->len > buf->buflen)
590 buf->len = buf->buflen;
Chuck Lever7be9cea32019-02-11 11:24:16 -0500591
592 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593}
594
Ben Hutchings2c530402012-07-10 10:55:09 +0000595/**
Chuck Levere8d70b32019-11-15 08:39:07 -0500596 * xdr_shrink_pagelen - shrinks buf->pages by up to @len bytes
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 * @buf: xdr_buf
598 * @len: bytes to remove from buf->pages
599 *
Chuck Levere8d70b32019-11-15 08:39:07 -0500600 * The extra data is not lost, but is instead moved into buf->tail.
601 * Returns the actual number of bytes moved.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 */
Chuck Lever7be9cea32019-02-11 11:24:16 -0500603static unsigned int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604xdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
605{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 unsigned int pglen = buf->page_len;
Chuck Lever7be9cea32019-02-11 11:24:16 -0500607 unsigned int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
Chuck Levere8d70b32019-11-15 08:39:07 -0500609 if (len > buf->page_len)
610 len = buf-> page_len;
Trond Myklebustcf187c22010-08-29 12:13:16 -0400611
Anna Schumaker43f0f082020-05-06 13:21:30 -0400612 result = _shift_data_right_tail(buf, pglen - len, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 buf->page_len -= len;
614 buf->buflen -= len;
615 /* Have we truncated the message? */
616 if (buf->len > buf->buflen)
617 buf->len = buf->buflen;
Chuck Lever7be9cea32019-02-11 11:24:16 -0500618
619 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620}
621
622void
623xdr_shift_buf(struct xdr_buf *buf, size_t len)
624{
625 xdr_shrink_bufhead(buf, len);
626}
Trond Myklebust468039e2008-12-23 15:21:31 -0500627EXPORT_SYMBOL_GPL(xdr_shift_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
629/**
Trond Myklebust4517d522012-06-21 17:14:46 -0400630 * xdr_stream_pos - Return the current offset from the start of the xdr_stream
631 * @xdr: pointer to struct xdr_stream
632 */
633unsigned int xdr_stream_pos(const struct xdr_stream *xdr)
634{
635 return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2;
636}
637EXPORT_SYMBOL_GPL(xdr_stream_pos);
638
639/**
Anna Schumakercf1f08c2020-04-17 11:00:24 -0400640 * xdr_page_pos - Return the current offset from the start of the xdr pages
641 * @xdr: pointer to struct xdr_stream
642 */
643unsigned int xdr_page_pos(const struct xdr_stream *xdr)
644{
645 unsigned int pos = xdr_stream_pos(xdr);
646
647 WARN_ON(pos < xdr->buf->head[0].iov_len);
648 return pos - xdr->buf->head[0].iov_len;
649}
650EXPORT_SYMBOL_GPL(xdr_page_pos);
651
652/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 * xdr_init_encode - Initialize a struct xdr_stream for sending data.
654 * @xdr: pointer to xdr_stream struct
655 * @buf: pointer to XDR buffer in which to encode data
656 * @p: current pointer inside XDR buffer
Chuck Lever0ccc61b2019-02-11 11:24:05 -0500657 * @rqst: pointer to controlling rpc_rqst, for debugging
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 *
659 * Note: at the moment the RPC client only passes the length of our
660 * scratch buffer in the xdr_buf's header kvec. Previously this
661 * meant we needed to call xdr_adjust_iovec() after encoding the
662 * data. With the new scheme, the xdr_stream manages the details
663 * of the buffer length, and takes care of adjusting the kvec
664 * length for us.
665 */
Chuck Lever0ccc61b2019-02-11 11:24:05 -0500666void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
667 struct rpc_rqst *rqst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668{
669 struct kvec *iov = buf->head;
Trond Myklebust334ccfd2005-06-22 17:16:19 +0000670 int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400672 xdr_set_scratch_buffer(xdr, NULL, 0);
Trond Myklebust334ccfd2005-06-22 17:16:19 +0000673 BUG_ON(scratch_len < 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 xdr->buf = buf;
675 xdr->iov = iov;
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700676 xdr->p = (__be32 *)((char *)iov->iov_base + iov->iov_len);
677 xdr->end = (__be32 *)((char *)iov->iov_base + scratch_len);
Trond Myklebust334ccfd2005-06-22 17:16:19 +0000678 BUG_ON(iov->iov_len > scratch_len);
679
680 if (p != xdr->p && p != NULL) {
681 size_t len;
682
683 BUG_ON(p < xdr->p || p > xdr->end);
684 len = (char *)p - (char *)xdr->p;
685 xdr->p = p;
686 buf->len += len;
687 iov->iov_len += len;
688 }
Chuck Lever0ccc61b2019-02-11 11:24:05 -0500689 xdr->rqst = rqst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690}
Trond Myklebust468039e2008-12-23 15:21:31 -0500691EXPORT_SYMBOL_GPL(xdr_init_encode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
693/**
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400694 * xdr_commit_encode - Ensure all data is written to buffer
695 * @xdr: pointer to xdr_stream
696 *
697 * We handle encoding across page boundaries by giving the caller a
698 * temporary location to write to, then later copying the data into
699 * place; xdr_commit_encode does that copying.
700 *
701 * Normally the caller doesn't need to call this directly, as the
702 * following xdr_reserve_space will do it. But an explicit call may be
703 * required at the end of encoding, or any other time when the xdr_buf
704 * data might be read.
705 */
Chuck Lever95bd8302019-08-19 18:37:05 -0400706inline void xdr_commit_encode(struct xdr_stream *xdr)
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400707{
708 int shift = xdr->scratch.iov_len;
709 void *page;
710
711 if (shift == 0)
712 return;
713 page = page_address(*xdr->page_ptr);
714 memcpy(xdr->scratch.iov_base, page, shift);
715 memmove(page, page + shift, (void *)xdr->p - page);
716 xdr->scratch.iov_len = 0;
717}
718EXPORT_SYMBOL_GPL(xdr_commit_encode);
719
Trond Myklebust22cb4382014-07-12 18:01:02 -0400720static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
721 size_t nbytes)
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400722{
YueHaibing025911a2018-11-08 02:04:57 +0000723 __be32 *p;
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400724 int space_left;
725 int frag1bytes, frag2bytes;
726
727 if (nbytes > PAGE_SIZE)
Chuck Lever55828632019-02-11 11:24:10 -0500728 goto out_overflow; /* Bigger buffers require special handling */
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400729 if (xdr->buf->len + nbytes > xdr->buf->buflen)
Chuck Lever55828632019-02-11 11:24:10 -0500730 goto out_overflow; /* Sorry, we're totally out of space */
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400731 frag1bytes = (xdr->end - xdr->p) << 2;
732 frag2bytes = nbytes - frag1bytes;
733 if (xdr->iov)
734 xdr->iov->iov_len += frag1bytes;
J. Bruce Fields05638dc2014-06-02 12:05:47 -0400735 else
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400736 xdr->buf->page_len += frag1bytes;
J. Bruce Fields05638dc2014-06-02 12:05:47 -0400737 xdr->page_ptr++;
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400738 xdr->iov = NULL;
739 /*
740 * If the last encode didn't end exactly on a page boundary, the
741 * next one will straddle boundaries. Encode into the next
742 * page, then copy it back later in xdr_commit_encode. We use
743 * the "scratch" iov to track any temporarily unused fragment of
744 * space at the end of the previous buffer:
745 */
746 xdr->scratch.iov_base = xdr->p;
747 xdr->scratch.iov_len = frag1bytes;
748 p = page_address(*xdr->page_ptr);
749 /*
750 * Note this is where the next encode will start after we've
751 * shifted this one back:
752 */
753 xdr->p = (void *)p + frag2bytes;
754 space_left = xdr->buf->buflen - xdr->buf->len;
755 xdr->end = (void *)p + min_t(int, space_left, PAGE_SIZE);
756 xdr->buf->page_len += frag2bytes;
757 xdr->buf->len += nbytes;
758 return p;
Chuck Lever55828632019-02-11 11:24:10 -0500759out_overflow:
760 trace_rpc_xdr_overflow(xdr, nbytes);
761 return NULL;
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400762}
763
764/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 * xdr_reserve_space - Reserve buffer space for sending
766 * @xdr: pointer to xdr_stream
767 * @nbytes: number of bytes to reserve
768 *
769 * Checks that we have enough buffer space to encode 'nbytes' more
770 * bytes of data. If so, update the total xdr_buf length, and
771 * adjust the length of the current kvec.
772 */
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700773__be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700775 __be32 *p = xdr->p;
776 __be32 *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400778 xdr_commit_encode(xdr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 /* align nbytes on the next 32-bit boundary */
780 nbytes += 3;
781 nbytes &= ~3;
782 q = p + (nbytes >> 2);
783 if (unlikely(q > xdr->end || q < p))
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400784 return xdr_get_next_encode_buffer(xdr, nbytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 xdr->p = q;
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400786 if (xdr->iov)
787 xdr->iov->iov_len += nbytes;
788 else
789 xdr->buf->page_len += nbytes;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 xdr->buf->len += nbytes;
791 return p;
792}
Trond Myklebust468039e2008-12-23 15:21:31 -0500793EXPORT_SYMBOL_GPL(xdr_reserve_space);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Anna Schumaker403217f2020-08-17 12:53:06 -0400795
796/**
797 * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending
798 * @xdr: pointer to xdr_stream
799 * @vec: pointer to a kvec array
800 * @nbytes: number of bytes to reserve
801 *
802 * Reserves enough buffer space to encode 'nbytes' of data and stores the
803 * pointers in 'vec'. The size argument passed to xdr_reserve_space() is
804 * determined based on the number of bytes remaining in the current page to
805 * avoid invalidating iov_base pointers when xdr_commit_encode() is called.
806 */
807int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec, size_t nbytes)
808{
809 int thislen;
810 int v = 0;
811 __be32 *p;
812
813 /*
814 * svcrdma requires every READ payload to start somewhere
815 * in xdr->pages.
816 */
817 if (xdr->iov == xdr->buf->head) {
818 xdr->iov = NULL;
819 xdr->end = xdr->p;
820 }
821
822 while (nbytes) {
823 thislen = xdr->buf->page_len % PAGE_SIZE;
824 thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen);
825
826 p = xdr_reserve_space(xdr, thislen);
827 if (!p)
828 return -EIO;
829
830 vec[v].iov_base = p;
831 vec[v].iov_len = thislen;
832 v++;
833 nbytes -= thislen;
834 }
835
836 return v;
837}
838EXPORT_SYMBOL_GPL(xdr_reserve_space_vec);
839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840/**
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500841 * xdr_truncate_encode - truncate an encode buffer
842 * @xdr: pointer to xdr_stream
843 * @len: new length of buffer
844 *
845 * Truncates the xdr stream, so that xdr->buf->len == len,
846 * and xdr->p points at offset len from the start of the buffer, and
847 * head, tail, and page lengths are adjusted to correspond.
848 *
849 * If this means moving xdr->p to a different buffer, we assume that
Randy Dunlap1cc52132020-08-22 18:07:38 -0700850 * the end pointer should be set to the end of the current page,
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500851 * except in the case of the head buffer when we assume the head
852 * buffer's current length represents the end of the available buffer.
853 *
854 * This is *not* safe to use on a buffer that already has inlined page
855 * cache pages (as in a zero-copy server read reply), except for the
856 * simple case of truncating from one position in the tail to another.
857 *
858 */
859void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
860{
861 struct xdr_buf *buf = xdr->buf;
862 struct kvec *head = buf->head;
863 struct kvec *tail = buf->tail;
864 int fraglen;
J. Bruce Fields49a068f2014-12-22 16:14:51 -0500865 int new;
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500866
867 if (len > buf->len) {
868 WARN_ON_ONCE(1);
869 return;
870 }
J. Bruce Fields2825a7f2013-08-26 16:04:46 -0400871 xdr_commit_encode(xdr);
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500872
873 fraglen = min_t(int, buf->len - len, tail->iov_len);
874 tail->iov_len -= fraglen;
875 buf->len -= fraglen;
J. Bruce Fieldsed38c062014-09-19 17:21:35 -0400876 if (tail->iov_len) {
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500877 xdr->p = tail->iov_base + tail->iov_len;
J. Bruce Fields280caac2014-10-01 11:36:31 -0400878 WARN_ON_ONCE(!xdr->end);
879 WARN_ON_ONCE(!xdr->iov);
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500880 return;
881 }
882 WARN_ON_ONCE(fraglen);
883 fraglen = min_t(int, buf->len - len, buf->page_len);
884 buf->page_len -= fraglen;
885 buf->len -= fraglen;
886
887 new = buf->page_base + buf->page_len;
J. Bruce Fields49a068f2014-12-22 16:14:51 -0500888
889 xdr->page_ptr = buf->pages + (new >> PAGE_SHIFT);
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500890
J. Bruce Fieldsed38c062014-09-19 17:21:35 -0400891 if (buf->page_len) {
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500892 xdr->p = page_address(*xdr->page_ptr);
893 xdr->end = (void *)xdr->p + PAGE_SIZE;
894 xdr->p = (void *)xdr->p + (new % PAGE_SIZE);
J. Bruce Fields280caac2014-10-01 11:36:31 -0400895 WARN_ON_ONCE(xdr->iov);
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500896 return;
897 }
Frank Sorenson5d7a5bc2018-10-30 15:10:40 -0500898 if (fraglen)
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500899 xdr->end = head->iov_base + head->iov_len;
900 /* (otherwise assume xdr->end is already set) */
Frank Sorenson5d7a5bc2018-10-30 15:10:40 -0500901 xdr->page_ptr--;
J. Bruce Fields3e19ce72014-02-25 17:44:21 -0500902 head->iov_len = len;
903 buf->len = len;
904 xdr->p = head->iov_base + head->iov_len;
905 xdr->iov = buf->head;
906}
907EXPORT_SYMBOL(xdr_truncate_encode);
908
909/**
J. Bruce Fieldsdb3f58a2014-03-06 13:22:18 -0500910 * xdr_restrict_buflen - decrease available buffer space
911 * @xdr: pointer to xdr_stream
912 * @newbuflen: new maximum number of bytes available
913 *
914 * Adjust our idea of how much space is available in the buffer.
915 * If we've already used too much space in the buffer, returns -1.
916 * If the available space is already smaller than newbuflen, returns 0
917 * and does nothing. Otherwise, adjusts xdr->buf->buflen to newbuflen
918 * and ensures xdr->end is set at most offset newbuflen from the start
919 * of the buffer.
920 */
921int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen)
922{
923 struct xdr_buf *buf = xdr->buf;
924 int left_in_this_buf = (void *)xdr->end - (void *)xdr->p;
925 int end_offset = buf->len + left_in_this_buf;
926
927 if (newbuflen < 0 || newbuflen < buf->len)
928 return -1;
929 if (newbuflen > buf->buflen)
930 return 0;
931 if (newbuflen < end_offset)
932 xdr->end = (void *)xdr->end + newbuflen - end_offset;
933 buf->buflen = newbuflen;
934 return 0;
935}
936EXPORT_SYMBOL(xdr_restrict_buflen);
937
938/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
940 * @xdr: pointer to xdr_stream
941 * @pages: list of pages
942 * @base: offset of first byte
943 * @len: length of data in bytes
944 *
945 */
946void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base,
947 unsigned int len)
948{
949 struct xdr_buf *buf = xdr->buf;
950 struct kvec *iov = buf->tail;
951 buf->pages = pages;
952 buf->page_base = base;
953 buf->page_len = len;
954
955 iov->iov_base = (char *)xdr->p;
956 iov->iov_len = 0;
957 xdr->iov = iov;
958
959 if (len & 3) {
960 unsigned int pad = 4 - (len & 3);
961
962 BUG_ON(xdr->p >= xdr->end);
963 iov->iov_base = (char *)xdr->p + (len & 3);
964 iov->iov_len += pad;
965 len += pad;
966 *xdr->p++ = 0;
967 }
968 buf->buflen += len;
969 buf->len += len;
970}
Trond Myklebust468039e2008-12-23 15:21:31 -0500971EXPORT_SYMBOL_GPL(xdr_write_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
Trond Myklebust66502392011-01-08 17:45:38 -0500973static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
Trond Myklebust15376932012-06-28 17:17:48 -0400974 unsigned int len)
Trond Myklebust66502392011-01-08 17:45:38 -0500975{
976 if (len > iov->iov_len)
977 len = iov->iov_len;
Trond Myklebust15376932012-06-28 17:17:48 -0400978 xdr->p = (__be32*)iov->iov_base;
Trond Myklebust66502392011-01-08 17:45:38 -0500979 xdr->end = (__be32*)(iov->iov_base + len);
980 xdr->iov = iov;
981 xdr->page_ptr = NULL;
982}
983
984static int xdr_set_page_base(struct xdr_stream *xdr,
985 unsigned int base, unsigned int len)
986{
987 unsigned int pgnr;
988 unsigned int maxlen;
989 unsigned int pgoff;
990 unsigned int pgend;
991 void *kaddr;
992
993 maxlen = xdr->buf->page_len;
994 if (base >= maxlen)
995 return -EINVAL;
996 maxlen -= base;
997 if (len > maxlen)
998 len = maxlen;
999
1000 base += xdr->buf->page_base;
1001
1002 pgnr = base >> PAGE_SHIFT;
1003 xdr->page_ptr = &xdr->buf->pages[pgnr];
1004 kaddr = page_address(*xdr->page_ptr);
1005
1006 pgoff = base & ~PAGE_MASK;
1007 xdr->p = (__be32*)(kaddr + pgoff);
1008
1009 pgend = pgoff + len;
1010 if (pgend > PAGE_SIZE)
1011 pgend = PAGE_SIZE;
1012 xdr->end = (__be32*)(kaddr + pgend);
1013 xdr->iov = NULL;
1014 return 0;
1015}
1016
Anna Schumakerf7d61ee2015-01-26 17:26:19 -05001017static void xdr_set_page(struct xdr_stream *xdr, unsigned int base,
1018 unsigned int len)
1019{
1020 if (xdr_set_page_base(xdr, base, len) < 0)
1021 xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2);
1022}
1023
Trond Myklebust66502392011-01-08 17:45:38 -05001024static void xdr_set_next_page(struct xdr_stream *xdr)
1025{
1026 unsigned int newbase;
1027
1028 newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT;
1029 newbase -= xdr->buf->page_base;
1030
Anna Schumakerf7d61ee2015-01-26 17:26:19 -05001031 xdr_set_page(xdr, newbase, PAGE_SIZE);
Trond Myklebust66502392011-01-08 17:45:38 -05001032}
1033
1034static bool xdr_set_next_buffer(struct xdr_stream *xdr)
1035{
1036 if (xdr->page_ptr != NULL)
1037 xdr_set_next_page(xdr);
1038 else if (xdr->iov == xdr->buf->head) {
Anna Schumakerf7d61ee2015-01-26 17:26:19 -05001039 xdr_set_page(xdr, 0, PAGE_SIZE);
Trond Myklebust66502392011-01-08 17:45:38 -05001040 }
1041 return xdr->p != xdr->end;
1042}
1043
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044/**
1045 * xdr_init_decode - Initialize an xdr_stream for decoding data.
1046 * @xdr: pointer to xdr_stream struct
1047 * @buf: pointer to XDR buffer from which to decode data
1048 * @p: current pointer inside XDR buffer
Chuck Lever0ccc61b2019-02-11 11:24:05 -05001049 * @rqst: pointer to controlling rpc_rqst, for debugging
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 */
Chuck Lever0ccc61b2019-02-11 11:24:05 -05001051void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
1052 struct rpc_rqst *rqst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 xdr->buf = buf;
Trond Myklebust66502392011-01-08 17:45:38 -05001055 xdr->scratch.iov_base = NULL;
1056 xdr->scratch.iov_len = 0;
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001057 xdr->nwords = XDR_QUADLEN(buf->len);
Trond Myklebust66502392011-01-08 17:45:38 -05001058 if (buf->head[0].iov_len != 0)
Trond Myklebust15376932012-06-28 17:17:48 -04001059 xdr_set_iov(xdr, buf->head, buf->len);
Trond Myklebust66502392011-01-08 17:45:38 -05001060 else if (buf->page_len != 0)
1061 xdr_set_page_base(xdr, 0, buf->len);
Benjamin Coddington06ef26a2016-04-06 11:32:52 -04001062 else
1063 xdr_set_iov(xdr, buf->head, buf->len);
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001064 if (p != NULL && p > xdr->p && xdr->end >= p) {
1065 xdr->nwords -= p - xdr->p;
Trond Myklebust15376932012-06-28 17:17:48 -04001066 xdr->p = p;
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001067 }
Chuck Lever0ccc61b2019-02-11 11:24:05 -05001068 xdr->rqst = rqst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069}
Trond Myklebust468039e2008-12-23 15:21:31 -05001070EXPORT_SYMBOL_GPL(xdr_init_decode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
Benny Halevyf7da7a12011-05-19 14:16:47 -04001072/**
Chuck Lever7ecce752017-04-11 13:23:59 -04001073 * xdr_init_decode_pages - Initialize an xdr_stream for decoding into pages
Benny Halevyf7da7a12011-05-19 14:16:47 -04001074 * @xdr: pointer to xdr_stream struct
1075 * @buf: pointer to XDR buffer from which to decode data
1076 * @pages: list of pages to decode into
1077 * @len: length in bytes of buffer in pages
1078 */
1079void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
1080 struct page **pages, unsigned int len)
1081{
1082 memset(buf, 0, sizeof(*buf));
1083 buf->pages = pages;
1084 buf->page_len = len;
1085 buf->buflen = len;
1086 buf->len = len;
Chuck Lever0ccc61b2019-02-11 11:24:05 -05001087 xdr_init_decode(xdr, buf, NULL, NULL);
Benny Halevyf7da7a12011-05-19 14:16:47 -04001088}
1089EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
1090
Trond Myklebust66502392011-01-08 17:45:38 -05001091static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
Trond Myklebustba8e4522010-10-19 19:58:49 -04001092{
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001093 unsigned int nwords = XDR_QUADLEN(nbytes);
Trond Myklebustba8e4522010-10-19 19:58:49 -04001094 __be32 *p = xdr->p;
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001095 __be32 *q = p + nwords;
Trond Myklebustba8e4522010-10-19 19:58:49 -04001096
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001097 if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p))
Trond Myklebustba8e4522010-10-19 19:58:49 -04001098 return NULL;
Trond Myklebust66502392011-01-08 17:45:38 -05001099 xdr->p = q;
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001100 xdr->nwords -= nwords;
Trond Myklebustba8e4522010-10-19 19:58:49 -04001101 return p;
1102}
Trond Myklebustba8e4522010-10-19 19:58:49 -04001103
1104/**
Trond Myklebust66502392011-01-08 17:45:38 -05001105 * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.
1106 * @xdr: pointer to xdr_stream struct
1107 * @buf: pointer to an empty buffer
1108 * @buflen: size of 'buf'
1109 *
1110 * The scratch buffer is used when decoding from an array of pages.
1111 * If an xdr_inline_decode() call spans across page boundaries, then
1112 * we copy the data into the scratch buffer in order to allow linear
1113 * access.
1114 */
1115void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen)
1116{
1117 xdr->scratch.iov_base = buf;
1118 xdr->scratch.iov_len = buflen;
1119}
1120EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
1121
1122static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes)
1123{
1124 __be32 *p;
Trond Myklebustace0e142016-09-20 14:33:42 -04001125 char *cpdest = xdr->scratch.iov_base;
Trond Myklebust66502392011-01-08 17:45:38 -05001126 size_t cplen = (char *)xdr->end - (char *)xdr->p;
1127
1128 if (nbytes > xdr->scratch.iov_len)
Chuck Lever55828632019-02-11 11:24:10 -05001129 goto out_overflow;
Trond Myklebustace0e142016-09-20 14:33:42 -04001130 p = __xdr_inline_decode(xdr, cplen);
1131 if (p == NULL)
1132 return NULL;
1133 memcpy(cpdest, p, cplen);
Chuck Lever55828632019-02-11 11:24:10 -05001134 if (!xdr_set_next_buffer(xdr))
1135 goto out_overflow;
Trond Myklebust66502392011-01-08 17:45:38 -05001136 cpdest += cplen;
1137 nbytes -= cplen;
Trond Myklebust66502392011-01-08 17:45:38 -05001138 p = __xdr_inline_decode(xdr, nbytes);
1139 if (p == NULL)
1140 return NULL;
1141 memcpy(cpdest, p, nbytes);
1142 return xdr->scratch.iov_base;
Chuck Lever55828632019-02-11 11:24:10 -05001143out_overflow:
1144 trace_rpc_xdr_overflow(xdr, nbytes);
1145 return NULL;
Trond Myklebust66502392011-01-08 17:45:38 -05001146}
1147
1148/**
1149 * xdr_inline_decode - Retrieve XDR data to decode
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 * @xdr: pointer to xdr_stream struct
1151 * @nbytes: number of bytes of data to decode
1152 *
1153 * Check if the input buffer is long enough to enable us to decode
1154 * 'nbytes' more bytes of data starting at the current position.
1155 * If so return the current pointer, then update the current
1156 * pointer position.
1157 */
Alexey Dobriyand8ed0292006-09-26 22:29:38 -07001158__be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159{
Trond Myklebust66502392011-01-08 17:45:38 -05001160 __be32 *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
Chuck Lever55828632019-02-11 11:24:10 -05001162 if (unlikely(nbytes == 0))
Trond Myklebust66502392011-01-08 17:45:38 -05001163 return xdr->p;
1164 if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))
Chuck Lever55828632019-02-11 11:24:10 -05001165 goto out_overflow;
Trond Myklebust66502392011-01-08 17:45:38 -05001166 p = __xdr_inline_decode(xdr, nbytes);
1167 if (p != NULL)
1168 return p;
1169 return xdr_copy_to_scratch(xdr, nbytes);
Chuck Lever55828632019-02-11 11:24:10 -05001170out_overflow:
1171 trace_rpc_xdr_overflow(xdr, nbytes);
1172 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173}
Trond Myklebust468039e2008-12-23 15:21:31 -05001174EXPORT_SYMBOL_GPL(xdr_inline_decode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
Anna Schumaker06216ec2020-04-20 17:38:17 -04001176static void xdr_realign_pages(struct xdr_stream *xdr)
1177{
1178 struct xdr_buf *buf = xdr->buf;
1179 struct kvec *iov = buf->head;
1180 unsigned int cur = xdr_stream_pos(xdr);
1181 unsigned int copied, offset;
1182
1183 /* Realign pages to current pointer position */
1184 if (iov->iov_len > cur) {
1185 offset = iov->iov_len - cur;
1186 copied = xdr_shrink_bufhead(buf, offset);
1187 trace_rpc_xdr_alignment(xdr, offset, copied);
1188 xdr->nwords = XDR_QUADLEN(buf->len - cur);
1189 }
1190}
1191
Trond Myklebust3994ee62012-06-26 12:34:05 -04001192static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193{
1194 struct xdr_buf *buf = xdr->buf;
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001195 unsigned int nwords = XDR_QUADLEN(len);
Trond Myklebustb760b312012-06-26 12:19:55 -04001196 unsigned int cur = xdr_stream_pos(xdr);
Chuck Lever7be9cea32019-02-11 11:24:16 -05001197 unsigned int copied, offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001199 if (xdr->nwords == 0)
Trond Myklebustc337d362012-06-21 17:05:37 -04001200 return 0;
Chuck Lever7be9cea32019-02-11 11:24:16 -05001201
Anna Schumaker06216ec2020-04-20 17:38:17 -04001202 xdr_realign_pages(xdr);
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001203 if (nwords > xdr->nwords) {
1204 nwords = xdr->nwords;
1205 len = nwords << 2;
1206 }
Trond Myklebusta11a2bf2012-08-02 13:21:43 -04001207 if (buf->page_len <= len)
Trond Myklebust8a9a8b82012-08-01 14:32:13 -04001208 len = buf->page_len;
Trond Myklebusta11a2bf2012-08-02 13:21:43 -04001209 else if (nwords < xdr->nwords) {
1210 /* Truncate page data and move it into the tail */
Chuck Lever7be9cea32019-02-11 11:24:16 -05001211 offset = buf->page_len - len;
1212 copied = xdr_shrink_pagelen(buf, offset);
1213 trace_rpc_xdr_alignment(xdr, offset, copied);
Trond Myklebusta11a2bf2012-08-02 13:21:43 -04001214 xdr->nwords = XDR_QUADLEN(buf->len - cur);
1215 }
Trond Myklebust3994ee62012-06-26 12:34:05 -04001216 return len;
1217}
Trond Myklebustbd00f842012-06-26 13:50:43 -04001218
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219/**
1220 * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
1221 * @xdr: pointer to xdr_stream struct
1222 * @len: number of bytes of page data
1223 *
1224 * Moves data beyond the current pointer position from the XDR head[] buffer
1225 * into the page list. Any data that lies beyond current position + "len"
1226 * bytes is moved into the XDR tail[].
Trond Myklebust3994ee62012-06-26 12:34:05 -04001227 *
1228 * Returns the number of XDR encoded bytes now contained in the pages
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 */
Trond Myklebust3994ee62012-06-26 12:34:05 -04001230unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
1232 struct xdr_buf *buf = xdr->buf;
1233 struct kvec *iov;
Trond Myklebust3994ee62012-06-26 12:34:05 -04001234 unsigned int nwords;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 unsigned int end;
Trond Myklebust3994ee62012-06-26 12:34:05 -04001236 unsigned int padding;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
Trond Myklebust3994ee62012-06-26 12:34:05 -04001238 len = xdr_align_pages(xdr, len);
1239 if (len == 0)
1240 return 0;
1241 nwords = XDR_QUADLEN(len);
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001242 padding = (nwords << 2) - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 xdr->iov = iov = buf->tail;
1244 /* Compute remaining message length. */
Trond Myklebustbd00f842012-06-26 13:50:43 -04001245 end = ((xdr->nwords - nwords) << 2) + padding;
1246 if (end > iov->iov_len)
1247 end = iov->iov_len;
1248
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 /*
1250 * Position current pointer at beginning of tail, and
1251 * set remaining message length.
1252 */
Alexey Dobriyand8ed0292006-09-26 22:29:38 -07001253 xdr->p = (__be32 *)((char *)iov->iov_base + padding);
1254 xdr->end = (__be32 *)((char *)iov->iov_base + end);
Trond Myklebust76cacaa2012-06-26 15:32:40 -04001255 xdr->page_ptr = NULL;
Trond Myklebustbfeea1d2012-06-20 09:58:35 -04001256 xdr->nwords = XDR_QUADLEN(end - padding);
Trond Myklebustc337d362012-06-21 17:05:37 -04001257 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258}
Trond Myklebust468039e2008-12-23 15:21:31 -05001259EXPORT_SYMBOL_GPL(xdr_read_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Anna Schumakere6ac0ac2020-04-21 11:27:00 -04001261uint64_t xdr_align_data(struct xdr_stream *xdr, uint64_t offset, uint32_t length)
1262{
1263 struct xdr_buf *buf = xdr->buf;
1264 unsigned int from, bytes;
1265 unsigned int shift = 0;
1266
1267 if ((offset + length) < offset ||
1268 (offset + length) > buf->page_len)
1269 length = buf->page_len - offset;
1270
1271 xdr_realign_pages(xdr);
1272 from = xdr_page_pos(xdr);
1273 bytes = xdr->nwords << 2;
1274 if (length < bytes)
1275 bytes = length;
1276
1277 /* Move page data to the left */
1278 if (from > offset) {
1279 shift = min_t(unsigned int, bytes, buf->page_len - from);
1280 _shift_data_left_pages(buf->pages,
1281 buf->page_base + offset,
1282 buf->page_base + from,
1283 shift);
1284 bytes -= shift;
1285
1286 /* Move tail data into the pages, if necessary */
1287 if (bytes > 0)
1288 _shift_data_left_tail(buf, offset + shift, bytes);
1289 }
1290
1291 xdr->nwords -= XDR_QUADLEN(length);
1292 xdr_set_page(xdr, from + length, PAGE_SIZE);
1293 return length;
1294}
1295EXPORT_SYMBOL_GPL(xdr_align_data);
1296
Anna Schumaker84ce1822014-05-28 13:38:53 -04001297uint64_t xdr_expand_hole(struct xdr_stream *xdr, uint64_t offset, uint64_t length)
1298{
1299 struct xdr_buf *buf = xdr->buf;
1300 unsigned int bytes;
1301 unsigned int from;
1302 unsigned int truncated = 0;
1303
1304 if ((offset + length) < offset ||
1305 (offset + length) > buf->page_len)
1306 length = buf->page_len - offset;
1307
1308 xdr_realign_pages(xdr);
1309 from = xdr_page_pos(xdr);
1310 bytes = xdr->nwords << 2;
1311
1312 if (offset + length + bytes > buf->page_len) {
1313 unsigned int shift = (offset + length + bytes) - buf->page_len;
1314 unsigned int res = _shift_data_right_tail(buf, from + bytes - shift, shift);
1315 truncated = shift - res;
1316 xdr->nwords -= XDR_QUADLEN(truncated);
1317 bytes -= shift;
1318 }
1319
1320 /* Now move the page data over and zero pages */
1321 if (bytes > 0)
1322 _shift_data_right_pages(buf->pages,
1323 buf->page_base + offset + length,
1324 buf->page_base + from,
1325 bytes);
1326 _zero_pages(buf->pages, buf->page_base + offset, length);
1327
1328 buf->len += length - (from - offset) - truncated;
1329 xdr_set_page(xdr, offset + length, PAGE_SIZE);
1330 return length;
1331}
1332EXPORT_SYMBOL_GPL(xdr_expand_hole);
1333
Trond Myklebust8b23ea72006-06-09 09:34:21 -04001334/**
1335 * xdr_enter_page - decode data from the XDR page
1336 * @xdr: pointer to xdr_stream struct
1337 * @len: number of bytes of page data
1338 *
1339 * Moves data beyond the current pointer position from the XDR head[] buffer
1340 * into the page list. Any data that lies beyond current position + "len"
1341 * bytes is moved into the XDR tail[]. The current pointer is then
1342 * repositioned at the beginning of the first XDR page.
1343 */
1344void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
1345{
Trond Myklebustf8bb7f02012-06-21 14:53:10 -04001346 len = xdr_align_pages(xdr, len);
Trond Myklebust8b23ea72006-06-09 09:34:21 -04001347 /*
1348 * Position current pointer at beginning of tail, and
1349 * set remaining message length.
1350 */
Trond Myklebustf8bb7f02012-06-21 14:53:10 -04001351 if (len != 0)
1352 xdr_set_page_base(xdr, 0, len);
Trond Myklebust8b23ea72006-06-09 09:34:21 -04001353}
Trond Myklebust468039e2008-12-23 15:21:31 -05001354EXPORT_SYMBOL_GPL(xdr_enter_page);
Trond Myklebust8b23ea72006-06-09 09:34:21 -04001355
Julia Lawallc2bd2c02020-01-01 08:43:30 +01001356static const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
1358void
1359xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
1360{
1361 buf->head[0] = *iov;
1362 buf->tail[0] = empty_iov;
1363 buf->page_len = 0;
1364 buf->buflen = buf->len = iov->iov_len;
1365}
Trond Myklebust468039e2008-12-23 15:21:31 -05001366EXPORT_SYMBOL_GPL(xdr_buf_from_iov);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
J. Bruce Fieldsde4aee22014-02-25 17:21:08 -05001368/**
1369 * xdr_buf_subsegment - set subbuf to a portion of buf
1370 * @buf: an xdr buffer
1371 * @subbuf: the result buffer
1372 * @base: beginning of range in bytes
1373 * @len: length of range in bytes
1374 *
1375 * sets @subbuf to an xdr buffer representing the portion of @buf of
1376 * length @len starting at offset @base.
1377 *
1378 * @buf and @subbuf may be pointers to the same struct xdr_buf.
1379 *
1380 * Returns -1 if base of length are out of bounds.
1381 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382int
1383xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
Trond Myklebust1e789572006-08-31 15:09:19 -04001384 unsigned int base, unsigned int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 subbuf->buflen = subbuf->len = len;
Trond Myklebust1e789572006-08-31 15:09:19 -04001387 if (base < buf->head[0].iov_len) {
1388 subbuf->head[0].iov_base = buf->head[0].iov_base + base;
1389 subbuf->head[0].iov_len = min_t(unsigned int, len,
1390 buf->head[0].iov_len - base);
1391 len -= subbuf->head[0].iov_len;
1392 base = 0;
1393 } else {
Trond Myklebust1e789572006-08-31 15:09:19 -04001394 base -= buf->head[0].iov_len;
Chuck Lever89a3c9f2020-06-25 11:32:34 -04001395 subbuf->head[0].iov_base = buf->head[0].iov_base;
J. Bruce Fieldsde4aee22014-02-25 17:21:08 -05001396 subbuf->head[0].iov_len = 0;
Trond Myklebust1e789572006-08-31 15:09:19 -04001397 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
1399 if (base < buf->page_len) {
Trond Myklebust1e789572006-08-31 15:09:19 -04001400 subbuf->page_len = min(buf->page_len - base, len);
1401 base += buf->page_base;
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +03001402 subbuf->page_base = base & ~PAGE_MASK;
1403 subbuf->pages = &buf->pages[base >> PAGE_SHIFT];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 len -= subbuf->page_len;
1405 base = 0;
1406 } else {
1407 base -= buf->page_len;
Chuck Lever89a3c9f2020-06-25 11:32:34 -04001408 subbuf->pages = buf->pages;
1409 subbuf->page_base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 subbuf->page_len = 0;
1411 }
1412
Trond Myklebust1e789572006-08-31 15:09:19 -04001413 if (base < buf->tail[0].iov_len) {
1414 subbuf->tail[0].iov_base = buf->tail[0].iov_base + base;
1415 subbuf->tail[0].iov_len = min_t(unsigned int, len,
1416 buf->tail[0].iov_len - base);
1417 len -= subbuf->tail[0].iov_len;
1418 base = 0;
1419 } else {
Trond Myklebust1e789572006-08-31 15:09:19 -04001420 base -= buf->tail[0].iov_len;
Chuck Lever89a3c9f2020-06-25 11:32:34 -04001421 subbuf->tail[0].iov_base = buf->tail[0].iov_base;
J. Bruce Fieldsde4aee22014-02-25 17:21:08 -05001422 subbuf->tail[0].iov_len = 0;
Trond Myklebust1e789572006-08-31 15:09:19 -04001423 }
1424
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 if (base || len)
1426 return -1;
1427 return 0;
1428}
Trond Myklebust468039e2008-12-23 15:21:31 -05001429EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430
Chuck Lever0a8e7b72020-04-15 17:36:22 -04001431/**
1432 * xdr_buf_trim - lop at most "len" bytes off the end of "buf"
1433 * @buf: buf to be trimmed
1434 * @len: number of bytes to reduce "buf" by
1435 *
1436 * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note
1437 * that it's possible that we'll trim less than that amount if the xdr_buf is
1438 * too small, or if (for instance) it's all in the head and the parser has
1439 * already read too far into it.
1440 */
1441void xdr_buf_trim(struct xdr_buf *buf, unsigned int len)
1442{
1443 size_t cur;
1444 unsigned int trim = len;
1445
1446 if (buf->tail[0].iov_len) {
1447 cur = min_t(size_t, buf->tail[0].iov_len, trim);
1448 buf->tail[0].iov_len -= cur;
1449 trim -= cur;
1450 if (!trim)
1451 goto fix_len;
1452 }
1453
1454 if (buf->page_len) {
1455 cur = min_t(unsigned int, buf->page_len, trim);
1456 buf->page_len -= cur;
1457 trim -= cur;
1458 if (!trim)
1459 goto fix_len;
1460 }
1461
1462 if (buf->head[0].iov_len) {
1463 cur = min_t(size_t, buf->head[0].iov_len, trim);
1464 buf->head[0].iov_len -= cur;
1465 trim -= cur;
1466 }
1467fix_len:
1468 buf->len -= (len - trim);
1469}
1470EXPORT_SYMBOL_GPL(xdr_buf_trim);
1471
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001472static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473{
Trond Myklebust1e789572006-08-31 15:09:19 -04001474 unsigned int this_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001476 this_len = min_t(unsigned int, len, subbuf->head[0].iov_len);
1477 memcpy(obj, subbuf->head[0].iov_base, this_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 len -= this_len;
1479 obj += this_len;
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001480 this_len = min_t(unsigned int, len, subbuf->page_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 if (this_len)
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001482 _copy_from_pages(obj, subbuf->pages, subbuf->page_base, this_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 len -= this_len;
1484 obj += this_len;
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001485 this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len);
1486 memcpy(obj, subbuf->tail[0].iov_base, this_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487}
1488
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001489/* obj is assumed to point to allocated memory of size at least len: */
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001490int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001491{
1492 struct xdr_buf subbuf;
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001493 int status;
1494
1495 status = xdr_buf_subsegment(buf, &subbuf, base, len);
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001496 if (status != 0)
1497 return status;
1498 __read_bytes_from_xdr_buf(&subbuf, obj, len);
1499 return 0;
1500}
Trond Myklebust468039e2008-12-23 15:21:31 -05001501EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001502
1503static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
1504{
1505 unsigned int this_len;
1506
1507 this_len = min_t(unsigned int, len, subbuf->head[0].iov_len);
1508 memcpy(subbuf->head[0].iov_base, obj, this_len);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001509 len -= this_len;
1510 obj += this_len;
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001511 this_len = min_t(unsigned int, len, subbuf->page_len);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001512 if (this_len)
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001513 _copy_to_pages(subbuf->pages, subbuf->page_base, obj, this_len);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001514 len -= this_len;
1515 obj += this_len;
Trond Myklebust4e3e43a2006-10-17 13:47:24 -04001516 this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len);
1517 memcpy(subbuf->tail[0].iov_base, obj, this_len);
1518}
1519
1520/* obj is assumed to point to allocated memory of size at least len: */
1521int write_bytes_to_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
1522{
1523 struct xdr_buf subbuf;
1524 int status;
1525
1526 status = xdr_buf_subsegment(buf, &subbuf, base, len);
1527 if (status != 0)
1528 return status;
1529 __write_bytes_to_xdr_buf(&subbuf, obj, len);
1530 return 0;
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001531}
Kevin Coffmanc43abae2010-03-17 13:02:58 -04001532EXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001533
1534int
Trond Myklebust1e789572006-08-31 15:09:19 -04001535xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536{
Alexey Dobriyand8ed0292006-09-26 22:29:38 -07001537 __be32 raw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 int status;
1539
1540 status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
1541 if (status)
1542 return status;
Benny Halevy98866b52009-08-14 17:18:49 +03001543 *obj = be32_to_cpu(raw);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 return 0;
1545}
Trond Myklebust468039e2008-12-23 15:21:31 -05001546EXPORT_SYMBOL_GPL(xdr_decode_word);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001548int
Trond Myklebust1e789572006-08-31 15:09:19 -04001549xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001550{
Benny Halevy9f162d22009-08-14 17:18:44 +03001551 __be32 raw = cpu_to_be32(obj);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001552
1553 return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
1554}
Trond Myklebust468039e2008-12-23 15:21:31 -05001555EXPORT_SYMBOL_GPL(xdr_encode_word);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001556
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001557/* Returns 0 on success, or else a negative error code. */
1558static int
1559xdr_xcode_array2(struct xdr_buf *buf, unsigned int base,
1560 struct xdr_array2_desc *desc, int encode)
1561{
1562 char *elem = NULL, *c;
1563 unsigned int copied = 0, todo, avail_here;
1564 struct page **ppages = NULL;
1565 int err;
1566
1567 if (encode) {
1568 if (xdr_encode_word(buf, base, desc->array_len) != 0)
1569 return -EINVAL;
1570 } else {
1571 if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
Trond Myklebust58fcb8d2005-08-10 18:15:12 -04001572 desc->array_len > desc->array_maxlen ||
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001573 (unsigned long) base + 4 + desc->array_len *
1574 desc->elem_size > buf->len)
1575 return -EINVAL;
1576 }
1577 base += 4;
1578
1579 if (!desc->xcode)
1580 return 0;
1581
1582 todo = desc->array_len * desc->elem_size;
1583
1584 /* process head */
1585 if (todo && base < buf->head->iov_len) {
1586 c = buf->head->iov_base + base;
1587 avail_here = min_t(unsigned int, todo,
1588 buf->head->iov_len - base);
1589 todo -= avail_here;
1590
1591 while (avail_here >= desc->elem_size) {
1592 err = desc->xcode(desc, c);
1593 if (err)
1594 goto out;
1595 c += desc->elem_size;
1596 avail_here -= desc->elem_size;
1597 }
1598 if (avail_here) {
1599 if (!elem) {
1600 elem = kmalloc(desc->elem_size, GFP_KERNEL);
1601 err = -ENOMEM;
1602 if (!elem)
1603 goto out;
1604 }
1605 if (encode) {
1606 err = desc->xcode(desc, elem);
1607 if (err)
1608 goto out;
1609 memcpy(c, elem, avail_here);
1610 } else
1611 memcpy(elem, c, avail_here);
1612 copied = avail_here;
1613 }
1614 base = buf->head->iov_len; /* align to start of pages */
1615 }
1616
1617 /* process pages array */
1618 base -= buf->head->iov_len;
1619 if (todo && base < buf->page_len) {
1620 unsigned int avail_page;
1621
1622 avail_here = min(todo, buf->page_len - base);
1623 todo -= avail_here;
1624
1625 base += buf->page_base;
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +03001626 ppages = buf->pages + (base >> PAGE_SHIFT);
1627 base &= ~PAGE_MASK;
1628 avail_page = min_t(unsigned int, PAGE_SIZE - base,
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001629 avail_here);
1630 c = kmap(*ppages) + base;
1631
1632 while (avail_here) {
1633 avail_here -= avail_page;
1634 if (copied || avail_page < desc->elem_size) {
1635 unsigned int l = min(avail_page,
1636 desc->elem_size - copied);
1637 if (!elem) {
1638 elem = kmalloc(desc->elem_size,
1639 GFP_KERNEL);
1640 err = -ENOMEM;
1641 if (!elem)
1642 goto out;
1643 }
1644 if (encode) {
1645 if (!copied) {
1646 err = desc->xcode(desc, elem);
1647 if (err)
1648 goto out;
1649 }
1650 memcpy(c, elem + copied, l);
1651 copied += l;
1652 if (copied == desc->elem_size)
1653 copied = 0;
1654 } else {
1655 memcpy(elem + copied, c, l);
1656 copied += l;
1657 if (copied == desc->elem_size) {
1658 err = desc->xcode(desc, elem);
1659 if (err)
1660 goto out;
1661 copied = 0;
1662 }
1663 }
1664 avail_page -= l;
1665 c += l;
1666 }
1667 while (avail_page >= desc->elem_size) {
1668 err = desc->xcode(desc, c);
1669 if (err)
1670 goto out;
1671 c += desc->elem_size;
1672 avail_page -= desc->elem_size;
1673 }
1674 if (avail_page) {
1675 unsigned int l = min(avail_page,
1676 desc->elem_size - copied);
1677 if (!elem) {
1678 elem = kmalloc(desc->elem_size,
1679 GFP_KERNEL);
1680 err = -ENOMEM;
1681 if (!elem)
1682 goto out;
1683 }
1684 if (encode) {
1685 if (!copied) {
1686 err = desc->xcode(desc, elem);
1687 if (err)
1688 goto out;
1689 }
1690 memcpy(c, elem + copied, l);
1691 copied += l;
1692 if (copied == desc->elem_size)
1693 copied = 0;
1694 } else {
1695 memcpy(elem + copied, c, l);
1696 copied += l;
1697 if (copied == desc->elem_size) {
1698 err = desc->xcode(desc, elem);
1699 if (err)
1700 goto out;
1701 copied = 0;
1702 }
1703 }
1704 }
1705 if (avail_here) {
1706 kunmap(*ppages);
1707 ppages++;
1708 c = kmap(*ppages);
1709 }
1710
1711 avail_page = min(avail_here,
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +03001712 (unsigned int) PAGE_SIZE);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001713 }
1714 base = buf->page_len; /* align to start of tail */
1715 }
1716
1717 /* process tail */
1718 base -= buf->page_len;
1719 if (todo) {
1720 c = buf->tail->iov_base + base;
1721 if (copied) {
1722 unsigned int l = desc->elem_size - copied;
1723
1724 if (encode)
1725 memcpy(c, elem + copied, l);
1726 else {
1727 memcpy(elem + copied, c, l);
1728 err = desc->xcode(desc, elem);
1729 if (err)
1730 goto out;
1731 }
1732 todo -= l;
1733 c += l;
1734 }
1735 while (todo) {
1736 err = desc->xcode(desc, c);
1737 if (err)
1738 goto out;
1739 c += desc->elem_size;
1740 todo -= desc->elem_size;
1741 }
1742 }
1743 err = 0;
1744
1745out:
Jesper Juhla51482b2005-11-08 09:41:34 -08001746 kfree(elem);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001747 if (ppages)
1748 kunmap(*ppages);
1749 return err;
1750}
1751
1752int
1753xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
1754 struct xdr_array2_desc *desc)
1755{
1756 if (base >= buf->len)
1757 return -EINVAL;
1758
1759 return xdr_xcode_array2(buf, base, desc, 0);
1760}
Trond Myklebust468039e2008-12-23 15:21:31 -05001761EXPORT_SYMBOL_GPL(xdr_decode_array2);
Andreas Gruenbacherbd8100e2005-06-22 17:16:24 +00001762
1763int
1764xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
1765 struct xdr_array2_desc *desc)
1766{
1767 if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
1768 buf->head->iov_len + buf->page_len + buf->tail->iov_len)
1769 return -EINVAL;
1770
1771 return xdr_xcode_array2(buf, base, desc, 1);
1772}
Trond Myklebust468039e2008-12-23 15:21:31 -05001773EXPORT_SYMBOL_GPL(xdr_encode_array2);
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001774
1775int
1776xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -08001777 int (*actor)(struct scatterlist *, void *), void *data)
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001778{
1779 int i, ret = 0;
Eric Dumazet95c96172012-04-15 05:58:06 +00001780 unsigned int page_len, thislen, page_offset;
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001781 struct scatterlist sg[1];
1782
Herbert Xu68e3f5d2007-10-27 00:52:07 -07001783 sg_init_table(sg, 1);
1784
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001785 if (offset >= buf->head[0].iov_len) {
1786 offset -= buf->head[0].iov_len;
1787 } else {
1788 thislen = buf->head[0].iov_len - offset;
1789 if (thislen > len)
1790 thislen = len;
1791 sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
1792 ret = actor(sg, data);
1793 if (ret)
1794 goto out;
1795 offset = 0;
1796 len -= thislen;
1797 }
1798 if (len == 0)
1799 goto out;
1800
1801 if (offset >= buf->page_len) {
1802 offset -= buf->page_len;
1803 } else {
1804 page_len = buf->page_len - offset;
1805 if (page_len > len)
1806 page_len = len;
1807 len -= page_len;
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +03001808 page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
1809 i = (offset + buf->page_base) >> PAGE_SHIFT;
1810 thislen = PAGE_SIZE - page_offset;
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001811 do {
1812 if (thislen > page_len)
1813 thislen = page_len;
Jens Axboe642f149032007-10-24 11:20:47 +02001814 sg_set_page(sg, buf->pages[i], thislen, page_offset);
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001815 ret = actor(sg, data);
1816 if (ret)
1817 goto out;
1818 page_len -= thislen;
1819 i++;
1820 page_offset = 0;
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +03001821 thislen = PAGE_SIZE;
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001822 } while (page_len != 0);
1823 offset = 0;
1824 }
1825 if (len == 0)
1826 goto out;
1827 if (offset < buf->tail[0].iov_len) {
1828 thislen = buf->tail[0].iov_len - offset;
1829 if (thislen > len)
1830 thislen = len;
1831 sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
1832 ret = actor(sg, data);
1833 len -= thislen;
1834 }
1835 if (len != 0)
1836 ret = -EINVAL;
1837out:
1838 return ret;
1839}
Trond Myklebust468039e2008-12-23 15:21:31 -05001840EXPORT_SYMBOL_GPL(xdr_process_buf);
Olga Kornievskaia37a4e6c2006-12-04 20:22:33 -05001841
Trond Myklebust5c741d42017-02-19 16:08:31 -05001842/**
Trond Myklebust0e779aa2018-03-20 17:03:05 -04001843 * xdr_stream_decode_opaque - Decode variable length opaque
1844 * @xdr: pointer to xdr_stream
1845 * @ptr: location to store opaque data
1846 * @size: size of storage buffer @ptr
1847 *
1848 * Return values:
1849 * On success, returns size of object stored in *@ptr
1850 * %-EBADMSG on XDR buffer overflow
1851 * %-EMSGSIZE on overflow of storage buffer @ptr
1852 */
1853ssize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr, size_t size)
1854{
1855 ssize_t ret;
1856 void *p;
1857
1858 ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
1859 if (ret <= 0)
1860 return ret;
1861 memcpy(ptr, p, ret);
1862 return ret;
1863}
1864EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque);
1865
1866/**
1867 * xdr_stream_decode_opaque_dup - Decode and duplicate variable length opaque
1868 * @xdr: pointer to xdr_stream
1869 * @ptr: location to store pointer to opaque data
1870 * @maxlen: maximum acceptable object size
1871 * @gfp_flags: GFP mask to use
1872 *
1873 * Return values:
1874 * On success, returns size of object stored in *@ptr
1875 * %-EBADMSG on XDR buffer overflow
1876 * %-EMSGSIZE if the size of the object would exceed @maxlen
1877 * %-ENOMEM on memory allocation failure
1878 */
1879ssize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr,
1880 size_t maxlen, gfp_t gfp_flags)
1881{
1882 ssize_t ret;
1883 void *p;
1884
1885 ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen);
1886 if (ret > 0) {
1887 *ptr = kmemdup(p, ret, gfp_flags);
1888 if (*ptr != NULL)
1889 return ret;
1890 ret = -ENOMEM;
1891 }
1892 *ptr = NULL;
1893 return ret;
1894}
1895EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_dup);
1896
1897/**
1898 * xdr_stream_decode_string - Decode variable length string
1899 * @xdr: pointer to xdr_stream
1900 * @str: location to store string
1901 * @size: size of storage buffer @str
1902 *
1903 * Return values:
1904 * On success, returns length of NUL-terminated string stored in *@str
1905 * %-EBADMSG on XDR buffer overflow
1906 * %-EMSGSIZE on overflow of storage buffer @str
1907 */
1908ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str, size_t size)
1909{
1910 ssize_t ret;
1911 void *p;
1912
1913 ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
1914 if (ret > 0) {
1915 memcpy(str, p, ret);
1916 str[ret] = '\0';
1917 return strlen(str);
1918 }
1919 *str = '\0';
1920 return ret;
1921}
1922EXPORT_SYMBOL_GPL(xdr_stream_decode_string);
1923
1924/**
Trond Myklebust5c741d42017-02-19 16:08:31 -05001925 * xdr_stream_decode_string_dup - Decode and duplicate variable length string
1926 * @xdr: pointer to xdr_stream
1927 * @str: location to store pointer to string
1928 * @maxlen: maximum acceptable string length
1929 * @gfp_flags: GFP mask to use
1930 *
1931 * Return values:
1932 * On success, returns length of NUL-terminated string stored in *@ptr
1933 * %-EBADMSG on XDR buffer overflow
1934 * %-EMSGSIZE if the size of the string would exceed @maxlen
1935 * %-ENOMEM on memory allocation failure
1936 */
1937ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
1938 size_t maxlen, gfp_t gfp_flags)
1939{
1940 void *p;
1941 ssize_t ret;
1942
1943 ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen);
1944 if (ret > 0) {
1945 char *s = kmalloc(ret + 1, gfp_flags);
1946 if (s != NULL) {
1947 memcpy(s, p, ret);
1948 s[ret] = '\0';
1949 *str = s;
1950 return strlen(s);
1951 }
1952 ret = -ENOMEM;
1953 }
1954 *str = NULL;
1955 return ret;
1956}
1957EXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup);