@@ -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.