From 4181aba3582e0b8582d1a541e5c44a88e8f1225c Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 13:35:31 -0700 Subject: [PATCH 01/10] The function posix_acl_from_nfsacl() needs to be called from the NFSv4.2 client code handling the POSIX draft ACL extensions. Signed-off-by: Rick Macklem --- fs/nfs_common/nfsacl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c index ea382b75b26c..66bab921b07a 100644 --- a/fs/nfs_common/nfsacl.c +++ b/fs/nfs_common/nfsacl.c @@ -287,7 +287,7 @@ cmp_acl_entry(const void *x, const void *y) /* * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL. */ -static int +int posix_acl_from_nfsacl(struct posix_acl *acl) { struct posix_acl_entry *pa, *pe, @@ -323,6 +323,7 @@ posix_acl_from_nfsacl(struct posix_acl *acl) } return 0; } +EXPORT_SYMBOL_GPL(posix_acl_from_nfsacl); /** * nfsacl_decode - Decode an NFSv3 ACL -- 2.34.1 From 5f1ff0f77535b6a5436289c22f4e1d4f568692c4 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 13:40:02 -0700 Subject: [PATCH 02/10] Make three functions global so the NFSv4.2 client can use them The three functions: nfs3_prepare_get_acl() nfs3_complete_get_acl() nfs3_abort_get_acl() have been moved to a new file called acl.c and renamed nfs_XXX(), so that they can be called from the NFSv4.2 client code implementing the POSIX draft ACL attributes. Signed-off-by: Rick Macklem --- fs/nfs/Makefile | 2 +- fs/nfs/acl.c | 40 ++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs.h | 3 +++ fs/nfs/nfs3acl.c | 44 +++++++------------------------------------- 4 files changed, 51 insertions(+), 38 deletions(-) create mode 100644 fs/nfs/acl.c diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 9fb2f2cac87e..0cb5e61edae1 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,7 +9,7 @@ CFLAGS_nfstrace.o += -I$(src) nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ io.o direct.o pagelist.o read.o symlink.o unlink.o \ write.o namespace.o mount_clnt.o nfstrace.o \ - export.o sysfs.o fs_context.o + export.o sysfs.o fs_context.o acl.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o diff --git a/fs/nfs/acl.c b/fs/nfs/acl.c new file mode 100644 index 000000000000..2f2098c492a1 --- /dev/null +++ b/fs/nfs/acl.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include "nfs.h" + +/* + * nfs_prepare_get_acl, nfs_complete_get_acl, nfs_abort_get_acl: Helpers for + * caching get_acl results in a race-free way. See fs/posix_acl.c:get_acl() + * for explanations. + */ +void nfs_prepare_get_acl(struct posix_acl **p) +{ + struct posix_acl *sentinel = uncached_acl_sentinel(current); + + /* If the ACL isn't being read yet, set our sentinel. */ + cmpxchg(p, ACL_NOT_CACHED, sentinel); +} +EXPORT_SYMBOL_GPL(nfs_prepare_get_acl); + +void nfs_complete_get_acl(struct posix_acl **p, struct posix_acl *acl) +{ + struct posix_acl *sentinel = uncached_acl_sentinel(current); + + /* Only cache the ACL if our sentinel is still in place. */ + posix_acl_dup(acl); + if (cmpxchg(p, sentinel, acl) != sentinel) + posix_acl_release(acl); +} +EXPORT_SYMBOL_GPL(nfs_complete_get_acl); + +void nfs_abort_get_acl(struct posix_acl **p) +{ + struct posix_acl *sentinel = uncached_acl_sentinel(current); + + /* Remove our sentinel upon failure. */ + cmpxchg(p, sentinel, ACL_NOT_CACHED); +} +EXPORT_SYMBOL_GPL(nfs_abort_get_acl); diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h index 0d3ce0460e35..7ab5c5c916ee 100644 --- a/fs/nfs/nfs.h +++ b/fs/nfs/nfs.h @@ -26,5 +26,8 @@ struct nfs_subversion *get_nfs_version(unsigned int); void put_nfs_version(struct nfs_subversion *); void register_nfs_version(struct nfs_subversion *); void unregister_nfs_version(struct nfs_subversion *); +void nfs_prepare_get_acl(struct posix_acl **); +void nfs_complete_get_acl(struct posix_acl **, struct posix_acl *); +void nfs_abort_get_acl(struct posix_acl **); #endif /* __LINUX_INTERNAL_NFS_H */ diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index 18d8f6529f61..c941ef8058d7 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -8,41 +8,11 @@ #include #include "internal.h" +#include "nfs.h" #include "nfs3_fs.h" #define NFSDBG_FACILITY NFSDBG_PROC -/* - * nfs3_prepare_get_acl, nfs3_complete_get_acl, nfs3_abort_get_acl: Helpers for - * caching get_acl results in a race-free way. See fs/posix_acl.c:get_acl() - * for explanations. - */ -static void nfs3_prepare_get_acl(struct posix_acl **p) -{ - struct posix_acl *sentinel = uncached_acl_sentinel(current); - - /* If the ACL isn't being read yet, set our sentinel. */ - cmpxchg(p, ACL_NOT_CACHED, sentinel); -} - -static void nfs3_complete_get_acl(struct posix_acl **p, struct posix_acl *acl) -{ - struct posix_acl *sentinel = uncached_acl_sentinel(current); - - /* Only cache the ACL if our sentinel is still in place. */ - posix_acl_dup(acl); - if (cmpxchg(p, sentinel, acl) != sentinel) - posix_acl_release(acl); -} - -static void nfs3_abort_get_acl(struct posix_acl **p) -{ - struct posix_acl *sentinel = uncached_acl_sentinel(current); - - /* Remove our sentinel upon failure. */ - cmpxchg(p, sentinel, ACL_NOT_CACHED); -} - struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu) { struct nfs_server *server = NFS_SERVER(inode); @@ -91,9 +61,9 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu) return ERR_PTR(-ENOMEM); if (args.mask & NFS_ACL) - nfs3_prepare_get_acl(&inode->i_acl); + nfs_prepare_get_acl(&inode->i_acl); if (args.mask & NFS_DFACL) - nfs3_prepare_get_acl(&inode->i_default_acl); + nfs_prepare_get_acl(&inode->i_default_acl); status = rpc_call_sync(server->client_acl, &msg, 0); dprintk("NFS reply getacl: %d\n", status); @@ -131,12 +101,12 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu) } if (res.mask & NFS_ACL) - nfs3_complete_get_acl(&inode->i_acl, res.acl_access); + nfs_complete_get_acl(&inode->i_acl, res.acl_access); else forget_cached_acl(inode, ACL_TYPE_ACCESS); if (res.mask & NFS_DFACL) - nfs3_complete_get_acl(&inode->i_default_acl, res.acl_default); + nfs_complete_get_acl(&inode->i_default_acl, res.acl_default); else forget_cached_acl(inode, ACL_TYPE_DEFAULT); @@ -150,8 +120,8 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu) } getout: - nfs3_abort_get_acl(&inode->i_acl); - nfs3_abort_get_acl(&inode->i_default_acl); + nfs_abort_get_acl(&inode->i_acl); + nfs_abort_get_acl(&inode->i_default_acl); posix_acl_release(res.acl_access); posix_acl_release(res.acl_default); nfs_free_fattr(res.fattr); -- 2.34.1 From 09dec7d863ef9f0995b187c3df157fd785f7e496 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 13:55:39 -0700 Subject: [PATCH 03/10] Add setting of SB_POSIXACL for NFSv4.2 Signed-off-by: Rick Macklem --- fs/nfs/super.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 9723b6c53397..5777ee79ff6a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1100,6 +1100,9 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx) break; case 4: sb->s_iflags |= SB_I_NOUMASK; +#ifdef CONFIG_NFS_V4_2 + sb->s_flags |= SB_POSIXACL; +#endif sb->s_time_gran = 1; sb->s_time_min = S64_MIN; sb->s_time_max = S64_MAX; -- 2.34.1 From 3e9dd779e6a5bdaa2a114ea417f100867e2d3aa7 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 13:59:19 -0700 Subject: [PATCH 04/10] Add prototypes for four new functions These four new functions are used to handle the POSIX draft ACL attributes being added as an extension to NFSv4.2. Signed-off-by: Rick Macklem --- fs/nfs/nfs4_fs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 7d383d29a995..a628968b7d86 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -658,6 +658,13 @@ extern void nfs4_xattr_cache_set_list(struct inode *inode, const char *buf, extern ssize_t nfs4_xattr_cache_list(struct inode *inode, char *buf, ssize_t buflen); extern void nfs4_xattr_cache_zap(struct inode *inode); +extern struct posix_acl *nfs4_get_posixacl(struct inode *inode, int type, + bool rcu); +extern int nfs4_set_posixacl(struct mnt_idmap *idmap, struct dentry *dentry, + struct posix_acl *acl, int type); +extern ssize_t nfs42_encode_posixacl(const struct nfs_server *server, + struct nfs_xdr_putpage_desc *desc, struct posix_acl *acl); +extern void nfs_xdr_putpage_cleanup(struct nfs_xdr_putpage_desc *desc); #else static inline void nfs4_xattr_cache_zap(struct inode *inode) { -- 2.34.1 From 8b78ba6b5f8bb9d54dc23443403ce6974b702e56 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:04:40 -0700 Subject: [PATCH 05/10] Add support for the NFSv4.2 POSIX draft ACL attributes The internet draft "POSIX Draft ACL support for Network File System Version 4, Minor Version 2" describes four new attributes that are extensions to the NFSv4.2 protocol. These new attributes provide support for POSIX draft ACLs without any need for mapping to/from NFSv4 ACLs. The code should currently be considered experimental and, in particular, the method used to handle large ACLs needs review, although the current code does appear to work. This extension allows the getfacl(1)/setfacl(1) commands to work over an NFSv4.2 mount where the client and server support the extension. Signed-off-by: Rick Macklem --- fs/nfs/nfs42proc.c | 325 +++++++++++++++++++++ fs/nfs/nfs42xdr.c | 689 +++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4proc.c | 12 +- 3 files changed, 1024 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 531c9c20ef1d..a6f67f316037 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -11,10 +11,12 @@ #include #include #include "nfs4_fs.h" +#include "nfs.h" #include "nfs42.h" #include "iostat.h" #include "pnfs.h" #include "nfs4session.h" +#include "nfs4idmap.h" #include "internal.h" #include "delegation.h" #include "nfs4trace.h" @@ -1444,6 +1446,329 @@ ssize_t nfs42_proc_listxattrs(struct inode *inode, void *buf, return err; } +static int _nfs4_proc_getposixacl(struct nfs42_getposixaclargs *arg, + struct nfs42_getposixaclres *res, + struct rpc_message *msg, + struct nfs_server *server, + struct inode *inode) +{ + struct nfs4_exception exception = { + .interruptible = true, + }; + int err; + do { + err = nfs4_call_sync(server->client, server, msg, + &arg->seq_args, &res->seq_res, 0); + err = nfs4_handle_exception(server, err, + &exception); + } while (exception.retry); + return err; +} + +struct posix_acl *nfs4_get_posixacl(struct inode *inode, int type, bool rcu) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct page *pages[NFS4_ACL_MAXPAGES] = { }; + struct nfs42_getposixaclargs args = { + .fh = NFS_FH(inode), + /* The xdr layer may allocate pages here. */ + .pages = pages, + }; + struct nfs42_getposixaclres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETPOSIXACL], + .rpc_argp = &args, + .rpc_resp = &res, + }; + int status, count; + + if (rcu) + return ERR_PTR(-ECHILD); + + /* + * Check to see if FATTR4_WORD_POSIX_ACCESS_ACL and + * FATTR4_WORD_POSIX_DEFAULT_ACL are set in attr_bitmask. + * We get acl_trueform and return EOPNOTSUPP if the acl_trueform + * is not POSIX_DRAFT_ACL. This allows the case where the + * acl_trueform's scope is file object to work when the acl_trueform + * is not POSIX_DRAFT_ACL. + */ +dprintk("getposixacl caps=0x%x bitm2=0x%x\n", server->caps, server->attr_bitmask[2]); + if (!(server->attr_bitmask[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) || + !(server->attr_bitmask[2] & FATTR4_WORD2_POSIX_ACCESS_ACL)) + return ERR_PTR(-EOPNOTSUPP); + + status = nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE); + if (status < 0) + return ERR_PTR(status); + + /* + * Only get the access acl when explicitly requested: We don't + * need it for access decisions, and only some applications use + * it. Applications which request the access acl first are not + * penalized from this optimization. + */ + if (type == ACL_TYPE_ACCESS) + args.mask |= NFS_ACLCNT|NFS_ACL; + if (S_ISDIR(inode->i_mode)) + args.mask |= NFS_DFACLCNT|NFS_DFACL; + if (args.mask == 0) + return NULL; + + dprintk("NFS4 call getacl\n"); + if (args.mask & NFS_ACL) + nfs_prepare_get_acl(&inode->i_acl); + if (args.mask & NFS_DFACL) + nfs_prepare_get_acl(&inode->i_default_acl); + +dprintk("at nfs4_proc_getposixacl\n"); + status = _nfs4_proc_getposixacl(&args, &res, &msg, server, inode); + dprintk("NFS4 reply getacl: %d\n", status); + + /* pages may have been allocated at the xdr layer. */ + for (count = 0; count < NFS4_ACL_MAXPAGES && args.pages[count]; count++) + __free_page(args.pages[count]); + + switch (status) { + case 0: + break; + case -EPFNOSUPPORT: + case -EPROTONOSUPPORT: + fallthrough; + case -ENOTSUPP: + status = -EOPNOTSUPP; + goto getout; + default: + goto getout; + } + if ((args.mask & res.mask) != args.mask) { + status = -EIO; + goto getout; + } + + if (res.acl_access != NULL) { +dprintk("res.acl_access cnt=%d\n", res.acl_access->a_count); + if ((posix_acl_equiv_mode(res.acl_access, NULL) == 0) || + res.acl_access->a_count == 0) { + posix_acl_release(res.acl_access); + res.acl_access = NULL; + } + } + + if (res.mask & NFS_ACL) + nfs_complete_get_acl(&inode->i_acl, res.acl_access); + else + forget_cached_acl(inode, ACL_TYPE_ACCESS); + + if (res.mask & NFS_DFACL) + nfs_complete_get_acl(&inode->i_default_acl, res.acl_default); + else + forget_cached_acl(inode, ACL_TYPE_DEFAULT); + + if (type == ACL_TYPE_ACCESS) { + posix_acl_release(res.acl_default); + return res.acl_access; + } else { + posix_acl_release(res.acl_access); + return res.acl_default; + } + +getout: + nfs_abort_get_acl(&inode->i_acl); + nfs_abort_get_acl(&inode->i_default_acl); + posix_acl_release(res.acl_access); + posix_acl_release(res.acl_default); +dprintk("eo nfs4_get_acl=%d\n", status); + return ERR_PTR(status); +} + +static int _nfs4_set_posixacl(struct inode *inode, struct posix_acl *acl, + struct posix_acl *dfacl) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct page *pages[NFS4_ACL_MAXPAGES]; + struct nfs42_setposixaclargs args = { + .server = NFS_SERVER(inode), + .fh = NFS_FH(inode), + .inode = inode, + .mask = NFS_ACL, + .acl_access = acl, + }; + struct nfs42_setposixaclres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETPOSIXACL], + .rpc_argp = &args, + .rpc_resp = &res, + }; + struct nfs4_exception exception = { + .interruptible = true, + }; + struct nfs_xdr_putpage_desc desc = { + .pages = pages, + .max_npages = NFS4_ACL_MAXPAGES, + }; + size_t argslen; + int idmax, status = 0; + +dprintk("in _nfs4_set_posixacl: acl=%p dfacl=%p\n", acl, dfacl); + if (acl == NULL && (!S_ISDIR(inode->i_mode) || dfacl == NULL)) + goto out; + + status = -EOPNOTSUPP; + /* + * Check to see if FATTR4_WORD_POSIX_ACCESS_ACL and + * FATTR4_WORD_POSIX_DEFAULT_ACL are set in attr_bitmask. + * We get acl_trueform and return EOPNOTSUPP if the acl_trueform + * is not POSIX_DRAFT_ACL. This allows the case where the + * acl_trueform's scope is file object to work when the acl_trueform + * is not POSIX_DRAFT_ACL. + */ +dprintk("setposixacl caps=0x%x bitm2=0x%x\n", server->caps, server->attr_bitmask[2]); + if (!(server->attr_bitmask[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) || + !(server->attr_bitmask[2] & FATTR4_WORD2_POSIX_ACCESS_ACL)) + goto out; + + idmax = (XDR_QUADLEN(IDMAP_NAMESZ) << 2); + argslen = 0; + status = -ENOSPC; + if (acl != NULL) { + if (acl->a_count > NFS_ACL_MAX_ENTRIES) + goto out; + argslen += ((1 + (3 * acl->a_count)) << 2); + if (acl->a_count > 4) + argslen += (acl->a_count - 4) * idmax; + } + if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES) + goto out; + if (S_ISDIR(inode->i_mode)) { + args.mask |= NFS_DFACL; + args.acl_default = dfacl; + if (dfacl != NULL) { + argslen += ((1 + (3 * dfacl->a_count)) << 2); + if (dfacl->a_count > 4) + argslen += (dfacl->a_count - 4) * idmax; + } else { + argslen += 4; + } + } + +dprintk("argslen=%d\n", argslen); + do { + /* + * We do not know how many pages will be needed for a large ACL, + * so additional pages are allocated, as required. + */ + if (argslen > NFS4_ACL_INLINE_BUFSIZE) { + ssize_t ret, size; + + status = -ENOMEM; + size = 0; + if (args.mask & NFS_DFACL) + size = nfs42_encode_posixacl(server, &desc, + dfacl); +dprintk("aft encode_posixacl0=%d\n", size); + if (size < 0) + goto out_freepages; + if ((args.mask & NFS_ACL) && size >= 0) { + ret = nfs42_encode_posixacl(server, &desc, acl); + if (ret < 0) + goto out_freepages; + size += ret; + } +dprintk("aft encode_posixacl1=%d\n", size); + args.len = size; + args.pages = desc.pages; + } + +dprintk("NFS4 call setacl\n"); + + status = nfs4_call_sync(server->client, server, &msg, + &args.seq_args, &res.seq_res, 0); + status = nfs4_handle_exception(server, status, &exception); +dprintk("aft nfs4_handle_exception=%d\n", status); + if (exception.retry) { + /* Reset to beginning of page array. */ + desc.page_pos = 0; + desc.p = NULL; + desc.endp = NULL; + } + } while (exception.retry); + nfs_access_zap_cache(inode); + nfs_zap_acl_cache(inode); + dprintk("NFS4 reply setacl: %d\n", status); + + switch (status) { + case 0: + break; + case -EPFNOSUPPORT: + case -EPROTONOSUPPORT: + dprintk("NFS_V4_ACL SETACL RPC not supported" + "(will not retry)\n"); + server->caps &= ~NFS_CAP_ACLS; + fallthrough; + case -ENOTSUPP: + status = -EOPNOTSUPP; + } +out_freepages: + if (desc.npages > 0) + nfs_xdr_putpage_cleanup(&desc); +out: + return status; +} + +int nfs4_set_posixacl(struct mnt_idmap *idmap, struct dentry *dentry, + struct posix_acl *acl, int type) +{ + struct posix_acl *orig = acl, *dfacl = NULL, *alloc; + struct inode *inode = d_inode(dentry); + int status; + +dprintk("nfs4_set_posixacl\n"); + if (S_ISDIR(inode->i_mode)) { + switch(type) { + case ACL_TYPE_ACCESS: +dprintk("in dir access\n"); + alloc = get_inode_acl(inode, ACL_TYPE_DEFAULT); + if (IS_ERR(alloc)) + goto fail; + dfacl = alloc; + break; + case ACL_TYPE_DEFAULT: +dprintk("in dir default\n"); + alloc = get_inode_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(alloc)) + goto fail; + dfacl = acl; + acl = alloc; + } + } + + if (acl == NULL) { + alloc = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); + if (IS_ERR(alloc)) + goto fail; + acl = alloc; + } +dprintk("at _nfs4_set_posixacl\n"); + status = _nfs4_set_posixacl(inode, acl, dfacl); +dprintk("aft _nfs4_set_posixacl=%d\n", status); +out: + if (acl != orig) + posix_acl_release(acl); + if (dfacl != orig) + posix_acl_release(dfacl); +dprintk("eo nfs4_set_posixacl=%d\n", status); + return status; + +fail: + status = PTR_ERR(alloc); + goto out; +} + int nfs42_proc_removexattr(struct inode *inode, const char *name) { struct nfs4_exception exception = { }; diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 9e3ae53e2205..ba98cbb39ac7 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -242,6 +242,33 @@ decode_putfh_maxsz + \ decode_removexattr_maxsz) +#define encode_getposixacl_maxsz (encode_getattr_maxsz) +#define decode_getposixacl_maxsz (op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz + \ + XDR_QUADLEN(NFS4_ACL_INLINE_BUFSIZE) + \ + pagepad_maxsz) +#define encode_setposixacl_maxsz (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + \ + nfs4_fattr_bitmap_maxsz + 1 + \ + XDR_QUADLEN(NFS4_ACL_INLINE_BUFSIZE)) +#define decode_setposixacl_maxsz (decode_setattr_maxsz) +#define NFS4_enc_getposixacl_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ + encode_putfh_maxsz + \ + encode_getposixacl_maxsz) +#define NFS4_dec_getposixacl_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_putfh_maxsz + \ + decode_getposixacl_maxsz) +#define NFS4_enc_setposixacl_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ + encode_putfh_maxsz + \ + encode_setposixacl_maxsz) +#define NFS4_dec_setposixacl_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_putfh_maxsz + \ + decode_setposixacl_maxsz) + /* * These values specify the maximum amount of data that is not * associated with the extended attribute name or extended @@ -1645,6 +1672,668 @@ static int nfs4_xdr_dec_listxattrs(struct rpc_rqst *rqstp, return status; } +static int +nfsacl4_posix_tagtotype(u32 tag) +{ + int type; + + switch(tag) { + case ACL_USER_OBJ: + type = POSIXACE4_TAG_USER_OBJ; + break; + case ACL_GROUP_OBJ: + type = POSIXACE4_TAG_GROUP_OBJ; + break; + case ACL_USER: + type = POSIXACE4_TAG_USER; + break; + case ACL_GROUP: + type = POSIXACE4_TAG_GROUP; + break; + case ACL_MASK: + type = POSIXACE4_TAG_MASK; + break; + case ACL_OTHER: + type = POSIXACE4_TAG_OTHER; + break; + default: + return -EINVAL; + } + return type; +} + +static int xdr_nfs4ace_stream_encode(struct xdr_stream *xdr, + const struct nfs_server *server, + struct posix_acl_entry *acep) +{ + char owner[IDMAP_NAMESZ]; + int len, size, type; + +dprintk("in xdr_nfs4ace_encode tag=%d\n", acep->e_tag); + type = nfsacl4_posix_tagtotype(acep->e_tag); + if (type < 0) + return -EINVAL; + if (xdr_stream_encode_u32(xdr, type) < 0) + return -EINVAL; +dprintk("at encode perm=%d\n", acep->e_perm); + if (xdr_stream_encode_u32(xdr, acep->e_perm) < 0) + return -EINVAL; + size = 8; + switch(acep->e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: +dprintk("at encode 0\n"); + if (xdr_stream_encode_u32(xdr, 0) < 0) + return -EINVAL; + size += 4; + break; + case ACL_USER: + len = nfs_map_uid_to_name(server, acep->e_uid, owner, + IDMAP_NAMESZ); +dprintk("user len=%d\n", len); + if (len < 0) { + dprintk("nfs: couldn't resolve uid %d to str\n", + from_kuid(&init_user_ns, acep->e_uid)); + return -EINVAL; + } + if (xdr_stream_encode_opaque(xdr, owner, len) < 0) + return -EINVAL; + size += 4 + (XDR_QUADLEN(len) << 2); +dprintk("aft opaque size=%d\n", size); + break; + case ACL_GROUP: + len = nfs_map_gid_to_group(server, acep->e_gid, owner, + IDMAP_NAMESZ); +dprintk("group len=%d\n", len); + if (len < 0) { + dprintk("nfs: couldn't resolve gid %d to str\n", + from_kgid(&init_user_ns, acep->e_gid)); + return -EINVAL; + } + if (xdr_stream_encode_opaque(xdr, owner, len) < 0) + return -EINVAL; + size += 4 + (XDR_QUADLEN(len) << 2); +dprintk("aft group opaque size=%d\n", size); + break; + default: + return -EINVAL; + } +dprintk("eo xdr_nfs4ace_encode size=%d\n", size); + return size; +} + +static int encode_stream_posixacl(struct xdr_stream *xdr, struct posix_acl *acl, + const struct nfs_server *server) +{ + unsigned int cnt; + int ret, size; + + if (acl == NULL) { +dprintk("in encode_posixacl NULL acl\n"); + if (xdr_stream_encode_u32(xdr, 0) < 0) + return -EINVAL; + return 4; + } + if (acl->a_count > NFS_ACL_MAX_ENTRIES) + return -EINVAL; + if (xdr_stream_encode_u32(xdr, acl->a_count) < 0) + return -EINVAL; + size = 4; +dprintk("in encode_posixacl size=%d\n", size); + + for (cnt = 0; cnt < acl->a_count; cnt++) { + ret = xdr_nfs4ace_stream_encode(xdr, server, + &acl->a_entries[cnt]); +dprintk("aft xdr_nfs4ace_encode=%d\n", ret); + if (ret < 0) + return ret; + size += ret; + } + +dprintk("eo encode_posixacl=%d\n", size); + return size; +} + +static bool nfs_xdr_putpage_bytes(struct nfs_xdr_putpage_desc *desc, + void *bytes, size_t len) +{ + size_t tmp, xfer; + + while (len > 0) { + if (desc->p == desc->endp) { + /* Need to move on to the next page. */ + if (desc->page_pos == desc->npages) { + /* Needs a new page. */ + if (desc->npages == desc->max_npages) + return false; + desc->pages[desc->npages] = + alloc_page(GFP_KERNEL); + if (desc->pages[desc->npages] == NULL) + return false; + desc->npages++; + } + desc->p = page_address(desc->pages[desc->page_pos]); + desc->endp = desc->p + PAGE_SIZE; + desc->page_pos++; + } + tmp = desc->endp - desc->p; + xfer = (tmp < len) ? tmp : len; +dprintk("nfs_xdr_putpage_bytes: xfer=%d\n", xfer); + memcpy(desc->p, bytes, xfer); + bytes += xfer; + desc->p += xfer; + len -= xfer; + } + return true; +} + +static bool nfs_xdr_putpage_word(struct nfs_xdr_putpage_desc *desc, u32 val) +{ + __be32 beval; + + beval = cpu_to_be32(val); + return nfs_xdr_putpage_bytes(desc, &beval, sizeof(beval)); +} + +void nfs_xdr_putpage_cleanup(struct nfs_xdr_putpage_desc *desc) +{ + + while (desc->npages != 0) { + desc->npages--; + __free_page(desc->pages[desc->npages]); + } +} + +static ssize_t xdr_nfs4ace_encode(const struct nfs_server *server, + struct nfs_xdr_putpage_desc *desc, struct posix_acl_entry *acep) +{ + char owner[IDMAP_NAMESZ]; + ssize_t len, size; + int type; + +dprintk("in xdr_nfs4ace_encode tag=%d\n", acep->e_tag); + type = nfsacl4_posix_tagtotype(acep->e_tag); + if (type < 0) + return -EINVAL; + if (!nfs_xdr_putpage_word(desc, type)) + return -EINVAL; +dprintk("at encode perm=%d\n", acep->e_perm); + if (!nfs_xdr_putpage_word(desc, acep->e_perm)) + return -EINVAL; + size = 8; + switch(acep->e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: +dprintk("at encode 0\n"); + if (!nfs_xdr_putpage_word(desc, 0)) + return -EINVAL; + size += 4; + break; + case ACL_USER: + len = nfs_map_uid_to_name(server, acep->e_uid, owner, + IDMAP_NAMESZ); +dprintk("user len=%d\n", len); + if (len < 0) { + dprintk("nfs: couldn't resolve uid %d to string\n", + from_kuid(&init_user_ns, acep->e_uid)); + return -EINVAL; + } + if (!nfs_xdr_putpage_word(desc, len)) + return -EINVAL; + size += 4; + while (len & 3) + owner[len++] = '\0'; + if (!nfs_xdr_putpage_bytes(desc, owner, len)) + return -EINVAL; + size += len; +dprintk("aft opaque size=%d\n", size); + break; + case ACL_GROUP: + len = nfs_map_gid_to_group(server, acep->e_gid, owner, + IDMAP_NAMESZ); +dprintk("group len=%d\n", len); + if (len < 0) { + dprintk("nfs: couldn't resolve gid %d to string\n", + from_kgid(&init_user_ns, acep->e_gid)); + return -EINVAL; + } + if (!nfs_xdr_putpage_word(desc, len)) + return -EINVAL; + size += 4; + while (len & 3) + owner[len++] = '\0'; + if (!nfs_xdr_putpage_bytes(desc, owner, len)) + return -EINVAL; + size += len; +dprintk("aft group opaque size=%d\n", size); + break; + default: + return -EINVAL; + } +dprintk("eo xdr_nfs4ace_encode size=%d\n", size); + return size; +} + +ssize_t nfs42_encode_posixacl(const struct nfs_server *server, + struct nfs_xdr_putpage_desc *desc, struct posix_acl *acl) +{ + unsigned int cnt; + ssize_t ret, size; + + if (acl == NULL) { +dprintk("in nfs42_encode_posixacl NULL acl\n"); + if (!nfs_xdr_putpage_word(desc, 0)) + return -EINVAL; + return 4; + } +dprintk("in nfs42_encode_posixacl count=%d\n", acl->a_count); + if (acl->a_count > NFS_ACL_MAX_ENTRIES) + return -EINVAL; + if (!nfs_xdr_putpage_word(desc, acl->a_count)) + return -EINVAL; + size = 4; +dprintk("in nfs42_encode_posixacl size=%d\n", size); + + for (cnt = 0; cnt < acl->a_count; cnt++) { + ret = xdr_nfs4ace_encode(server, desc, &acl->a_entries[cnt]); + if (ret < 0) + return ret; + size += ret; +dprintk("aft xdr_nfs4ace_encode=%d size=%d\n", ret, size); + } + +dprintk("eo encode_posixacl=%d\n", size); + return size; +} + +static void encode_setposixacl(struct rpc_rqst *req, struct xdr_stream *xdr, + const struct nfs42_setposixaclargs *arg, + const struct nfs_server *server, + struct compound_hdr *hdr) +{ + uint32_t bitmap[3]; + __be32 *sizep; + ssize_t ret, size; + +dprintk("in encode_setposixacl\n"); + bitmap[0] = 0; + bitmap[1] = 0; + bitmap[2] = 0; + if (arg->mask & NFS_ACL) + bitmap[2] |= FATTR4_WORD2_POSIX_ACCESS_ACL; + if (arg->mask & NFS_DFACL) + bitmap[2] |= FATTR4_WORD2_POSIX_DEFAULT_ACL; +dprintk("bitmap=0x%x\n", bitmap[2]); + + encode_op_hdr(xdr, OP_SETATTR, decode_setposixacl_maxsz, hdr); +dprintk("at encode_nfs4_stateid\n"); + encode_nfs4_stateid(xdr, &zero_stateid); +dprintk("at encode_bitmap4\n"); + xdr_encode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap)); + sizep = reserve_space(xdr, 4); +dprintk("sizep=%p\n", sizep); + if (sizep != NULL) { + size = 0; + if (arg->len > 0) { + xdr_write_pages(xdr, arg->pages, 0, arg->len); + size = arg->len; +dprintk("aft xdr_write_pages len=%d\n", arg->len); + } else { + if (arg->mask & NFS_DFACL) + size = encode_stream_posixacl(xdr, + arg->acl_default, server); +dprintk("aft encode_posixacl0=%d\n", size); + if ((arg->mask & NFS_ACL) && size >= 0) { + ret = encode_stream_posixacl(xdr, + arg->acl_access, server); + if (ret > 0) + size += ret; + } + } +dprintk("aft encode_posixacl1=%d\n", size); + if (size >= 0) + *sizep = cpu_to_be32(size); + } +dprintk("aft set sizep\n"); +} + +/* + * Encode a GETPOSIXACL request + */ +static void nfs4_xdr_enc_getposixacl(struct rpc_rqst *req, + struct xdr_stream *xdr, const void *data) +{ + const struct nfs42_getposixaclargs *args = data; + struct compound_hdr hdr = { + .minorversion = nfs4_xdr_minorversion(&args->seq_args), + }; + uint32_t bitmask[3]; + int getacl_cnt; + + bitmask[0] = 0; + bitmask[1] = 0; + bitmask[2] = FATTR4_WORD2_ACL_TRUEFORM; + getacl_cnt = 0; + if (args->mask & (NFS_ACLCNT|NFS_ACL)) { + bitmask[2] |= FATTR4_WORD2_POSIX_ACCESS_ACL; + getacl_cnt++; + } + if (args->mask & (NFS_DFACLCNT|NFS_DFACL)) { + bitmask[2] |= FATTR4_WORD2_POSIX_DEFAULT_ACL; + getacl_cnt++; + } + encode_compound_hdr(xdr, req, &hdr); + encode_sequence(xdr, &args->seq_args, &hdr); + encode_putfh(xdr, args->fh, &hdr); + encode_getattr(xdr, bitmask, NULL, 3, &hdr); + + if (getacl_cnt > 0) { + rpc_prepare_reply_pages(req, args->pages, 0, + (NFS4_ACL_MAXPAGES * getacl_cnt) << + PAGE_SHIFT, NFS4_dec_getposixacl_sz - + pagepad_maxsz); + req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES; + } + encode_nops(&hdr); +} + +/* + * Encode a SETPOSIXACL request + */ +static void nfs4_xdr_enc_setposixacl(struct rpc_rqst *req, + struct xdr_stream *xdr, const void *data) +{ + const struct nfs42_setposixaclargs *args = data; + struct compound_hdr hdr = { + .minorversion = nfs4_xdr_minorversion(&args->seq_args), + }; + +dprintk("in nfs4_xdr_enc_setposixacl\n"); + encode_compound_hdr(xdr, req, &hdr); +dprintk("at encode_sequence\n"); + encode_sequence(xdr, &args->seq_args, &hdr); +dprintk("at encode_putfh\n"); + encode_putfh(xdr, args->fh, &hdr); +dprintk("at encode_setposixacl=%d\n", xdr->buf->buflen); + encode_setposixacl(req, xdr, args, args->server, &hdr); +dprintk("at encode_nops\n"); + encode_nops(&hdr); +} + +static bool +nfsacl4_posix_xdrtotag(struct xdr_stream *xdr, u32 *tag) +{ + u32 type; + int ret; + + ret = xdr_stream_decode_u32(xdr, &type); + if (ret < 0) + return false; + switch(type) { + case POSIXACE4_TAG_USER_OBJ: + *tag = ACL_USER_OBJ; + break; + case POSIXACE4_TAG_GROUP_OBJ: + *tag = ACL_GROUP_OBJ; + break; + case POSIXACE4_TAG_USER: + *tag = ACL_USER; + break; + case POSIXACE4_TAG_GROUP: + *tag = ACL_GROUP; + break; + case POSIXACE4_TAG_MASK: + *tag = ACL_MASK; + break; + case POSIXACE4_TAG_OTHER: + *tag = ACL_OTHER; + break; + default: + return false; + } + return true; +} + +struct nfsacl4_decode_desc { + unsigned int array_len; + unsigned int count; + struct posix_acl *acl; +}; + +static ssize_t +xdr_nfs4ace_decode(struct xdr_stream *xdr, const struct nfs_server *server, + struct nfsacl4_decode_desc *desc) +{ + struct posix_acl_entry *entry; + char *owner; + kuid_t uid; + kgid_t gid; + u32 val; + ssize_t ret; + + if (!desc->acl) { + desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL); + if (!desc->acl) + return -ENOMEM; + desc->count = 0; + } + + entry = &desc->acl->a_entries[desc->count++]; + if (!nfsacl4_posix_xdrtotag(xdr, &val)) + return -EBADMSG; + entry->e_tag = val; + ret = xdr_stream_decode_u32(xdr, &val); + if (ret < 0) + return -EBADMSG; +dprintk("perm=%d\n", val); + if (val & ~S_IRWXO) + return -EINVAL; + entry->e_perm = val; + ret = xdr_stream_decode_opaque_inline(xdr, (void **)&owner, + IDMAP_NAMESZ); + if (ret < 0) + return -EBADMSG; +dprintk("owner=%s\n", owner); + + switch(entry->e_tag) { + case ACL_USER: + if (ret == 0) + return -EBADMSG; + if (nfs_map_name_to_uid(server, owner, ret, &uid) == 0) + entry->e_uid = uid; + else + return -EINVAL; + break; + case ACL_GROUP: + if (ret == 0) + return -EBADMSG; + if (nfs_map_group_to_gid(server, owner, ret, &gid) == 0) + entry->e_gid = gid; + else + return -EINVAL; + } + + return (XDR_QUADLEN(ret) << 2) + 12; +} + +static ssize_t nfs_stream_decode_acl4(struct xdr_stream *xdr, + const struct nfs_server *server, unsigned int *aclcnt, + struct posix_acl **pacl) +{ + struct nfsacl4_decode_desc nfsacl_desc; + u32 entries, i; + ssize_t ret, retlen; + +dprintk("in nfs_stream_decode_acl4\n"); + ret = xdr_stream_decode_u32(xdr, &entries); + if (ret < 0) + return -EBADMSG; +dprintk("entries=%d\n", entries); + if (entries > NFS_ACL_MAX_ENTRIES) + return -EINVAL; + retlen = 4; + + nfsacl_desc.array_len = entries; + nfsacl_desc.count = 0; + nfsacl_desc.acl = NULL; + for (i = 0; i < entries; i++) { + ret = xdr_nfs4ace_decode(xdr, server, &nfsacl_desc); +dprintk("aft xdr_nfs4ace_decode=%d\n", ret); + if (ret < 0) + return ret; + retlen += ret; + } + + if (pacl) { + if (posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) { + posix_acl_release(nfsacl_desc.acl); + return -EINVAL; + } + *pacl = nfsacl_desc.acl; + } + if (aclcnt) + *aclcnt = entries; + return retlen; +} + +static int decode_getposixacl(struct xdr_stream *xdr, + struct nfs42_getposixaclres *res, + const struct nfs_server *server) +{ + uint32_t bitmap[3] = {0}; + u32 attrlen, attrsize, trueform; + char scratch_buf[IDMAP_NAMESZ]; + int status; + + status = decode_op_hdr(xdr, OP_GETATTR); + if (status < 0) + goto xdr_error; + + status = decode_attr_bitmap(xdr, bitmap); + if (status < 0) + goto xdr_error; + + if (bitmap[0] || bitmap[1] || + (bitmap[2] & ~(FATTR4_WORD2_POSIX_ACCESS_ACL | + FATTR4_WORD2_POSIX_DEFAULT_ACL | + FATTR4_WORD2_ACL_TRUEFORM))) { + status = -EBADMSG; + goto xdr_error; + } + + status = xdr_stream_decode_u32(xdr, &attrlen); + if (status < 0) + goto xdr_error; + + trueform = ACL_MODEL_NFS4; + if (bitmap[2] & FATTR4_WORD2_ACL_TRUEFORM) { + status = xdr_stream_decode_u32(xdr, &trueform); + if (status < 0) + goto xdr_error; + attrsize = 4; + } + +dprintk("acl_trueform=%d\n", trueform); + /* + * For a ACL_MODEL_NFS4 true form, return EOPNOTSUPP. + * Hopefully this error can be used by getfacl(1) to indicate + * that nfs4_getfacl(1) should be used. + */ + if (trueform == ACL_MODEL_NFS4) { + status = -EOPNOTSUPP; + goto xdr_error; + } + + xdr_set_scratch_buffer(xdr, &scratch_buf, sizeof(scratch_buf)); + res->mask = 0; + if (bitmap[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) { + status = nfs_stream_decode_acl4(xdr, server, + &res->acl_default_count, + &res->acl_default); + if (status < 0) + goto xdr_error2; + attrsize += status; + res->mask |= NFS_DFACL|NFS_DFACLCNT; + } + if (bitmap[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) { + status = nfs_stream_decode_acl4(xdr, server, + &res->acl_access_count, + &res->acl_access); + if (status < 0) + goto xdr_error2; + attrsize += status; + res->mask |= NFS_ACL|NFS_ACLCNT; + } + status = 0; + +dprintk("ATTR SIZE len=%d attrs=%d\n", attrlen, attrsize); + if (attrlen != attrsize) + status = -EBADMSG; +xdr_error2: + xdr_reset_scratch_buffer(xdr); +xdr_error: + dprintk("%s: xdr returned %d\n", __func__, -status); + return status; +} + +/* + * Decode GETPOSIXACL response + */ +static int +nfs4_xdr_dec_getposixacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, + void *data) +{ + struct nfs42_getposixaclres *res = data; + struct compound_hdr hdr; + int status; + + status = decode_compound_hdr(xdr, &hdr); + if (status) + goto out; + status = decode_sequence(xdr, &res->seq_res, rqstp); + if (status) + goto out; + status = decode_putfh(xdr); + if (status) + goto out; + status = decode_getposixacl(xdr, res, res->server); + +out: + return status; +} + +/* + * Decode SETPOSIXACL response + */ +static int nfs4_xdr_dec_setposixacl(struct rpc_rqst *rqstp, + struct xdr_stream *xdr, + void *data) +{ + struct nfs42_setposixaclres *res = data; + struct compound_hdr hdr; + int status; + + status = decode_compound_hdr(xdr, &hdr); + if (status) + goto out; + status = decode_sequence(xdr, &res->seq_res, rqstp); + if (status) + goto out; + status = decode_putfh(xdr); + if (status) + goto out; + status = decode_setattr(xdr); + if (status) + goto out; +out: + return status; +} + + /* * Decode REMOVEXATTR request */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cd2fbde2e6d7..06f7143e4791 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3902,7 +3902,7 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) #define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL) #define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL) -#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_OPEN_ARGUMENTS - 1UL) +#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_POSIX_ACCESS_ACL - 1UL) #define FATTR4_WORD2_NFS42_TIME_DELEG_MASK \ (FATTR4_WORD2_TIME_DELEG_MODIFY|FATTR4_WORD2_TIME_DELEG_ACCESS) @@ -3970,7 +3970,10 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f case 2: res.attr_bitmask[2] &= FATTR4_WORD2_NFS42_MASK; bitmask[2] = (FATTR4_WORD2_SUPPATTR_EXCLCREAT | - FATTR4_WORD2_OPEN_ARGUMENTS) & + FATTR4_WORD2_OPEN_ARGUMENTS | + FATTR4_WORD2_ACL_TRUEFORM | + FATTR4_WORD2_POSIX_DEFAULT_ACL | + FATTR4_WORD2_POSIX_ACCESS_ACL) & res.attr_bitmask[2]; } memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask)); @@ -5991,6 +5994,7 @@ static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl static void nfs4_zap_acl_attr(struct inode *inode) { nfs4_set_cached_acl(inode, NULL); + forget_all_cached_acls(inode); } static ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, @@ -10859,6 +10863,8 @@ static const struct inode_operations nfs4_dir_inode_operations = { .getattr = nfs_getattr, .setattr = nfs_setattr, .listxattr = nfs4_listxattr, + .get_inode_acl = nfs4_get_posixacl, + .set_acl = nfs4_set_posixacl, }; static const struct inode_operations nfs4_file_inode_operations = { @@ -10866,6 +10872,8 @@ static const struct inode_operations nfs4_file_inode_operations = { .getattr = nfs_getattr, .setattr = nfs_setattr, .listxattr = nfs4_listxattr, + .get_inode_acl = nfs4_get_posixacl, + .set_acl = nfs4_set_posixacl, }; const struct nfs_rpc_ops nfs_v4_clientops = { -- 2.34.1 From e51526f7bf9ec567caa8807c17b857fcc227b9b0 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:08:58 -0700 Subject: [PATCH 06/10] Fix up a couple of blank lines No semantics change. Signed-off-by: Rick Macklem --- fs/nfs/nfs42proc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index a6f67f316037..8c30cf568300 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -1664,7 +1664,7 @@ dprintk("argslen=%d\n", argslen); */ if (argslen > NFS4_ACL_INLINE_BUFSIZE) { ssize_t ret, size; - + status = -ENOMEM; size = 0; if (args.mask & NFS_DFACL) @@ -1683,9 +1683,8 @@ dprintk("aft encode_posixacl1=%d\n", size); args.len = size; args.pages = desc.pages; } - + dprintk("NFS4 call setacl\n"); - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); status = nfs4_handle_exception(server, status, &exception); -- 2.34.1 From 8f06b7f2194c99efdbda1d23376cdc39ea37a26c Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:12:44 -0700 Subject: [PATCH 07/10] Add the GETPOSIXACL and SETPOSIXACL entries for the NFSv4.2 client This patch adds GETPOSIXACL and SETPOSIXACL procedures, so that the NFSv4.2 client can getfacl(1)/setfacl(1) POSIX draft ACLs over a NFSv4.2 mount that supports the extension. Signed-off-by: Rick Macklem --- fs/nfs/nfs4xdr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index e8ac3f615f93..8dece8ea2466 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -7710,6 +7710,8 @@ const struct rpc_procinfo nfs4_procedures[] = { PROC42(LISTXATTRS, enc_listxattrs, dec_listxattrs), PROC42(REMOVEXATTR, enc_removexattr, dec_removexattr), PROC42(READ_PLUS, enc_read_plus, dec_read_plus), + PROC42(GETPOSIXACL, enc_getposixacl, dec_getposixacl), + PROC42(SETPOSIXACL, enc_setposixacl, dec_setposixacl), }; static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)]; -- 2.34.1 From 7362810224b7f831952aeeea76918b304bc4142c Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:15:43 -0700 Subject: [PATCH 08/10] Fix switch statement indentation No semantics change. Signed-off-by: Rick Macklem --- fs/nfs/nfs42xdr.c | 108 +++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index ba98cbb39ac7..62d7ada27aee 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -1678,26 +1678,26 @@ nfsacl4_posix_tagtotype(u32 tag) int type; switch(tag) { - case ACL_USER_OBJ: - type = POSIXACE4_TAG_USER_OBJ; - break; - case ACL_GROUP_OBJ: - type = POSIXACE4_TAG_GROUP_OBJ; - break; - case ACL_USER: - type = POSIXACE4_TAG_USER; - break; - case ACL_GROUP: - type = POSIXACE4_TAG_GROUP; - break; - case ACL_MASK: - type = POSIXACE4_TAG_MASK; - break; - case ACL_OTHER: - type = POSIXACE4_TAG_OTHER; - break; - default: - return -EINVAL; + case ACL_USER_OBJ: + type = POSIXACE4_TAG_USER_OBJ; + break; + case ACL_GROUP_OBJ: + type = POSIXACE4_TAG_GROUP_OBJ; + break; + case ACL_USER: + type = POSIXACE4_TAG_USER; + break; + case ACL_GROUP: + type = POSIXACE4_TAG_GROUP; + break; + case ACL_MASK: + type = POSIXACE4_TAG_MASK; + break; + case ACL_OTHER: + type = POSIXACE4_TAG_OTHER; + break; + default: + return -EINVAL; } return type; } @@ -1720,45 +1720,45 @@ dprintk("at encode perm=%d\n", acep->e_perm); return -EINVAL; size = 8; switch(acep->e_tag) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: dprintk("at encode 0\n"); - if (xdr_stream_encode_u32(xdr, 0) < 0) - return -EINVAL; - size += 4; - break; - case ACL_USER: - len = nfs_map_uid_to_name(server, acep->e_uid, owner, - IDMAP_NAMESZ); + if (xdr_stream_encode_u32(xdr, 0) < 0) + return -EINVAL; + size += 4; + break; + case ACL_USER: + len = nfs_map_uid_to_name(server, acep->e_uid, owner, + IDMAP_NAMESZ); dprintk("user len=%d\n", len); - if (len < 0) { - dprintk("nfs: couldn't resolve uid %d to str\n", - from_kuid(&init_user_ns, acep->e_uid)); - return -EINVAL; - } - if (xdr_stream_encode_opaque(xdr, owner, len) < 0) - return -EINVAL; - size += 4 + (XDR_QUADLEN(len) << 2); + if (len < 0) { + dprintk("nfs: couldn't resolve uid %d to str\n", + from_kuid(&init_user_ns, acep->e_uid)); + return -EINVAL; + } + if (xdr_stream_encode_opaque(xdr, owner, len) < 0) + return -EINVAL; + size += 4 + (XDR_QUADLEN(len) << 2); dprintk("aft opaque size=%d\n", size); - break; - case ACL_GROUP: - len = nfs_map_gid_to_group(server, acep->e_gid, owner, - IDMAP_NAMESZ); + break; + case ACL_GROUP: + len = nfs_map_gid_to_group(server, acep->e_gid, owner, + IDMAP_NAMESZ); dprintk("group len=%d\n", len); - if (len < 0) { - dprintk("nfs: couldn't resolve gid %d to str\n", - from_kgid(&init_user_ns, acep->e_gid)); - return -EINVAL; - } - if (xdr_stream_encode_opaque(xdr, owner, len) < 0) - return -EINVAL; - size += 4 + (XDR_QUADLEN(len) << 2); -dprintk("aft group opaque size=%d\n", size); - break; - default: + if (len < 0) { + dprintk("nfs: couldn't resolve gid %d to str\n", + from_kgid(&init_user_ns, acep->e_gid)); return -EINVAL; + } + if (xdr_stream_encode_opaque(xdr, owner, len) < 0) + return -EINVAL; + size += 4 + (XDR_QUADLEN(len) << 2); +dprintk("aft group opaque size=%d\n", size); + break; + default: + return -EINVAL; } dprintk("eo xdr_nfs4ace_encode size=%d\n", size); return size; -- 2.34.1 From 937b7abc7b9b6df2ccd82af857bbc803e9f5cbc7 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:18:08 -0700 Subject: [PATCH 09/10] Replace 4 with XDR_UNIT for readability No semantics change. Signed-off-by: Rick Macklem --- fs/nfs/nfs42xdr.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 62d7ada27aee..4b371e79a02d 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -1718,7 +1718,7 @@ dprintk("in xdr_nfs4ace_encode tag=%d\n", acep->e_tag); dprintk("at encode perm=%d\n", acep->e_perm); if (xdr_stream_encode_u32(xdr, acep->e_perm) < 0) return -EINVAL; - size = 8; + size = 2 * XDR_UNIT; switch(acep->e_tag) { case ACL_USER_OBJ: case ACL_GROUP_OBJ: @@ -1727,7 +1727,7 @@ dprintk("at encode perm=%d\n", acep->e_perm); dprintk("at encode 0\n"); if (xdr_stream_encode_u32(xdr, 0) < 0) return -EINVAL; - size += 4; + size += XDR_UNIT; break; case ACL_USER: len = nfs_map_uid_to_name(server, acep->e_uid, owner, @@ -1740,7 +1740,7 @@ dprintk("user len=%d\n", len); } if (xdr_stream_encode_opaque(xdr, owner, len) < 0) return -EINVAL; - size += 4 + (XDR_QUADLEN(len) << 2); + size += XDR_UNIT + (XDR_QUADLEN(len) << 2); dprintk("aft opaque size=%d\n", size); break; case ACL_GROUP: @@ -1754,7 +1754,7 @@ dprintk("group len=%d\n", len); } if (xdr_stream_encode_opaque(xdr, owner, len) < 0) return -EINVAL; - size += 4 + (XDR_QUADLEN(len) << 2); + size += XDR_UNIT + (XDR_QUADLEN(len) << 2); dprintk("aft group opaque size=%d\n", size); break; default: @@ -1774,13 +1774,13 @@ static int encode_stream_posixacl(struct xdr_stream *xdr, struct posix_acl *acl, dprintk("in encode_posixacl NULL acl\n"); if (xdr_stream_encode_u32(xdr, 0) < 0) return -EINVAL; - return 4; + return XDR_UNIT; } if (acl->a_count > NFS_ACL_MAX_ENTRIES) return -EINVAL; if (xdr_stream_encode_u32(xdr, acl->a_count) < 0) return -EINVAL; - size = 4; + size = XDR_UNIT; dprintk("in encode_posixacl size=%d\n", size); for (cnt = 0; cnt < acl->a_count; cnt++) { @@ -1862,7 +1862,7 @@ dprintk("in xdr_nfs4ace_encode tag=%d\n", acep->e_tag); dprintk("at encode perm=%d\n", acep->e_perm); if (!nfs_xdr_putpage_word(desc, acep->e_perm)) return -EINVAL; - size = 8; + size = 2 * XDR_UNIT; switch(acep->e_tag) { case ACL_USER_OBJ: case ACL_GROUP_OBJ: @@ -1871,7 +1871,7 @@ dprintk("at encode perm=%d\n", acep->e_perm); dprintk("at encode 0\n"); if (!nfs_xdr_putpage_word(desc, 0)) return -EINVAL; - size += 4; + size += XDR_UNIT; break; case ACL_USER: len = nfs_map_uid_to_name(server, acep->e_uid, owner, @@ -1884,7 +1884,7 @@ dprintk("user len=%d\n", len); } if (!nfs_xdr_putpage_word(desc, len)) return -EINVAL; - size += 4; + size += XDR_UNIT; while (len & 3) owner[len++] = '\0'; if (!nfs_xdr_putpage_bytes(desc, owner, len)) @@ -1903,7 +1903,7 @@ dprintk("group len=%d\n", len); } if (!nfs_xdr_putpage_word(desc, len)) return -EINVAL; - size += 4; + size += XDR_UNIT; while (len & 3) owner[len++] = '\0'; if (!nfs_xdr_putpage_bytes(desc, owner, len)) @@ -1928,14 +1928,14 @@ ssize_t nfs42_encode_posixacl(const struct nfs_server *server, dprintk("in nfs42_encode_posixacl NULL acl\n"); if (!nfs_xdr_putpage_word(desc, 0)) return -EINVAL; - return 4; + return XDR_UNIT; } dprintk("in nfs42_encode_posixacl count=%d\n", acl->a_count); if (acl->a_count > NFS_ACL_MAX_ENTRIES) return -EINVAL; if (!nfs_xdr_putpage_word(desc, acl->a_count)) return -EINVAL; - size = 4; + size = XDR_UNIT; dprintk("in nfs42_encode_posixacl size=%d\n", size); for (cnt = 0; cnt < acl->a_count; cnt++) { @@ -2157,7 +2157,7 @@ dprintk("owner=%s\n", owner); return -EINVAL; } - return (XDR_QUADLEN(ret) << 2) + 12; + return (XDR_QUADLEN(ret) << 2) + 3 * XDR_UNIT; } static ssize_t nfs_stream_decode_acl4(struct xdr_stream *xdr, @@ -2175,7 +2175,7 @@ dprintk("in nfs_stream_decode_acl4\n"); dprintk("entries=%d\n", entries); if (entries > NFS_ACL_MAX_ENTRIES) return -EINVAL; - retlen = 4; + retlen = XDR_UNIT; nfsacl_desc.array_len = entries; nfsacl_desc.count = 0; @@ -2234,7 +2234,7 @@ static int decode_getposixacl(struct xdr_stream *xdr, status = xdr_stream_decode_u32(xdr, &trueform); if (status < 0) goto xdr_error; - attrsize = 4; + attrsize = XDR_UNIT; } dprintk("acl_trueform=%d\n", trueform); -- 2.34.1 From ca4f3f02f2c9c719b7f0f80ecc038677eb8058f6 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 4 Nov 2024 13:12:35 -0800 Subject: [PATCH 10/10] Add a prototype for posix_acl_to_nfsacl() The function posix_acl_to_nfsacl() is now global, so a prototype for it is required in a .h file. Signed-off-by: Rick Macklem --- include/linux/nfsacl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/nfsacl.h b/include/linux/nfsacl.h index 8e76a79cdc6a..f068160bfdc5 100644 --- a/include/linux/nfsacl.h +++ b/include/linux/nfsacl.h @@ -44,5 +44,7 @@ nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt, extern bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode, struct posix_acl *acl, int encode_entries, int typeflag); +extern int +posix_acl_from_nfsacl(struct posix_acl *acl); #endif /* __LINUX_NFSACL_H */ -- 2.34.1