From: Trond Myklebust <trond.myklebust@fys.uio.no>

The following patch (backported from the NFSv4 code) should do the right
thing.


---

 25-akpm/fs/nfs/nfs2xdr.c |   11 +++++++----
 25-akpm/fs/nfs/nfs3xdr.c |   11 +++++++----
 25-akpm/fs/nfs/nfs4xdr.c |   11 ++++++-----
 3 files changed, 20 insertions(+), 13 deletions(-)

diff -puN fs/nfs/nfs2xdr.c~nfs-long-symlinks-fix fs/nfs/nfs2xdr.c
--- 25/fs/nfs/nfs2xdr.c~nfs-long-symlinks-fix	2004-05-16 16:07:47.817456872 -0700
+++ 25-akpm/fs/nfs/nfs2xdr.c	2004-05-16 16:07:47.824455808 -0700
@@ -511,8 +511,8 @@ static int
 nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
 {
 	struct rpc_auth *auth = req->rq_task->tk_auth;
+	unsigned int count = args->count - 5;
 	unsigned int replen;
-	u32 count = args->count - 4;
 
 	p = xdr_encode_fhandle(p, args->fh);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -547,12 +547,15 @@ nfs_xdr_readlinkres(struct rpc_rqst *req
 	strlen = (u32*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
 	/* Convert length of symlink */
 	len = ntohl(*strlen);
-	if (len > rcvbuf->page_len)
-		len = rcvbuf->page_len;
+	if (len > rcvbuf->page_len) {
+		dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
+		kunmap_atomic(strlen, KM_USER0);
+		return -ENAMETOOLONG;
+	}
 	*strlen = len;
 	/* NULL terminate the string we got */
 	string = (char *)(strlen + 1);
-	string[len] = 0;
+	string[len] = '\0';
 	kunmap_atomic(strlen, KM_USER0);
 	return 0;
 }
diff -puN fs/nfs/nfs3xdr.c~nfs-long-symlinks-fix fs/nfs/nfs3xdr.c
--- 25/fs/nfs/nfs3xdr.c~nfs-long-symlinks-fix	2004-05-16 16:07:47.819456568 -0700
+++ 25-akpm/fs/nfs/nfs3xdr.c	2004-05-16 16:07:47.825455656 -0700
@@ -702,8 +702,8 @@ static int
 nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
 {
 	struct rpc_auth *auth = req->rq_task->tk_auth;
+	unsigned int count = args->count - 5;
 	unsigned int replen;
-	u32 count = args->count - 4;
 
 	p = xdr_encode_fhandle(p, args->fh);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -742,12 +742,15 @@ nfs3_xdr_readlinkres(struct rpc_rqst *re
 	strlen = (u32*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
 	/* Convert length of symlink */
 	len = ntohl(*strlen);
-	if (len > rcvbuf->page_len)
-		len = rcvbuf->page_len;
+	if (len > rcvbuf->page_len) {
+		dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
+		kunmap_atomic(strlen, KM_USER0);
+		return -ENAMETOOLONG;
+	}
 	*strlen = len;
 	/* NULL terminate the string we got */
 	string = (char *)(strlen + 1);
-	string[len] = 0;
+	string[len] = '\0';
 	kunmap_atomic(strlen, KM_USER0);
 	return 0;
 }
diff -puN fs/nfs/nfs4xdr.c~nfs-long-symlinks-fix fs/nfs/nfs4xdr.c
--- 25/fs/nfs/nfs4xdr.c~nfs-long-symlinks-fix	2004-05-16 16:07:47.820456416 -0700
+++ 25-akpm/fs/nfs/nfs4xdr.c	2004-05-16 16:07:47.827455352 -0700
@@ -947,7 +947,8 @@ static int encode_readdir(struct xdr_str
 static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req)
 {
 	struct rpc_auth *auth = req->rq_task->tk_auth;
-	int replen;
+	unsigned int count = readlink->count - 5;
+	unsigned int replen;
 	uint32_t *p;
 
 	RESERVE_SPACE(4);
@@ -958,7 +959,7 @@ static int encode_readlink(struct xdr_st
 	 *      + OP_READLINK + status  = 7
 	 */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2;
-	xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->pages, 0, readlink->count);
+	xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->pages, 0, count);
 	
 	return 0;
 }
@@ -2921,10 +2922,10 @@ static int decode_readlink(struct xdr_st
 	 */
 	strlen = (uint32_t *) kmap_atomic(rcvbuf->pages[0], KM_USER0);
 	len = ntohl(*strlen);
-	if (len > PAGE_CACHE_SIZE - 5) {
-		printk(KERN_WARNING "nfs: server returned giant symlink!\n");
+	if (len > rcvbuf->page_len) {
+		dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
 		kunmap_atomic(strlen, KM_USER0);
-		return -EIO;
+		return -ENAMETOOLONG;
 	}
 	*strlen = len;
 

_