p[quadlen] = 0; /* zero trailing bytes */
*p++ = cpu_to_be32(obj->len);
memcpy(p, obj->data, obj->len); return p + XDR_QUADLEN(obj->len);
}
EXPORT_SYMBOL_GPL(xdr_encode_netobj);
/** * xdr_encode_opaque_fixed - Encode fixed length opaque data * @p: pointer to current position in XDR buffer. * @ptr: pointer to data to encode (or NULL) * @nbytes: size of data. * * Copy the array of data of length nbytes at ptr to the XDR buffer * at position p, then align to the next 32-bit boundary by padding * with zero bytes (see RFC1832). * Note: if ptr is NULL, only the padding is performed. * * Returns the updated current XDR buffer position *
*/
__be32 *xdr_encode_opaque_fixed(__be32 *p, constvoid *ptr, unsignedint nbytes)
{ if (likely(nbytes != 0)) { unsignedint quadlen = XDR_QUADLEN(nbytes); unsignedint padding = (quadlen << 2) - nbytes;
if (ptr != NULL)
memcpy(p, ptr, nbytes); if (padding != 0)
memset((char *)p + nbytes, 0, padding);
p += quadlen;
} return p;
}
EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
/** * xdr_encode_opaque - Encode variable length opaque data * @p: pointer to current position in XDR buffer. * @ptr: pointer to data to encode (or NULL) * @nbytes: size of data. * * Returns the updated current XDR buffer position
*/
__be32 *xdr_encode_opaque(__be32 *p, constvoid *ptr, unsignedint nbytes)
{
*p++ = cpu_to_be32(nbytes); return xdr_encode_opaque_fixed(p, ptr, nbytes);
}
EXPORT_SYMBOL_GPL(xdr_encode_opaque);
/** * xdr_inline_pages - Prepare receive buffer for a large reply * @xdr: xdr_buf into which reply will be placed * @offset: expected offset where data payload will start, in bytes * @pages: vector of struct page pointers * @base: offset in first page where receive should start, in bytes * @len: expected size of the upper layer data payload, in bytes *
*/ void
xdr_inline_pages(struct xdr_buf *xdr, unsignedint offset, struct page **pages, unsignedint base, unsignedint len)
{ struct kvec *head = xdr->head; struct kvec *tail = xdr->tail; char *buf = (char *)head->iov_base; unsignedint buflen = head->iov_len;
/* * Helper routines for doing 'memmove' like operations on a struct xdr_buf
*/
/** * _shift_data_left_pages * @pages: vector of pages containing both the source and dest memory area. * @pgto_base: page vector address of destination * @pgfrom_base: page vector address of source * @len: number of bytes to copy * * Note: the addresses pgto_base and pgfrom_base are both calculated in * the same way: * if a memory area starts at byte 'base' in page 'pages[i]', * then its address is given as (i << PAGE_CACHE_SHIFT) + base * Alse note: pgto_base must be < pgfrom_base, but the memory areas * they point to may overlap.
*/ staticvoid
_shift_data_left_pages(struct page **pages, size_t pgto_base,
size_t pgfrom_base, size_t len)
{ struct page **pgfrom, **pgto; char *vfrom, *vto;
size_t copy;
/** * _shift_data_right_pages * @pages: vector of pages containing both the source and dest memory area. * @pgto_base: page vector address of destination * @pgfrom_base: page vector address of source * @len: number of bytes to copy * * Note: the addresses pgto_base and pgfrom_base are both calculated in * the same way: * if a memory area starts at byte 'base' in page 'pages[i]', * then its address is given as (i << PAGE_SHIFT) + base * Also note: pgfrom_base must be < pgto_base, but the memory areas * they point to may overlap.
*/ staticvoid
_shift_data_right_pages(struct page **pages, size_t pgto_base,
size_t pgfrom_base, size_t len)
{ struct page **pgfrom, **pgto; char *vfrom, *vto;
size_t copy;
/** * _copy_to_pages * @pages: array of pages * @pgbase: page vector address of destination * @p: pointer to source data * @len: length * * Copies data from an arbitrary memory location into an array of pages * The copy is assumed to be non-overlapping.
*/ staticvoid
_copy_to_pages(struct page **pages, size_t pgbase, constchar *p, size_t len)
{ struct page **pgto; char *vto;
size_t copy;
pgbase += copy; if (pgbase == PAGE_SIZE) {
flush_dcache_page(*pgto);
pgbase = 0;
pgto++;
}
p += copy;
}
flush_dcache_page(*pgto);
}
/** * _copy_from_pages * @p: pointer to destination * @pages: array of pages * @pgbase: offset of source data * @len: length * * Copies data into an arbitrary memory location from an array of pages * The copy is assumed to be non-overlapping.
*/ void
_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
{ struct page **pgfrom; char *vfrom;
size_t copy;
if (base >= head->iov_len) return; if (len > head->iov_len - base)
len = head->iov_len - base; if (to >= buf->page_len + head->iov_len) {
tato = to - buf->page_len - head->iov_len;
talen = len;
} elseif (to >= head->iov_len) {
pgto = to - head->iov_len;
pglen = len; if (pgto + pglen > buf->page_len) {
talen = pgto + pglen - buf->page_len;
pglen -= talen;
}
} else {
pglen = len - to; if (pglen > buf->page_len) {
talen = pglen - buf->page_len;
pglen = buf->page_len;
}
}
len -= talen;
base += len; if (talen + tato > tail->iov_len)
talen = tail->iov_len > tato ? tail->iov_len - tato : 0;
memcpy(tail->iov_base + tato, head->iov_base + base, talen);
len -= pglen;
base -= pglen;
_copy_to_pages(buf->pages, buf->page_base + pgto, head->iov_base + base,
pglen);
base -= len;
memmove(head->iov_base + to, head->iov_base + base, len);
}
if (shift > base) {
bytes = (shift - base); if (bytes >= len) return;
base += bytes;
len -= bytes;
}
if (base < head->iov_len) {
bytes = min_t(unsignedint, len, head->iov_len - base);
memmove(head->iov_base + (base - shift),
head->iov_base + base, bytes);
base += bytes;
len -= bytes;
}
xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift);
}
/** * xdr_shrink_bufhead * @buf: xdr_buf * @len: new length of buf->head[0] * * Shrinks XDR buffer's header kvec buf->head[0], setting it to * 'len' bytes. The extra data is not lost, but is instead * moved into the inlined pages and/or the tail.
*/ staticunsignedint xdr_shrink_bufhead(struct xdr_buf *buf, unsignedint len)
{ struct kvec *head = buf->head; unsignedint shift, buflen = max(buf->len, len);
/** * xdr_shrink_pagelen - shrinks buf->pages to @len bytes * @buf: xdr_buf * @len: new page buffer length * * The extra data is not lost, but is instead moved into buf->tail. * Returns the actual number of bytes moved.
*/ staticunsignedint xdr_shrink_pagelen(struct xdr_buf *buf, unsignedint len)
{ unsignedint shift, buflen = buf->len - buf->head->iov_len;
/** * xdr_stream_pos - Return the current offset from the start of the xdr_stream * @xdr: pointer to struct xdr_stream
*/ unsignedint xdr_stream_pos(conststruct xdr_stream *xdr)
{ return (unsignedint)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2;
}
EXPORT_SYMBOL_GPL(xdr_stream_pos);
/** * xdr_page_pos - Return the current offset from the start of the xdr pages * @xdr: pointer to struct xdr_stream
*/ unsignedint xdr_page_pos(conststruct xdr_stream *xdr)
{ unsignedint pos = xdr_stream_pos(xdr);
/** * xdr_init_encode - Initialize a struct xdr_stream for sending data. * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer in which to encode data * @p: current pointer inside XDR buffer * @rqst: pointer to controlling rpc_rqst, for debugging * * Note: at the moment the RPC client only passes the length of our * scratch buffer in the xdr_buf's header kvec. Previously this * meant we needed to call xdr_adjust_iovec() after encoding the * data. With the new scheme, the xdr_stream manages the details * of the buffer length, and takes care of adjusting the kvec * length for us.
*/ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, struct rpc_rqst *rqst)
{ struct kvec *iov = buf->head; int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
/** * xdr_init_encode_pages - Initialize an xdr_stream for encoding into pages * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer into which to encode data *
*/ void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf)
{
xdr_reset_scratch_buffer(xdr);
/** * __xdr_commit_encode - Ensure all data is written to buffer * @xdr: pointer to xdr_stream * * We handle encoding across page boundaries by giving the caller a * temporary location to write to, then later copying the data into * place; xdr_commit_encode does that copying. * * Normally the caller doesn't need to call this directly, as the * following xdr_reserve_space will do it. But an explicit call may be * required at the end of encoding, or any other time when the xdr_buf * data might be read.
*/ void __xdr_commit_encode(struct xdr_stream *xdr)
{
size_t shift = xdr->scratch.iov_len; void *page;
/* * The buffer space to be reserved crosses the boundary between * xdr->buf->head and xdr->buf->pages, or between two pages * in xdr->buf->pages.
*/ static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
size_t nbytes)
{ int space_left; int frag1bytes, frag2bytes; void *p;
if (nbytes > PAGE_SIZE) goto out_overflow; /* Bigger buffers require special handling */ if (xdr->buf->len + nbytes > xdr->buf->buflen) goto out_overflow; /* Sorry, we're totally out of space */
frag1bytes = (xdr->end - xdr->p) << 2;
frag2bytes = nbytes - frag1bytes; if (xdr->iov)
xdr->iov->iov_len += frag1bytes; else
xdr->buf->page_len += frag1bytes;
xdr->page_ptr++;
xdr->iov = NULL;
/* * If the last encode didn't end exactly on a page boundary, the * next one will straddle boundaries. Encode into the next * page, then copy it back later in xdr_commit_encode. We use * the "scratch" iov to track any temporarily unused fragment of * space at the end of the previous buffer:
*/
xdr_set_scratch_buffer(xdr, xdr->p, frag1bytes);
/* * xdr->p is where the next encode will start after * xdr_commit_encode() has shifted this one back:
*/
p = page_address(*xdr->page_ptr);
xdr->p = p + frag2bytes;
space_left = xdr->buf->buflen - xdr->buf->len; if (space_left - frag1bytes >= PAGE_SIZE)
xdr->end = p + PAGE_SIZE; else
xdr->end = p + space_left - frag1bytes;
/** * xdr_reserve_space - Reserve buffer space for sending * @xdr: pointer to xdr_stream * @nbytes: number of bytes to reserve * * Checks that we have enough buffer space to encode 'nbytes' more * bytes of data. If so, update the total xdr_buf length, and * adjust the length of the current kvec. * * The returned pointer is valid only until the next call to * xdr_reserve_space() or xdr_commit_encode() on @xdr. The current * implementation of this API guarantees that space reserved for a * four-byte data item remains valid until @xdr is destroyed, but * that might not always be true in the future.
*/
__be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
{
__be32 *p = xdr->p;
__be32 *q;
xdr_commit_encode(xdr); /* align nbytes on the next 32-bit boundary */
nbytes += 3;
nbytes &= ~3;
q = p + (nbytes >> 2); if (unlikely(q > xdr->end || q < p)) return xdr_get_next_encode_buffer(xdr, nbytes);
xdr->p = q; if (xdr->iov)
xdr->iov->iov_len += nbytes; else
xdr->buf->page_len += nbytes;
xdr->buf->len += nbytes; return p;
}
EXPORT_SYMBOL_GPL(xdr_reserve_space);
/** * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending * @xdr: pointer to xdr_stream * @nbytes: number of bytes to reserve * * The size argument passed to xdr_reserve_space() is determined based * on the number of bytes remaining in the current page to avoid * invalidating iov_base pointers when xdr_commit_encode() is called. * * Return values: * %0: success * %-EMSGSIZE: not enough space is available in @xdr
*/ int xdr_reserve_space_vec(struct xdr_stream *xdr, size_t nbytes)
{
size_t thislen;
__be32 *p;
/* * svcrdma requires every READ payload to start somewhere * in xdr->pages.
*/ if (xdr->iov == xdr->buf->head) {
xdr->iov = NULL;
xdr->end = xdr->p;
}
/* XXX: Let's find a way to make this more efficient */ while (nbytes) {
thislen = xdr->buf->page_len % PAGE_SIZE;
thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen);
p = xdr_reserve_space(xdr, thislen); if (!p) return -EMSGSIZE;
/** * xdr_truncate_encode - truncate an encode buffer * @xdr: pointer to xdr_stream * @len: new length of buffer * * Truncates the xdr stream, so that xdr->buf->len == len, * and xdr->p points at offset len from the start of the buffer, and * head, tail, and page lengths are adjusted to correspond. * * If this means moving xdr->p to a different buffer, we assume that * the end pointer should be set to the end of the current page, * except in the case of the head buffer when we assume the head * buffer's current length represents the end of the available buffer. * * This is *not* safe to use on a buffer that already has inlined page * cache pages (as in a zero-copy server read reply), except for the * simple case of truncating from one position in the tail to another. *
*/ void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
{ struct xdr_buf *buf = xdr->buf; struct kvec *head = buf->head; struct kvec *tail = buf->tail; int fraglen; intnew;
if (len > buf->len) {
WARN_ON_ONCE(1); return;
}
xdr_commit_encode(xdr);
/** * xdr_restrict_buflen - decrease available buffer space * @xdr: pointer to xdr_stream * @newbuflen: new maximum number of bytes available * * Adjust our idea of how much space is available in the buffer. * If we've already used too much space in the buffer, returns -1. * If the available space is already smaller than newbuflen, returns 0 * and does nothing. Otherwise, adjusts xdr->buf->buflen to newbuflen * and ensures xdr->end is set at most offset newbuflen from the start * of the buffer.
*/ int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen)
{ struct xdr_buf *buf = xdr->buf; int left_in_this_buf = (void *)xdr->end - (void *)xdr->p; int end_offset = buf->len + left_in_this_buf;
/** * xdr_write_pages - Insert a list of pages into an XDR buffer for sending * @xdr: pointer to xdr_stream * @pages: array of pages to insert * @base: starting offset of first data byte in @pages * @len: number of data bytes in @pages to insert * * After the @pages are added, the tail iovec is instantiated pointing to * end of the head buffer, and the stream is set up to encode subsequent * items into the tail.
*/ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsignedint base, unsignedint len)
{ struct xdr_buf *buf = xdr->buf; struct kvec *tail = buf->tail;
/** * xdr_init_decode - Initialize an xdr_stream for decoding data. * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer from which to decode data * @p: current pointer inside XDR buffer * @rqst: pointer to controlling rpc_rqst, for debugging
*/ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, struct rpc_rqst *rqst)
{
xdr->buf = buf;
xdr->page_kaddr = NULL;
xdr_reset_scratch_buffer(xdr);
xdr->nwords = XDR_QUADLEN(buf->len); if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 &&
xdr_set_page_base(xdr, 0, buf->len) == 0)
xdr_set_iov(xdr, buf->tail, 0, buf->len); if (p != NULL && p > xdr->p && xdr->end >= p) {
xdr->nwords -= p - xdr->p;
xdr->p = p;
}
xdr->rqst = rqst;
}
EXPORT_SYMBOL_GPL(xdr_init_decode);
/** * xdr_init_decode_pages - Initialize an xdr_stream for decoding into pages * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer from which to decode data * @pages: list of pages to decode into * @len: length in bytes of buffer in pages
*/ void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, struct page **pages, unsignedint len)
{
memset(buf, 0, sizeof(*buf));
buf->pages = pages;
buf->page_len = len;
buf->buflen = len;
buf->len = len;
xdr_init_decode(xdr, buf, NULL, NULL);
}
EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
/** * xdr_finish_decode - Clean up the xdr_stream after decoding data. * @xdr: pointer to xdr_stream struct
*/ void xdr_finish_decode(struct xdr_stream *xdr)
{
xdr_stream_unmap_current_page(xdr);
}
EXPORT_SYMBOL(xdr_finish_decode);
if (nbytes > xdr->scratch.iov_len) goto out_overflow;
p = __xdr_inline_decode(xdr, cplen); if (p == NULL) return NULL;
memcpy(cpdest, p, cplen); if (!xdr_set_next_buffer(xdr)) goto out_overflow;
cpdest += cplen;
nbytes -= cplen;
p = __xdr_inline_decode(xdr, nbytes); if (p == NULL) return NULL;
memcpy(cpdest, p, nbytes); return xdr->scratch.iov_base;
out_overflow:
trace_rpc_xdr_overflow(xdr, nbytes); return NULL;
}
/** * xdr_inline_decode - Retrieve XDR data to decode * @xdr: pointer to xdr_stream struct * @nbytes: number of bytes of data to decode * * Check if the input buffer is long enough to enable us to decode * 'nbytes' more bytes of data starting at the current position. * If so return the current pointer, then update the current * pointer position.
*/
__be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
{
__be32 *p;
if (unlikely(nbytes == 0)) return xdr->p; if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) goto out_overflow;
p = __xdr_inline_decode(xdr, nbytes); if (p != NULL) return p; return xdr_copy_to_scratch(xdr, nbytes);
out_overflow:
trace_rpc_xdr_overflow(xdr, nbytes); return NULL;
}
EXPORT_SYMBOL_GPL(xdr_inline_decode);
xdr_realign_pages(xdr); if (nwords > xdr->nwords) {
nwords = xdr->nwords;
len = nwords << 2;
} if (buf->page_len <= len)
len = buf->page_len; elseif (nwords < xdr->nwords) { /* Truncate page data and move it into the tail */
copied = xdr_shrink_pagelen(buf, len);
trace_rpc_xdr_alignment(xdr, len, copied);
} return len;
}
/** * xdr_read_pages - align page-based XDR data to current pointer position * @xdr: pointer to xdr_stream struct * @len: number of bytes of page data * * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + @len * bytes is moved into the XDR tail[]. The xdr_stream current position is * then advanced past that data to align to the next XDR object in the tail. * * Returns the number of XDR encoded bytes now contained in the pages
*/ unsignedint xdr_read_pages(struct xdr_stream *xdr, unsignedint len)
{ unsignedint nwords = XDR_QUADLEN(len); unsignedint base, end, pglen;
base = (nwords << 2) - pglen;
end = xdr_stream_remaining(xdr) - pglen;
xdr_set_tail_base(xdr, base, end); return len <= pglen ? len : pglen;
}
EXPORT_SYMBOL_GPL(xdr_read_pages);
/** * xdr_set_pagelen - Sets the length of the XDR pages * @xdr: pointer to xdr_stream struct * @len: new length of the XDR page data * * Either grows or shrinks the length of the xdr pages by setting pagelen to * @len bytes. When shrinking, any extra data is moved into buf->tail, whereas * when growing any data beyond the current pointer is moved into the tail. * * Returns True if the operation was successful, and False otherwise.
*/ void xdr_set_pagelen(struct xdr_stream *xdr, unsignedint len)
{ struct xdr_buf *buf = xdr->buf;
size_t remaining = xdr_stream_remaining(xdr);
size_t base = 0;
if (len < buf->page_len) {
base = buf->page_len - len;
xdr_shrink_pagelen(buf, len);
} else {
xdr_buf_head_shift_right(buf, xdr_stream_pos(xdr),
buf->page_len, remaining); if (len > buf->page_len)
xdr_buf_try_expand(buf, len - buf->page_len);
}
xdr_set_tail_base(xdr, base, remaining);
}
EXPORT_SYMBOL_GPL(xdr_set_pagelen);
/** * xdr_enter_page - decode data from the XDR page * @xdr: pointer to xdr_stream struct * @len: number of bytes of page data * * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + "len" * bytes is moved into the XDR tail[]. The current pointer is then * repositioned at the beginning of the first XDR page.
*/ void xdr_enter_page(struct xdr_stream *xdr, unsignedint len)
{
len = xdr_align_pages(xdr, len); /* * Position current pointer at beginning of tail, and * set remaining message length.
*/ if (len != 0)
xdr_set_page_base(xdr, 0, len);
}
EXPORT_SYMBOL_GPL(xdr_enter_page);
/** * xdr_buf_subsegment - set subbuf to a portion of buf * @buf: an xdr buffer * @subbuf: the result buffer * @base: beginning of range in bytes * @len: length of range in bytes * * sets @subbuf to an xdr buffer representing the portion of @buf of * length @len starting at offset @base. * * @buf and @subbuf may be pointers to the same struct xdr_buf. * * Returns -1 if base or length are out of bounds.
*/ int xdr_buf_subsegment(conststruct xdr_buf *buf, struct xdr_buf *subbuf, unsignedint base, unsignedint len)
{
subbuf->buflen = subbuf->len = len; if (base < buf->head[0].iov_len) {
subbuf->head[0].iov_base = buf->head[0].iov_base + base;
subbuf->head[0].iov_len = min_t(unsignedint, len,
buf->head[0].iov_len - base);
len -= subbuf->head[0].iov_len;
base = 0;
} else {
base -= buf->head[0].iov_len;
subbuf->head[0].iov_base = buf->head[0].iov_base;
subbuf->head[0].iov_len = 0;
}
if (base < buf->page_len) {
subbuf->page_len = min(buf->page_len - base, len);
base += buf->page_base;
subbuf->page_base = base & ~PAGE_MASK;
subbuf->pages = &buf->pages[base >> PAGE_SHIFT];
len -= subbuf->page_len;
base = 0;
} else {
base -= buf->page_len;
subbuf->pages = buf->pages;
subbuf->page_base = 0;
subbuf->page_len = 0;
}
if (base < buf->tail[0].iov_len) {
subbuf->tail[0].iov_base = buf->tail[0].iov_base + base;
subbuf->tail[0].iov_len = min_t(unsignedint, len,
buf->tail[0].iov_len - base);
len -= subbuf->tail[0].iov_len;
base = 0;
} else {
base -= buf->tail[0].iov_len;
subbuf->tail[0].iov_base = buf->tail[0].iov_base;
subbuf->tail[0].iov_len = 0;
}
if (base || len) return -1; return 0;
}
EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
/** * xdr_stream_subsegment - set @subbuf to a portion of @xdr * @xdr: an xdr_stream set up for decoding * @subbuf: the result buffer * @nbytes: length of @xdr to extract, in bytes * * Sets up @subbuf to represent a portion of @xdr. The portion * starts at the current offset in @xdr, and extends for a length * of @nbytes. If this is successful, @xdr is advanced to the next * XDR data item following that portion. * * Return values: * %true: @subbuf has been initialized, and @xdr has been advanced. * %false: a bounds error has occurred
*/ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, unsignedint nbytes)
{ unsignedint start = xdr_stream_pos(xdr); unsignedint remaining, len;
/* Extract @subbuf and bounds-check the fn arguments */ if (xdr_buf_subsegment(xdr->buf, subbuf, start, nbytes)) returnfalse;
/* Advance @xdr by @nbytes */ for (remaining = nbytes; remaining;) { if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) returnfalse;
/** * xdr_stream_move_subsegment - Move part of a stream to another position * @xdr: the source xdr_stream * @offset: the source offset of the segment * @target: the target offset of the segment * @length: the number of bytes to move * * Moves @length bytes from @offset to @target in the xdr_stream, overwriting * anything in its space. Returns the number of bytes in the segment.
*/ unsignedint xdr_stream_move_subsegment(struct xdr_stream *xdr, unsignedint offset, unsignedint target, unsignedint length)
{ struct xdr_buf buf; unsignedint shift;
/** * xdr_stream_zero - zero out a portion of an xdr_stream * @xdr: an xdr_stream to zero out * @offset: the starting point in the stream * @length: the number of bytes to zero
*/ unsignedint xdr_stream_zero(struct xdr_stream *xdr, unsignedint offset, unsignedint length)
{ struct xdr_buf buf;
if (xdr_buf_subsegment(xdr->buf, &buf, offset, length) < 0) return 0; if (buf.head[0].iov_len)
xdr_buf_iov_zero(buf.head, 0, buf.head[0].iov_len); if (buf.page_len > 0)
xdr_buf_pages_zero(&buf, 0, buf.page_len); if (buf.tail[0].iov_len)
xdr_buf_iov_zero(buf.tail, 0, buf.tail[0].iov_len); return length;
}
EXPORT_SYMBOL_GPL(xdr_stream_zero);
/** * xdr_buf_trim - lop at most "len" bytes off the end of "buf" * @buf: buf to be trimmed * @len: number of bytes to reduce "buf" by * * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note * that it's possible that we'll trim less than that amount if the xdr_buf is * too small, or if (for instance) it's all in the head and the parser has * already read too far into it.
*/ void xdr_buf_trim(struct xdr_buf *buf, unsignedint len)
{
size_t cur; unsignedint trim = len;
if (buf->tail[0].iov_len) {
cur = min_t(size_t, buf->tail[0].iov_len, trim);
buf->tail[0].iov_len -= cur;
trim -= cur; if (!trim) goto fix_len;
}
if (buf->page_len) {
cur = min_t(unsignedint, buf->page_len, trim);
buf->page_len -= cur;
trim -= cur; if (!trim) goto fix_len;
}
if (buf->head[0].iov_len) {
cur = min_t(size_t, buf->head[0].iov_len, trim);
buf->head[0].iov_len -= cur;
trim -= cur;
}
fix_len:
buf->len -= (len - trim);
}
EXPORT_SYMBOL_GPL(xdr_buf_trim);
/* obj is assumed to point to allocated memory of size at least len: */ int read_bytes_from_xdr_buf(conststruct xdr_buf *buf, unsignedint base, void *obj, unsignedint len)
{ struct xdr_buf subbuf; int status;
status = xdr_buf_subsegment(buf, &subbuf, base, len); if (status != 0) return status;
__read_bytes_from_xdr_buf(&subbuf, obj, len); return 0;
}
EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
/* obj is assumed to point to allocated memory of size at least len: */ int write_bytes_to_xdr_buf(conststruct xdr_buf *buf, unsignedint base, void *obj, unsignedint len)
{ struct xdr_buf subbuf; int status;
status = xdr_buf_subsegment(buf, &subbuf, base, len); if (status != 0) return status;
__write_bytes_to_xdr_buf(&subbuf, obj, len); return 0;
}
EXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf);
int xdr_decode_word(conststruct xdr_buf *buf, unsignedint base, u32 *obj)
{
__be32 raw; int status;
status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); if (status) return status;
*obj = be32_to_cpu(raw); return 0;
}
EXPORT_SYMBOL_GPL(xdr_decode_word);
int xdr_encode_word(conststruct xdr_buf *buf, unsignedint base, u32 obj)
{
__be32 raw = cpu_to_be32(obj);
/* Returns 0 on success, or else a negative error code. */ staticint xdr_xcode_array2(conststruct xdr_buf *buf, unsignedint base, struct xdr_array2_desc *desc, int encode)
{ char *elem = NULL, *c; unsignedint copied = 0, todo, avail_here; struct page **ppages = NULL; int err;
if (encode) { if (xdr_encode_word(buf, base, desc->array_len) != 0) return -EINVAL;
} else { if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
desc->array_len > desc->array_maxlen ||
(unsignedlong) base + 4 + desc->array_len *
desc->elem_size > buf->len) return -EINVAL;
}
base += 4;
if (!desc->xcode) return 0;
todo = desc->array_len * desc->elem_size;
/* process head */ if (todo && base < buf->head->iov_len) {
c = buf->head->iov_base + base;
avail_here = min_t(unsignedint, todo,
buf->head->iov_len - base);
todo -= avail_here;
while (avail_here >= desc->elem_size) {
err = desc->xcode(desc, c); if (err) goto out;
c += desc->elem_size;
avail_here -= desc->elem_size;
} if (avail_here) { if (!elem) {
elem = kmalloc(desc->elem_size, GFP_KERNEL);
err = -ENOMEM; if (!elem) goto out;
} if (encode) {
err = desc->xcode(desc, elem); if (err) goto out;
memcpy(c, elem, avail_here);
} else
memcpy(elem, c, avail_here);
copied = avail_here;
}
base = buf->head->iov_len; /* align to start of pages */
}
/* process pages array */
base -= buf->head->iov_len; if (todo && base < buf->page_len) { unsignedint avail_page;
avail_here = min(todo, buf->page_len - base);
todo -= avail_here;
base += buf->page_base;
ppages = buf->pages + (base >> PAGE_SHIFT);
base &= ~PAGE_MASK;
avail_page = min_t(unsignedint, PAGE_SIZE - base,
avail_here);
c = kmap(*ppages) + base;
while (avail_here) {
avail_here -= avail_page; if (copied || avail_page < desc->elem_size) { unsignedint l = min(avail_page,
desc->elem_size - copied); if (!elem) {
elem = kmalloc(desc->elem_size,
GFP_KERNEL);
err = -ENOMEM; if (!elem) goto out;
} if (encode) { if (!copied) {
err = desc->xcode(desc, elem); if (err) goto out;
}
memcpy(c, elem + copied, l);
copied += l; if (copied == desc->elem_size)
copied = 0;
} else {
memcpy(elem + copied, c, l);
copied += l; if (copied == desc->elem_size) {
err = desc->xcode(desc, elem); if (err) goto out;
copied = 0;
}
}
avail_page -= l;
c += l;
} while (avail_page >= desc->elem_size) {
err = desc->xcode(desc, c); if (err) goto out;
c += desc->elem_size;
avail_page -= desc->elem_size;
} if (avail_page) { unsignedint l = min(avail_page,
desc->elem_size - copied); if (!elem) {
elem = kmalloc(desc->elem_size,
GFP_KERNEL);
err = -ENOMEM; if (!elem) goto out;
} if (encode) { if (!copied) {
err = desc->xcode(desc, elem); if (err) goto out;
}
memcpy(c, elem + copied, l);
copied += l; if (copied == desc->elem_size)
copied = 0;
} else {
memcpy(elem + copied, c, l);
copied += l; if (copied == desc->elem_size) {
err = desc->xcode(desc, elem); if (err) goto out;
copied = 0;
}
}
} if (avail_here) {
kunmap(*ppages);
ppages++;
c = kmap(*ppages);
}
avail_page = min(avail_here,
(unsignedint) PAGE_SIZE);
}
base = buf->page_len; /* align to start of tail */
}
/* process tail */
base -= buf->page_len; if (todo) {
c = buf->tail->iov_base + base; if (copied) { unsignedint l = desc->elem_size - copied;
if (encode)
memcpy(c, elem + copied, l); else {
memcpy(elem + copied, c, l);
err = desc->xcode(desc, elem); if (err) goto out;
}
todo -= l;
c += l;
} while (todo) {
err = desc->xcode(desc, c); if (err) goto out;
c += desc->elem_size;
todo -= desc->elem_size;
}
}
err = 0;
out:
kfree(elem); if (ppages)
kunmap(*ppages); return err;
}
int xdr_decode_array2(conststruct xdr_buf *buf, unsignedint base, struct xdr_array2_desc *desc)
{ if (base >= buf->len) return -EINVAL;
int xdr_process_buf(conststruct xdr_buf *buf, unsignedint offset, unsignedint len, int (*actor)(struct scatterlist *, void *), void *data)
{ int i, ret = 0; unsignedint page_len, thislen, page_offset; struct scatterlist sg[1];
sg_init_table(sg, 1);
if (offset >= buf->head[0].iov_len) {
offset -= buf->head[0].iov_len;
} else {
thislen = buf->head[0].iov_len - offset; if (thislen > len)
thislen = len;
sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
ret = actor(sg, data); if (ret) goto out;
offset = 0;
len -= thislen;
} if (len == 0) goto out;
if (offset >= buf->page_len) {
offset -= buf->page_len;
} else {
page_len = buf->page_len - offset; if (page_len > len)
page_len = len;
len -= page_len;
page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
i = (offset + buf->page_base) >> PAGE_SHIFT;
thislen = PAGE_SIZE - page_offset; do { if (thislen > page_len)
thislen = page_len;
sg_set_page(sg, buf->pages[i], thislen, page_offset);
ret = actor(sg, data); if (ret) goto out;
page_len -= thislen;
i++;
page_offset = 0;
thislen = PAGE_SIZE;
} while (page_len != 0);
offset = 0;
} if (len == 0) goto out; if (offset < buf->tail[0].iov_len) {
thislen = buf->tail[0].iov_len - offset; if (thislen > len)
thislen = len;
sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
ret = actor(sg, data);
len -= thislen;
} if (len != 0)
ret = -EINVAL;
out: return ret;
}
EXPORT_SYMBOL_GPL(xdr_process_buf);
/** * xdr_stream_decode_string_dup - Decode and duplicate variable length string * @xdr: pointer to xdr_stream * @str: location to store pointer to string * @maxlen: maximum acceptable string length * @gfp_flags: GFP mask to use * * Return values: * On success, returns length of NUL-terminated string stored in *@ptr * %-EBADMSG on XDR buffer overflow * %-EMSGSIZE if the size of the string would exceed @maxlen * %-ENOMEM on memory allocation failure
*/
ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
size_t maxlen, gfp_t gfp_flags)
{ void *p;
ssize_t ret;
ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen); if (ret > 0) { char *s = kmemdup_nul(p, ret, gfp_flags); if (s != NULL) {
*str = s; return strlen(s);
}
ret = -ENOMEM;
}
*str = NULL; return ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup);
/** * xdr_stream_decode_opaque_auth - Decode struct opaque_auth (RFC5531 S8.2) * @xdr: pointer to xdr_stream * @flavor: location to store decoded flavor * @body: location to store decode body * @body_len: location to store length of decoded body * * Return values: * On success, returns the number of buffer bytes consumed * %-EBADMSG on XDR buffer overflow * %-EMSGSIZE if the decoded size of the body field exceeds 400 octets
*/
ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor, void **body, unsignedint *body_len)
{
ssize_t ret, len;
len = xdr_stream_decode_u32(xdr, flavor); if (unlikely(len < 0)) return len;
ret = xdr_stream_decode_opaque_inline(xdr, body, RPC_MAX_AUTH_SIZE); if (unlikely(ret < 0)) return ret;
*body_len = ret; return len + ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_auth);
/** * xdr_stream_encode_opaque_auth - Encode struct opaque_auth (RFC5531 S8.2) * @xdr: pointer to xdr_stream * @flavor: verifier flavor to encode * @body: content of body to encode * @body_len: length of body to encode * * Return values: * On success, returns length in bytes of XDR buffer consumed * %-EBADMSG on XDR buffer overflow * %-EMSGSIZE if the size of @body exceeds 400 octets
*/
ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor, void *body, unsignedint body_len)
{
ssize_t ret, len;
if (unlikely(body_len > RPC_MAX_AUTH_SIZE)) return -EMSGSIZE;
len = xdr_stream_encode_u32(xdr, flavor); if (unlikely(len < 0)) return len;
ret = xdr_stream_encode_opaque(xdr, body, body_len); if (unlikely(ret < 0)) return ret; return len + ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_encode_opaque_auth);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.