nfs4: Ensure that ACL pages sent over NFS were not allocated from the slab
diff mbox

Message ID 1299257053-13175-1-git-send-email-nhorman@tuxdriver.com
State Superseded, archived
Headers show

Commit Message

Neil Horman March 4, 2011, 4:44 p.m. UTC
None

Patch
diff mbox

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 78936a8..e4bd9b5 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -51,6 +51,7 @@ 
 #include <linux/sunrpc/bc_xprt.h>
 #include <linux/xattr.h>
 #include <linux/utsname.h>
+#include <linux/mm.h>
 
 #include "nfs4_fs.h"
 #include "delegation.h"
@@ -3252,6 +3253,36 @@  static void buf_to_pages(const void *buf, size_t buflen,
 	}
 }
 
+static int buf_to_pages_noslab(const void *buf, size_t buflen,
+		struct page **pages, unsigned int *pgbase)
+{
+	const void *p = buf;
+	struct page *page, *newpage, **spages;
+	int rc = -ENOMEM;
+
+	spages = pages;
+	*pgbase = offset_in_page(buf);
+	p -= *pgbase;
+	while (p < buf + buflen) {
+		page = virt_to_page(p);
+		newpage = alloc_page(GFP_KERNEL);
+		if (!newpage)
+			goto unwind;
+		memcpy(page_address(newpage), page_address(page),
+		       PAGE_CACHE_SIZE);
+		*(pages++) = newpage;
+		p += PAGE_CACHE_SIZE;
+	}
+
+	rc = 0;
+	return rc;
+
+unwind:
+	while (*spages != NULL)
+		__free_page(*(spages++));
+	return rc;
+}
+
 struct nfs4_cached_acl {
 	int cached;
 	size_t len;
@@ -3420,13 +3451,20 @@  static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
 		.rpc_argp	= &arg,
 		.rpc_resp	= &res,
 	};
-	int ret;
+	int ret, i;
 
 	if (!nfs4_server_supports_acls(server))
 		return -EOPNOTSUPP;
+	memset(pages, 0, sizeof(struct page *) * NFS4ACL_MAXPAGES);
+	ret = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
+	if (ret)
+		return ret;
 	nfs_inode_return_delegation(inode);
-	buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
 	ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
+
+	for (i = 0; pages[i] != NULL; i++)
+		put_page(pages[i]);
+
 	/*
 	 * Acl update can result in inode attribute update.
 	 * so mark the attribute cache invalid.