From c3d73903f6eab8699b8d881359fdd2f7d1fdce05 Mon Sep 17 00:00:00 2001
From: David Kocher <dkocher@iterate.ch>
Date: Tue, 26 May 2020 08:51:13 +0200
Subject: [PATCH] Provide open and close callbacks to virtual filesystem
 implementations as described in #38.

Signed-off-by: David Kocher <dkocher@iterate.ch>
---
 .../java/org/dcache/nfs/v4/OperationCLOSE.java |  1 +
 .../java/org/dcache/nfs/v4/OperationOPEN.java  | 15 +++++++++++----
 .../main/java/org/dcache/nfs/vfs/PseudoFs.java | 11 +++++++++++
 .../main/java/org/dcache/nfs/vfs/VfsCache.java | 11 +++++++++++
 .../org/dcache/nfs/vfs/VirtualFileSystem.java  | 18 ++++++++++++++++++
 .../java/org/dcache/nfs/v4/NFS4ClientTest.java |  3 +++
 .../test/java/org/dcache/nfs/vfs/DummyVFS.java | 11 +++++++++++
 7 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationCLOSE.java b/core/src/main/java/org/dcache/nfs/v4/OperationCLOSE.java
index 9e98d9ff3..4f356b315 100644
--- a/core/src/main/java/org/dcache/nfs/v4/OperationCLOSE.java
+++ b/core/src/main/java/org/dcache/nfs/v4/OperationCLOSE.java
@@ -47,6 +47,7 @@ public void process(CompoundContext context, nfs_resop4 result)
         Inode inode = context.currentInode();
 
         stateid4 stateid = Stateids.getCurrentStateidIfNeeded(context, _args.opclose.open_stateid);
+        context.getFs().close(inode, stateid);
         NFS4Client client;
         if (context.getMinorversion() > 0) {
             client = context.getSession().getClient();
diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationOPEN.java b/core/src/main/java/org/dcache/nfs/v4/OperationOPEN.java
index 14d7ea3cc..8a422e52d 100644
--- a/core/src/main/java/org/dcache/nfs/v4/OperationOPEN.java
+++ b/core/src/main/java/org/dcache/nfs/v4/OperationOPEN.java
@@ -206,6 +206,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
                 }
 
                 context.currentInode(inode);
+                context.getFs().open(inode, this.getAccessMode(_args.opopen.share_access), result.opopen.resok4.stateid);
 
                 break;
             case open_claim_type4.CLAIM_PREVIOUS:
@@ -279,11 +280,10 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
     }
 
 
-    private void checkCanAccess(CompoundContext context, Inode inode, uint32_t share_access) throws IOException {
-
-        int accessMode;
+    private int getAccessMode(final uint32_t share_access) throws IOException {
+        final int accessMode;
 
-        switch (share_access.value & ~nfs4_prot.OPEN4_SHARE_ACCESS_WANT_DELEG_MASK) {
+        switch(share_access.value & ~nfs4_prot.OPEN4_SHARE_ACCESS_WANT_DELEG_MASK) {
             case nfs4_prot.OPEN4_SHARE_ACCESS_READ:
                 accessMode = nfs4_prot.ACCESS4_READ;
                 break;
@@ -296,6 +296,13 @@ private void checkCanAccess(CompoundContext context, Inode inode, uint32_t share
             default:
                 throw new InvalException("Invalid share_access mode: " + share_access.value);
         }
+        return accessMode;
+    }
+
+
+    private void checkCanAccess(CompoundContext context, Inode inode, uint32_t share_access) throws IOException {
+
+        int accessMode = getAccessMode(share_access);
 
         if (context.getFs().access(inode, accessMode) != accessMode) {
             throw new AccessException();
diff --git a/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java b/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java
index b0182b0d1..2b4176e62 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java
@@ -37,6 +37,7 @@
 import org.dcache.nfs.status.*;
 import org.dcache.nfs.v4.acl.Acls;
 import org.dcache.nfs.v4.xdr.acemask4;
+import org.dcache.nfs.v4.xdr.stateid4;
 import org.dcache.oncrpc4j.rpc.RpcCall;
 
 import static org.dcache.nfs.v4.xdr.nfs4_prot.*;
@@ -183,6 +184,16 @@ public Inode create(Inode parent, Stat.Type type, String path, Subject subject,
         return pushExportIndex(parent, _inner.create(parent, type, path, effectiveSubject, mode));
     }
 
+    @Override
+    public void open(Inode inode, int mode, stateid4 stateid) throws IOException {
+
+    }
+
+    @Override
+    public void close(Inode inode, stateid4 stateid) throws IOException {
+
+    }
+
     @Override
     public Inode getRootInode() throws IOException {
         /*
diff --git a/core/src/main/java/org/dcache/nfs/vfs/VfsCache.java b/core/src/main/java/org/dcache/nfs/vfs/VfsCache.java
index 97641633a..b46077e28 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/VfsCache.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/VfsCache.java
@@ -34,6 +34,7 @@
 import javax.security.auth.Subject;
 import org.dcache.nfs.util.GuavaCacheMXBeanImpl;
 import org.dcache.nfs.util.Opaque;
+import org.dcache.nfs.v4.xdr.stateid4;
 
 import static java.util.Objects.requireNonNull;
 
@@ -172,6 +173,16 @@ public Inode create(Inode parent, Stat.Type type, String path, Subject subject,
         return inode;
     }
 
+    @Override
+    public void open(Inode inode, int mode, stateid4 stateid) throws IOException {
+        _inner.open(inode, mode, stateid);
+    }
+
+    @Override
+    public void close(Inode inode, stateid4 stateid) throws IOException {
+        _inner.close(inode, stateid);
+    }
+
     @Override
     public Stat getattr(Inode inode) throws IOException {
         return statFromCacheOrLoad(inode);
diff --git a/core/src/main/java/org/dcache/nfs/vfs/VirtualFileSystem.java b/core/src/main/java/org/dcache/nfs/vfs/VirtualFileSystem.java
index f4cae7d5c..cebe6c3cb 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/VirtualFileSystem.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/VirtualFileSystem.java
@@ -25,6 +25,7 @@
 import org.dcache.nfs.v4.NfsIdMapping;
 import org.dcache.nfs.v4.xdr.nfsace4;
 import org.dcache.nfs.v4.xdr.stable_how4;
+import org.dcache.nfs.v4.xdr.stateid4;
 
 /**
  * An interface to file system.
@@ -69,6 +70,23 @@ public interface VirtualFileSystem {
      */
     Inode create(Inode parent, Stat.Type type, String name, Subject subject, int mode) throws IOException;
 
+    /**
+     * Notify about file handle opened
+     * @param inode inode of the object
+     * @param mode Access mode bitmask like ACCESS4_READ
+     * @param stateid Open state id
+     * @throws IOException
+     */
+    void open(Inode inode, int mode, stateid4 stateid) throws IOException;
+
+    /**
+     * Notify about file handle closed
+     * @param inode inode of the object
+     * @param stateid Open state id
+     * @throws IOException
+     */
+    void close(Inode inode, stateid4 stateid) throws IOException;
+
     /**
      * Get file system's usage information.
      *
diff --git a/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java b/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java
index 953904fb3..2aef19abf 100644
--- a/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java
+++ b/core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java
@@ -30,6 +30,7 @@
 import org.dcache.nfs.v4.xdr.nfs_opnum4;
 import org.dcache.nfs.v4.xdr.nfs_resop4;
 import org.dcache.nfs.v4.xdr.stateid4;
+import org.dcache.nfs.vfs.VirtualFileSystem;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -73,6 +74,7 @@ public void testStateCleanOnOpenCloseV41() throws Exception {
         OperationCLOSE CLOSE = new OperationCLOSE(close_args);
         result = nfs_resop4.resopFor(nfs_opnum4.OP_CLOSE);
         context = new CompoundContextBuilder()
+                .withFs(mock(VirtualFileSystem.class))
                 .withStateHandler(stateHandler)
                 .withMinorversion(1)
                 .withDeviceManager(mock(NFSv41DeviceManager.class))
@@ -100,6 +102,7 @@ public void testStateCleanOnOpenCloseV40() throws Exception {
         OperationCLOSE CLOSE = new OperationCLOSE(close_args);
         result = nfs_resop4.resopFor(nfs_opnum4.OP_CLOSE);
         context = new CompoundContextBuilder()
+                .withFs(mock(VirtualFileSystem.class))
                 .withStateHandler(stateHandler)
                 .withMinorversion(0)
                 .withCall(generateRpcCall())
diff --git a/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java b/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java
index 83fe2576e..d2d110948 100644
--- a/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java
+++ b/core/src/test/java/org/dcache/nfs/vfs/DummyVFS.java
@@ -22,6 +22,7 @@
 import com.google.common.jimfs.Configuration;
 import com.google.common.jimfs.Jimfs;
 import com.google.common.primitives.Longs;
+import org.dcache.nfs.v4.xdr.stateid4;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -208,6 +209,16 @@ public Inode create(Inode parent, Type type, String path, Subject subject, int m
         return toFileHandle(newInodeNumber);
     }
 
+    @Override
+    public void open(Inode inode, int mode, stateid4 stateid) throws IOException {
+
+    }
+
+    @Override
+    public void close(Inode inode, stateid4 stateid) throws IOException {
+
+    }
+
     @Override
     public FsStat getFsStat() throws IOException {
         FileStore store = Files.getFileStore(_root);