From 80cf5245aaa34c6b2cc745fb44ae398c3ad45108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kohlschu=CC=88tter?= Date: Fri, 18 Jul 2025 16:06:06 +0200 Subject: [PATCH] core: Make support for "stateless stateids" configurable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit c8d57f2a introduced support for stateless stateids. This may not be desirable, as this breaks the inherent sequence order of stateids, adding extra requirements for logging/journaling and advanced VirtualFileSystems. Moreover, this may leave the macOS NFSv4.0 client in a state where all stateids remain "stateless". Make support for this feature configurable by adding a flag to NFSv4StateHandler and move the support logic into NFSv4StateHandler. Also fix OperationREAD and OperationWRITE to get the NFSv4StateHandler only once per call from the context. Signed-off-by: Christian Kohlschütter --- .../org/dcache/nfs/v4/NFSv4StateHandler.java | 38 +++++++++++++++++++ .../java/org/dcache/nfs/v4/OperationREAD.java | 10 +++-- .../org/dcache/nfs/v4/OperationWRITE.java | 9 +++-- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java b/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java index b7d64a4d..fe095f6e 100644 --- a/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java +++ b/core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java @@ -109,6 +109,8 @@ public class NFSv4StateHandler { */ private final Clock _clock; + private boolean supportStateless = true; + public NFSv4StateHandler() { this(Duration.ofSeconds(NFSv4Defaults.NFS4_LEASE_TIME), 0, new EphemeralClientRecoveryStore()); } @@ -500,4 +502,40 @@ public Duration getLeaseTime() { return _leaseTime; } + /** + * Checks if stateless stateids are supported. + * + * @return true if supported. + */ + public boolean isSupportStateless() { + return supportStateless; + } + + /** + * Enables/disables support for stateless stateids. + * + * @param supportStateless flag. + */ + public void setSupportStateless(boolean supportStateless) { + this.supportStateless = supportStateless; + } + + /** + * Checks if the given stateid is stateless. If so but stateless stateids are not supported, a + * {@link BadStateidException} is thrown. + * + * @param stateid The stateid. + * @return {@code true} if stateless. + * @throws BadStateidException if stateless but not supported. + * @see #setSupportStateless(boolean) + */ + public boolean checkStatelessAndSupported(stateid4 stateid) throws BadStateidException { + if (Stateids.isStateLess(stateid)) { + if (supportStateless) { + return true; + } + throw new BadStateidException(); + } + return false; + } } diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationREAD.java b/core/src/main/java/org/dcache/nfs/v4/OperationREAD.java index 2dde9c9e..e52d4cf7 100644 --- a/core/src/main/java/org/dcache/nfs/v4/OperationREAD.java +++ b/core/src/main/java/org/dcache/nfs/v4/OperationREAD.java @@ -49,7 +49,9 @@ public void process(CompoundContext context, nfs_resop4 result) throws IOExcepti stateid4 stateid = Stateids.getCurrentStateidIfNeeded(context, _args.opread.stateid); var inode = context.currentInode(); - if (Stateids.isStateLess(stateid)) { + + NFSv4StateHandler stateHandler = context.getStateHandler(); + if (stateHandler.checkStatelessAndSupported(stateid)) { // Anonymous access as per RFC 7530 // https://datatracker.ietf.org/doc/html/rfc7530#section-9.1.4.3 // we only check file access rights. @@ -64,13 +66,13 @@ public void process(CompoundContext context, nfs_resop4 result) throws IOExcepti * * With introduction of sessions in v4.1 update of the lease time done through SEQUENCE operations. */ - context.getStateHandler().updateClientLeaseTime(stateid); - client = context.getStateHandler().getClientIdByStateId(stateid); + stateHandler.updateClientLeaseTime(stateid); + client = stateHandler.getClientIdByStateId(stateid); } else { client = context.getSession().getClient(); } - int shareAccess = context.getStateHandler().getFileTracker().getShareAccess(client, inode, stateid); + int shareAccess = stateHandler.getFileTracker().getShareAccess(client, inode, stateid); if ((shareAccess & nfs4_prot.OPEN4_SHARE_ACCESS_READ) == 0) { throw new OpenModeException("Invalid open mode"); } diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationWRITE.java b/core/src/main/java/org/dcache/nfs/v4/OperationWRITE.java index 11d72a14..b35d66c7 100644 --- a/core/src/main/java/org/dcache/nfs/v4/OperationWRITE.java +++ b/core/src/main/java/org/dcache/nfs/v4/OperationWRITE.java @@ -68,7 +68,8 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF } var inode = context.currentInode(); - if (Stateids.isStateLess(stateid)) { + NFSv4StateHandler stateHandler = context.getStateHandler(); + if (stateHandler.checkStatelessAndSupported(stateid)) { // Anonymous access as per RFC 7530 // https://datatracker.ietf.org/doc/html/rfc7530#section-9.1.4.3 // we only check file access rights. @@ -85,13 +86,13 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF * * With introduction of sessions in v4.1 update of the lease time done through SEQUENCE operations. */ - context.getStateHandler().updateClientLeaseTime(stateid); - client = context.getStateHandler().getClientIdByStateId(stateid); + stateHandler.updateClientLeaseTime(stateid); + client = stateHandler.getClientIdByStateId(stateid); } else { client = context.getSession().getClient(); } - int shareAccess = context.getStateHandler().getFileTracker().getShareAccess(client, inode, stateid); + int shareAccess = stateHandler.getFileTracker().getShareAccess(client, inode, stateid); if ((shareAccess & nfs4_prot.OPEN4_SHARE_ACCESS_WRITE) == 0) { throw new OpenModeException("Invalid open mode"); }