[RFC PATCH v2 09/11] NFSv4: add reexport support for SETACL nfs4_acl passthru

Mike Snitzer
2026-02-24T19:24:52Z

Wire up the export_operations .setacl hook and use the upper layer
provided nfs4_acl's pages in the call to NFSPROC4_CLNT_SETACL.

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
fs/nfs/export.c | 10 ++++++++++
fs/nfs/nfs4proc.c | 33 +++++++++++++++++++++++++++------
fs/nfs/nfs4xdr.c | 2 +-
include/linux/nfs_xdr.h | 3 +++
4 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/fs/nfs/export.c b/fs/nfs/export.c
index a10dd5f9d078..9a90eee3e433 100644
--- a/fs/nfs/export.c
+++ b/fs/nfs/export.c
@@ -152,10 +152,20 @@ nfs_get_parent(struct dentry *dentry)
return parent;
}

+static int nfs_set_nfs4_acl(struct inode *inode, struct nfs4_acl *acl)
+{
+ const struct nfs_rpc_ops *rpc_ops = NFS_SERVER(inode)->nfs_client->rpc_ops;
+
+ if (rpc_ops->set_nfs4_acl == NULL)
+ return -EOPNOTSUPP;
+ return rpc_ops->set_nfs4_acl(inode, acl);
+}
+
const struct export_operations nfs_export_ops = {
.encode_fh = nfs_encode_fh,
.fh_to_dentry = nfs_fh_to_dentry,
.get_parent = nfs_get_parent,
+ .setacl = nfs_set_nfs4_acl,
.flags = EXPORT_OP_NOWCC |
EXPORT_OP_NOSUBTREECHK |
EXPORT_OP_CLOSE_BEFORE_UNLINK |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 91bcf67bd743..a71be8fcc893 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -6165,7 +6165,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen,
}

static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
- size_t buflen, enum nfs4_acl_type type)
+ size_t buflen, enum nfs4_acl_type type,
+ struct nfs4_acl *acl)
{
struct nfs_server *server = NFS_SERVER(inode);
struct page *pages[NFS4ACL_MAXPAGES];
@@ -6173,6 +6174,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
.fh = NFS_FH(inode),
.acl_type = type,
.acl_len = buflen,
+ .acl_pgbase = 0,
.acl_pages = pages,
};
struct nfs_setaclres res;
@@ -6184,6 +6186,15 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
int ret, i;

+ if (acl != NULL) {
+ arg.acl_len = acl->len;
+ arg.acl_pgbase = acl->pgbase;
+ arg.acl_pages = acl->pages;
+ nfs4_inode_make_writeable(inode);
+ ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
+ goto out;
+ }
+
/* You can't remove system.nfs4_acl: */
if (buflen == 0)
return -EINVAL;
@@ -6208,6 +6219,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
* Acl update can result in inode attribute update.
* so mark the attribute cache invalid.
*/
+out:
spin_lock(&inode->i_lock);
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
NFS_INO_INVALID_CTIME |
@@ -6219,7 +6231,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
}

static int nfs4_proc_set_acl(struct inode *inode, const void *buf,
- size_t buflen, enum nfs4_acl_type type)
+ size_t buflen, enum nfs4_acl_type type,
+ struct nfs4_acl *acl)
{
struct nfs4_exception exception = { };
int err;
@@ -6227,7 +6240,7 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf,
if (unlikely(NFS_FH(inode)->size == 0))
return -ENODATA;
do {
- err = __nfs4_proc_set_acl(inode, buf, buflen, type);
+ err = __nfs4_proc_set_acl(inode, buf, buflen, type, acl);
trace_nfs4_set_acl(inode, err);
if (err == -NFS4ERR_BADOWNER || err == -NFS4ERR_BADNAME) {
/*
@@ -6243,6 +6256,13 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf,
return err;
}

+static int nfs4_set_nfs4_acl(struct inode *inode, struct nfs4_acl *acl)
+{
+ if (!nfs4_server_supports_acls(NFS_SERVER(inode), acl->type))
+ return -EOPNOTSUPP;
+ return nfs4_proc_set_acl(inode, NULL, 0, acl->type, acl);
+}
+
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
static int _nfs4_get_security_label(struct inode *inode, void *buf,
size_t buflen)
@@ -7804,7 +7824,7 @@ static int nfs4_xattr_set_nfs4_acl(const struct xattr_handler *handler,
const char *key, const void *buf,
size_t buflen, int flags)
{
- return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_ACL);
+ return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_ACL, NULL);
}

static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler,
@@ -7827,7 +7847,7 @@ static int nfs4_xattr_set_nfs4_dacl(const struct xattr_handler *handler,
const char *key, const void *buf,
size_t buflen, int flags)
{
- return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_DACL);
+ return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_DACL, NULL);
}

static int nfs4_xattr_get_nfs4_dacl(const struct xattr_handler *handler,
@@ -7850,7 +7870,7 @@ static int nfs4_xattr_set_nfs4_sacl(const struct xattr_handler *handler,
const char *key, const void *buf,
size_t buflen, int flags)
{
- return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_SACL);
+ return nfs4_proc_set_acl(inode, buf, buflen, NFS4ACL_SACL, NULL);
}

static int nfs4_xattr_get_nfs4_sacl(const struct xattr_handler *handler,
@@ -10683,6 +10703,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.discover_trunking = nfs4_discover_trunking,
.enable_swap = nfs4_enable_swap,
.disable_swap = nfs4_disable_swap,
+ .set_nfs4_acl = nfs4_set_nfs4_acl,
};

static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index c23c2eee1b5c..cd01b1c6610a 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1722,7 +1722,7 @@ static void encode_setacl(struct xdr_stream *xdr,
encode_nfs4_stateid(xdr, &zero_stateid);
xdr_encode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
encode_uint32(xdr, arg->acl_len);
- xdr_write_pages(xdr, arg->acl_pages, 0, arg->acl_len);
+ xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
}

static void
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 3260d3116127..a86ed4532465 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -833,6 +833,7 @@ struct nfs_setaclargs {
struct nfs_fh * fh;
enum nfs4_acl_type acl_type;
size_t acl_len;
+ unsigned int acl_pgbase;
struct page ** acl_pages;
};

@@ -1763,6 +1764,7 @@ struct nfs_mount_info;
struct nfs_client_initdata;
struct nfs_pageio_descriptor;
struct fs_context;
+struct nfs4_acl;

/*
* RPC procedure vector for NFSv2/NFSv3 demuxing
@@ -1850,6 +1852,7 @@ struct nfs_rpc_ops {
int (*discover_trunking)(struct nfs_server *, struct nfs_fh *);
void (*enable_swap)(struct inode *inode);
void (*disable_swap)(struct inode *inode);
+ int (*set_nfs4_acl)(struct inode *, struct nfs4_acl *);
};

/*
--
2.44.0