* [PATCH v3 01/13] xdrgen: Implement pass-through lines in specifications
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 02/13] NFSD: Add a Kconfig setting to enable support for NFSv4 POSIX ACLs Chuck Lever
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Chuck Lever
From: Chuck Lever <chuck.lever@oracle.com>
XDR specification files can contain lines prefixed with '%' that
pass through unchanged to generated output. Traditional rpcgen
removes the '%' and emits the remainder verbatim, allowing direct
insertion of C includes, pragma directives, or other language-
specific content into the generated code.
Until now, xdrgen silently discarded these lines during parsing.
This prevented specifications from including necessary headers or
preprocessor directives that might be required for the generated
code to compile correctly.
The grammar now captures pass-through lines instead of ignoring
them. A new AST node type represents pass-through content, and
the AST transformer strips the leading '%' character. Definition
and source generators emit pass-through content in document order,
preserving the original placement within the specification.
This brings xdrgen closer to feature parity with traditional
rpcgen while maintaining the existing document-order processing
model.
Existing generated xdrgen source code has been regenerated.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4xdr_gen.c | 11 +++++-
fs/nfsd/nfs4xdr_gen.h | 2 +-
include/linux/sunrpc/xdrgen/nfs4_1.h | 11 +++++-
tools/net/sunrpc/xdrgen/README | 2 -
.../net/sunrpc/xdrgen/generators/passthru.py | 26 +++++++++++++
tools/net/sunrpc/xdrgen/grammars/xdr.lark | 6 ++-
.../net/sunrpc/xdrgen/subcmds/declarations.py | 4 +-
.../net/sunrpc/xdrgen/subcmds/definitions.py | 5 ++-
tools/net/sunrpc/xdrgen/subcmds/source.py | 24 ++++++++----
.../xdrgen/templates/C/passthru/definition.j2 | 3 ++
.../xdrgen/templates/C/passthru/source.j2 | 3 ++
tools/net/sunrpc/xdrgen/xdr_ast.py | 39 ++++++++++++++++++-
12 files changed, 116 insertions(+), 20 deletions(-)
create mode 100644 tools/net/sunrpc/xdrgen/generators/passthru.py
create mode 100644 tools/net/sunrpc/xdrgen/templates/C/passthru/definition.j2
create mode 100644 tools/net/sunrpc/xdrgen/templates/C/passthru/source.j2
diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
index 1e5e2243625c..ce5c36e9070a 100644
--- a/fs/nfsd/nfs4xdr_gen.c
+++ b/fs/nfsd/nfs4xdr_gen.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
-// XDR specification modification time: Thu Dec 25 13:44:43 2025
+// XDR specification modification time: Thu Jan 8 23:11:48 2026
#include <linux/sunrpc/svc.h>
@@ -178,6 +178,10 @@ xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_argument
return xdrgen_decode_open_arguments4(xdr, ptr);
}
+/*
+ * Determine what OPEN supports.
+ */
+
bool
xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
{
@@ -190,6 +194,11 @@ xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg
return xdrgen_decode_nfstime4(xdr, ptr);
}
+/*
+ * New RECOMMENDED Attribute for
+ * delegation caching of times
+ */
+
static bool __maybe_unused
xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr)
{
diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
index 47437876e803..8dfe10246506 100644
--- a/fs/nfsd/nfs4xdr_gen.h
+++ b/fs/nfsd/nfs4xdr_gen.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Thu Dec 25 13:44:43 2025 */
+/* XDR specification modification time: Thu Jan 8 23:11:48 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
diff --git a/include/linux/sunrpc/xdrgen/nfs4_1.h b/include/linux/sunrpc/xdrgen/nfs4_1.h
index 352bffda08f7..5283739242c7 100644
--- a/include/linux/sunrpc/xdrgen/nfs4_1.h
+++ b/include/linux/sunrpc/xdrgen/nfs4_1.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Thu Dec 25 13:44:43 2025 */
+/* XDR specification modification time: Thu Jan 8 23:11:48 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
@@ -87,6 +87,10 @@ typedef enum open_args_createmode4 open_args_createmode4;
typedef struct open_arguments4 fattr4_open_arguments;
+/*
+ * Determine what OPEN supports.
+ */
+
enum { FATTR4_OPEN_ARGUMENTS = 86 };
enum { OPEN4_RESULT_NO_OPEN_STATEID = 0x00000010 };
@@ -95,6 +99,11 @@ typedef struct nfstime4 fattr4_time_deleg_access;
typedef struct nfstime4 fattr4_time_deleg_modify;
+/*
+ * New RECOMMENDED Attribute for
+ * delegation caching of times
+ */
+
enum { FATTR4_TIME_DELEG_ACCESS = 84 };
enum { FATTR4_TIME_DELEG_MODIFY = 85 };
diff --git a/tools/net/sunrpc/xdrgen/README b/tools/net/sunrpc/xdrgen/README
index 27218a78ab40..2cf05d1e4cd9 100644
--- a/tools/net/sunrpc/xdrgen/README
+++ b/tools/net/sunrpc/xdrgen/README
@@ -250,8 +250,6 @@ Add more pragma directives:
Enable something like a #include to dynamically insert the content
of other specification files
-Properly support line-by-line pass-through via the "%" decorator
-
Build a unit test suite for verifying translation of XDR language
into compilable code
diff --git a/tools/net/sunrpc/xdrgen/generators/passthru.py b/tools/net/sunrpc/xdrgen/generators/passthru.py
new file mode 100644
index 000000000000..cb17bd977f1e
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/generators/passthru.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# ex: set filetype=python:
+
+"""Generate code for XDR pass-through lines"""
+
+from generators import SourceGenerator, create_jinja2_environment
+from xdr_ast import _XdrPassthru
+
+
+class XdrPassthruGenerator(SourceGenerator):
+ """Generate source code for XDR pass-through content"""
+
+ def __init__(self, language: str, peer: str):
+ """Initialize an instance of this class"""
+ self.environment = create_jinja2_environment(language, "passthru")
+ self.peer = peer
+
+ def emit_definition(self, node: _XdrPassthru) -> None:
+ """Emit one pass-through line"""
+ template = self.environment.get_template("definition.j2")
+ print(template.render(content=node.content))
+
+ def emit_decoder(self, node: _XdrPassthru) -> None:
+ """Emit one pass-through line"""
+ template = self.environment.get_template("source.j2")
+ print(template.render(content=node.content))
diff --git a/tools/net/sunrpc/xdrgen/grammars/xdr.lark b/tools/net/sunrpc/xdrgen/grammars/xdr.lark
index b7c664f2acb7..1d2afff98ac5 100644
--- a/tools/net/sunrpc/xdrgen/grammars/xdr.lark
+++ b/tools/net/sunrpc/xdrgen/grammars/xdr.lark
@@ -78,6 +78,9 @@ definition : constant_def
| type_def
| program_def
| pragma_def
+ | passthru_def
+
+passthru_def : PASSTHRU
//
// RPC program definitions not specified in RFC 4506
@@ -115,8 +118,7 @@ decimal_constant : /[\+-]?(0|[1-9][0-9]*)/
hexadecimal_constant : /0x([a-f]|[A-F]|[0-9])+/
octal_constant : /0[0-7]+/
-PASSTHRU : "%" | "%" /.+/
-%ignore PASSTHRU
+PASSTHRU : /%.*/
%import common.C_COMMENT
%ignore C_COMMENT
diff --git a/tools/net/sunrpc/xdrgen/subcmds/declarations.py b/tools/net/sunrpc/xdrgen/subcmds/declarations.py
index 97ffb76a02f1..ed83d48d1f68 100644
--- a/tools/net/sunrpc/xdrgen/subcmds/declarations.py
+++ b/tools/net/sunrpc/xdrgen/subcmds/declarations.py
@@ -10,7 +10,6 @@ from argparse import Namespace
from lark import logger
from lark.exceptions import VisitError
-from generators.constant import XdrConstantGenerator
from generators.enum import XdrEnumGenerator
from generators.header_bottom import XdrHeaderBottomGenerator
from generators.header_top import XdrHeaderTopGenerator
@@ -21,8 +20,7 @@ from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
-from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer
-from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
+from xdr_ast import _XdrEnum, _XdrPointer, _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
from xdr_parse import make_error_handler, XdrParseError
from xdr_parse import handle_transform_error
diff --git a/tools/net/sunrpc/xdrgen/subcmds/definitions.py b/tools/net/sunrpc/xdrgen/subcmds/definitions.py
index b17526a03dda..a48ca0549382 100644
--- a/tools/net/sunrpc/xdrgen/subcmds/definitions.py
+++ b/tools/net/sunrpc/xdrgen/subcmds/definitions.py
@@ -14,6 +14,7 @@ from generators.constant import XdrConstantGenerator
from generators.enum import XdrEnumGenerator
from generators.header_bottom import XdrHeaderBottomGenerator
from generators.header_top import XdrHeaderTopGenerator
+from generators.passthru import XdrPassthruGenerator
from generators.pointer import XdrPointerGenerator
from generators.program import XdrProgramGenerator
from generators.typedef import XdrTypedefGenerator
@@ -21,7 +22,7 @@ from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, Specification
-from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer
+from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPassthru, _XdrPointer
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
from xdr_parse import make_error_handler, XdrParseError
@@ -47,6 +48,8 @@ def emit_header_definitions(root: Specification, language: str, peer: str) -> No
gen = XdrStructGenerator(language, peer)
elif isinstance(definition.value, _XdrUnion):
gen = XdrUnionGenerator(language, peer)
+ elif isinstance(definition.value, _XdrPassthru):
+ gen = XdrPassthruGenerator(language, peer)
else:
continue
gen.emit_definition(definition.value)
diff --git a/tools/net/sunrpc/xdrgen/subcmds/source.py b/tools/net/sunrpc/xdrgen/subcmds/source.py
index 6508563494fe..27e8767b1b58 100644
--- a/tools/net/sunrpc/xdrgen/subcmds/source.py
+++ b/tools/net/sunrpc/xdrgen/subcmds/source.py
@@ -12,6 +12,7 @@ from lark.exceptions import VisitError
from generators.source_top import XdrSourceTopGenerator
from generators.enum import XdrEnumGenerator
+from generators.passthru import XdrPassthruGenerator
from generators.pointer import XdrPointerGenerator
from generators.program import XdrProgramGenerator
from generators.typedef import XdrTypedefGenerator
@@ -19,7 +20,7 @@ from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
-from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer
+from xdr_ast import _XdrAst, _XdrEnum, _XdrPassthru, _XdrPointer
from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation
@@ -74,22 +75,31 @@ def generate_server_source(filename: str, root: Specification, language: str) ->
gen.emit_source(filename, root)
for definition in root.definitions:
- emit_source_decoder(definition.value, language, "server")
+ if isinstance(definition.value, _XdrPassthru):
+ passthru_gen = XdrPassthruGenerator(language, "server")
+ passthru_gen.emit_decoder(definition.value)
+ else:
+ emit_source_decoder(definition.value, language, "server")
for definition in root.definitions:
- emit_source_encoder(definition.value, language, "server")
+ if not isinstance(definition.value, _XdrPassthru):
+ emit_source_encoder(definition.value, language, "server")
def generate_client_source(filename: str, root: Specification, language: str) -> None:
- """Generate server-side source code"""
+ """Generate client-side source code"""
gen = XdrSourceTopGenerator(language, "client")
gen.emit_source(filename, root)
- print("")
for definition in root.definitions:
- emit_source_encoder(definition.value, language, "client")
+ if isinstance(definition.value, _XdrPassthru):
+ passthru_gen = XdrPassthruGenerator(language, "client")
+ passthru_gen.emit_decoder(definition.value)
+ else:
+ emit_source_encoder(definition.value, language, "client")
for definition in root.definitions:
- emit_source_decoder(definition.value, language, "client")
+ if not isinstance(definition.value, _XdrPassthru):
+ emit_source_decoder(definition.value, language, "client")
# cel: todo: client needs PROC macros
diff --git a/tools/net/sunrpc/xdrgen/templates/C/passthru/definition.j2 b/tools/net/sunrpc/xdrgen/templates/C/passthru/definition.j2
new file mode 100644
index 000000000000..900c7516a29c
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/passthru/definition.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{{ content }}
diff --git a/tools/net/sunrpc/xdrgen/templates/C/passthru/source.j2 b/tools/net/sunrpc/xdrgen/templates/C/passthru/source.j2
new file mode 100644
index 000000000000..900c7516a29c
--- /dev/null
+++ b/tools/net/sunrpc/xdrgen/templates/C/passthru/source.j2
@@ -0,0 +1,3 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{{ content }}
diff --git a/tools/net/sunrpc/xdrgen/xdr_ast.py b/tools/net/sunrpc/xdrgen/xdr_ast.py
index dc2fa9fd8ec2..14bff9477473 100644
--- a/tools/net/sunrpc/xdrgen/xdr_ast.py
+++ b/tools/net/sunrpc/xdrgen/xdr_ast.py
@@ -516,6 +516,13 @@ class _Pragma(_XdrAst):
"""Empty class for pragma directives"""
+@dataclass
+class _XdrPassthru(_XdrAst):
+ """Passthrough line to emit verbatim in output"""
+
+ content: str
+
+
@dataclass
class Definition(_XdrAst, ast_utils.WithMeta):
"""Corresponds to 'definition' in the grammar"""
@@ -738,14 +745,42 @@ class ParseToAst(Transformer):
raise NotImplementedError("Directive not supported")
return _Pragma()
+ def passthru_def(self, children):
+ """Instantiate one _XdrPassthru object"""
+ token = children[0]
+ content = token.value[1:]
+ return _XdrPassthru(content)
+
transformer = ast_utils.create_transformer(this_module, ParseToAst())
+def _merge_consecutive_passthru(definitions: List[Definition]) -> List[Definition]:
+ """Merge consecutive passthru definitions into single nodes"""
+ result = []
+ i = 0
+ while i < len(definitions):
+ if isinstance(definitions[i].value, _XdrPassthru):
+ lines = [definitions[i].value.content]
+ meta = definitions[i].meta
+ j = i + 1
+ while j < len(definitions) and isinstance(definitions[j].value, _XdrPassthru):
+ lines.append(definitions[j].value.content)
+ j += 1
+ merged = _XdrPassthru("\n".join(lines))
+ result.append(Definition(meta, merged))
+ i = j
+ else:
+ result.append(definitions[i])
+ i += 1
+ return result
+
+
def transform_parse_tree(parse_tree):
"""Transform productions into an abstract syntax tree"""
-
- return transformer.transform(parse_tree)
+ ast = transformer.transform(parse_tree)
+ ast.definitions = _merge_consecutive_passthru(ast.definitions)
+ return ast
def get_header_name() -> str:
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 02/13] NFSD: Add a Kconfig setting to enable support for NFSv4 POSIX ACLs
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
2026-01-09 16:21 ` [PATCH v3 01/13] xdrgen: Implement pass-through lines in specifications Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 03/13] Add RPC language definition of NFSv4 POSIX ACL extension Chuck Lever
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Chuck Lever
From: Chuck Lever <chuck.lever@oracle.com>
A new IETF draft extends NFSv4.2 with POSIX ACL attributes:
https://www.ietf.org/archive/id/draft-ietf-nfsv4-posix-acls-00.txt
This draft has not yet been ratified. A build-time configuration
option allows developers and distributors to decide whether to
expose this experimental protocol extension to NFSv4 clients. The
option is disabled by default to prevent unintended deployment of
potentially unstable protocol features in production environments.
This approach mirrors the existing NFSD_V4_DELEG_TIMESTAMPS option,
which gates another experimental NFSv4 extension based on an
unratified IETF draft.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/Kconfig | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index 0b5c1a0bf1cf..4fd6e818565e 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -186,3 +186,22 @@ config NFSD_V4_DELEG_TIMESTAMPS
draft-ietf-nfsv4-delstid-08 "Extending the Opening of Files". This
is currently an experimental feature and is therefore left disabled
by default.
+
+config NFSD_V4_POSIX_ACLS
+ bool "Support NFSv4 POSIX draft ACLs"
+ depends on NFSD_V4
+ default n
+ help
+ Include experimental support for POSIX Access Control Lists
+ (ACLs) in NFSv4 as specified in the IETF draft
+ draft-ietf-nfsv4-posix-acls. This protocol extension enables
+ NFSv4 clients to retrieve and modify POSIX ACLs on exported
+ filesystems that support them.
+
+ This feature is based on an unratified IETF draft
+ specification that may change in ways that impact
+ interoperability with existing clients. Enable only for
+ testing environments or when interoperability with specific
+ clients that implement this draft is required.
+
+ If unsure, say N.
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 03/13] Add RPC language definition of NFSv4 POSIX ACL extension
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
2026-01-09 16:21 ` [PATCH v3 01/13] xdrgen: Implement pass-through lines in specifications Chuck Lever
2026-01-09 16:21 ` [PATCH v3 02/13] NFSD: Add a Kconfig setting to enable support for NFSv4 POSIX ACLs Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 04/13] NFSD: Add nfsd4_encode_fattr4_acl_trueform Chuck Lever
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Chuck Lever
From: Chuck Lever <chuck.lever@oracle.com>
The language definition was extracted from the new
draft-ietf-nfsv4-posix-acls specification. This ensures good
constant and type name alignment between the spec and the Linux
kernel source code, and brings in some basic XDR utilities for
handling NFSv4 POSIX draft ACLs.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
Documentation/sunrpc/xdr/nfs4_1.x | 61 +++++++
fs/nfsd/nfs4xdr_gen.c | 239 ++++++++++++++++++++++++++-
fs/nfsd/nfs4xdr_gen.h | 12 +-
include/linux/nfs4.h | 4 +
include/linux/sunrpc/xdrgen/nfs4_1.h | 97 ++++++++++-
5 files changed, 410 insertions(+), 3 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs4_1.x b/Documentation/sunrpc/xdr/nfs4_1.x
index ca95150a3a29..5b45547b2ebc 100644
--- a/Documentation/sunrpc/xdr/nfs4_1.x
+++ b/Documentation/sunrpc/xdr/nfs4_1.x
@@ -53,6 +53,11 @@ typedef unsigned int uint32_t;
*/
typedef uint32_t bitmap4<>;
+typedef opaque utf8string<>;
+typedef utf8string utf8str_cis;
+typedef utf8string utf8str_cs;
+typedef utf8string utf8str_mixed;
+
/*
* Timeval
*/
@@ -184,3 +189,59 @@ enum open_delegation_type4 {
OPEN_DELEGATE_READ_ATTRS_DELEG = 4,
OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5
};
+
+
+/*
+ * The following content was extracted from draft-ietf-nfsv4-posix-acls
+ */
+
+enum aclmodel4 {
+ ACL_MODEL_NFS4 = 1,
+ ACL_MODEL_POSIX_DRAFT = 2,
+ ACL_MODEL_NONE = 3
+};
+pragma public aclmodel4;
+
+enum aclscope4 {
+ ACL_SCOPE_FILE_OBJECT = 1,
+ ACL_SCOPE_FILE_SYSTEM = 2,
+ ACL_SCOPE_SERVER = 3
+};
+pragma public aclscope4;
+
+enum posixacetag4 {
+ POSIXACE4_TAG_USER_OBJ = 1,
+ POSIXACE4_TAG_USER = 2,
+ POSIXACE4_TAG_GROUP_OBJ = 3,
+ POSIXACE4_TAG_GROUP = 4,
+ POSIXACE4_TAG_MASK = 5,
+ POSIXACE4_TAG_OTHER = 6
+};
+pragma public posixacetag4;
+
+typedef uint32_t posixaceperm4;
+pragma public posixaceperm4;
+
+/* Bit definitions for posixaceperm4. */
+const POSIXACE4_PERM_EXECUTE = 0x00000001;
+const POSIXACE4_PERM_WRITE = 0x00000002;
+const POSIXACE4_PERM_READ = 0x00000004;
+
+struct posixace4 {
+ posixacetag4 tag;
+ posixaceperm4 perm;
+ utf8str_mixed who;
+};
+
+typedef aclmodel4 fattr4_acl_trueform;
+typedef aclscope4 fattr4_acl_trueform_scope;
+typedef posixace4 fattr4_posix_default_acl<>;
+typedef posixace4 fattr4_posix_access_acl<>;
+
+%/*
+% * New for POSIX ACL extension
+% */
+const FATTR4_ACL_TRUEFORM = 89;
+const FATTR4_ACL_TRUEFORM_SCOPE = 90;
+const FATTR4_POSIX_DEFAULT_ACL = 91;
+const FATTR4_POSIX_ACCESS_ACL = 92;
diff --git a/fs/nfsd/nfs4xdr_gen.c b/fs/nfsd/nfs4xdr_gen.c
index ce5c36e9070a..824497051b87 100644
--- a/fs/nfsd/nfs4xdr_gen.c
+++ b/fs/nfsd/nfs4xdr_gen.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
-// XDR specification modification time: Thu Jan 8 23:11:48 2026
+// XDR specification modification time: Thu Jan 8 23:12:07 2026
#include <linux/sunrpc/svc.h>
@@ -30,6 +30,30 @@ xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
return true;
}
+static bool __maybe_unused
+xdrgen_decode_utf8string(struct xdr_stream *xdr, utf8string *ptr)
+{
+ return xdrgen_decode_opaque(xdr, ptr, 0);
+}
+
+static bool __maybe_unused
+xdrgen_decode_utf8str_cis(struct xdr_stream *xdr, utf8str_cis *ptr)
+{
+ return xdrgen_decode_utf8string(xdr, ptr);
+}
+
+static bool __maybe_unused
+xdrgen_decode_utf8str_cs(struct xdr_stream *xdr, utf8str_cs *ptr)
+{
+ return xdrgen_decode_utf8string(xdr, ptr);
+}
+
+static bool __maybe_unused
+xdrgen_decode_utf8str_mixed(struct xdr_stream *xdr, utf8str_mixed *ptr)
+{
+ return xdrgen_decode_utf8string(xdr, ptr);
+}
+
static bool __maybe_unused
xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
{
@@ -222,6 +246,125 @@ xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type
return true;
}
+bool
+xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr)
+{
+ u32 val;
+
+ if (xdr_stream_decode_u32(xdr, &val) < 0)
+ return false;
+ /* Compiler may optimize to a range check for dense enums */
+ switch (val) {
+ case ACL_MODEL_NFS4:
+ case ACL_MODEL_POSIX_DRAFT:
+ case ACL_MODEL_NONE:
+ break;
+ default:
+ return false;
+ }
+ *ptr = val;
+ return true;
+}
+
+bool
+xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr)
+{
+ u32 val;
+
+ if (xdr_stream_decode_u32(xdr, &val) < 0)
+ return false;
+ /* Compiler may optimize to a range check for dense enums */
+ switch (val) {
+ case ACL_SCOPE_FILE_OBJECT:
+ case ACL_SCOPE_FILE_SYSTEM:
+ case ACL_SCOPE_SERVER:
+ break;
+ default:
+ return false;
+ }
+ *ptr = val;
+ return true;
+}
+
+bool
+xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr)
+{
+ u32 val;
+
+ if (xdr_stream_decode_u32(xdr, &val) < 0)
+ return false;
+ /* Compiler may optimize to a range check for dense enums */
+ switch (val) {
+ case POSIXACE4_TAG_USER_OBJ:
+ case POSIXACE4_TAG_USER:
+ case POSIXACE4_TAG_GROUP_OBJ:
+ case POSIXACE4_TAG_GROUP:
+ case POSIXACE4_TAG_MASK:
+ case POSIXACE4_TAG_OTHER:
+ break;
+ default:
+ return false;
+ }
+ *ptr = val;
+ return true;
+}
+
+bool
+xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr)
+{
+ return xdrgen_decode_uint32_t(xdr, ptr);
+}
+
+static bool __maybe_unused
+xdrgen_decode_posixace4(struct xdr_stream *xdr, struct posixace4 *ptr)
+{
+ if (!xdrgen_decode_posixacetag4(xdr, &ptr->tag))
+ return false;
+ if (!xdrgen_decode_posixaceperm4(xdr, &ptr->perm))
+ return false;
+ if (!xdrgen_decode_utf8str_mixed(xdr, &ptr->who))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_acl_trueform(struct xdr_stream *xdr, fattr4_acl_trueform *ptr)
+{
+ return xdrgen_decode_aclmodel4(xdr, ptr);
+}
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, fattr4_acl_trueform_scope *ptr)
+{
+ return xdrgen_decode_aclscope4(xdr, ptr);
+}
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_posix_default_acl(struct xdr_stream *xdr, fattr4_posix_default_acl *ptr)
+{
+ if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
+ return false;
+ for (u32 i = 0; i < ptr->count; i++)
+ if (!xdrgen_decode_posixace4(xdr, &ptr->element[i]))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_decode_fattr4_posix_access_acl(struct xdr_stream *xdr, fattr4_posix_access_acl *ptr)
+{
+ if (xdr_stream_decode_u32(xdr, &ptr->count) < 0)
+ return false;
+ for (u32 i = 0; i < ptr->count; i++)
+ if (!xdrgen_decode_posixace4(xdr, &ptr->element[i]))
+ return false;
+ return true;
+}
+
+/*
+ * New for POSIX ACL extension
+ */
+
static bool __maybe_unused
xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
{
@@ -245,6 +388,30 @@ xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
return true;
}
+static bool __maybe_unused
+xdrgen_encode_utf8string(struct xdr_stream *xdr, const utf8string value)
+{
+ return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0;
+}
+
+static bool __maybe_unused
+xdrgen_encode_utf8str_cis(struct xdr_stream *xdr, const utf8str_cis value)
+{
+ return xdrgen_encode_utf8string(xdr, value);
+}
+
+static bool __maybe_unused
+xdrgen_encode_utf8str_cs(struct xdr_stream *xdr, const utf8str_cs value)
+{
+ return xdrgen_encode_utf8string(xdr, value);
+}
+
+static bool __maybe_unused
+xdrgen_encode_utf8str_mixed(struct xdr_stream *xdr, const utf8str_mixed value)
+{
+ return xdrgen_encode_utf8string(xdr, value);
+}
+
static bool __maybe_unused
xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
{
@@ -330,3 +497,73 @@ xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}
+
+bool
+xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+bool
+xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+bool
+xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value)
+{
+ return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
+}
+
+bool
+xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value)
+{
+ return xdrgen_encode_uint32_t(xdr, value);
+}
+
+static bool __maybe_unused
+xdrgen_encode_posixace4(struct xdr_stream *xdr, const struct posixace4 *value)
+{
+ if (!xdrgen_encode_posixacetag4(xdr, value->tag))
+ return false;
+ if (!xdrgen_encode_posixaceperm4(xdr, value->perm))
+ return false;
+ if (!xdrgen_encode_utf8str_mixed(xdr, value->who))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_acl_trueform(struct xdr_stream *xdr, const fattr4_acl_trueform value)
+{
+ return xdrgen_encode_aclmodel4(xdr, value);
+}
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, const fattr4_acl_trueform_scope value)
+{
+ return xdrgen_encode_aclscope4(xdr, value);
+}
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_posix_default_acl(struct xdr_stream *xdr, const fattr4_posix_default_acl value)
+{
+ if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value.count; i++)
+ if (!xdrgen_encode_posixace4(xdr, &value.element[i]))
+ return false;
+ return true;
+}
+
+static bool __maybe_unused
+xdrgen_encode_fattr4_posix_access_acl(struct xdr_stream *xdr, const fattr4_posix_access_acl value)
+{
+ if (xdr_stream_encode_u32(xdr, value.count) != XDR_UNIT)
+ return false;
+ for (u32 i = 0; i < value.count; i++)
+ if (!xdrgen_encode_posixace4(xdr, &value.element[i]))
+ return false;
+ return true;
+}
diff --git a/fs/nfsd/nfs4xdr_gen.h b/fs/nfsd/nfs4xdr_gen.h
index 8dfe10246506..1c487f1a11ab 100644
--- a/fs/nfsd/nfs4xdr_gen.h
+++ b/fs/nfsd/nfs4xdr_gen.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Thu Jan 8 23:11:48 2026 */
+/* XDR specification modification time: Thu Jan 8 23:12:07 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
@@ -21,5 +21,15 @@ bool xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4
bool xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr);
bool xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value);
+bool xdrgen_decode_aclmodel4(struct xdr_stream *xdr, aclmodel4 *ptr);
+bool xdrgen_encode_aclmodel4(struct xdr_stream *xdr, aclmodel4 value);
+bool xdrgen_decode_aclscope4(struct xdr_stream *xdr, aclscope4 *ptr);
+bool xdrgen_encode_aclscope4(struct xdr_stream *xdr, aclscope4 value);
+bool xdrgen_decode_posixacetag4(struct xdr_stream *xdr, posixacetag4 *ptr);
+bool xdrgen_encode_posixacetag4(struct xdr_stream *xdr, posixacetag4 value);
+
+bool xdrgen_decode_posixaceperm4(struct xdr_stream *xdr, posixaceperm4 *ptr);
+bool xdrgen_encode_posixaceperm4(struct xdr_stream *xdr, const posixaceperm4 value);
+
#endif /* _LINUX_XDRGEN_NFS4_1_DECL_H */
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index e947af6a3684..d87be1f25273 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -598,6 +598,10 @@ enum {
#define FATTR4_WORD2_TIME_DELEG_ACCESS BIT(FATTR4_TIME_DELEG_ACCESS - 64)
#define FATTR4_WORD2_TIME_DELEG_MODIFY BIT(FATTR4_TIME_DELEG_MODIFY - 64)
#define FATTR4_WORD2_OPEN_ARGUMENTS BIT(FATTR4_OPEN_ARGUMENTS - 64)
+#define FATTR4_WORD2_ACL_TRUEFORM BIT(FATTR4_ACL_TRUEFORM - 64)
+#define FATTR4_WORD2_ACL_TRUEFORM_SCOPE BIT(FATTR4_ACL_TRUEFORM_SCOPE - 64)
+#define FATTR4_WORD2_POSIX_DEFAULT_ACL BIT(FATTR4_POSIX_DEFAULT_ACL - 64)
+#define FATTR4_WORD2_POSIX_ACCESS_ACL BIT(FATTR4_POSIX_ACCESS_ACL - 64)
/* MDS threshold bitmap bits */
#define THRESHOLD_RD (1UL << 0)
diff --git a/include/linux/sunrpc/xdrgen/nfs4_1.h b/include/linux/sunrpc/xdrgen/nfs4_1.h
index 5283739242c7..4ac54bdbd335 100644
--- a/include/linux/sunrpc/xdrgen/nfs4_1.h
+++ b/include/linux/sunrpc/xdrgen/nfs4_1.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
-/* XDR specification modification time: Thu Jan 8 23:11:48 2026 */
+/* XDR specification modification time: Thu Jan 8 23:12:07 2026 */
#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
@@ -18,6 +18,14 @@ typedef struct {
uint32_t *element;
} bitmap4;
+typedef opaque utf8string;
+
+typedef utf8string utf8str_cis;
+
+typedef utf8string utf8str_cs;
+
+typedef utf8string utf8str_mixed;
+
struct nfstime4 {
int64_t seconds;
uint32_t nseconds;
@@ -141,11 +149,85 @@ enum open_delegation_type4 {
typedef enum open_delegation_type4 open_delegation_type4;
+enum aclmodel4 {
+ ACL_MODEL_NFS4 = 1,
+ ACL_MODEL_POSIX_DRAFT = 2,
+ ACL_MODEL_NONE = 3,
+};
+
+typedef enum aclmodel4 aclmodel4;
+
+enum aclscope4 {
+ ACL_SCOPE_FILE_OBJECT = 1,
+ ACL_SCOPE_FILE_SYSTEM = 2,
+ ACL_SCOPE_SERVER = 3,
+};
+
+typedef enum aclscope4 aclscope4;
+
+enum posixacetag4 {
+ POSIXACE4_TAG_USER_OBJ = 1,
+ POSIXACE4_TAG_USER = 2,
+ POSIXACE4_TAG_GROUP_OBJ = 3,
+ POSIXACE4_TAG_GROUP = 4,
+ POSIXACE4_TAG_MASK = 5,
+ POSIXACE4_TAG_OTHER = 6,
+};
+
+typedef enum posixacetag4 posixacetag4;
+
+typedef uint32_t posixaceperm4;
+
+enum { POSIXACE4_PERM_EXECUTE = 0x00000001 };
+
+enum { POSIXACE4_PERM_WRITE = 0x00000002 };
+
+enum { POSIXACE4_PERM_READ = 0x00000004 };
+
+struct posixace4 {
+ posixacetag4 tag;
+ posixaceperm4 perm;
+ utf8str_mixed who;
+};
+
+typedef aclmodel4 fattr4_acl_trueform;
+
+typedef aclscope4 fattr4_acl_trueform_scope;
+
+typedef struct {
+ u32 count;
+ struct posixace4 *element;
+} fattr4_posix_default_acl;
+
+typedef struct {
+ u32 count;
+ struct posixace4 *element;
+} fattr4_posix_access_acl;
+
+/*
+ * New for POSIX ACL extension
+ */
+
+enum { FATTR4_ACL_TRUEFORM = 89 };
+
+enum { FATTR4_ACL_TRUEFORM_SCOPE = 90 };
+
+enum { FATTR4_POSIX_DEFAULT_ACL = 91 };
+
+enum { FATTR4_POSIX_ACCESS_ACL = 92 };
+
#define NFS4_int64_t_sz \
(XDR_hyper)
#define NFS4_uint32_t_sz \
(XDR_unsigned_int)
#define NFS4_bitmap4_sz (XDR_unsigned_int)
+#define NFS4_utf8string_sz (XDR_unsigned_int)
+#define NFS4_utf8str_cis_sz \
+ (NFS4_utf8string_sz)
+#define NFS4_utf8str_cs_sz \
+ (NFS4_utf8string_sz)
+#define NFS4_utf8str_mixed_sz \
+ (NFS4_utf8string_sz)
#define NFS4_nfstime4_sz \
(NFS4_int64_t_sz + NFS4_uint32_t_sz)
#define NFS4_fattr4_offline_sz \
@@ -164,5 +246,18 @@ typedef enum open_delegation_type4 open_delegation_type4;
#define NFS4_fattr4_time_deleg_modify_sz \
(NFS4_nfstime4_sz)
#define NFS4_open_delegation_type4_sz (XDR_int)
+#define NFS4_aclmodel4_sz (XDR_int)
+#define NFS4_aclscope4_sz (XDR_int)
+#define NFS4_posixacetag4_sz (XDR_int)
+#define NFS4_posixaceperm4_sz \
+ (NFS4_uint32_t_sz)
+#define NFS4_posixace4_sz \
+ (NFS4_posixacetag4_sz + NFS4_posixaceperm4_sz + NFS4_utf8str_mixed_sz)
+#define NFS4_fattr4_acl_trueform_sz \
+ (NFS4_aclmodel4_sz)
+#define NFS4_fattr4_acl_trueform_scope_sz \
+ (NFS4_aclscope4_sz)
+#define NFS4_fattr4_posix_default_acl_sz (XDR_unsigned_int)
+#define NFS4_fattr4_posix_access_acl_sz (XDR_unsigned_int)
#endif /* _LINUX_XDRGEN_NFS4_1_DEF_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 04/13] NFSD: Add nfsd4_encode_fattr4_acl_trueform
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (2 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 03/13] Add RPC language definition of NFSv4 POSIX ACL extension Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 05/13] NFSD: Add nfsd4_encode_fattr4_acl_trueform_scope Chuck Lever
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
Mapping between NFSv4 ACLs and POSIX ACLs is semantically imprecise:
a client that sets an NFSv4 ACL and reads it back may see a different
ACL than it wrote. The proposed NFSv4 POSIX ACL extension introduces
the FATTR4_ACL_TRUEFORM attribute, which reports whether a file
object stores its access control permissions using NFSv4 ACLs or
POSIX ACLs.
A client aware of this extension can avoid lossy translation by
requesting and setting ACLs in their native format.
When NFSD is built with CONFIG_NFSD_V4_POSIX_ACLS, report
ACL_MODEL_POSIX_DRAFT for file objects on file systems with the
SB_POSIXACL flag set, and ACL_MODEL_NONE otherwise. Linux file
systems do not store NFSv4 ACLs natively, so ACL_MODEL_NFS4 is never
reported.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5065727204b9..9b47cf17ddde 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3470,6 +3470,22 @@ static __be32 nfsd4_encode_fattr4_open_arguments(struct xdr_stream *xdr,
return nfs_ok;
}
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+
+static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr,
+ const struct nfsd4_fattr_args *args)
+{
+ aclmodel4 trueform = ACL_MODEL_NONE;
+
+ if (IS_POSIXACL(d_inode(args->dentry)))
+ trueform = ACL_MODEL_POSIX_DRAFT;
+ if (!xdrgen_encode_aclmodel4(xdr, trueform))
+ return nfserr_resource;
+ return nfs_ok;
+}
+
+#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
+
static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_SUPPORTED_ATTRS] = nfsd4_encode_fattr4_supported_attrs,
[FATTR4_TYPE] = nfsd4_encode_fattr4_type,
@@ -3573,6 +3589,16 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_TIME_DELEG_ACCESS] = nfsd4_encode_fattr4__inval,
[FATTR4_TIME_DELEG_MODIFY] = nfsd4_encode_fattr4__inval,
[FATTR4_OPEN_ARGUMENTS] = nfsd4_encode_fattr4_open_arguments,
+
+ /* Reserved */
+ [87] = nfsd4_encode_fattr4__inval,
+ [88] = nfsd4_encode_fattr4__inval,
+
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ [FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform,
+#else
+ [FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop,
+#endif
};
/*
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 05/13] NFSD: Add nfsd4_encode_fattr4_acl_trueform_scope
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (3 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 04/13] NFSD: Add nfsd4_encode_fattr4_acl_trueform Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 06/13] NFSD: Add nfsd4_encode_fattr4_posix_default_acl Chuck Lever
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
The FATTR4_ACL_TRUEFORM_SCOPE attribute indicates the granularity at
which the ACL model can vary: per file object, per file system, or
uniformly across the entire server.
In Linux, the ACL model is determined by the SB_POSIXACL superblock
flag, which applies uniformly to all files within a file system.
Different exported file systems can have different ACL models, but
individual files cannot differ from their containing file system.
ACL_SCOPE_FILE_SYSTEM accurately reflects this behavior.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4xdr.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 9b47cf17ddde..63295cff23ed 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3484,6 +3484,14 @@ static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr,
return nfs_ok;
}
+static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr,
+ const struct nfsd4_fattr_args *args)
+{
+ if (!xdrgen_encode_aclscope4(xdr, ACL_SCOPE_FILE_SYSTEM))
+ return nfserr_resource;
+ return nfs_ok;
+}
+
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
@@ -3596,8 +3604,10 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform,
+ [FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4_acl_trueform_scope,
#else
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop,
+ [FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4__noop,
#endif
};
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 06/13] NFSD: Add nfsd4_encode_fattr4_posix_default_acl
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (4 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 05/13] NFSD: Add nfsd4_encode_fattr4_acl_trueform_scope Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 07/13] NFSD: Add nfsd4_encode_fattr4_posix_access_acl Chuck Lever
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
The POSIX ACL extension to NFSv4 defines FATTR4_POSIX_DEFAULT_ACL
for retrieving a directory's default ACL. This patch adds the
XDR encoder for that attribute.
For directories, the default ACL is retrieved via get_inode_acl()
and each entry is encoded as a posixace4: tag type, permission
bits, and principal name (empty for structural entries like
USER_OBJ/GROUP_OBJ/MASK/OTHER, resolved via idmapping for
USER/GROUP entries).
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4xdr.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 63295cff23ed..781f662d8918 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -43,6 +43,7 @@
#include <linux/sunrpc/addr.h>
#include <linux/xattr.h>
#include <linux/vmalloc.h>
+#include <linux/nfsacl.h>
#include <uapi/linux/xattr.h>
@@ -2849,6 +2850,89 @@ nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
{ return 0; }
#endif
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+
+static int nfsd4_posix_tagtotype(short tag)
+{
+ switch (tag) {
+ case ACL_USER_OBJ: return POSIXACE4_TAG_USER_OBJ;
+ case ACL_GROUP_OBJ: return POSIXACE4_TAG_GROUP_OBJ;
+ case ACL_USER: return POSIXACE4_TAG_USER;
+ case ACL_GROUP: return POSIXACE4_TAG_GROUP;
+ case ACL_MASK: return POSIXACE4_TAG_MASK;
+ case ACL_OTHER: return POSIXACE4_TAG_OTHER;
+ default: return -EINVAL;
+ }
+}
+
+static __be32
+nfsd4_encode_posixace4(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+ struct posix_acl_entry *acep)
+{
+ __be32 status;
+ int type;
+
+ type = nfsd4_posix_tagtotype(acep->e_tag);
+ if (type < 0)
+ return nfserr_resource;
+ if (!xdrgen_encode_posixacetag4(xdr, type))
+ return nfserr_resource;
+ if (!xdrgen_encode_posixaceperm4(xdr, acep->e_perm))
+ return nfserr_resource;
+
+ /* who */
+ switch (acep->e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
+ return nfserr_resource;
+ break;
+ case ACL_USER:
+ status = nfsd4_encode_user(xdr, rqstp, acep->e_uid);
+ if (status != nfs_ok)
+ return status;
+ break;
+ case ACL_GROUP:
+ status = nfsd4_encode_group(xdr, rqstp, acep->e_gid);
+ if (status != nfs_ok)
+ return status;
+ break;
+ default:
+ return nfserr_resource;
+ }
+ return nfs_ok;
+}
+
+static __be32
+nfsd4_encode_posixacl(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+ struct posix_acl *acl)
+{
+ __be32 status;
+ int i;
+
+ if (!acl) {
+ if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
+ return nfserr_resource;
+ return nfs_ok;
+ }
+
+ if (acl->a_count > NFS_ACL_MAX_ENTRIES)
+ return nfserr_resource;
+ if (xdr_stream_encode_u32(xdr, acl->a_count) != XDR_UNIT)
+ return nfserr_resource;
+ for (i = 0; i < acl->a_count; i++) {
+ status = nfsd4_encode_posixace4(xdr, rqstp, &acl->a_entries[i]);
+ if (status != nfs_ok)
+ return status;
+ }
+
+ return nfs_ok;
+}
+
+#endif /* CONFIG_NFSD_V4_POSIX_ACL */
+
static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32 *rdattr_err)
{
/* As per referral draft: */
@@ -2929,6 +3013,9 @@ struct nfsd4_fattr_args {
u64 change_attr;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
struct lsm_context context;
+#endif
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ struct posix_acl *dpacl;
#endif
u32 rdattr_err;
bool contextsupport;
@@ -3492,6 +3579,12 @@ static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr,
return nfs_ok;
}
+static __be32 nfsd4_encode_fattr4_posix_default_acl(struct xdr_stream *xdr,
+ const struct nfsd4_fattr_args *args)
+{
+ return nfsd4_encode_posixacl(xdr, args->rqstp, args->dpacl);
+}
+
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
@@ -3605,9 +3698,11 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform,
[FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4_acl_trueform_scope,
+ [FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4_posix_default_acl,
#else
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop,
[FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4__noop,
+ [FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4__noop,
#endif
};
@@ -3649,6 +3744,9 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
args.context.context = NULL;
#endif
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ args.dpacl = NULL;
+#endif
/*
* Make a local copy of the attribute bitmap that can be modified.
@@ -3755,6 +3853,32 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
}
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ if (attrmask[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) {
+ struct inode *inode = d_inode(dentry);
+ struct posix_acl *dpacl;
+
+ if (S_ISDIR(inode->i_mode)) {
+ dpacl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
+ if (IS_ERR(dpacl)) {
+ switch (PTR_ERR(dpacl)) {
+ case -EOPNOTSUPP:
+ attrmask[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL;
+ break;
+ case -EINVAL:
+ status = nfserr_attrnotsupp;
+ goto out;
+ default:
+ err = PTR_ERR(dpacl);
+ goto out_nfserr;
+ }
+ } else {
+ args.dpacl = dpacl;
+ }
+ }
+ }
+#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
+
/* attrmask */
status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
attrmask[2]);
@@ -3778,6 +3902,10 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
status = nfs_ok;
out:
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ if (args.dpacl)
+ posix_acl_release(args.dpacl);
+#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if (args.context.context)
security_release_secctx(&args.context);
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 07/13] NFSD: Add nfsd4_encode_fattr4_posix_access_acl
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (5 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 06/13] NFSD: Add nfsd4_encode_fattr4_posix_default_acl Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 08/13] NFSD: Do not allow NFSv4 (N)VERIFY to check POSIX ACL attributes Chuck Lever
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
The POSIX ACL extension to NFSv4 defines FATTR4_POSIX_ACCESS_ACL
for retrieving the access ACL of a file or directory. This patch
adds the XDR encoder for that attribute.
The access ACL is retrieved via get_inode_acl(). If the filesystem
provides no explicit access ACL, one is synthesized from the file
mode via posix_acl_from_mode(). Each entry is encoded as a
posixace4: tag type, permission bits, and principal name (empty
for structural entries, resolved via idmapping for USER/GROUP
entries).
Unlike the default ACL encoder which applies only to directories,
this encoder handles all inode types and ensures an access ACL is
always available through mode-based synthesis when needed.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4xdr.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 781f662d8918..358fa014be15 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3016,6 +3016,7 @@ struct nfsd4_fattr_args {
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
struct posix_acl *dpacl;
+ struct posix_acl *pacl;
#endif
u32 rdattr_err;
bool contextsupport;
@@ -3585,6 +3586,12 @@ static __be32 nfsd4_encode_fattr4_posix_default_acl(struct xdr_stream *xdr,
return nfsd4_encode_posixacl(xdr, args->rqstp, args->dpacl);
}
+static __be32 nfsd4_encode_fattr4_posix_access_acl(struct xdr_stream *xdr,
+ const struct nfsd4_fattr_args *args)
+{
+ return nfsd4_encode_posixacl(xdr, args->rqstp, args->pacl);
+}
+
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
@@ -3699,10 +3706,12 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4_acl_trueform,
[FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4_acl_trueform_scope,
[FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4_posix_default_acl,
+ [FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4_posix_access_acl,
#else
[FATTR4_ACL_TRUEFORM] = nfsd4_encode_fattr4__noop,
[FATTR4_ACL_TRUEFORM_SCOPE] = nfsd4_encode_fattr4__noop,
[FATTR4_POSIX_DEFAULT_ACL] = nfsd4_encode_fattr4__noop,
+ [FATTR4_POSIX_ACCESS_ACL] = nfsd4_encode_fattr4__noop,
#endif
};
@@ -3746,6 +3755,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#endif
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
args.dpacl = NULL;
+ args.pacl = NULL;
#endif
/*
@@ -3877,6 +3887,29 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
}
}
}
+ if (attrmask[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) {
+ struct inode *inode = d_inode(dentry);
+ struct posix_acl *pacl;
+
+ pacl = get_inode_acl(inode, ACL_TYPE_ACCESS);
+ if (!pacl)
+ pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+ if (IS_ERR(pacl)) {
+ switch (PTR_ERR(pacl)) {
+ case -EOPNOTSUPP:
+ attrmask[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
+ break;
+ case -EINVAL:
+ status = nfserr_attrnotsupp;
+ goto out;
+ default:
+ err = PTR_ERR(pacl);
+ goto out_nfserr;
+ }
+ } else {
+ args.pacl = pacl;
+ }
+ }
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
/* attrmask */
@@ -3905,6 +3938,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#ifdef CONFIG_NFSD_V4_POSIX_ACLS
if (args.dpacl)
posix_acl_release(args.dpacl);
+ if (args.pacl)
+ posix_acl_release(args.pacl);
#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if (args.context.context)
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 08/13] NFSD: Do not allow NFSv4 (N)VERIFY to check POSIX ACL attributes
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (6 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 07/13] NFSD: Add nfsd4_encode_fattr4_posix_access_acl Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 09/13] NFSD: Refactor nfsd_setattr()'s ACL error reporting Chuck Lever
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
Section 9.3 of draft-ietf-nfsv4-posix-acls-00 prohibits use of
the POSIX ACL attributes with VERIFY and NVERIFY operations: the
server MUST reply NFS4ERR_INVAL when a client attempts this.
Beyond the protocol requirement, comparison of POSIX draft ACLs
via (N)VERIFY presents an implementation challenge. Clients are
not required to order the ACEs within a POSIX ACL in any
particular way, making reliable attribute comparison impractical.
Return nfserr_inval when the client requests FATTR4_POSIX_ACCESS_ACL
or FATTR4_POSIX_DEFAULT_ACL in a VERIFY or NVERIFY operation.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4proc.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index e7ec87b6c331..a77ec0685eee 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -2380,6 +2380,11 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (verify->ve_attrlen & 3)
return nfserr_inval;
+ /* The POSIX draft ACLs cannot be tested via (N)VERIFY. */
+ if (verify->ve_bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
+ FATTR4_WORD2_POSIX_ACCESS_ACL))
+ return nfserr_inval;
+
/* count in words:
* bitmap_len(1) + bitmap(2) + attr_len(1) = 4
*/
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 09/13] NFSD: Refactor nfsd_setattr()'s ACL error reporting
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (7 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 08/13] NFSD: Do not allow NFSv4 (N)VERIFY to check POSIX ACL attributes Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 10/13] NFSD: Add support for XDR decoding POSIX draft ACLs Chuck Lever
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
Support for FATTR4_POSIX_ACCESS_ACL and FATTR4_POSIX_DEFAULT_ACL
attributes in subsequent patches allows clients to set both ACL
types simultaneously during SETATTR and file creation. Each ACL
type can succeed or fail independently, requiring the server to
clear individual attribute bits in the reply bitmap when one
fails while the other succeeds.
The existing na_aclerr field cannot distinguish which ACL type
encountered an error. Separate error fields (na_paclerr for
access ACLs, na_dpaclerr for default ACLs) enable the server to
report per-ACL-type failures accurately.
This refactoring also adds validation previously absent: default
ACL processing rejects non-directory targets with EINVAL and
passes NULL to set_posix_acl() when a_count is zero to delete
the ACL. Access ACL processing rejects zero a_count with EINVAL
for ACL_SCOPE_FILE_SYSTEM semantics (the only scope currently
supported).
The changes preserve compatibility with existing NFSv4 ACL code.
NFSv4 ACL conversion (nfs4_acl_nfsv4_to_posix()) never produces
POSIX ACLs with a_count == 0, so the new validation logic only
affects future POSIX ACL attribute handling.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4proc.c | 8 +++++---
fs/nfsd/vfs.c | 34 +++++++++++++++++++++++++++-------
fs/nfsd/vfs.h | 3 ++-
3 files changed, 34 insertions(+), 11 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a77ec0685eee..2da092f9ac40 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -377,7 +377,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (attrs.na_labelerr)
open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
- if (attrs.na_aclerr)
+ if (attrs.na_paclerr || attrs.na_dpaclerr)
open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
out:
end_creating(child);
@@ -858,7 +858,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (attrs.na_labelerr)
create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
- if (attrs.na_aclerr)
+ if (attrs.na_paclerr || attrs.na_dpaclerr)
create->cr_bmval[0] &= ~FATTR4_WORD0_ACL;
set_change_info(&create->cr_cinfo, &cstate->current_fh);
fh_dup2(&cstate->current_fh, &resfh);
@@ -1232,7 +1232,9 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!status)
status = nfserrno(attrs.na_labelerr);
if (!status)
- status = nfserrno(attrs.na_aclerr);
+ status = nfserrno(attrs.na_dpaclerr);
+ if (!status)
+ status = nfserrno(attrs.na_paclerr);
out:
nfsd_attrs_free(&attrs);
fh_drop_write(&cstate->current_fh);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 168d3ccc8155..c884c3f34afb 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -596,15 +596,35 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (attr->na_seclabel && attr->na_seclabel->len)
attr->na_labelerr = security_inode_setsecctx(dentry,
attr->na_seclabel->data, attr->na_seclabel->len);
- if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl)
- attr->na_aclerr = set_posix_acl(&nop_mnt_idmap,
- dentry, ACL_TYPE_ACCESS,
- attr->na_pacl);
- if (IS_ENABLED(CONFIG_FS_POSIX_ACL) &&
- !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode))
- attr->na_aclerr = set_posix_acl(&nop_mnt_idmap,
+ if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_dpacl) {
+ if (!S_ISDIR(inode->i_mode))
+ attr->na_dpaclerr = -EINVAL;
+ else if (attr->na_dpacl->a_count > 0)
+ /* a_count == 0 means delete the ACL. */
+ attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap,
dentry, ACL_TYPE_DEFAULT,
attr->na_dpacl);
+ else
+ attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap,
+ dentry, ACL_TYPE_DEFAULT,
+ NULL);
+ }
+ if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) {
+ /*
+ * For any file system that is not ACL_SCOPE_FILE_OBJECT,
+ * a_count == 0 MUST reply nfserr_inval.
+ * For a file system that is ACL_SCOPE_FILE_OBJECT,
+ * a_count == 0 deletes the ACL.
+ * XXX File systems that are ACL_SCOPE_FILE_OBJECT
+ * are not yet supported.
+ */
+ if (attr->na_pacl->a_count > 0)
+ attr->na_paclerr = set_posix_acl(&nop_mnt_idmap,
+ dentry, ACL_TYPE_ACCESS,
+ attr->na_pacl);
+ else
+ attr->na_paclerr = -EINVAL;
+ }
out_fill_attrs:
/*
* RFC 1813 Section 3.3.2 does not mandate that an NFS server
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index e192dca4a679..702a844f2106 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -53,7 +53,8 @@ struct nfsd_attrs {
struct posix_acl *na_dpacl; /* input */
int na_labelerr; /* output */
- int na_aclerr; /* output */
+ int na_dpaclerr; /* output */
+ int na_paclerr; /* output */
};
static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 10/13] NFSD: Add support for XDR decoding POSIX draft ACLs
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (8 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 09/13] NFSD: Refactor nfsd_setattr()'s ACL error reporting Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 11/13] NFSD: Add support for POSIX draft ACLs for file creation Chuck Lever
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
The POSIX ACL extension to NFSv4 defines FATTR4_POSIX_ACCESS_ACL
and FATTR4_POSIX_DEFAULT_ACL for setting access and default ACLs
via CREATE, OPEN, and SETATTR operations. This patch adds the XDR
decoders for those attributes.
The nfsd4_decode_fattr4() function gains two additional parameters
for receiving decoded POSIX ACLs. CREATE, OPEN, and SETATTR
decoders pass pointers to these new parameters, enabling clients
to set POSIX ACLs during object creation or modification.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/acl.h | 1 +
fs/nfsd/nfs4acl.c | 17 ++++--
fs/nfsd/nfs4xdr.c | 148 ++++++++++++++++++++++++++++++++++++++++++++--
fs/nfsd/xdr4.h | 6 ++
4 files changed, 162 insertions(+), 10 deletions(-)
diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 4b7324458a94..2003523d0e65 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -49,5 +49,6 @@ int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
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);
#endif /* LINUX_NFS4_ACL_H */
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 936ea1ad9586..2c2f2fd89e87 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -369,12 +369,21 @@ pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2)
return false;
}
-static void
-sort_pacl_range(struct posix_acl *pacl, int start, int end) {
+/**
+ * sort_pacl_range - sort a range of POSIX ACL entries by tag and id
+ * @pacl: POSIX ACL containing entries to sort
+ * @start: starting index of range to sort
+ * @end: ending index of range to sort (inclusive)
+ *
+ * Sorts ACL entries in place so that USER entries are ordered by UID
+ * and GROUP entries are ordered by GID. Required before calling
+ * posix_acl_valid().
+ */
+void sort_pacl_range(struct posix_acl *pacl, int start, int end)
+{
int sorted = 0, i;
- /* We just do a bubble sort; easy to do in place, and we're not
- * expecting acl's to be long enough to justify anything more. */
+ /* Bubble sort: acceptable here because ACLs are typically short. */
while (!sorted) {
sorted = 1;
for (i = start; i < end; i++) {
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 358fa014be15..5172dbd0cb05 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -378,10 +378,111 @@ nfsd4_decode_security_label(struct nfsd4_compoundargs *argp,
return nfs_ok;
}
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+
+static short nfsd4_posixacetag4_to_tag(posixacetag4 tag)
+{
+ switch (tag) {
+ case POSIXACE4_TAG_USER_OBJ: return ACL_USER_OBJ;
+ case POSIXACE4_TAG_GROUP_OBJ: return ACL_GROUP_OBJ;
+ case POSIXACE4_TAG_USER: return ACL_USER;
+ case POSIXACE4_TAG_GROUP: return ACL_GROUP;
+ case POSIXACE4_TAG_MASK: return ACL_MASK;
+ case POSIXACE4_TAG_OTHER: return ACL_OTHER;
+ }
+ return ACL_OTHER;
+}
+
+static __be32
+nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp,
+ struct posix_acl_entry *ace)
+{
+ posixaceperm4 perm;
+ __be32 *p, status;
+ posixacetag4 tag;
+ u32 len;
+
+ if (!xdrgen_decode_posixacetag4(argp->xdr, &tag))
+ return nfserr_bad_xdr;
+ ace->e_tag = nfsd4_posixacetag4_to_tag(tag);
+
+ if (!xdrgen_decode_posixaceperm4(argp->xdr, &perm))
+ return nfserr_bad_xdr;
+ if (perm & ~S_IRWXO)
+ return nfserr_bad_xdr;
+ ace->e_perm = perm;
+
+ if (xdr_stream_decode_u32(argp->xdr, &len) < 0)
+ return nfserr_bad_xdr;
+ p = xdr_inline_decode(argp->xdr, len);
+ if (!p)
+ return nfserr_bad_xdr;
+ switch (tag) {
+ case POSIXACE4_TAG_USER:
+ if (len > 0)
+ status = nfsd_map_name_to_uid(argp->rqstp,
+ (char *)p, len, &ace->e_uid);
+ else
+ status = nfserr_bad_xdr;
+ break;
+ case POSIXACE4_TAG_GROUP:
+ if (len > 0)
+ status = nfsd_map_name_to_gid(argp->rqstp,
+ (char *)p, len, &ace->e_gid);
+ else
+ status = nfserr_bad_xdr;
+ break;
+ default:
+ status = nfs_ok;
+ }
+
+ return status;
+}
+
+static noinline __be32
+nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
+{
+ struct posix_acl_entry *ace;
+ __be32 status;
+ u32 count;
+
+ if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
+ return nfserr_bad_xdr;
+
+ *acl = posix_acl_alloc(count, GFP_KERNEL);
+ if (*acl == NULL)
+ return nfserr_resource;
+
+ (*acl)->a_count = count;
+ for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) {
+ status = nfsd4_decode_posixace4(argp, ace);
+ if (status) {
+ posix_acl_release(*acl);
+ *acl = NULL;
+ return status;
+ }
+ }
+
+ /*
+ * posix_acl_valid() requires the ACEs to be sorted.
+ * If they are already sorted, sort_pacl_range() will return
+ * after one pass through the ACEs, since it implements bubble sort.
+ * Note that a count == 0 is used to delete a POSIX ACL and a count
+ * of 1 or 2 will always be found invalid by posix_acl_valid().
+ */
+ if (count >= 3)
+ sort_pacl_range(*acl, 0, count - 1);
+
+ return nfs_ok;
+}
+
+#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
+
static __be32
nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
struct iattr *iattr, struct nfs4_acl **acl,
- struct xdr_netobj *label, int *umask)
+ struct xdr_netobj *label, int *umask,
+ struct posix_acl **dpaclp, struct posix_acl **paclp)
{
unsigned int starting_pos;
u32 attrlist4_count;
@@ -544,9 +645,40 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG;
}
+ *dpaclp = NULL;
+ *paclp = NULL;
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ if (bmval[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) {
+ struct posix_acl *dpacl;
+
+ status = nfsd4_decode_posixacl(argp, &dpacl);
+ if (status)
+ return status;
+ *dpaclp = dpacl;
+ }
+ if (bmval[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) {
+ struct posix_acl *pacl;
+
+ status = nfsd4_decode_posixacl(argp, &pacl);
+ if (status) {
+ posix_acl_release(*dpaclp);
+ *dpaclp = NULL;
+ return status;
+ }
+ *paclp = pacl;
+ }
+#endif /* CONFIG_NFSD_V4_POSIX_ACLS */
+
/* request sanity: did attrlist4 contain the expected number of words? */
- if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
+ if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos) {
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+ posix_acl_release(*dpaclp);
+ posix_acl_release(*paclp);
+ *dpaclp = NULL;
+ *paclp = NULL;
+#endif
return nfserr_bad_xdr;
+ }
return nfs_ok;
}
@@ -850,7 +982,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
status = nfsd4_decode_fattr4(argp, create->cr_bmval,
ARRAY_SIZE(create->cr_bmval),
&create->cr_iattr, &create->cr_acl,
- &create->cr_label, &create->cr_umask);
+ &create->cr_label, &create->cr_umask,
+ &create->cr_dpacl, &create->cr_pacl);
if (status)
return status;
@@ -1001,7 +1134,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open
status = nfsd4_decode_fattr4(argp, open->op_bmval,
ARRAY_SIZE(open->op_bmval),
&open->op_iattr, &open->op_acl,
- &open->op_label, &open->op_umask);
+ &open->op_label, &open->op_umask,
+ &open->op_dpacl, &open->op_pacl);
if (status)
return status;
break;
@@ -1019,7 +1153,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open
status = nfsd4_decode_fattr4(argp, open->op_bmval,
ARRAY_SIZE(open->op_bmval),
&open->op_iattr, &open->op_acl,
- &open->op_label, &open->op_umask);
+ &open->op_label, &open->op_umask,
+ &open->op_dpacl, &open->op_pacl);
if (status)
return status;
break;
@@ -1346,7 +1481,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
ARRAY_SIZE(setattr->sa_bmval),
&setattr->sa_iattr, &setattr->sa_acl,
- &setattr->sa_label, NULL);
+ &setattr->sa_label, NULL, &setattr->sa_dpacl,
+ &setattr->sa_pacl);
}
static __be32
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 1be2814b5288..417e9ad9fbb3 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -245,6 +245,8 @@ struct nfsd4_create {
int cr_umask; /* request */
struct nfsd4_change_info cr_cinfo; /* response */
struct nfs4_acl *cr_acl;
+ struct posix_acl *cr_dpacl;
+ struct posix_acl *cr_pacl;
struct xdr_netobj cr_label;
};
#define cr_datalen u.link.datalen
@@ -397,6 +399,8 @@ struct nfsd4_open {
struct nfs4_ol_stateid *op_stp; /* used during processing */
struct nfs4_clnt_odstate *op_odstate; /* used during processing */
struct nfs4_acl *op_acl;
+ struct posix_acl *op_dpacl;
+ struct posix_acl *op_pacl;
struct xdr_netobj op_label;
struct svc_rqst *op_rqstp;
};
@@ -483,6 +487,8 @@ struct nfsd4_setattr {
struct iattr sa_iattr; /* request */
struct nfs4_acl *sa_acl;
struct xdr_netobj sa_label;
+ struct posix_acl *sa_dpacl;
+ struct posix_acl *sa_pacl;
};
struct nfsd4_setclientid {
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 11/13] NFSD: Add support for POSIX draft ACLs for file creation
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (9 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 10/13] NFSD: Add support for XDR decoding POSIX draft ACLs Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 12/13] NFSD: Add POSIX draft ACL support to the NFSv4 SETATTR operation Chuck Lever
2026-01-09 16:21 ` [PATCH v3 13/13] NFSD: Add POSIX ACL file attributes to SUPPATTR bitmasks Chuck Lever
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
NFSv4.2 clients can specify POSIX draft ACLs when creating file
objects via OPEN(CREATE) and CREATE operations. The previous patch
added POSIX ACL support to the NFSv4 SETATTR operation for modifying
existing objects, but file creation follows different code paths
that also require POSIX ACL handling.
This patch integrates POSIX ACL support into nfsd4_create() and
nfsd4_create_file(). Ownership of the decoded ACL pointers
(op_dpacl, op_pacl, cr_dpacl, cr_pacl) transfers to the nfsd_attrs
structure immediately, with the original fields cleared to NULL.
This transfer ensures nfsd_attrs_free() releases the ACLs upon
completion while preventing double-free on error paths.
Mutual exclusion between NFSv4 ACLs and POSIX ACLs is enforced:
setting both op_acl and op_dpacl/op_pacl simultaneously returns
nfserr_inval. Errors during ACL application clear the corresponding
bits in the result bitmask (fattr->bmval), signaling partial
completion to the client.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4proc.c | 62 +++++++++++++++++++++++++++++++++++++++-------
1 file changed, 53 insertions(+), 9 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 2da092f9ac40..72edf4add536 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -91,6 +91,10 @@ check_attr_support(struct nfsd4_compound_state *cstate, u32 *bmval,
return nfserr_attrnotsupp;
if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry)))
return nfserr_attrnotsupp;
+ if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
+ FATTR4_WORD2_POSIX_ACCESS_ACL)) &&
+ !IS_POSIXACL(d_inode(dentry)))
+ return nfserr_attrnotsupp;
if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) &&
!(exp->ex_flags & NFSEXP_SECURITY_LABEL))
return nfserr_attrnotsupp;
@@ -265,8 +269,20 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (host_err)
return nfserrno(host_err);
- if (is_create_with_attrs(open))
- nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
+ if (open->op_acl) {
+ if (open->op_dpacl || open->op_pacl) {
+ status = nfserr_inval;
+ goto out_write;
+ }
+ if (is_create_with_attrs(open))
+ nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
+ } else if (is_create_with_attrs(open)) {
+ /* The dpacl and pacl will get released by nfsd_attrs_free(). */
+ attrs.na_dpacl = open->op_dpacl;
+ attrs.na_pacl = open->op_pacl;
+ open->op_dpacl = NULL;
+ open->op_pacl = NULL;
+ }
child = start_creating(&nop_mnt_idmap, parent,
&QSTR_LEN(open->op_fname, open->op_fnamelen));
@@ -379,6 +395,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
if (attrs.na_paclerr || attrs.na_dpaclerr)
open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
+ if (attrs.na_dpaclerr)
+ open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL;
+ if (attrs.na_paclerr)
+ open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
out:
end_creating(child);
nfsd_attrs_free(&attrs);
@@ -546,8 +566,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
open->op_rqstp = rqstp;
/* This check required by spec. */
- if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
- return nfserr_inval;
+ if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) {
+ status = nfserr_inval;
+ goto out_err;
+ }
open->op_created = false;
/*
@@ -556,8 +578,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
*/
if (nfsd4_has_session(cstate) &&
!test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) &&
- open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
- return nfserr_grace;
+ open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) {
+ status = nfserr_grace;
+ goto out_err;
+ }
if (nfsd4_has_session(cstate))
copy_clientid(&open->op_clientid, cstate->session);
@@ -644,6 +668,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
nfsd4_cleanup_open_state(cstate, open);
nfsd4_bump_seqid(cstate, status);
+out_err:
+ posix_acl_release(open->op_dpacl);
+ posix_acl_release(open->op_pacl);
return status;
}
@@ -784,22 +811,34 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd_attrs attrs = {
.na_iattr = &create->cr_iattr,
.na_seclabel = &create->cr_label,
+ .na_dpacl = create->cr_dpacl,
+ .na_pacl = create->cr_pacl,
};
struct svc_fh resfh;
__be32 status;
dev_t rdev;
+ create->cr_dpacl = NULL;
+ create->cr_pacl = NULL;
+
fh_init(&resfh, NFS4_FHSIZE);
status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_NOP);
if (status)
- return status;
+ goto out_aftermask;
status = check_attr_support(cstate, create->cr_bmval, nfsd_attrmask);
if (status)
- return status;
+ goto out_aftermask;
- status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs);
+ if (create->cr_acl) {
+ if (create->cr_dpacl || create->cr_pacl) {
+ status = nfserr_inval;
+ goto out_aftermask;
+ }
+ status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl,
+ &attrs);
+ }
current->fs->umask = create->cr_umask;
switch (create->cr_type) {
case NF4LNK:
@@ -860,12 +899,17 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
if (attrs.na_paclerr || attrs.na_dpaclerr)
create->cr_bmval[0] &= ~FATTR4_WORD0_ACL;
+ if (attrs.na_dpaclerr)
+ create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL;
+ if (attrs.na_paclerr)
+ create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL;
set_change_info(&create->cr_cinfo, &cstate->current_fh);
fh_dup2(&cstate->current_fh, &resfh);
out:
fh_put(&resfh);
out_umask:
current->fs->umask = 0;
+out_aftermask:
nfsd_attrs_free(&attrs);
return status;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 12/13] NFSD: Add POSIX draft ACL support to the NFSv4 SETATTR operation
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (10 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 11/13] NFSD: Add support for POSIX draft ACLs for file creation Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
2026-01-09 16:21 ` [PATCH v3 13/13] NFSD: Add POSIX ACL file attributes to SUPPATTR bitmasks Chuck Lever
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Rick Macklem
From: Rick Macklem <rmacklem@uoguelph.ca>
The POSIX ACL extension to NFSv4 enables clients to set access
and default ACLs via FATTR4_POSIX_ACCESS_ACL and
FATTR4_POSIX_DEFAULT_ACL attributes. Integration of these
attributes into SETATTR processing requires wiring them through
the nfsd_attrs structure and ensuring proper cleanup on all
code paths.
This patch connects the na_pacl and na_dpacl fields in
nfsd_attrs to the decoded ACL pointers from the NFSv4 SETATTR
decoder. Ownership of these ACL references transfers to attrs
immediately after initialization, with the decoder's pointers
cleared to NULL. This transfer ensures nfsd_attrs_free()
releases the ACLs on normal completion, while new error paths
call posix_acl_release() directly when cleanup occurs before
nfsd_attrs_free() runs.
Early returns in the nfsd4_setattr() function gain conversions
to goto statements that branch to proper cleanup handlers.
Error paths before fh_want_write() branch to out_err for ACL
release only; paths after fh_want_write() use the existing out
label for full cleanup via nfsd_attrs_free().
The patch adds mutual exclusion between NFSv4 ACLs (sa_acl) and
POSIX ACLs. Setting both types simultaneously returns
nfserr_inval because these ACL models cannot coexist on the
same file object.
Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfs4proc.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 72edf4add536..d476d108f6e1 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1214,6 +1214,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd_attrs attrs = {
.na_iattr = &setattr->sa_iattr,
.na_seclabel = &setattr->sa_label,
+ .na_pacl = setattr->sa_pacl,
+ .na_dpacl = setattr->sa_dpacl,
};
bool save_no_wcc, deleg_attrs;
struct nfs4_stid *st = NULL;
@@ -1221,6 +1223,10 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status = nfs_ok;
int err;
+ /* Transfer ownership to attrs for cleanup via nfsd_attrs_free() */
+ setattr->sa_pacl = NULL;
+ setattr->sa_dpacl = NULL;
+
deleg_attrs = setattr->sa_bmval[2] & (FATTR4_WORD2_TIME_DELEG_ACCESS |
FATTR4_WORD2_TIME_DELEG_MODIFY);
@@ -1234,7 +1240,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&cstate->current_fh, &setattr->sa_stateid,
flags, NULL, &st);
if (status)
- return status;
+ goto out_err;
}
if (deleg_attrs) {
@@ -1252,17 +1258,24 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (st)
nfs4_put_stid(st);
if (status)
- return status;
+ goto out_err;
err = fh_want_write(&cstate->current_fh);
- if (err)
- return nfserrno(err);
+ if (err) {
+ status = nfserrno(err);
+ goto out_err;
+ }
status = nfs_ok;
status = check_attr_support(cstate, setattr->sa_bmval, nfsd_attrmask);
if (status)
goto out;
+ if (setattr->sa_acl && (attrs.na_dpacl || attrs.na_pacl)) {
+ status = nfserr_inval;
+ 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);
@@ -1280,8 +1293,9 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!status)
status = nfserrno(attrs.na_paclerr);
out:
- nfsd_attrs_free(&attrs);
fh_drop_write(&cstate->current_fh);
+out_err:
+ nfsd_attrs_free(&attrs);
return status;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 13/13] NFSD: Add POSIX ACL file attributes to SUPPATTR bitmasks
2026-01-09 16:21 [PATCH v3 00/13] Add NFSv4.2 POSIX ACL support Chuck Lever
` (11 preceding siblings ...)
2026-01-09 16:21 ` [PATCH v3 12/13] NFSD: Add POSIX draft ACL support to the NFSv4 SETATTR operation Chuck Lever
@ 2026-01-09 16:21 ` Chuck Lever
12 siblings, 0 replies; 14+ messages in thread
From: Chuck Lever @ 2026-01-09 16:21 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: linux-nfs, Chuck Lever
From: Chuck Lever <chuck.lever@oracle.com>
Now that infrastructure for NFSv4 POSIX draft ACL has been added
to NFSD, it should be safe to advertise support to NFS clients.
NFSD_SUPPATTR_EXCLCREAT_WORD2 includes NFSv4.2-only attributes,
but version filtering occurs via nfsd_suppattrs[] before this
mask is applied, ensuring pre-4.2 clients never see unsupported
attributes.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/nfsd.h | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index b0283213a8f5..a01d70953358 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -454,6 +454,16 @@ enum {
#define NFSD4_2_SECURITY_ATTRS 0
#endif
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+#define NFSD4_2_POSIX_ACL_ATTRS \
+ (FATTR4_WORD2_ACL_TRUEFORM | \
+ FATTR4_WORD2_ACL_TRUEFORM_SCOPE | \
+ FATTR4_WORD2_POSIX_DEFAULT_ACL | \
+ FATTR4_WORD2_POSIX_ACCESS_ACL)
+#else
+#define NFSD4_2_POSIX_ACL_ATTRS 0
+#endif
+
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_MODE_UMASK | \
@@ -462,7 +472,8 @@ enum {
FATTR4_WORD2_XATTR_SUPPORT | \
FATTR4_WORD2_TIME_DELEG_ACCESS | \
FATTR4_WORD2_TIME_DELEG_MODIFY | \
- FATTR4_WORD2_OPEN_ARGUMENTS)
+ FATTR4_WORD2_OPEN_ARGUMENTS | \
+ NFSD4_2_POSIX_ACL_ATTRS)
extern const u32 nfsd_suppattrs[3][3];
@@ -530,11 +541,18 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
#else
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL 0
#endif
+#ifdef CONFIG_NFSD_V4_POSIX_ACLS
+#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \
+ FATTR4_WORD2_POSIX_DEFAULT_ACL | FATTR4_WORD2_POSIX_ACCESS_ACL
+#else
+#define MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS 0
+#endif
#define NFSD_WRITEABLE_ATTRS_WORD2 \
(FATTR4_WORD2_MODE_UMASK \
| MAYBE_FATTR4_WORD2_SECURITY_LABEL \
| FATTR4_WORD2_TIME_DELEG_ACCESS \
| FATTR4_WORD2_TIME_DELEG_MODIFY \
+ | MAYBE_FATTR4_WORD2_POSIX_ACL_ATTRS \
)
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
@@ -550,6 +568,10 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
* The FATTR4_WORD2_TIME_DELEG attributes are not to be allowed for
* OPEN(create) with EXCLUSIVE4_1. It doesn't make sense to set a
* delegated timestamp on a new file.
+ *
+ * This mask includes NFSv4.2-only attributes (e.g., POSIX ACLs).
+ * Version filtering occurs via nfsd_suppattrs[] before this mask
+ * is applied, so pre-4.2 clients never see unsupported attributes.
*/
#define NFSD_SUPPATTR_EXCLCREAT_WORD2 \
(NFSD_WRITEABLE_ATTRS_WORD2 & \
--
2.52.0
^ permalink raw reply related [flat|nested] 14+ messages in thread