diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index 2763e3e..b824067 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -384,7 +384,6 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
  * We cannot currently handle tokens with rotated data.  We need a
  * generalized routine to rotate the data in place.  It is anticipated
  * that we won't encounter rotated data in the general case.
- */
 static u32
 rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
 {
@@ -397,6 +396,139 @@ rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
 		"rrc %u, realrrc %u\n", __func__, rrc, realrrc);
 	return 1;
 }
+ */
+
+#include <linux/ctype.h>
+void print_xdrbuf(const char *msg, struct xdr_buf *x);
+void print_xdrbuf_data(const char *msg, struct xdr_buf *x, unsigned int offset);
+
+#define LOCAL_BUF_LEN 32
+
+static u32
+rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
+{
+	unsigned int realhead, realpage, realtail;
+	unsigned int remainder;
+	unsigned int rot_len = buf->len - offset - 16;
+	unsigned int realrrc = rrc % rot_len;
+	struct kvec *head, *tail;
+	void *headstart;    /* start of data past rpc header */
+	u32 err;
+
+	printk("%s: rrc %u, buf->len %u, offset %u, rot_len %u, realrrc %u\n",
+		__func__, rrc, buf->len, offset, rot_len, realrrc);
+	print_xdrbuf("in rotate_left", buf);
+	print_xdrbuf_data("BEFORE: ", buf, offset);
+
+	if (realrrc == 0)
+		return 0;
+
+	/*
+	 * At the time this function is called, the only length
+	 * values we can absolutely trust are buf->len (the length
+	 * of data actually read off the network) and buf->buflen
+	 * (the total available buffer space).
+	 *
+	 * The head, tail, and page lengths are the available
+	 * buffer space for each, not how much data is actually
+	 * available in each.  We know that data is read to fill
+	 * up the head, then page data, and then tail; so we can
+	 * calculate the *real* lengths of each.
+	 */
+	realhead = realpage = realtail = 0;
+	remainder = buf->len;
+	head = &buf->head[0];
+	tail = &buf->tail[0];
+
+	if (remainder) {
+		realhead = min_t(unsigned int, buf->len, head->iov_len);
+		remainder -= realhead;
+	}
+	if (remainder) {
+		realpage = min_t(unsigned int, remainder, buf->page_len);
+		remainder -= realpage;
+	}
+	if (remainder) {
+		realtail = min_t(unsigned int, remainder, tail->iov_len);
+		remainder -= realtail;
+	}
+	BUG_ON(remainder);
+	printk("%s: realhead %u, realpage %u, realtail %u\n",
+		__func__, realhead, realpage, realtail);
+
+	headstart = head->iov_base + offset + 16;
+
+	if (realpage == 0 && realtail == 0) {
+		/*
+		 * All the data is in the head.  Therefore all
+		 * the data to be moved is in the head
+		 */
+		char localbuf[LOCAL_BUF_LEN];
+		unsigned int amtleft = realrrc;
+		unsigned int thismove;
+		printk("%s: We are lucky! (moving %u within head)\n",
+			__func__, realrrc);
+		while (amtleft != 0) {
+			thismove = min_t(unsigned int, amtleft, LOCAL_BUF_LEN);
+			memcpy(localbuf, headstart, thismove);
+			memmove(headstart, headstart + thismove,
+				realhead - offset - 16 - thismove);
+			memcpy(head->iov_base + realhead - thismove,
+				localbuf, thismove);
+			amtleft -= thismove;
+		}
+	} else if (realhead - offset - 16 < realrrc) {
+		/*
+		 * The part to be moved is split between the head and
+		 * page data or tail
+		 */
+		printk("%s: We are not lucky.  All the realrrc data (%u) "
+			" is not in the head!\n", __func__, realrrc);
+		return 1;
+	} else if (realtail != 0 && tail->iov_len - realtail >= realrrc) {
+		/*
+		 * We are lucky enough to simply move rrc bytes
+		 * from the head to the end of the tail
+		 */
+		printk("%s: We are lucky! (moving %u from head to tail)\n",
+			__func__, realrrc);
+		memcpy(tail->iov_base + realtail,
+		       head->iov_base + offset + 16,
+		       realrrc);
+		tail->iov_len = realtail + realrrc;
+		memmove(headstart, headstart + realrrc,
+			realhead - offset - 16 - realrrc);
+		head->iov_len -= realrrc;
+	} else if (realpage != 0 && realtail == 0) {
+		/*
+		 * There is page data and no existing tail
+		 */
+		printk("%s: We are lucky! (moving %u from head to pages)\n",
+			__func__, realrrc);
+		buf->page_len += realrrc;
+		err = write_bytes_to_xdr_buf(buf, realhead + realpage,
+					     headstart, realrrc);
+		if (err) {
+			printk("%s: We are not lucky getting err %d while "
+				"writing %u bytes to xdr_buf\n",
+				__func__, err, realrrc);
+			return 1;
+		}
+		memmove(headstart, headstart + realrrc,
+			realhead - offset - 16 - realrrc);
+		head->iov_len -= realrrc;
+	} else {
+		/*
+		 * None of the quick fixes works.  Do it the "hard way".
+		 */
+		printk("%s: We are not lucky with realrrc %u\n",
+			__func__, realrrc);
+		return 1;
+	}
+
+	print_xdrbuf_data("AFTER : ", buf, offset);
+	return 0;
+}
 
 static u32
 gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
@@ -585,3 +717,74 @@ gss_unwrap_kerberos(struct gss_ctx *gctx, int offset, struct xdr_buf *buf)
 	}
 }
 
+void
+print_hexl(const char *title, u8 *p, u_int length, u_int offset)
+{
+	u_int i, j, jm;
+	u8 c, *cp;
+
+	printk("RPC:       %s: length %d: %p\n", title, length, p);
+	cp = (u8 *) p;
+
+	for (i = 0; i < length; i += 0x10) {
+	        printk("  %04x: ", (u_int)(i + offset));
+	        jm = length - i;
+	        jm = jm > 16 ? 16 : jm;
+
+	        for (j = 0; j < jm; j++) {
+	                if ((j % 2) == 1)
+	                        printk("%02x ", (u_int)cp[i+j]);
+	                else
+	                        printk("%02x", (u_int)cp[i+j]);
+	        }
+	        for (; j < 16; j++) {
+	                if ((j % 2) == 1)
+	                        printk("   ");
+	                else
+	                        printk("  ");
+	        }
+	        printk(" ");
+
+	        for (j = 0; j < jm; j++) {
+	                c = cp[i+j];
+	                c = isprint(c) ? c : '.';
+	                printk("%c", c);
+	        }
+	        printk("\n");
+	}
+	printk("\n");
+}
+EXPORT_SYMBOL(print_hexl);
+
+void print_xdrbuf(const char *msg, struct xdr_buf *x)
+{
+	printk("---------- xdr_buf at %p: %s ----------\n", x, msg);
+	printk("  head[0].iov_base %p, iov_len %zu\n",
+	        x->head[0].iov_base, x->head[0].iov_len);
+	printk("  tail[0].iov_base %p, iov_len %zu\n",
+	        x->tail[0].iov_base, x->tail[0].iov_len);
+	printk("  pages %p\n", x->pages);
+	printk("  page_base %u, page_len %u, flags %u\n",
+	        x->page_base, x->page_len, x->flags);
+	printk("  buflen %u, len %u\n", x->buflen, x->len);
+}
+EXPORT_SYMBOL(print_xdrbuf);
+
+//extern void print_hexl(const char *title, u8 *p, u_int length, u_int offset);
+void print_xdrbuf_data(const char *msg, struct xdr_buf *x, unsigned int offset)
+{
+	printk("========== xdr_buf %p offset %u: %s ==========\n",
+	        x, offset, msg);
+	if (offset < x->head[0].iov_len)
+	        print_hexl("head data", x->head[0].iov_base + offset,
+	                                x->head[0].iov_len - offset, 0);
+	if (x->page_len) {
+	        int plen = (x->page_len > 512) ? 512 : x->page_len;
+	        print_hexl("page data",
+	                page_address(x->pages[0]) + x->page_base, plen, 0);
+	}
+	if (x->tail[0].iov_base != NULL && x->tail[0].iov_len != 0)
+	        print_hexl("tail data", x->tail[0].iov_base,
+	                                x->tail[0].iov_len, 0);
+}
+EXPORT_SYMBOL(print_xdrbuf_data);
