@@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf,
775775 xdr_buf_tail_copy_left (buf , 0 , len - buf -> page_len , shift );
776776}
777777
778+ static void xdr_buf_head_shift_left (const struct xdr_buf * buf ,
779+ unsigned int base , unsigned int len ,
780+ unsigned int shift )
781+ {
782+ const struct kvec * head = buf -> head ;
783+ unsigned int bytes ;
784+
785+ if (!shift || !len )
786+ return ;
787+
788+ if (shift > base ) {
789+ bytes = (shift - base );
790+ if (bytes >= len )
791+ return ;
792+ base += bytes ;
793+ len -= bytes ;
794+ }
795+
796+ if (base < head -> iov_len ) {
797+ bytes = min_t (unsigned int , len , head -> iov_len - base );
798+ memmove (head -> iov_base + (base - shift ),
799+ head -> iov_base + base , bytes );
800+ base += bytes ;
801+ len -= bytes ;
802+ }
803+ xdr_buf_pages_shift_left (buf , base - head -> iov_len , len , shift );
804+ }
805+
778806/**
779807 * xdr_shrink_bufhead
780808 * @buf: xdr_buf
@@ -1680,6 +1708,37 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
16801708}
16811709EXPORT_SYMBOL_GPL (xdr_stream_subsegment );
16821710
1711+ /**
1712+ * xdr_stream_move_subsegment - Move part of a stream to another position
1713+ * @xdr: the source xdr_stream
1714+ * @offset: the source offset of the segment
1715+ * @target: the target offset of the segment
1716+ * @length: the number of bytes to move
1717+ *
1718+ * Moves @length bytes from @offset to @target in the xdr_stream, overwriting
1719+ * anything in its space. Returns the number of bytes in the segment.
1720+ */
1721+ unsigned int xdr_stream_move_subsegment (struct xdr_stream * xdr , unsigned int offset ,
1722+ unsigned int target , unsigned int length )
1723+ {
1724+ struct xdr_buf buf ;
1725+ unsigned int shift ;
1726+
1727+ if (offset < target ) {
1728+ shift = target - offset ;
1729+ if (xdr_buf_subsegment (xdr -> buf , & buf , offset , shift + length ) < 0 )
1730+ return 0 ;
1731+ xdr_buf_head_shift_right (& buf , 0 , length , shift );
1732+ } else if (offset > target ) {
1733+ shift = offset - target ;
1734+ if (xdr_buf_subsegment (xdr -> buf , & buf , target , shift + length ) < 0 )
1735+ return 0 ;
1736+ xdr_buf_head_shift_left (& buf , shift , length , shift );
1737+ }
1738+ return length ;
1739+ }
1740+ EXPORT_SYMBOL_GPL (xdr_stream_move_subsegment );
1741+
16831742/**
16841743 * xdr_buf_trim - lop at most "len" bytes off the end of "buf"
16851744 * @buf: buf to be trimmed
0 commit comments