patch-2.1.58 linux/fs/nfs/nfs2xdr.c

Next file: linux/fs/nfs/nfs3xdr.c
Previous file: linux/fs/nfs/inode.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.57/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
@@ -23,6 +23,7 @@
 #include <linux/nfs_fs.h>
 
 #define NFSDBG_FACILITY		NFSDBG_XDR
+/* #define NFS_PARANOIA 1 */
 
 #define QUADLEN(len)		(((len) + 3) >> 2)
 static int			nfs_stat_to_errno(int stat);
@@ -371,17 +372,18 @@
  * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
  * After decoding, the layout in memory looks like this:
  *	entry1 entry2 ... entryN <space> stringN ... string2 string1
+ * Each entry consists of three __u32 values, the same space as NFS uses.
  * Note that the strings are not null-terminated so that the entire number
  * of entries returned by the server should fit into the buffer.
  */
 static int
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
-	struct nfs_entry	*entry;
 	struct iovec		*iov = req->rq_rvec;
 	int			status, nr, len;
-	char			*string;
+	char			*string, *start;
 	u32			*end;
+	__u32			fileid, cookie, *entry;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
@@ -396,10 +398,11 @@
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 
 	/* Get start and end of dirent buffer */
-	entry  = (struct nfs_entry *) res->buffer;
+	entry  = (__u32 *) res->buffer;
+	start  = (char *) res->buffer;
 	string = (char *) res->buffer + res->bufsiz;
-	for (nr = 0; *p++; nr++, entry++) {
-		entry->fileid = ntohl(*p++);
+	for (nr = 0; *p++; nr++) {
+		fileid = ntohl(*p++);
 
 		len = ntohl(*p++);
 		if ((p + QUADLEN(len) + 3) > end) {
@@ -413,27 +416,36 @@
 			return -errno_NFSERR_IO;
 		}
 		string -= len;
-		if ((void *) (entry+1) > (void *) string) {
-			/* This may actually happen because an nfs_entry
-			 * will take up more space than the XDR data. On
-			 * 32bit machines that's due to 8byte alignment,
-			 * on 64bit machines that's because the char * takes
-			 * up 2 longs.
-			 *
-			 * THIS IS BAD!
+		if ((void *) (entry+3) > (void *) string) {
+			/* 
+			 * This error is impossible as long as the temp
+			 * buffer is no larger than the user buffer. The 
+			 * current packing algorithm uses the same amount
+			 * of space in the user buffer as in the XDR data,
+			 * so it's guaranteed to fit.
 			 */
-			printk(KERN_NOTICE "NFS: should not happen in %s!\n",
+			printk("NFS: incorrect buffer size in %s!\n",
 				__FUNCTION__);
 			break;
 		}
 
-		entry->name = string;
-		entry->length = len;
 		memmove(string, p, len);
 		p += QUADLEN(len);
-		entry->cookie = ntohl(*p++);
-		entry->eof = !p[0] && p[1];
+		cookie = ntohl(*p++);
+		/*
+		 * To make everything fit, we encode the length, offset,
+		 * and eof flag into 32 bits. This works for filenames
+		 * up to 32K and PAGE_SIZE up to 64K.
+		 */
+		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
+		*entry++ = fileid;
+		*entry++ = cookie;
+		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
 	}
+#ifdef NFS_PARANOIA
+printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
+nr, ((char *) entry - start), (start + res->bufsiz - string));
+#endif
 	return nr;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov