public inbox for linux-nfs@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
@ 2026-02-24 19:24 Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 01/11] exportfs: add ability to advertise NFSv4 ACL passthru support Mike Snitzer
                   ` (12 more replies)
  0 siblings, 13 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

Hi,

This patchset aims to enable NFS v4.1 ACLs to be fully supported from
an NFS v4.1 client to an NFSD v4.1 export that reexports NFS v4.2
filesystem which has full support for NFS v4.1 ACLs (DACL and SACL).

The first 6 patches focus on nfs4_acl passthru enablement (primarily
for NFSD), patch 7 adds 4.1 nfs4_acl passthru support (DACL and SACL),
patch 8 optimizes particular nfs4_acl passthru implementation in NFSD
to skip memcpy if nfs4_acl passthru isn't needed, patches 9-11 offer
the corresponding required NFSv4 client changes.

This work is based on the nfsd-testing branch (commit 22f4955340fc).

This patchset is marked as RFC because I expect there will be
suggestions for possible NFSD implementation improvements.

All review appreciated, thanks.
Mike

v2: rebased v1 ontop of nfsd-testing commit 22f4955340fc

Mike Snitzer (11):
  exportfs: add ability to advertise NFSv4 ACL passthru support
  NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h
  NFS/NFSD: data structure enablement for nfs4_acl passthru support
  NFSD: prepare to support SETACL nfs4_acl passthru
  NFSD: add NFS4 reexport support for SETACL nfs4_acl passthru
  NFSD: add NFS4 reexport support for GETACL nfs4_acl passthru
  NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support
  NFSD: avoid extra nfs4_acl passthru work unless needed
  NFSv4: add reexport support for SETACL nfs4_acl passthru
  NFSv4: add reexport support for GETACL nfs4_acl passthru
  NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag

 fs/nfs/export.c          |  23 ++++-
 fs/nfs/nfs4proc.c        | 112 +++++++++++++++-------
 fs/nfs/nfs4xdr.c         |   2 +-
 fs/nfsd/acl.h            |  11 ++-
 fs/nfsd/nfs4acl.c        |  69 +++++++++++++-
 fs/nfsd/nfs4proc.c       |  32 +++++--
 fs/nfsd/nfs4xdr.c        | 194 +++++++++++++++++++++++++++++++++------
 fs/nfsd/nfsd.h           |   5 +-
 fs/nfsd/xdr4.h           |   2 +
 include/linux/exportfs.h |  22 +++++
 include/linux/nfs4.h     |  23 ++++-
 include/linux/nfs_xdr.h  |  11 +--
 include/linux/nfsacl.h   |   7 ++
 13 files changed, 431 insertions(+), 82 deletions(-)

-- 
2.44.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 01/11] exportfs: add ability to advertise NFSv4 ACL passthru support
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 02/11] NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h Mike Snitzer
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Add EXPORT_OP_NFSV4_ACL_PASSTHRU flag that an export should set if
relevant new methods are added to export_operations (e.g. .setacl and
.getacl which will be added in future commits).

NFSD will use exportfs_may_passthru_nfs4acl() to check for this flag
before passing nfs4_acl thru to exported FS (using new methods in
export_operations).

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 include/linux/exportfs.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 8bcdba28b406..7823c9693346 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -307,6 +307,7 @@ struct export_operations {
 						*/
 #define EXPORT_OP_FLUSH_ON_CLOSE	(0x20) /* fs flushes file data on close */
 #define EXPORT_OP_NOLOCKS		(0x40) /* no file locking support */
+#define EXPORT_OP_NFSV4_ACL_PASSTHRU	(0x80) /* fs MAY handle NFSv4 ACL passthru */
 	unsigned long	flags;
 };
 
@@ -322,6 +323,18 @@ exportfs_cannot_lock(const struct export_operations *export_ops)
 	return export_ops->flags & EXPORT_OP_NOLOCKS;
 }
 
+/**
+ * exportfs_may_passthru_nfs4acl() - check if export MAY passthru NFSv4 ACLs
+ * @export_ops:	the nfs export operations to check
+ *
+ * Returns true if the export MAY support NFSv4 ACL passthru.
+ */
+static inline bool
+exportfs_may_passthru_nfs4acl(const struct export_operations *export_ops)
+{
+	return export_ops->flags & EXPORT_OP_NFSV4_ACL_PASSTHRU;
+}
+
 extern int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
 				    int *max_len, struct inode *parent,
 				    int flags);
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 02/11] NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 01/11] exportfs: add ability to advertise NFSv4 ACL passthru support Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 03/11] NFS/NFSD: data structure enablement for nfs4_acl passthru support Mike Snitzer
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Check both IS_POSIXACL() and exportfs_may_passthru_nfs4acl() to
prepare for passing through nfs4_acl directly to NFSv4 filesystem
(NFSv4 will set EXPORT_OP_NFSV4_ACL_PASSTHRU flag in later commit).

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfsd/acl.h      | 8 ++++++++
 fs/nfsd/nfs4proc.c | 2 +-
 fs/nfsd/nfs4xdr.c  | 6 +++---
 3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 2003523d0e65..699a3b19bdb8 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -35,6 +35,8 @@
 #ifndef LINUX_NFS4_ACL_H
 #define LINUX_NFS4_ACL_H
 
+#include <linux/exportfs.h>
+
 struct nfs4_acl;
 struct svc_fh;
 struct svc_rqst;
@@ -51,4 +53,10 @@ __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
 			 struct nfsd_attrs *attr);
 void sort_pacl_range(struct posix_acl *pacl, int start, int end);
 
+static inline bool nfsd_supports_nfs4_acl(struct dentry *dentry)
+{
+	return IS_POSIXACL(d_inode(dentry)) ||
+		exportfs_may_passthru_nfs4acl(dentry->d_sb->s_export_op);
+}
+
 #endif /* LINUX_NFS4_ACL_H */
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 85e94c30285a..bd847db8b8b4 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -89,7 +89,7 @@ check_attr_support(struct nfsd4_compound_state *cstate, u32 *bmval,
 
 	if (!nfsd_attrs_supported(cstate->minorversion, bmval))
 		return nfserr_attrnotsupp;
-	if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry)))
+	if ((bmval[0] & FATTR4_WORD0_ACL) && !nfsd_supports_nfs4_acl(dentry))
 		return nfserr_attrnotsupp;
 	if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
 					FATTR4_WORD2_POSIX_ACCESS_ACL)) &&
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 07c48b74c559..e7793c53d214 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3195,7 +3195,7 @@ static __be32 nfsd4_encode_fattr4_supported_attrs(struct xdr_stream *xdr,
 	u32 supp[3];
 
 	memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
-	if (!IS_POSIXACL(d_inode(args->dentry)))
+	if (!nfsd_supports_nfs4_acl(args->dentry))
 		supp[0] &= ~FATTR4_WORD0_ACL;
 	if (!args->contextsupport)
 		supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
@@ -3328,7 +3328,7 @@ static __be32 nfsd4_encode_fattr4_aclsupport(struct xdr_stream *xdr,
 	u32 mask;
 
 	mask = 0;
-	if (IS_POSIXACL(d_inode(args->dentry)))
+	if (nfsd_supports_nfs4_acl(args->dentry))
 		mask = ACL4_SUPPORT_ALLOW_ACL | ACL4_SUPPORT_DENY_ACL;
 	return nfsd4_encode_uint32_t(xdr, mask);
 }
@@ -3600,7 +3600,7 @@ static __be32 nfsd4_encode_fattr4_suppattr_exclcreat(struct xdr_stream *xdr,
 	u32 supp[3];
 
 	memcpy(supp, nfsd_suppattrs[resp->cstate.minorversion], sizeof(supp));
-	if (!IS_POSIXACL(d_inode(args->dentry)))
+	if (!nfsd_supports_nfs4_acl(args->dentry))
 		supp[0] &= ~FATTR4_WORD0_ACL;
 	if (!args->contextsupport)
 		supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 03/11] NFS/NFSD: data structure enablement for nfs4_acl passthru support
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 01/11] exportfs: add ability to advertise NFSv4 ACL passthru support Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 02/11] NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 04/11] NFSD: prepare to support SETACL nfs4_acl passthru Mike Snitzer
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Add setacl and getacl to export_operations structure. Both of these
methods allow NFSD to pass an nfs4_acl structure to exported
filesystem.

Update the nfs4_acl structure to allow for dual use of the new
nfs4_acl passthru support in addition to its existing usage. This
duality is reflected through use of a union, but the xdr_buf payload
is used to initialize all members in the union needed for passthru (in
a later NFSD commit).

Move nfs4_acl_type structure to include/linux/nfsacl.h so that it can
be used by NFS and NFSD.

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 include/linux/exportfs.h |  9 +++++++++
 include/linux/nfs4.h     | 23 +++++++++++++++++++++--
 include/linux/nfs_xdr.h  |  7 -------
 include/linux/nfsacl.h   |  7 +++++++
 4 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 7823c9693346..9e3a818019f8 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -11,6 +11,7 @@ struct inode;
 struct iomap;
 struct super_block;
 struct vfsmount;
+struct nfs4_acl;
 
 /* limit the handle size to NFSv4 handle size now */
 #define MAX_HANDLE_SZ 128
@@ -269,6 +270,12 @@ struct handle_to_path_ctx {
  * @commit_blocks:
  *    Commit blocks in a layout once the client is done with them.
  *
+ * @setacl:
+ *    @setacl will use the nfs4_acl @acl to establish the acl using SETATTR.
+ *
+ * @getacl:
+ *    @getacl will use the nfs4_acl @acl to retrieve the acl using GETATTR.
+ *
  * @flags:
  *    Allows the filesystem to communicate to nfsd that it may want to do things
  *    differently when dealing with it.
@@ -298,6 +305,8 @@ struct export_operations {
 			     int nr_iomaps, struct iattr *iattr);
 	int (*permission)(struct handle_to_path_ctx *ctx, unsigned int oflags);
 	struct file * (*open)(const struct path *path, unsigned int oflags);
+	int (*setacl)(struct inode *inode, struct nfs4_acl *acl);
+	int (*getacl)(struct inode *inode, struct nfs4_acl *acl);
 #define	EXPORT_OP_NOWCC			(0x1) /* don't collect v3 wcc data */
 #define	EXPORT_OP_NOSUBTREECHK		(0x2) /* no subtree checking */
 #define	EXPORT_OP_CLOSE_BEFORE_UNLINK	(0x4) /* close files before unlink */
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index d87be1f25273..8cfa748c7efa 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -16,6 +16,7 @@
 #include <linux/list.h>
 #include <linux/uidgid.h>
 #include <uapi/linux/nfs4.h>
+#include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/xdrgen/nfs4_1.h>
 
@@ -38,8 +39,26 @@ struct nfs4_ace {
 };
 
 struct nfs4_acl {
-	uint32_t	naces;
-	struct nfs4_ace	aces[];
+	union {
+		struct {
+			/*
+			 * Payload to use for deferred decode into
+			 * pages once ACL passthru is required
+			 * (which uses abnormal members below).
+			 */
+			struct xdr_buf payload;
+			/* Normal counted list of ACEs */
+			uint32_t naces;
+			struct nfs4_ace	aces[];
+		} __attribute__ ((packed));
+		struct {
+			/* Abnormal counted list of pages */
+			uint32_t type;
+			uint32_t len;
+			uint32_t pgbase;
+			struct page *pages[];
+		} __attribute__ ((packed));
+	};
 };
 
 #define NFS4_MAXLABELLEN	2048
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index ff1f12aa73d2..3260d3116127 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -828,13 +828,6 @@ struct nfs_setattrargs {
 	const struct nfs4_label		*label;
 };
 
-enum nfs4_acl_type {
-	NFS4ACL_NONE = 0,
-	NFS4ACL_ACL,
-	NFS4ACL_DACL,
-	NFS4ACL_SACL,
-};
-
 struct nfs_setaclargs {
 	struct nfs4_sequence_args	seq_args;
 	struct nfs_fh *			fh;
diff --git a/include/linux/nfsacl.h b/include/linux/nfsacl.h
index 8e76a79cdc6a..a1941dc56db0 100644
--- a/include/linux/nfsacl.h
+++ b/include/linux/nfsacl.h
@@ -22,6 +22,13 @@
 #define NFS_ACL_MAX_ENTRIES_INLINE	(5)
 #define NFS_ACL_INLINE_BUFSIZE	((2*(2+3*NFS_ACL_MAX_ENTRIES_INLINE)) << 2)
 
+enum nfs4_acl_type {
+	NFS4ACL_NONE = 0,
+	NFS4ACL_ACL,
+	NFS4ACL_DACL,
+	NFS4ACL_SACL,
+};
+
 static inline unsigned int
 nfsacl_size(struct posix_acl *acl_access, struct posix_acl *acl_default)
 {
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 04/11] NFSD: prepare to support SETACL nfs4_acl passthru
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (2 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 03/11] NFS/NFSD: data structure enablement for nfs4_acl passthru support Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 05/11] NFSD: add NFS4 reexport support for " Mike Snitzer
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Use a separate xdr_buf and xdr_stream in both nfsd4_decode_acl() and
nfsd4_decode_nfsace4().

This prepares for nfsd4_decode_acl() to save off the xdr_buf
associated with the ACL payload so that deferred decode is possible
when nfs4_acl passthru is needed (in next commit).

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfsd/nfs4xdr.c | 57 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 37 insertions(+), 20 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index e7793c53d214..464c301d6655 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -288,31 +288,32 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
 }
 
 static __be32
-nfsd4_decode_nfsace4(struct nfsd4_compoundargs *argp, struct nfs4_ace *ace)
+nfsd4_decode_nfsace4(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+		     struct nfs4_ace *ace)
 {
 	__be32 *p, status;
 	u32 length;
 
-	if (xdr_stream_decode_u32(argp->xdr, &ace->type) < 0)
+	if (xdr_stream_decode_u32(xdr, &ace->type) < 0)
 		return nfserr_bad_xdr;
-	if (xdr_stream_decode_u32(argp->xdr, &ace->flag) < 0)
+	if (xdr_stream_decode_u32(xdr, &ace->flag) < 0)
 		return nfserr_bad_xdr;
-	if (xdr_stream_decode_u32(argp->xdr, &ace->access_mask) < 0)
+	if (xdr_stream_decode_u32(xdr, &ace->access_mask) < 0)
 		return nfserr_bad_xdr;
 
-	if (xdr_stream_decode_u32(argp->xdr, &length) < 0)
+	if (xdr_stream_decode_u32(xdr, &length) < 0)
 		return nfserr_bad_xdr;
-	p = xdr_inline_decode(argp->xdr, length);
+	p = xdr_inline_decode(xdr, length);
 	if (!p)
 		return nfserr_bad_xdr;
 	ace->whotype = nfs4_acl_get_whotype((char *)p, length);
 	if (ace->whotype != NFS4_ACL_WHO_NAMED)
 		status = nfs_ok;
 	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
-		status = nfsd_map_name_to_gid(argp->rqstp,
+		status = nfsd_map_name_to_gid(rqstp,
 				(char *)p, length, &ace->who_gid);
 	else
-		status = nfsd_map_name_to_uid(argp->rqstp,
+		status = nfsd_map_name_to_uid(rqstp,
 				(char *)p, length, &ace->who_uid);
 
 	return status;
@@ -320,35 +321,51 @@ nfsd4_decode_nfsace4(struct nfsd4_compoundargs *argp, struct nfs4_ace *ace)
 
 /* A counted array of nfsace4's */
 static noinline __be32
-nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl)
+nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl,
+		 u32 acl_len)
 {
+
+	struct xdr_buf payload;
+	struct xdr_stream xdr;
 	struct nfs4_ace *ace;
-	__be32 status;
+	__be32 status = nfs_ok;
 	u32 count;
 
-	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
+	if (!xdr_stream_subsegment(argp->xdr, &payload, acl_len))
 		return nfserr_bad_xdr;
 
-	if (count > xdr_stream_remaining(argp->xdr) / 20)
+	xdr_init_decode(&xdr, &payload, payload.head[0].iov_base, NULL);
+
+	if (xdr_stream_decode_u32(&xdr, &count) < 0) {
+		status = nfserr_bad_xdr;
+		goto out;
+	}
+
+	if (count > xdr_stream_remaining(&xdr) / 20) {
 		/*
 		 * Even with 4-byte names there wouldn't be
 		 * space for that many aces; something fishy is
 		 * going on:
 		 */
-		return nfserr_fbig;
+		status = nfserr_fbig;
+		goto out;
+	}
 
 	*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(count));
-	if (*acl == NULL)
-		return nfserr_jukebox;
+	if (*acl == NULL) {
+		status = nfserr_jukebox;
+		goto out;
+	}
 
 	(*acl)->naces = count;
 	for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) {
-		status = nfsd4_decode_nfsace4(argp, ace);
+		status = nfsd4_decode_nfsace4(&xdr, argp->rqstp, ace);
 		if (status)
-			return status;
+			goto out;
 	}
-
-	return nfs_ok;
+out:
+	xdr_finish_decode(&xdr);
+	return status;
 }
 
 static noinline __be32
@@ -514,7 +531,7 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
 		iattr->ia_valid |= ATTR_SIZE;
 	}
 	if (bmval[0] & FATTR4_WORD0_ACL) {
-		status = nfsd4_decode_acl(argp, acl);
+		status = nfsd4_decode_acl(argp, acl, attrlist4_count);
 		if (status)
 			return status;
 	} else
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 05/11] NFSD: add NFS4 reexport support for SETACL nfs4_acl passthru
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (3 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 04/11] NFSD: prepare to support SETACL nfs4_acl passthru Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 06/11] NFSD: add NFS4 reexport support for GETACL " Mike Snitzer
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Allow NFSD's 4.1 reexport of a 4.2 mount to perform SETACL by passing
thru nfs4_acl that was decoded from 4.1 client directly to 4.2
client.

Update nfsd4_decode_acl() to save the ACL's payload off to an xdr_buf
in the nfs4_acl. But only do the work to decode that ACL payload into
pages stored in the nfs4_acl, via nfsd4_decode_nfs4_acl_passthru(), if
the exported filesystem supports nfs4_acl passthru (like NFSv4 client
does).

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfsd/nfs4proc.c | 27 +++++++++++++++++++++------
 fs/nfsd/nfs4xdr.c  | 43 ++++++++++++++++++++++++++++++++++++++++++-
 fs/nfsd/xdr4.h     |  2 ++
 3 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index bd847db8b8b4..d05ac00f934e 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1224,6 +1224,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	};
 	bool save_no_wcc, deleg_attrs;
 	struct nfs4_stid *st = NULL;
+	struct dentry *dentry;
 	struct inode *inode;
 	__be32 status = nfs_ok;
 	int err;
@@ -1281,12 +1282,14 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		goto out;
 	}
 
-	inode = cstate->current_fh.fh_dentry->d_inode;
-	status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG,
-				   setattr->sa_acl, &attrs);
-
-	if (status)
-		goto out;
+	dentry = cstate->current_fh.fh_dentry;
+	inode = dentry->d_inode;
+	if (IS_POSIXACL(inode)) {
+		status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG,
+					   setattr->sa_acl, &attrs);
+		if (status)
+			goto out;
+	}
 	save_no_wcc = cstate->current_fh.fh_no_wcc;
 	cstate->current_fh.fh_no_wcc = true;
 	status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs, NULL);
@@ -1297,6 +1300,18 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		status = nfserrno(attrs.na_dpaclerr);
 	if (!status)
 		status = nfserrno(attrs.na_paclerr);
+	if (!status && !IS_POSIXACL(inode) && setattr->sa_acl &&
+	    exportfs_may_passthru_nfs4acl(dentry->d_sb->s_export_op)) {
+		const struct export_operations *ops = dentry->d_sb->s_export_op;
+		if (likely(ops->setacl)) {
+			status = nfsd4_decode_nfs4_acl_passthru(rqstp->rq_argp,
+					setattr->sa_bmval, &setattr->sa_acl);
+			if (status)
+				goto out;
+			status = nfserrno(ops->setacl(inode, setattr->sa_acl));
+		} else
+			status = nfserr_attrnotsupp;
+	}
 out:
 	fh_drop_write(&cstate->current_fh);
 out_err:
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 464c301d6655..62b1d79f80cb 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -287,6 +287,45 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
 	return status == -EBADMSG ? nfserr_bad_xdr : nfs_ok;
 }
 
+__be32 nfsd4_decode_nfs4_acl_passthru(struct nfsd4_compoundargs *argp,
+				      u32 *bmval, struct nfs4_acl **acl)
+{
+	u32 acl_len = (*acl)->payload.len;
+	unsigned int pgbase, num_pages;
+	struct xdr_stream xdr;
+	__be32 status = nfs_ok;
+	void *p;
+
+	xdr_init_decode(&xdr, &(*acl)->payload,
+			(*acl)->payload.head[0].iov_base, NULL);
+
+	p = xdr_inline_decode(&xdr, acl_len);
+	if (p == NULL) {
+		status = nfserr_bad_xdr;
+		goto out;
+	}
+
+	pgbase = (unsigned long)p & ~PAGE_MASK;
+	num_pages = DIV_ROUND_UP(pgbase + acl_len, PAGE_SIZE);
+
+	*acl = svcxdr_tmpalloc(argp, (sizeof(struct nfs4_acl) +
+				      num_pages * sizeof(struct page *)));
+	if (*acl == NULL) {
+		status = nfserr_jukebox;
+		goto out;
+	}
+
+	(*acl)->type = NFS4ACL_ACL;
+	(*acl)->len = acl_len;
+	(*acl)->pgbase = pgbase;
+
+	for (int i = 0; i < num_pages; i++)
+		(*acl)->pages[i] = virt_to_page(p + (i << PAGE_SHIFT));
+out:
+	xdr_finish_decode(&xdr);
+	return status;
+}
+
 static __be32
 nfsd4_decode_nfsace4(struct xdr_stream *xdr, struct svc_rqst *rqstp,
 		     struct nfs4_ace *ace)
@@ -325,7 +364,7 @@ nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl,
 		 u32 acl_len)
 {
 
-	struct xdr_buf payload;
+	struct xdr_buf payload, saved_payload;
 	struct xdr_stream xdr;
 	struct nfs4_ace *ace;
 	__be32 status = nfs_ok;
@@ -333,6 +372,7 @@ nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl,
 
 	if (!xdr_stream_subsegment(argp->xdr, &payload, acl_len))
 		return nfserr_bad_xdr;
+	memcpy(&saved_payload, &payload, sizeof(struct xdr_buf));
 
 	xdr_init_decode(&xdr, &payload, payload.head[0].iov_base, NULL);
 
@@ -356,6 +396,7 @@ nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl,
 		status = nfserr_jukebox;
 		goto out;
 	}
+	memcpy(&(*acl)->payload, &saved_payload, sizeof(struct xdr_buf));
 
 	(*acl)->naces = count;
 	for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) {
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 417e9ad9fbb3..d3561ce76a12 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -1011,6 +1011,8 @@ extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
 extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
 		struct nfsd4_compound_state *, union nfsd4_op_u *);
 extern void nfsd4_bump_seqid(struct nfsd4_compound_state *, __be32 nfserr);
+__be32 nfsd4_decode_nfs4_acl_passthru(struct nfsd4_compoundargs *,
+		u32 *bmval, struct nfs4_acl **acl);
 
 enum nfsd4_op_flags {
 	ALLOWED_WITHOUT_FH = 1 << 0,    /* No current filehandle required */
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 06/11] NFSD: add NFS4 reexport support for GETACL nfs4_acl passthru
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (4 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 05/11] NFSD: add NFS4 reexport support for " Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 07/11] NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support Mike Snitzer
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Allow NFSD's 4.1 reexport of a 4.2 mount to perform GETACL by passing
thru nfs4_acl whose pages are allocated in nfsd4_get_nfs4_acl_passthru
and then passed down to exported filesystem's ops->getacl(). Once
nfs4_acl is retrieved nfsd4_encode_fattr4_acl() will send the
ACL payload to the client using nfsd4_encode_nfs4_acl_passthru().

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfsd/acl.h     |  3 ++-
 fs/nfsd/nfs4acl.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++-
 fs/nfsd/nfs4xdr.c | 33 ++++++++++++++++++++++-
 3 files changed, 102 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 699a3b19bdb8..488be04551e4 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -42,13 +42,14 @@ struct svc_fh;
 struct svc_rqst;
 struct nfsd_attrs;
 enum nfs_ftype4;
+enum nfs4_acl_type;
 
 int nfs4_acl_bytes(int entries);
 int nfs4_acl_get_whotype(char *, u32);
 __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
 
 int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
-		struct nfs4_acl **acl);
+		enum nfs4_acl_type acl_type, struct nfs4_acl **acl);
 __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
 			 struct nfsd_attrs *attr);
 void sort_pacl_range(struct posix_acl *pacl, int start, int end);
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 2c2f2fd89e87..2d494909e63a 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -37,6 +37,7 @@
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/posix_acl.h>
+#include <linux/nfsacl.h>
 
 #include "nfsfh.h"
 #include "nfsd.h"
@@ -125,9 +126,62 @@ static short ace2type(struct nfs4_ace *);
 static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,
 				unsigned int);
 
+static int
+nfsd4_get_nfs4_acl_passthru(struct inode *inode,
+			    const struct export_operations *ops,
+			    enum nfs4_acl_type acl_type,
+			    u32 acl_len, struct nfs4_acl **acl)
+{
+	int error = 0;
+	int i = 0;
+	unsigned int npages;
+
+	npages = DIV_ROUND_UP(acl_len, PAGE_SIZE);
+	*acl = kmalloc(sizeof(struct nfs4_acl) +
+		       npages * sizeof(struct page *), GFP_KERNEL);
+	if (*acl == NULL)
+		return -ENOMEM;
+
+	(*acl)->type = acl_type;
+	(*acl)->len = acl_len = npages * PAGE_SIZE;
+	(*acl)->pgbase = 0;
+
+	for (; i < npages; i++) {
+		(*acl)->pages[i] = alloc_page(GFP_KERNEL);
+		if (!(*acl)->pages[i]) {
+			error = -ENOMEM;
+			goto out;
+		}
+	}
+
+	if (unlikely(!ops->getacl)) {
+		error = -EOPNOTSUPP;
+		goto out;
+	}
+
+	error = ops->getacl(inode, *acl);
+	if (likely(error > 0)) {
+		error = 0; /* don't error out below */
+		if ((*acl)->len < acl_len) {
+			/* free any unused pages */
+			npages = DIV_ROUND_UP((*acl)->len, PAGE_SIZE);
+			while (--i >= npages)
+				__free_page((*acl)->pages[i]);
+		}
+	}
+out:
+	if (error) {
+		while (--i >= 0)
+			__free_page((*acl)->pages[i]);
+		kfree(*acl);
+		*acl = NULL;
+	}
+	return error;
+}
+
 int
 nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
-		struct nfs4_acl **acl)
+		   enum nfs4_acl_type acl_type, struct nfs4_acl **acl)
 {
 	struct inode *inode = d_inode(dentry);
 	int error = 0;
@@ -157,6 +211,19 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
 			size += 2 * dpacl->a_count;
 	}
 
+	if (!IS_POSIXACL(inode) &&
+	    exportfs_may_passthru_nfs4acl(dentry->d_sb->s_export_op)) {
+		/* Ensure NFSv4 ACL has adequate space based on POSIX ACL size */
+		u32 acl_len = min_t(u32, svc_max_payload(rqstp),
+				    (2 * nfs4_acl_bytes(size) -
+				     2 * sizeof(struct nfs4_acl)));
+		const struct export_operations *ops = dentry->d_sb->s_export_op;
+
+		error = nfsd4_get_nfs4_acl_passthru(inode, ops, acl_type,
+						    acl_len, acl);
+		goto out;
+	}
+
 	*acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
 	if (*acl == NULL) {
 		error = -ENOMEM;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 62b1d79f80cb..80d2e7ea8cc9 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3391,6 +3391,33 @@ static __be32 nfsd4_encode_fattr4_aclsupport(struct xdr_stream *xdr,
 	return nfsd4_encode_uint32_t(xdr, mask);
 }
 
+static __be32 nfsd4_encode_nfs4_acl_passthru(struct xdr_stream *xdr,
+					     struct nfs4_acl *acl)
+{
+	uint32_t pgbase = acl->pgbase;
+	uint32_t remaining = acl->len;
+	unsigned int npages = DIV_ROUND_UP(remaining, PAGE_SIZE);
+
+	for (int i = 0; i < npages; i++) {
+		void *vaddr = page_address(acl->pages[i]);
+		size_t len = (remaining < PAGE_SIZE) ? remaining : PAGE_SIZE;
+
+		if (pgbase) {
+			vaddr += pgbase;
+			pgbase = 0;
+		}
+		WARN_ON_ONCE(xdr_stream_encode_opaque_fixed(xdr, vaddr, len) < 0);
+		remaining -= len;
+		/*
+		 * Free each page that was allocated using alloc_page()
+		 * in nfsd4_get_nfs4_acl_passthru().
+		 */
+		__free_page(acl->pages[i]);
+	}
+
+	return nfs_ok;
+}
+
 static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr,
 				      const struct nfsd4_fattr_args *args)
 {
@@ -3403,6 +3430,10 @@ static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr,
 		if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
 			return nfserr_resource;
 	} else {
+		if (!IS_POSIXACL(d_inode(args->dentry)) &&
+		    exportfs_may_passthru_nfs4acl(args->dentry->d_sb->s_export_op))
+			return nfsd4_encode_nfs4_acl_passthru(xdr, acl);
+
 		if (xdr_stream_encode_u32(xdr, acl->naces) != XDR_UNIT)
 			return nfserr_resource;
 		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
@@ -4028,7 +4059,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 		args.fhp = fhp;
 
 	if (attrmask[0] & FATTR4_WORD0_ACL) {
-		err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
+		err = nfsd4_get_nfs4_acl(rqstp, dentry, NFS4ACL_ACL, &args.acl);
 		if (err == -EOPNOTSUPP)
 			attrmask[0] &= ~FATTR4_WORD0_ACL;
 		else if (err == -EINVAL) {
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 07/11] NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (5 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 06/11] NFSD: add NFS4 reexport support for GETACL " Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 08/11] NFSD: avoid extra nfs4_acl passthru work unless needed Mike Snitzer
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

This 4.1 DACL and SACL support is confined to NFSD's NFS reexport case
(e.g. when NFSD 4.1 reexports NFS 4.2).

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfsd/nfs4proc.c |  3 +++
 fs/nfsd/nfs4xdr.c  | 49 ++++++++++++++++++++++++++++++++++++++--------
 fs/nfsd/nfsd.h     |  5 +++--
 3 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index d05ac00f934e..4a43e5052deb 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -91,6 +91,9 @@ check_attr_support(struct nfsd4_compound_state *cstate, u32 *bmval,
 		return nfserr_attrnotsupp;
 	if ((bmval[0] & FATTR4_WORD0_ACL) && !nfsd_supports_nfs4_acl(dentry))
 		return nfserr_attrnotsupp;
+	if ((bmval[1] & (FATTR4_WORD1_DACL | FATTR4_WORD1_SACL)) &&
+	    !nfsd_supports_nfs4_acl(dentry))
+		return nfserr_attrnotsupp;
 	if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
 					FATTR4_WORD2_POSIX_ACCESS_ACL)) &&
 					!IS_POSIXACL(d_inode(dentry)))
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 80d2e7ea8cc9..99db768ad97e 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -315,7 +315,13 @@ __be32 nfsd4_decode_nfs4_acl_passthru(struct nfsd4_compoundargs *argp,
 		goto out;
 	}
 
-	(*acl)->type = NFS4ACL_ACL;
+	if (bmval[0] & FATTR4_WORD0_ACL)
+		(*acl)->type = NFS4ACL_ACL;
+	else if (bmval[1] & FATTR4_WORD1_DACL)
+		(*acl)->type = NFS4ACL_DACL;
+	else if (bmval[1] & FATTR4_WORD1_SACL)
+		(*acl)->type = NFS4ACL_SACL;
+
 	(*acl)->len = acl_len;
 	(*acl)->pgbase = pgbase;
 
@@ -571,7 +577,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
 		iattr->ia_size = size;
 		iattr->ia_valid |= ATTR_SIZE;
 	}
-	if (bmval[0] & FATTR4_WORD0_ACL) {
+	if (bmval[0] & FATTR4_WORD0_ACL ||
+	    (bmval[1] & (FATTR4_WORD1_DACL | FATTR4_WORD1_SACL))) {
 		status = nfsd4_decode_acl(argp, acl, attrlist4_count);
 		if (status)
 			return status;
@@ -3253,8 +3260,12 @@ static __be32 nfsd4_encode_fattr4_supported_attrs(struct xdr_stream *xdr,
 	u32 supp[3];
 
 	memcpy(supp, nfsd_suppattrs[minorversion], sizeof(supp));
-	if (!nfsd_supports_nfs4_acl(args->dentry))
-		supp[0] &= ~FATTR4_WORD0_ACL;
+	if (!nfsd_supports_nfs4_acl(args->dentry)) {
+		if (supp[0] & FATTR4_WORD0_ACL)
+			supp[0] &= ~FATTR4_WORD0_ACL;
+		else if ((supp[1] & (FATTR4_WORD1_DACL | FATTR4_WORD1_SACL)))
+			supp[1] &= ~(FATTR4_WORD1_DACL | FATTR4_WORD1_SACL);
+	}
 	if (!args->contextsupport)
 		supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
 
@@ -3689,8 +3700,12 @@ static __be32 nfsd4_encode_fattr4_suppattr_exclcreat(struct xdr_stream *xdr,
 	u32 supp[3];
 
 	memcpy(supp, nfsd_suppattrs[resp->cstate.minorversion], sizeof(supp));
-	if (!nfsd_supports_nfs4_acl(args->dentry))
-		supp[0] &= ~FATTR4_WORD0_ACL;
+	if (!nfsd_supports_nfs4_acl(args->dentry)) {
+		if (supp[0] & FATTR4_WORD0_ACL)
+			supp[0] &= ~FATTR4_WORD0_ACL;
+		else if ((supp[1] & (FATTR4_WORD1_DACL | FATTR4_WORD1_SACL)))
+			supp[1] &= ~(FATTR4_WORD1_DACL | FATTR4_WORD1_SACL);
+	}
 	if (!args->contextsupport)
 		supp[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
 
@@ -3879,8 +3894,8 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
 	[FATTR4_MOUNTED_ON_FILEID]	= nfsd4_encode_fattr4_mounted_on_fileid,
 	[FATTR4_DIR_NOTIF_DELAY]	= nfsd4_encode_fattr4__noop,
 	[FATTR4_DIRENT_NOTIF_DELAY]	= nfsd4_encode_fattr4__noop,
-	[FATTR4_DACL]			= nfsd4_encode_fattr4__noop,
-	[FATTR4_SACL]			= nfsd4_encode_fattr4__noop,
+	[FATTR4_DACL]			= nfsd4_encode_fattr4_acl,
+	[FATTR4_SACL]			= nfsd4_encode_fattr4_acl,
 	[FATTR4_CHANGE_POLICY]		= nfsd4_encode_fattr4__noop,
 	[FATTR4_FS_STATUS]		= nfsd4_encode_fattr4__noop,
 
@@ -4067,6 +4082,24 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 			goto out;
 		} else if (err != 0)
 			goto out_nfserr;
+	} else if (attrmask[1] & FATTR4_WORD1_DACL) {
+		err = nfsd4_get_nfs4_acl(rqstp, dentry, NFS4ACL_DACL, &args.acl);
+		if (err == -EOPNOTSUPP)
+			attrmask[1] &= ~FATTR4_WORD1_DACL;
+		else if (err == -EINVAL) {
+			status = nfserr_attrnotsupp;
+			goto out;
+		} else if (err != 0)
+			goto out_nfserr;
+	} else if (attrmask[1] & FATTR4_WORD1_SACL) {
+		err = nfsd4_get_nfs4_acl(rqstp, dentry, NFS4ACL_SACL, &args.acl);
+		if (err == -EOPNOTSUPP)
+			attrmask[1] &= ~FATTR4_WORD1_SACL;
+		else if (err == -EINVAL) {
+			status = nfserr_attrnotsupp;
+			goto out;
+		} else if (err != 0)
+			goto out_nfserr;
 	}
 
 	args.contextsupport = false;
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 7c009f07c90b..0f2aaabf4f8c 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -447,7 +447,8 @@ enum {
 	NFSD4_SUPPORTED_ATTRS_WORD0
 
 #define NFSD4_1_SUPPORTED_ATTRS_WORD1 \
-	(NFSD4_SUPPORTED_ATTRS_WORD1	| PNFSD_SUPPORTED_ATTRS_WORD1)
+	(NFSD4_SUPPORTED_ATTRS_WORD1	| PNFSD_SUPPORTED_ATTRS_WORD1 | \
+	 FATTR4_WORD1_DACL | FATTR4_WORD1_SACL)
 
 #define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
 	(NFSD4_SUPPORTED_ATTRS_WORD2	| PNFSD_SUPPORTED_ATTRS_WORD2 | \
@@ -540,7 +541,7 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
 #define NFSD_WRITEABLE_ATTRS_WORD1 \
 	(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
 	| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_CREATE \
-	| FATTR4_WORD1_TIME_MODIFY_SET)
+	| FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_DACL | FATTR4_WORD1_SACL)
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 #define MAYBE_FATTR4_WORD2_SECURITY_LABEL \
 	FATTR4_WORD2_SECURITY_LABEL
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 08/11] NFSD: avoid extra nfs4_acl passthru work unless needed
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (6 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 07/11] NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 09/11] NFSv4: add reexport support for SETACL nfs4_acl passthru Mike Snitzer
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

Avoid the nfsd4_decode_acl()'s memcpy() work, that
nfsd4_decode_nfs4_acl_passthru() requires, unless
nfsd4_encode_nfs4_acl_passthru() has already been called.

Implemented by enabling and checking a static_key.

Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfsd/nfs4xdr.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 99db768ad97e..78941125eaef 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -287,6 +287,8 @@ nfsd4_decode_bitmap4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen)
 	return status == -EBADMSG ? nfserr_bad_xdr : nfs_ok;
 }
 
+static DEFINE_STATIC_KEY_FALSE(nfs4_acl_passthru);
+
 __be32 nfsd4_decode_nfs4_acl_passthru(struct nfsd4_compoundargs *argp,
 				      u32 *bmval, struct nfs4_acl **acl)
 {
@@ -296,6 +298,9 @@ __be32 nfsd4_decode_nfs4_acl_passthru(struct nfsd4_compoundargs *argp,
 	__be32 status = nfs_ok;
 	void *p;
 
+	if (WARN_ON_ONCE(!static_key_enabled(&nfs4_acl_passthru.key)))
+		return nfserr_resource;
+
 	xdr_init_decode(&xdr, &(*acl)->payload,
 			(*acl)->payload.head[0].iov_base, NULL);
 
@@ -378,8 +383,8 @@ nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl,
 
 	if (!xdr_stream_subsegment(argp->xdr, &payload, acl_len))
 		return nfserr_bad_xdr;
-	memcpy(&saved_payload, &payload, sizeof(struct xdr_buf));
-
+	if (static_key_enabled(&nfs4_acl_passthru.key))
+		memcpy(&saved_payload, &payload, sizeof(struct xdr_buf));
 	xdr_init_decode(&xdr, &payload, payload.head[0].iov_base, NULL);
 
 	if (xdr_stream_decode_u32(&xdr, &count) < 0) {
@@ -402,7 +407,9 @@ nfsd4_decode_acl(struct nfsd4_compoundargs *argp, struct nfs4_acl **acl,
 		status = nfserr_jukebox;
 		goto out;
 	}
-	memcpy(&(*acl)->payload, &saved_payload, sizeof(struct xdr_buf));
+	if (static_key_enabled(&nfs4_acl_passthru.key))
+		memcpy(&(*acl)->payload, &saved_payload,
+		       sizeof(struct xdr_buf));
 
 	(*acl)->naces = count;
 	for (ace = (*acl)->aces; ace < (*acl)->aces + count; ace++) {
@@ -3409,6 +3416,13 @@ static __be32 nfsd4_encode_nfs4_acl_passthru(struct xdr_stream *xdr,
 	uint32_t remaining = acl->len;
 	unsigned int npages = DIV_ROUND_UP(remaining, PAGE_SIZE);
 
+	/*
+	 * GETACL will precede SETACL, if passthru is used for
+	 * GETACL then enable SETACL's extra work required
+	 * to support passthru.
+	 */
+	static_key_enable(&nfs4_acl_passthru.key);
+
 	for (int i = 0; i < npages; i++) {
 		void *vaddr = page_address(acl->pages[i]);
 		size_t len = (remaining < PAGE_SIZE) ? remaining : PAGE_SIZE;
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 09/11] NFSv4: add reexport support for SETACL nfs4_acl passthru
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (7 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 08/11] NFSD: avoid extra nfs4_acl passthru work unless needed Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 10/11] NFSv4: add reexport support for GETACL " Mike Snitzer
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

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


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 10/11] NFSv4: add reexport support for GETACL nfs4_acl passthru
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (8 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 09/11] NFSv4: add reexport support for SETACL nfs4_acl passthru Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 19:24 ` [RFC PATCH v2 11/11] NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag Mike Snitzer
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

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

In this reexporting case (e.g. 4.1 reexporting 4.2),
__nfs4_get_acl_uncached() is always used because the NFSv4 client's
cache is designed to reply with the buffer that gets generated from
the ACL's pages. So the frontend client (4.1) will cache the ACL
returned by the backend (4.2) client.

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfs/export.c         | 10 ++++++
 fs/nfs/nfs4proc.c       | 79 ++++++++++++++++++++++++++++-------------
 include/linux/nfs_xdr.h |  1 +
 3 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/fs/nfs/export.c b/fs/nfs/export.c
index 9a90eee3e433..6623eb13f4e6 100644
--- a/fs/nfs/export.c
+++ b/fs/nfs/export.c
@@ -161,11 +161,21 @@ static int nfs_set_nfs4_acl(struct inode *inode, struct nfs4_acl *acl)
 	return rpc_ops->set_nfs4_acl(inode, acl);
 }
 
+static int nfs_get_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->get_nfs4_acl == NULL)
+		return -EOPNOTSUPP;
+	return rpc_ops->get_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,
+	.getacl = nfs_get_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 a71be8fcc893..7d535787d824 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -6045,7 +6045,8 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages,
  * the server, this time with the input buf of the required size.
  */
 static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf,
-				       size_t buflen, enum nfs4_acl_type type)
+				       size_t buflen, enum nfs4_acl_type type,
+				       struct nfs4_acl *acl)
 {
 	struct page **pages;
 	struct nfs_getaclargs args = {
@@ -6069,26 +6070,29 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf,
 	if (buflen == 0)
 		buflen = server->rsize;
 
-	npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1;
-	pages = kmalloc_objs(struct page *, npages);
-	if (!pages)
-		return -ENOMEM;
+	if (!acl) {
+		npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1;
+		pages = kmalloc_objs(struct page *, npages);
+		if (!pages)
+			return -ENOMEM;
+		for (i = 0; i < npages; i++) {
+			pages[i] = alloc_page(GFP_KERNEL);
+			if (!pages[i])
+				goto out_free;
+		}
+		args.acl_len = npages * PAGE_SIZE;
+	} else {
+		npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
+		pages = acl->pages;
+	}
 
 	args.acl_pages = pages;
 
-	for (i = 0; i < npages; i++) {
-		pages[i] = alloc_page(GFP_KERNEL);
-		if (!pages[i])
-			goto out_free;
-	}
-
 	/* for decoding across pages */
 	res.acl_scratch = folio_alloc(GFP_KERNEL, 0);
 	if (!res.acl_scratch)
 		goto out_free;
 
-	args.acl_len = npages * PAGE_SIZE;
-
 	dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
 		__func__, buf, buflen, npages, args.acl_len);
 	ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
@@ -6104,8 +6108,18 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf,
 		ret = -ERANGE;
 		goto out_free;
 	}
-	nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len,
-			      type);
+
+	if (!acl) {
+		nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
+				      res.acl_len, type);
+	} else {
+		if (res.acl_len > buflen) {
+			ret = -ERANGE;
+			goto out_free;
+		}
+		acl->len = res.acl_len;
+		acl->pgbase = res.acl_data_offset;
+	}
 	if (buf) {
 		if (res.acl_len > buflen) {
 			ret = -ERANGE;
@@ -6116,23 +6130,26 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf,
 out_ok:
 	ret = res.acl_len;
 out_free:
-	while (--i >= 0)
-		__free_page(pages[i]);
+	if (!acl) {
+		while (--i >= 0)
+			__free_page(pages[i]);
+		kfree(pages);
+	}
 	if (res.acl_scratch)
 		folio_put(res.acl_scratch);
-	kfree(pages);
 	return ret;
 }
 
 static ssize_t nfs4_get_acl_uncached(struct inode *inode, 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 = {
 		.interruptible = true,
 	};
 	ssize_t ret;
 	do {
-		ret = __nfs4_get_acl_uncached(inode, buf, buflen, type);
+		ret = __nfs4_get_acl_uncached(inode, buf, buflen, type, acl);
 		trace_nfs4_get_acl(inode, ret);
 		if (ret >= 0)
 			break;
@@ -6142,7 +6159,7 @@ static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf,
 }
 
 static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen,
-				 enum nfs4_acl_type type)
+				 enum nfs4_acl_type type, struct nfs4_acl *acl)
 {
 	struct nfs_server *server = NFS_SERVER(inode);
 	int ret;
@@ -6156,12 +6173,23 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen,
 		return ret;
 	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
 		nfs_zap_acl_cache(inode);
+	/* Must get ACL if @acl != NULL, frontend NFS layer will cache it */
+	if (acl != NULL)
+		goto get_acl_uncached;
 	ret = nfs4_read_cached_acl(inode, buf, buflen, type);
 	if (ret != -ENOENT)
 		/* -ENOENT is returned if there is no ACL or if there is an ACL
 		 * but no cached acl data, just the acl length */
 		return ret;
-	return nfs4_get_acl_uncached(inode, buf, buflen, type);
+get_acl_uncached:
+	return nfs4_get_acl_uncached(inode, buf, buflen, type, acl);
+}
+
+static int nfs4_get_nfs4_acl(struct inode *inode, struct nfs4_acl *acl)
+{
+	if (!nfs4_server_supports_acls(NFS_SERVER(inode), acl->type))
+		return -EOPNOTSUPP;
+	return nfs4_proc_get_acl(inode, NULL, acl->len, acl->type, acl);
 }
 
 static int __nfs4_proc_set_acl(struct inode *inode, const void *buf,
@@ -7831,7 +7859,7 @@ static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler,
 				   struct dentry *unused, struct inode *inode,
 				   const char *key, void *buf, size_t buflen)
 {
-	return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_ACL);
+	return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_ACL, NULL);
 }
 
 static bool nfs4_xattr_list_nfs4_acl(struct dentry *dentry)
@@ -7854,7 +7882,7 @@ static int nfs4_xattr_get_nfs4_dacl(const struct xattr_handler *handler,
 				    struct dentry *unused, struct inode *inode,
 				    const char *key, void *buf, size_t buflen)
 {
-	return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_DACL);
+	return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_DACL, NULL);
 }
 
 static bool nfs4_xattr_list_nfs4_dacl(struct dentry *dentry)
@@ -7877,7 +7905,7 @@ static int nfs4_xattr_get_nfs4_sacl(const struct xattr_handler *handler,
 				    struct dentry *unused, struct inode *inode,
 				    const char *key, void *buf, size_t buflen)
 {
-	return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_SACL);
+	return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_SACL, NULL);
 }
 
 static bool nfs4_xattr_list_nfs4_sacl(struct dentry *dentry)
@@ -10704,6 +10732,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
 	.enable_swap	= nfs4_enable_swap,
 	.disable_swap	= nfs4_disable_swap,
 	.set_nfs4_acl	= nfs4_set_nfs4_acl,
+	.get_nfs4_acl	= nfs4_get_nfs4_acl,
 };
 
 static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index a86ed4532465..5d629ad9781f 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1853,6 +1853,7 @@ struct nfs_rpc_ops {
 	void	(*enable_swap)(struct inode *inode);
 	void	(*disable_swap)(struct inode *inode);
 	int	(*set_nfs4_acl)(struct inode *, struct nfs4_acl *);
+	int	(*get_nfs4_acl)(struct inode *, struct nfs4_acl *);
 };
 
 /*
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 11/11] NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (9 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 10/11] NFSv4: add reexport support for GETACL " Mike Snitzer
@ 2026-02-24 19:24 ` Mike Snitzer
  2026-02-24 21:58 ` [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Chuck Lever
  2026-03-10 13:26 ` Christoph Hellwig
  12 siblings, 0 replies; 20+ messages in thread
From: Mike Snitzer @ 2026-02-24 19:24 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker; +Cc: linux-nfs

From: Mike Snitzer <snitzer@hammerspace.com>

NFSv4 implements both .setacl and .getacl and so it is appropriate to
enable nfs4_acl passthru.

Signed-off-by: Mike Snitzer <snitzer@hammerspace.com>
---
 fs/nfs/export.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/nfs/export.c b/fs/nfs/export.c
index 6623eb13f4e6..f59839b48721 100644
--- a/fs/nfs/export.c
+++ b/fs/nfs/export.c
@@ -182,5 +182,6 @@ const struct export_operations nfs_export_ops = {
 		 EXPORT_OP_REMOTE_FS		|
 		 EXPORT_OP_NOATOMIC_ATTR	|
 		 EXPORT_OP_FLUSH_ON_CLOSE	|
-		 EXPORT_OP_NOLOCKS,
+		 EXPORT_OP_NOLOCKS              |
+		 EXPORT_OP_NFSV4_ACL_PASSTHRU,
 };
-- 
2.44.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (10 preceding siblings ...)
  2026-02-24 19:24 ` [RFC PATCH v2 11/11] NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag Mike Snitzer
@ 2026-02-24 21:58 ` Chuck Lever
  2026-02-25 16:53   ` Mike Snitzer
  2026-03-10 13:26 ` Christoph Hellwig
  12 siblings, 1 reply; 20+ messages in thread
From: Chuck Lever @ 2026-02-24 21:58 UTC (permalink / raw)
  To: Mike Snitzer, Chuck Lever, Jeff Layton, Trond Myklebust,
	Anna Schumaker
  Cc: linux-nfs


On Tue, Feb 24, 2026, at 2:24 PM, Mike Snitzer wrote:
> Hi,
>
> This patchset aims to enable NFS v4.1 ACLs to be fully supported from
> an NFS v4.1 client to an NFSD v4.1 export that reexports NFS v4.2
> filesystem which has full support for NFS v4.1 ACLs (DACL and SACL).
>
> The first 6 patches focus on nfs4_acl passthru enablement (primarily
> for NFSD), patch 7 adds 4.1 nfs4_acl passthru support (DACL and SACL),
> patch 8 optimizes particular nfs4_acl passthru implementation in NFSD
> to skip memcpy if nfs4_acl passthru isn't needed, patches 9-11 offer
> the corresponding required NFSv4 client changes.
>
> This work is based on the nfsd-testing branch (commit 22f4955340fc).
>
> This patchset is marked as RFC because I expect there will be
> suggestions for possible NFSD implementation improvements.
>
> All review appreciated, thanks.
> Mike
>
> v2: rebased v1 ontop of nfsd-testing commit 22f4955340fc
>
> Mike Snitzer (11):
>   exportfs: add ability to advertise NFSv4 ACL passthru support
>   NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h
>   NFS/NFSD: data structure enablement for nfs4_acl passthru support
>   NFSD: prepare to support SETACL nfs4_acl passthru
>   NFSD: add NFS4 reexport support for SETACL nfs4_acl passthru
>   NFSD: add NFS4 reexport support for GETACL nfs4_acl passthru
>   NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support
>   NFSD: avoid extra nfs4_acl passthru work unless needed
>   NFSv4: add reexport support for SETACL nfs4_acl passthru
>   NFSv4: add reexport support for GETACL nfs4_acl passthru
>   NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag
>
>  fs/nfs/export.c          |  23 ++++-
>  fs/nfs/nfs4proc.c        | 112 +++++++++++++++-------
>  fs/nfs/nfs4xdr.c         |   2 +-
>  fs/nfsd/acl.h            |  11 ++-
>  fs/nfsd/nfs4acl.c        |  69 +++++++++++++-
>  fs/nfsd/nfs4proc.c       |  32 +++++--
>  fs/nfsd/nfs4xdr.c        | 194 +++++++++++++++++++++++++++++++++------
>  fs/nfsd/nfsd.h           |   5 +-
>  fs/nfsd/xdr4.h           |   2 +
>  include/linux/exportfs.h |  22 +++++
>  include/linux/nfs4.h     |  23 ++++-
>  include/linux/nfs_xdr.h  |  11 +--
>  include/linux/nfsacl.h   |   7 ++
>  13 files changed, 431 insertions(+), 82 deletions(-)
>
> -- 
> 2.44.0

So what you want is indeed pass-through, it's not support for
local file systems with NFSv4 ACL. NFSD is not only leaving the
ACEs alone, but it is also not doing any idmapping... and the
NFS client currently has nothing at all that deals with NFSv4
ACLs. So that explains the raw byte pass-through design.

Passing pages of raw wire data between client and server is
awkward, however, and has resulted in correctness bugs and
architectural issues (found during review). It would be faster
for us to start with proper support in NFSD for:

6.2.2.  Attribute 58: dacl

   The dacl attribute is like the acl attribute, but dacl allows just
   ALLOW and DENY ACEs.  The dacl attribute supports automatic
   inheritance (see Section 6.4.3.2).

6.2.3.  Attribute 59: sacl

   The sacl attribute is like the acl attribute, but sacl allows just
   AUDIT and ALARM ACEs.  The sacl attribute supports automatic
   inheritance (see Section 6.4.3.2).

This subfeature can get worked out and merged while we sort through
the API contract issues with ACL pass-through.

Current NFSD support for DACL/SACL:

- FATTR4_DACL (bit 58) and FATTR4_SACL (bit 59) are defined in
  `include/uapi/linux/nfs4.h` but not in any supported attrs mask
- Dispatch table (`fs/nfsd/nfs4xdr.c`):
      [FATTR4_DACL] = nfsd4_encode_fattr4__noop,
      [FATTR4_SACL] = nfsd4_encode_fattr4__noop,
- Not decoded in nfsd4_decode_fattr4()
- Not in NFSD_WRITEABLE_ATTRS_WORD1 (`fs/nfsd/nfsd.h`)

Patch sequence might look something like:

 1. NFSD: add acl_flags to struct nfs4_acl for nfsacl41
 2. NFSD: add DACL/SACL decode at correct fattr4 bit position
 3. NFSD: handle nfsacl41 wire format (aclflag4 + aces) in decode
 4. NFSD: add nfsacl41 encode for DACL/SACL responses
 5. NFSD: clear ACL, DACL, and SACL together in supported_attrs
 6. NFSD: add DACL/SACL to supported and writable attribute masks


For the API between NFSD and the local NFS client, I might favor an
alternative to new export operations: NFSD can set "system.nfs4_acl"
xattrs on NFS client inodes. The only issue there is it limits the
total number of bytes to 64K. But we can worry about that once the
DACL/SACL attributes are implemented.

-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-02-24 21:58 ` [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Chuck Lever
@ 2026-02-25 16:53   ` Mike Snitzer
  2026-02-25 18:21     ` Chuck Lever
  0 siblings, 1 reply; 20+ messages in thread
From: Mike Snitzer @ 2026-02-25 16:53 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker,
	linux-nfs

On Tue, Feb 24, 2026 at 04:58:10PM -0500, Chuck Lever wrote:
> 
> On Tue, Feb 24, 2026, at 2:24 PM, Mike Snitzer wrote:
> > Hi,
> >
> > This patchset aims to enable NFS v4.1 ACLs to be fully supported from
> > an NFS v4.1 client to an NFSD v4.1 export that reexports NFS v4.2
> > filesystem which has full support for NFS v4.1 ACLs (DACL and SACL).
> >
> > The first 6 patches focus on nfs4_acl passthru enablement (primarily
> > for NFSD), patch 7 adds 4.1 nfs4_acl passthru support (DACL and SACL),
> > patch 8 optimizes particular nfs4_acl passthru implementation in NFSD
> > to skip memcpy if nfs4_acl passthru isn't needed, patches 9-11 offer
> > the corresponding required NFSv4 client changes.
> >
> > This work is based on the nfsd-testing branch (commit 22f4955340fc).
> >
> > This patchset is marked as RFC because I expect there will be
> > suggestions for possible NFSD implementation improvements.
> >
> > All review appreciated, thanks.
> > Mike
> >
> > v2: rebased v1 ontop of nfsd-testing commit 22f4955340fc
> >
> > Mike Snitzer (11):
> >   exportfs: add ability to advertise NFSv4 ACL passthru support
> >   NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h
> >   NFS/NFSD: data structure enablement for nfs4_acl passthru support
> >   NFSD: prepare to support SETACL nfs4_acl passthru
> >   NFSD: add NFS4 reexport support for SETACL nfs4_acl passthru
> >   NFSD: add NFS4 reexport support for GETACL nfs4_acl passthru
> >   NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support
> >   NFSD: avoid extra nfs4_acl passthru work unless needed
> >   NFSv4: add reexport support for SETACL nfs4_acl passthru
> >   NFSv4: add reexport support for GETACL nfs4_acl passthru
> >   NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag
> >
> >  fs/nfs/export.c          |  23 ++++-
> >  fs/nfs/nfs4proc.c        | 112 +++++++++++++++-------
> >  fs/nfs/nfs4xdr.c         |   2 +-
> >  fs/nfsd/acl.h            |  11 ++-
> >  fs/nfsd/nfs4acl.c        |  69 +++++++++++++-
> >  fs/nfsd/nfs4proc.c       |  32 +++++--
> >  fs/nfsd/nfs4xdr.c        | 194 +++++++++++++++++++++++++++++++++------
> >  fs/nfsd/nfsd.h           |   5 +-
> >  fs/nfsd/xdr4.h           |   2 +
> >  include/linux/exportfs.h |  22 +++++
> >  include/linux/nfs4.h     |  23 ++++-
> >  include/linux/nfs_xdr.h  |  11 +--
> >  include/linux/nfsacl.h   |   7 ++
> >  13 files changed, 431 insertions(+), 82 deletions(-)
> >
> > -- 
> > 2.44.0
> 
> So what you want is indeed pass-through, it's not support for
> local file systems with NFSv4 ACL. NFSD is not only leaving the
> ACEs alone, but it is also not doing any idmapping... and the
> NFS client currently has nothing at all that deals with NFSv4
> ACLs. So that explains the raw byte pass-through design.

Yes, I'm using the NFSv4 ACL payload that is provided by the NFSv4
client. And then NFSD passes that through to the NFSv4 client it is
reexporting.
 
> Passing pages of raw wire data between client and server is
> awkward, however, and has resulted in correctness bugs and
> architectural issues (found during review).

The NFSv4 client goes out of its way to be a passthrough mechanism for
ACLs, because we don't want identity mapping, etc transforming the
payload.

So there is new NFSD code in this series to enable passthru of that
ACL payload from NFSD to NFSv4 client it is reeexporting (because
NFSD's existing limited ACE decode into a list of ACEs is not needed
or useful for passing through to NFSv4 client's existing ACL payload
handling).

> It would be faster for us to start with proper support in NFSD for:
> 
> 6.2.2.  Attribute 58: dacl
> 
>    The dacl attribute is like the acl attribute, but dacl allows just
>    ALLOW and DENY ACEs.  The dacl attribute supports automatic
>    inheritance (see Section 6.4.3.2).
> 
> 6.2.3.  Attribute 59: sacl
> 
>    The sacl attribute is like the acl attribute, but sacl allows just
>    AUDIT and ALARM ACEs.  The sacl attribute supports automatic
>    inheritance (see Section 6.4.3.2).
> 
> This subfeature can get worked out and merged while we sort through
> the API contract issues with ACL pass-through.

This would be useful for NFSD's benefit if native DACL and SACL
support were the goal. But I don't yet see how it helps the reexport
pass-through case my series is focused on. Given the way the NFSv4
client handles ACLs, any extra NFSD processing into intermediate ACLs
actively works against ACL support for the NFSv4 reexport usecase.

> Current NFSD support for DACL/SACL:
> 
> - FATTR4_DACL (bit 58) and FATTR4_SACL (bit 59) are defined in
>   `include/uapi/linux/nfs4.h` but not in any supported attrs mask
> - Dispatch table (`fs/nfsd/nfs4xdr.c`):
>       [FATTR4_DACL] = nfsd4_encode_fattr4__noop,
>       [FATTR4_SACL] = nfsd4_encode_fattr4__noop,
> - Not decoded in nfsd4_decode_fattr4()
> - Not in NFSD_WRITEABLE_ATTRS_WORD1 (`fs/nfsd/nfsd.h`)
> 
> Patch sequence might look something like:
> 
>  1. NFSD: add acl_flags to struct nfs4_acl for nfsacl41
>  2. NFSD: add DACL/SACL decode at correct fattr4 bit position
>  3. NFSD: handle nfsacl41 wire format (aclflag4 + aces) in decode
>  4. NFSD: add nfsacl41 encode for DACL/SACL responses
>  5. NFSD: clear ACL, DACL, and SACL together in supported_attrs
>  6. NFSD: add DACL/SACL to supported and writable attribute masks
> 
> 
> For the API between NFSD and the local NFS client, I might favor an
> alternative to new export operations: NFSD can set "system.nfs4_acl"
> xattrs on NFS client inodes. The only issue there is it limits the
> total number of bytes to 64K. But we can worry about that once the
> DACL/SACL attributes are implemented.

This line of work _seems_ outside the scope of what is needed, so I
won't be pursuing it unless you can help me better understand how this
stepping stone of NFSD having native support for DACL and SACL will
begin to offer more capable ACL pass-through support for NFS reexport.

Further review of the code provided in this series would be
appreciated. I do think I've captured the essence of what is needed.

Thanks,
Mike

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-02-25 16:53   ` Mike Snitzer
@ 2026-02-25 18:21     ` Chuck Lever
  0 siblings, 0 replies; 20+ messages in thread
From: Chuck Lever @ 2026-02-25 18:21 UTC (permalink / raw)
  To: Mike Snitzer
  Cc: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker,
	linux-nfs

On 2/25/26 11:53 AM, Mike Snitzer wrote:
> On Tue, Feb 24, 2026 at 04:58:10PM -0500, Chuck Lever wrote:
>> Current NFSD support for DACL/SACL:
>>
>> - FATTR4_DACL (bit 58) and FATTR4_SACL (bit 59) are defined in
>>   `include/uapi/linux/nfs4.h` but not in any supported attrs mask
>> - Dispatch table (`fs/nfsd/nfs4xdr.c`):
>>       [FATTR4_DACL] = nfsd4_encode_fattr4__noop,
>>       [FATTR4_SACL] = nfsd4_encode_fattr4__noop,
>> - Not decoded in nfsd4_decode_fattr4()
>> - Not in NFSD_WRITEABLE_ATTRS_WORD1 (`fs/nfsd/nfsd.h`)
>>
>> Patch sequence might look something like:
>>
>>  1. NFSD: add acl_flags to struct nfs4_acl for nfsacl41
>>  2. NFSD: add DACL/SACL decode at correct fattr4 bit position
>>  3. NFSD: handle nfsacl41 wire format (aclflag4 + aces) in decode
>>  4. NFSD: add nfsacl41 encode for DACL/SACL responses
>>  5. NFSD: clear ACL, DACL, and SACL together in supported_attrs
>>  6. NFSD: add DACL/SACL to supported and writable attribute masks
>>
>>
>> For the API between NFSD and the local NFS client, I might favor an
>> alternative to new export operations: NFSD can set "system.nfs4_acl"
>> xattrs on NFS client inodes. The only issue there is it limits the
>> total number of bytes to 64K. But we can worry about that once the
>> DACL/SACL attributes are implemented.
> 
> This line of work _seems_ outside the scope of what is needed, so I
> won't be pursuing it unless you can help me better understand how this
> stepping stone of NFSD having native support for DACL and SACL will
> begin to offer more capable ACL pass-through support for NFS reexport.

This is not about what narrow use case Hammerspace happens to need.

Your series adds incomplete support for the DACL and SACL attributes.
That incomplete support is not sufficient for a properly compliant
server implementation, and makes that part of your proposal unacceptable
for merge because it adds significant technical debt and is likely to
result in bug reports. That's no skin off your nose, but it's
considerable effort for the community to have to deal with long-term.

Since your patches already do some of this work, I'm asking you to do
the right thing and provide a clean and complete implementation of the
feature.

If you want to look at this transactionally, I'm asking Hammerspace to
pay a small one-time price for the community's long-term support of this
feature. I regard that as very fair.


> Further review of the code provided in this series would be
> appreciated.

Adding .setacl/.getacl operations to the export_operations structure is
simply wrong, architecturally speaking. So NACK to that implementation.
All of that needs to be redone once we have agreed on a proper cross-
system API.

> I do think I've captured the essence of what is needed.

That's exactly what a prototype should do. That does not make a
prototype implementation acceptable in the long run, however.


-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
                   ` (11 preceding siblings ...)
  2026-02-24 21:58 ` [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Chuck Lever
@ 2026-03-10 13:26 ` Christoph Hellwig
  2026-03-10 14:53   ` Trond Myklebust
  12 siblings, 1 reply; 20+ messages in thread
From: Christoph Hellwig @ 2026-03-10 13:26 UTC (permalink / raw)
  To: Mike Snitzer
  Cc: Chuck Lever, Jeff Layton, Trond Myklebust, Anna Schumaker,
	linux-nfs

NAK on this whole thing.  Linux does not support NFSv4 ACLs for pretty
good reasons.  If you want to add it you'd have to do it properly (even
if that is a bad idea in my opinion).  But adding a weird special case
for passthrough is a no-go.  To be honest I really don't understand why
your (as in Hammerspae, not you personally) want to abuse the kernel
nfsd and nfs client for that.  If you want to pass in the protocol do
it in userspace without burdening the kernel with it.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-03-10 13:26 ` Christoph Hellwig
@ 2026-03-10 14:53   ` Trond Myklebust
  2026-03-10 14:58     ` Christoph Hellwig
  2026-03-10 14:59     ` Chuck Lever
  0 siblings, 2 replies; 20+ messages in thread
From: Trond Myklebust @ 2026-03-10 14:53 UTC (permalink / raw)
  To: Christoph Hellwig, Mike Snitzer
  Cc: Chuck Lever, Jeff Layton, Anna Schumaker, linux-nfs

On Tue, 2026-03-10 at 06:26 -0700, Christoph Hellwig wrote:
> NAK on this whole thing.  Linux does not support NFSv4 ACLs for
> pretty
> good reasons.  If you want to add it you'd have to do it properly
> (even
> if that is a bad idea in my opinion).  But adding a weird special
> case
> for passthrough is a no-go.  To be honest I really don't understand
> why
> your (as in Hammerspae, not you personally) want to abuse the kernel
> nfsd and nfs client for that.  If you want to pass in the protocol do
> it in userspace without burdening the kernel with it.
> 

Like it or not, Linux knfsd _does_ pretend to support NFSv4 ACLs. It
does so by using a (lossy!) mapping to try to convert the NFSv4 ACL
into a POSIX style ACL.
This is a problem when you're re-exporting NFSv4, as we need to do,
because at best it mangles your ACL. At worst, it throws random error
codes back at the client.

So Hammerspace does need that passthrough ACL in order to have re-
exports work as expected.

If the upstream community is unwilling to take patches to address the
issue, then we're quite happy to maintain the code separately. It will
still be available to those who need it through our github site.

-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trondmy@kernel.org, trond.myklebust@hammerspace.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-03-10 14:53   ` Trond Myklebust
@ 2026-03-10 14:58     ` Christoph Hellwig
  2026-03-10 16:41       ` Chuck Lever
  2026-03-10 14:59     ` Chuck Lever
  1 sibling, 1 reply; 20+ messages in thread
From: Christoph Hellwig @ 2026-03-10 14:58 UTC (permalink / raw)
  To: Trond Myklebust
  Cc: Christoph Hellwig, Mike Snitzer, Chuck Lever, Jeff Layton,
	Anna Schumaker, linux-nfs

On Tue, Mar 10, 2026 at 10:53:56AM -0400, Trond Myklebust wrote:
> If the upstream community is unwilling to take patches to address the
> issue, then we're quite happy to maintain the code separately. It will
> still be available to those who need it through our github site.

For something so special purpose I think that is the better way.
Having it publically available in this case would be very helpful
in the case someone ever needs it, so thanks for that.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-03-10 14:53   ` Trond Myklebust
  2026-03-10 14:58     ` Christoph Hellwig
@ 2026-03-10 14:59     ` Chuck Lever
  1 sibling, 0 replies; 20+ messages in thread
From: Chuck Lever @ 2026-03-10 14:59 UTC (permalink / raw)
  To: Trond Myklebust, Christoph Hellwig, Mike Snitzer
  Cc: Jeff Layton, Anna Schumaker, linux-nfs

On 3/10/26 10:53 AM, Trond Myklebust wrote:
> On Tue, 2026-03-10 at 06:26 -0700, Christoph Hellwig wrote:
>> NAK on this whole thing.  Linux does not support NFSv4 ACLs for
>> pretty
>> good reasons.  If you want to add it you'd have to do it properly
>> (even
>> if that is a bad idea in my opinion).  But adding a weird special
>> case
>> for passthrough is a no-go.  To be honest I really don't understand
>> why
>> your (as in Hammerspae, not you personally) want to abuse the kernel
>> nfsd and nfs client for that.  If you want to pass in the protocol do
>> it in userspace without burdening the kernel with it.
>>
> 
> Like it or not, Linux knfsd _does_ pretend to support NFSv4 ACLs. It
> does so by using a (lossy!) mapping to try to convert the NFSv4 ACL
> into a POSIX style ACL.
> This is a problem when you're re-exporting NFSv4, as we need to do,
> because at best it mangles your ACL. At worst, it throws random error
> codes back at the client.
> 
> So Hammerspace does need that passthrough ACL in order to have re-
> exports work as expected.
> 
> If the upstream community is unwilling to take patches to address the
> issue, then we're quite happy to maintain the code separately. It will
> still be available to those who need it through our github site.

I recognize the need to avoid ACE mapping when re-exporting NFSv4.
This is an issue specifically for NFSD (which remaps NFSv4 ACLs to
POSIX ACLS) and NFSv4 re-export.

I will accept a clean implementation of that for merge, and I've
already pointed out where the prototype implementation can be
improved.


-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport
  2026-03-10 14:58     ` Christoph Hellwig
@ 2026-03-10 16:41       ` Chuck Lever
  0 siblings, 0 replies; 20+ messages in thread
From: Chuck Lever @ 2026-03-10 16:41 UTC (permalink / raw)
  To: Christoph Hellwig, Trond Myklebust
  Cc: Mike Snitzer, Chuck Lever, Jeff Layton, Anna Schumaker, linux-nfs



On Tue, Mar 10, 2026, at 10:58 AM, Christoph Hellwig wrote:
> On Tue, Mar 10, 2026 at 10:53:56AM -0400, Trond Myklebust wrote:
>> If the upstream community is unwilling to take patches to address the
>> issue, then we're quite happy to maintain the code separately. It will
>> still be available to those who need it through our github site.
>
> For something so special purpose I think that is the better way.

My experience is that Hammerspace is not the only consumer of
NFSv4 re-export, so I regard the issue as a shortcoming of the
current NFSD NFSv4 ACL implementation that will have broader
impact than one storage vendor. Thus I feel that having Hammerspace
maintain this feature separately will in the end not be good for
the community -- it should be merged upstream if it can be.

Even so, NFS re-export itself is not a commonly used feature. It
would be helpful to construct ACL bypass so that some or much of
it can be tested in common configurations. Then it can benefit
from commodity testing experience, rather than languishing as a
niche feature. This is why I encouraged the implementation of
FATR4_DACL and FATTR4_SACL as full-fledged features rather than
specifically and only for NFS re-export. That enables the
construction of pynfs and other unit tests that can be deployed
in CI without having to set up a re-export scenario.

Where I have trouble generally with Hammerspace's contributions
is that they do tend towards the niche, and frequently they are
presented here without consideration for how the community will
test, support, and maintain them in the long run. The proposed
features are often complex and they will have real ongoing costs
for us.

Food for discussion.

-- 
Chuck Lever

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-03-10 16:42 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-24 19:24 [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 01/11] exportfs: add ability to advertise NFSv4 ACL passthru support Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 02/11] NFSD: factor out nfsd_supports_nfs4_acl() to nfsd/acl.h Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 03/11] NFS/NFSD: data structure enablement for nfs4_acl passthru support Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 04/11] NFSD: prepare to support SETACL nfs4_acl passthru Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 05/11] NFSD: add NFS4 reexport support for " Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 06/11] NFSD: add NFS4 reexport support for GETACL " Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 07/11] NFSD: add NFS4ACL_DACL and NFS4ACL_SACL passthru support Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 08/11] NFSD: avoid extra nfs4_acl passthru work unless needed Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 09/11] NFSv4: add reexport support for SETACL nfs4_acl passthru Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 10/11] NFSv4: add reexport support for GETACL " Mike Snitzer
2026-02-24 19:24 ` [RFC PATCH v2 11/11] NFSv4: set EXPORT_OP_NFSV4_ACL_PASSTHRU flag Mike Snitzer
2026-02-24 21:58 ` [RFC PATCH v2 00/11] NFS/NFSD: nfs4_acl passthru for NFSv4 reexport Chuck Lever
2026-02-25 16:53   ` Mike Snitzer
2026-02-25 18:21     ` Chuck Lever
2026-03-10 13:26 ` Christoph Hellwig
2026-03-10 14:53   ` Trond Myklebust
2026-03-10 14:58     ` Christoph Hellwig
2026-03-10 16:41       ` Chuck Lever
2026-03-10 14:59     ` Chuck Lever

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox