NFSv4.2: add the extended attribute proc functions.
authorFrank van der Linden <fllinden@amazon.com>
Tue, 23 Jun 2020 22:39:02 +0000 (22:39 +0000)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 13 Jul 2020 21:52:45 +0000 (17:52 -0400)
Implement the extended attribute procedures for NFSv4.2 extended
attribute support (RFC 8276).

Signed-off-by: Frank van der Linden <fllinden@amazon.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/nfs42.h
fs/nfs/nfs42proc.c

index 51de8dd..0fe5aac 100644 (file)
@@ -39,6 +39,14 @@ static inline bool nfs42_files_from_same_server(struct file *in,
                                               c_out->cl_serverowner);
 }
 
+ssize_t nfs42_proc_getxattr(struct inode *inode, const char *name,
+                           void *buf, size_t buflen);
+int nfs42_proc_setxattr(struct inode *inode, const char *name,
+                       const void *buf, size_t buflen, int flags);
+ssize_t nfs42_proc_listxattrs(struct inode *inode, void *buf,
+                              size_t buflen, u64 *cookiep, bool *eofp);
+int nfs42_proc_removexattr(struct inode *inode, const char *name);
+
 /*
  * Maximum XDR buffer size needed for a listxattr buffer of buflen size.
  *
index e2ae54b..8c2e52b 100644 (file)
@@ -1088,3 +1088,239 @@ out_put_src_lock:
        nfs_put_lock_context(src_lock);
        return err;
 }
+
+#define NFS4XATTR_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
+
+static int _nfs42_proc_removexattr(struct inode *inode, const char *name)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs42_removexattrargs args = {
+               .fh = NFS_FH(inode),
+               .xattr_name = name,
+       };
+       struct nfs42_removexattrres res;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVEXATTR],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       int ret;
+       unsigned long timestamp = jiffies;
+
+       ret = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
+           &res.seq_res, 1);
+       if (!ret)
+               nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
+
+       return ret;
+}
+
+static int _nfs42_proc_setxattr(struct inode *inode, const char *name,
+                               const void *buf, size_t buflen, int flags)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct page *pages[NFS4XATTR_MAXPAGES];
+       struct nfs42_setxattrargs arg = {
+               .fh             = NFS_FH(inode),
+               .xattr_pages    = pages,
+               .xattr_len      = buflen,
+               .xattr_name     = name,
+               .xattr_flags    = flags,
+       };
+       struct nfs42_setxattrres res;
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETXATTR],
+               .rpc_argp       = &arg,
+               .rpc_resp       = &res,
+       };
+       int ret, np;
+       unsigned long timestamp = jiffies;
+
+       if (buflen > server->sxasize)
+               return -ERANGE;
+
+       if (buflen > 0) {
+               np = nfs4_buf_to_pages_noslab(buf, buflen, arg.xattr_pages);
+               if (np < 0)
+                       return np;
+       } else
+               np = 0;
+
+       ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
+           &res.seq_res, 1);
+
+       for (; np > 0; np--)
+               put_page(pages[np - 1]);
+
+       if (!ret)
+               nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
+
+       return ret;
+}
+
+static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
+                               void *buf, size_t buflen)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct page *pages[NFS4XATTR_MAXPAGES] = {};
+       struct nfs42_getxattrargs arg = {
+               .fh             = NFS_FH(inode),
+               .xattr_pages    = pages,
+               .xattr_len      = buflen,
+               .xattr_name     = name,
+       };
+       struct nfs42_getxattrres res;
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_GETXATTR],
+               .rpc_argp       = &arg,
+               .rpc_resp       = &res,
+       };
+       int ret, np;
+
+       ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
+           &res.seq_res, 0);
+       if (ret < 0)
+               return ret;
+
+       if (buflen) {
+               if (res.xattr_len > buflen)
+                       return -ERANGE;
+               _copy_from_pages(buf, pages, 0, res.xattr_len);
+       }
+
+       np = DIV_ROUND_UP(res.xattr_len, PAGE_SIZE);
+       while (--np >= 0)
+               __free_page(pages[np]);
+
+       return res.xattr_len;
+}
+
+static ssize_t _nfs42_proc_listxattrs(struct inode *inode, void *buf,
+                                size_t buflen, u64 *cookiep, bool *eofp)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct page **pages;
+       struct nfs42_listxattrsargs arg = {
+               .fh             = NFS_FH(inode),
+               .cookie         = *cookiep,
+       };
+       struct nfs42_listxattrsres res = {
+               .eof = false,
+               .xattr_buf = buf,
+               .xattr_len = buflen,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_LISTXATTRS],
+               .rpc_argp       = &arg,
+               .rpc_resp       = &res,
+       };
+       u32 xdrlen;
+       int ret, np;
+
+
+       res.scratch = alloc_page(GFP_KERNEL);
+       if (!res.scratch)
+               return -ENOMEM;
+
+       xdrlen = nfs42_listxattr_xdrsize(buflen);
+       if (xdrlen > server->lxasize)
+               xdrlen = server->lxasize;
+       np = xdrlen / PAGE_SIZE + 1;
+
+       pages = kcalloc(np, sizeof(struct page *), GFP_KERNEL);
+       if (pages == NULL) {
+               __free_page(res.scratch);
+               return -ENOMEM;
+       }
+
+       arg.xattr_pages = pages;
+       arg.count = xdrlen;
+
+       ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
+           &res.seq_res, 0);
+
+       if (ret >= 0) {
+               ret = res.copied;
+               *cookiep = res.cookie;
+               *eofp = res.eof;
+       }
+
+       while (--np >= 0) {
+               if (pages[np])
+                       __free_page(pages[np]);
+       }
+
+       __free_page(res.scratch);
+       kfree(pages);
+
+       return ret;
+
+}
+
+ssize_t nfs42_proc_getxattr(struct inode *inode, const char *name,
+                             void *buf, size_t buflen)
+{
+       struct nfs4_exception exception = { };
+       ssize_t err;
+
+       do {
+               err = _nfs42_proc_getxattr(inode, name, buf, buflen);
+               if (err >= 0)
+                       break;
+               err = nfs4_handle_exception(NFS_SERVER(inode), err,
+                               &exception);
+       } while (exception.retry);
+
+       return err;
+}
+
+int nfs42_proc_setxattr(struct inode *inode, const char *name,
+                             const void *buf, size_t buflen, int flags)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = _nfs42_proc_setxattr(inode, name, buf, buflen, flags);
+               if (!err)
+                       break;
+               err = nfs4_handle_exception(NFS_SERVER(inode), err,
+                               &exception);
+       } while (exception.retry);
+
+       return err;
+}
+
+ssize_t nfs42_proc_listxattrs(struct inode *inode, void *buf,
+                             size_t buflen, u64 *cookiep, bool *eofp)
+{
+       struct nfs4_exception exception = { };
+       ssize_t err;
+
+       do {
+               err = _nfs42_proc_listxattrs(inode, buf, buflen,
+                   cookiep, eofp);
+               if (err >= 0)
+                       break;
+               err = nfs4_handle_exception(NFS_SERVER(inode), err,
+                               &exception);
+       } while (exception.retry);
+
+       return err;
+}
+
+int nfs42_proc_removexattr(struct inode *inode, const char *name)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = _nfs42_proc_removexattr(inode, name);
+               if (!err)
+                       break;
+               err = nfs4_handle_exception(NFS_SERVER(inode), err,
+                               &exception);
+       } while (exception.retry);
+
+       return err;
+}