Commit 4f5f3b60 authored by Anna Schumaker's avatar Anna Schumaker Committed by Trond Myklebust

SUNRPC: Introduce xdr_stream_move_subsegment()

I do this by creating an xdr subsegment for the range we will be
operating over. This lets me shift data to the correct place without
potentially overwriting anything already there.
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 33ce83ef
...@@ -262,6 +262,8 @@ extern unsigned int xdr_align_data(struct xdr_stream *, unsigned int offset, uns ...@@ -262,6 +262,8 @@ extern unsigned int xdr_align_data(struct xdr_stream *, unsigned int offset, uns
extern unsigned int xdr_expand_hole(struct xdr_stream *, unsigned int offset, unsigned int length); extern unsigned int xdr_expand_hole(struct xdr_stream *, unsigned int offset, unsigned int length);
extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
unsigned int len); unsigned int len);
extern unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
unsigned int target, unsigned int length);
/** /**
* xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.
......
...@@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf, ...@@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf,
xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift); xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift);
} }
static void xdr_buf_head_shift_left(const struct xdr_buf *buf,
unsigned int base, unsigned int len,
unsigned int shift)
{
const struct kvec *head = buf->head;
unsigned int bytes;
if (!shift || !len)
return;
if (shift > base) {
bytes = (shift - base);
if (bytes >= len)
return;
base += bytes;
len -= bytes;
}
if (base < head->iov_len) {
bytes = min_t(unsigned int, 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 * xdr_shrink_bufhead
* @buf: xdr_buf * @buf: xdr_buf
...@@ -1680,6 +1708,37 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, ...@@ -1680,6 +1708,37 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
} }
EXPORT_SYMBOL_GPL(xdr_stream_subsegment); EXPORT_SYMBOL_GPL(xdr_stream_subsegment);
/**
* 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.
*/
unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
unsigned int target, unsigned int length)
{
struct xdr_buf buf;
unsigned int shift;
if (offset < target) {
shift = target - offset;
if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0)
return 0;
xdr_buf_head_shift_right(&buf, 0, length, shift);
} else if (offset > target) {
shift = offset - target;
if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0)
return 0;
xdr_buf_head_shift_left(&buf, shift, length, shift);
}
return length;
}
EXPORT_SYMBOL_GPL(xdr_stream_move_subsegment);
/** /**
* xdr_buf_trim - lop at most "len" bytes off the end of "buf" * xdr_buf_trim - lop at most "len" bytes off the end of "buf"
* @buf: buf to be trimmed * @buf: buf to be trimmed
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment