linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Chuck Lever <cel@kernel.org>
To: <linux-nfs@vger.kernel.org>
Cc: Calum Mackay <calum.mackay@oracle.com>,
	aurelien.couderc2002@gmail.com,
	Chuck Lever <chuck.lever@oracle.com>
Subject: [PATCH v2 3/9] Add a helper to compute POSIX mode bits from NFSv4 ACLs
Date: Tue, 25 Nov 2025 14:49:28 -0500	[thread overview]
Message-ID: <20251125194936.770792-4-cel@kernel.org> (raw)
In-Reply-To: <20251125194936.770792-1-cel@kernel.org>

From: Chuck Lever <chuck.lever@oracle.com>

NFSv4 servers are permitted to adjust a file's mode bits when
the file's ACL is set. RFC 8881 Section 6.3.2 describes what is
expected.

I'm about to add new tests that set an ACL and then retrieve
the file's mode to verify that the server is following that
section of RFC 8881. Mode bit verification is common, so it
is added as a utility function.

To avoid duplicating this code for both NFSv4.0 and NFSv4.1
ACL-related tests (I'm expecting to see a common NFSv4 ACL
document published soon), a symlink is added to make ACL
utility functions visible to tests for both minor versions.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 nfs4.0/nfs4acl.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++
 nfs4.1/nfs4acl.py |  1 +
 2 files changed, 61 insertions(+)
 create mode 120000 nfs4.1/nfs4acl.py

diff --git a/nfs4.0/nfs4acl.py b/nfs4.0/nfs4acl.py
index 69e0d0bcfbe1..62193123ef7c 100644
--- a/nfs4.0/nfs4acl.py
+++ b/nfs4.0/nfs4acl.py
@@ -208,6 +208,66 @@ def chk_groups(acl, flags, not_mask):
             chk_triple(mask, allow, acl[0], flags | GROUP, not_mask)
         del acl[:1]
 
+def acl2mode_rfc8881(acl):
+    """
+    Compute mode from ACL according to RFC 8881 Section 6.3.2.
+
+    For each special identifier (OWNER@, GROUP@, EVERYONE@), evaluate the
+    ACL in order considering only ALLOW and DENY ACEs for EVERYONE@ and
+    the identifier under consideration. Then translate to mode bits:
+    - Read bit: Set if ACE4_READ_DATA is permitted
+    - Write bit: Set if BOTH ACE4_WRITE_DATA AND ACE4_APPEND_DATA are permitted
+    - Execute bit: Set if ACE4_EXECUTE is permitted
+
+    Returns the low-order 9 bits of the mode (user/group/other permissions).
+    """
+    identifiers = [
+        ("OWNER@", MODE4_RUSR, MODE4_WUSR, MODE4_XUSR),
+        ("GROUP@", MODE4_RGRP, MODE4_WGRP, MODE4_XGRP),
+        ("EVERYONE@", MODE4_ROTH, MODE4_WOTH, MODE4_XOTH)
+    ]
+
+    mode = 0
+
+    for who, read_bit, write_bit, exec_bit in identifiers:
+        # Start with no permissions
+        allowed_mask = 0
+        denied_mask = 0
+
+        # Evaluate ACL in order, considering only ALLOW/DENY for this
+        # identifier and EVERYONE@
+        for ace in acl:
+            # Skip non-relevant ACEs
+            if ace.who not in (who, "EVERYONE@"):
+                continue
+            if ace.type not in (ALLOWED, DENIED):
+                continue
+            # Skip inherit-only ACEs (they don't affect current permissions)
+            if ace.flag & ACE4_INHERIT_ONLY_ACE:
+                continue
+
+            if ace.type == ALLOWED:
+                # Add allowed permissions not already denied
+                allowed_mask |= (ace.access_mask & ~denied_mask)
+            elif ace.type == DENIED:
+                # Add denied permissions not already allowed
+                denied_mask |= (ace.access_mask & ~allowed_mask)
+
+        # Translate permitted mask to mode bits per RFC 8881 §6.3.2
+        # Read bit: ACE4_READ_DATA must be set
+        if allowed_mask & ACE4_READ_DATA:
+            mode |= read_bit
+
+        # Write bit: BOTH ACE4_WRITE_DATA and ACE4_APPEND_DATA must be set
+        if (allowed_mask & ACE4_WRITE_DATA) and (allowed_mask & ACE4_APPEND_DATA):
+            mode |= write_bit
+
+        # Execute bit: ACE4_EXECUTE must be set
+        if allowed_mask & ACE4_EXECUTE:
+            mode |= exec_bit
+
+    return mode
+
 def printableacl(acl):
     type_str = ["ACCESS", "DENY"]
     out = ""
diff --git a/nfs4.1/nfs4acl.py b/nfs4.1/nfs4acl.py
new file mode 120000
index 000000000000..f05cb4015424
--- /dev/null
+++ b/nfs4.1/nfs4acl.py
@@ -0,0 +1 @@
+../nfs4.0/nfs4acl.py
\ No newline at end of file
-- 
2.51.1


  parent reply	other threads:[~2025-11-25 19:49 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-25 19:49 [PATCH v2 0/9] pynfs tests for setting ACL+MODE Chuck Lever
2025-11-25 19:49 ` [PATCH v2 1/9] Add helper to report unsupported protocol features Chuck Lever
2025-11-25 19:49 ` [PATCH v2 2/9] Add helper to format attribute bitmaps Chuck Lever
2025-11-25 19:49 ` Chuck Lever [this message]
2025-11-25 19:49 ` [PATCH v2 4/9] Add access_mask_to_str() helper to nfs4.0/nfs4acl.py Chuck Lever
2025-11-25 19:49 ` [PATCH v2 5/9] Add make_test_acl() helper to nfs4acl modules Chuck Lever
2025-11-25 19:49 ` [PATCH v2 6/9] Add verify_acl() " Chuck Lever
2025-11-25 19:49 ` [PATCH v2 7/9] Add verify_mode_and_acl() " Chuck Lever
2025-11-25 19:49 ` [PATCH v2 8/9] Add tests for SETATTR with MODE and ACL Chuck Lever
2025-11-25 19:49 ` [PATCH v2 9/9] Add tests for OPEN(create) with ACLs Chuck Lever

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251125194936.770792-4-cel@kernel.org \
    --to=cel@kernel.org \
    --cc=aurelien.couderc2002@gmail.com \
    --cc=calum.mackay@oracle.com \
    --cc=chuck.lever@oracle.com \
    --cc=linux-nfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).