diff --git a/core/src/main/java/org/dcache/nfs/util/Opaque.java b/core/src/main/java/org/dcache/nfs/util/Opaque.java
index dbf84bb0..119d4fdd 100644
--- a/core/src/main/java/org/dcache/nfs/util/Opaque.java
+++ b/core/src/main/java/org/dcache/nfs/util/Opaque.java
@@ -19,49 +19,178 @@
*/
package org.dcache.nfs.util;
-import java.io.Serializable;
+import java.nio.ByteBuffer;
import java.util.Arrays;
-
-import com.google.common.io.BaseEncoding;
+import java.util.Base64;
/**
- * A helper class for opaque data manipulations. Enabled opaque date to be used as a key in {@link java.util.Collection}
+ * Describes something that can be used as a key in {@link java.util.Map} and that can be converted to a {@code byte[]}
+ * and a Base64 string representation.
*/
-public class Opaque implements Serializable {
-
- private static final long serialVersionUID = 1532238396149112674L;
-
- private final byte[] _opaque;
-
- public Opaque(byte[] opaque) {
- _opaque = opaque;
+public interface Opaque {
+ /**
+ * Returns an {@link Opaque} instance based on a copy of the given bytes.
+ *
+ * @param bytes The bytes.
+ * @return The {@link Opaque} instance.
+ */
+ static Opaque forBytes(byte[] bytes) {
+ return new OpaqueImpl(bytes.clone());
}
- public byte[] getOpaque() {
- return _opaque;
+ /**
+ * Returns an {@link Opaque} instance based on a copy of the {@code length} bytes from the given {@link ByteBuffer}.
+ *
+ * @param buf The buffer.
+ * @param length The number of bytes.
+ * @return The {@link Opaque} instance.
+ */
+ static Opaque forBytes(ByteBuffer buf, int length) {
+ byte[] bytes = new byte[length];
+ buf.get(bytes);
+
+ return new OpaqueImpl(bytes);
}
- @Override
- public int hashCode() {
- return Arrays.hashCode(_opaque);
+ /**
+ * Default implementation for {@link #hashCode()}.
+ *
+ * @param obj The instance object.
+ * @return The hash code.
+ * @see #hashCode()
+ */
+ static int defaultHashCode(Opaque obj) {
+ return Arrays.hashCode(obj.toBytes());
}
- @Override
- public boolean equals(Object o) {
- if (o == this) {
+ /**
+ * Default implementation for {@link #equals(Object)}.
+ *
+ * @param obj The instance object.
+ * @param other The other object.
+ * @return {@code true} if equal.
+ * @see #equals(Object)
+ */
+ static boolean defaultEquals(Opaque obj, Object other) {
+ if (other == obj) {
return true;
}
- if (!(o instanceof Opaque)) {
+ if (!(other instanceof Opaque)) {
return false;
}
+ return Arrays.equals(obj.toBytes(), ((Opaque) other).toBytes());
+ }
- return Arrays.equals(_opaque, ((Opaque) o)._opaque);
+ /**
+ * Returns a byte-representation of this opaque object.
+ *
+ * @return A new array.
+ */
+ byte[] toBytes();
+
+ /**
+ * Returns the number of bytes in this opaque object;
+ *
+ * @return The number of bytes;
+ */
+ int numBytes();
+
+ /**
+ * Returns a Base64 string representing this opaque object.
+ *
+ * @return A Base64 string.
+ */
+ String toBase64();
+
+ /**
+ * Writes the bytes of this {@link Opaque} to the given {@link ByteBuffer}.
+ *
+ * @param buf The target buffer.
+ */
+ default void putBytes(ByteBuffer buf) {
+ buf.put(toBytes());
}
+ /**
+ * Returns the hashCode based on the byte-representation of this instance.
+ *
+ * This method must behave like {@link #defaultHashCode(Opaque)}, but may be optimized.
+ *
+ * @return The hashCode.
+ */
+ @Override
+ int hashCode();
+
+ /**
+ * Compares this object to another one.
+ *
+ * This method must behave like {@link #defaultEquals(Opaque, Object)}, but may be optimized.
+ *
+ * @return {@code true} if both objects are equal.
+ */
@Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append('[').append(BaseEncoding.base16().lowerCase().encode(_opaque)).append(']');
- return sb.toString();
+ boolean equals(Object o);
+
+ final class OpaqueImpl implements Opaque {
+ private final byte[] _opaque;
+ private String base64 = null;
+
+ private OpaqueImpl(byte[] opaque) {
+ _opaque = opaque;
+ }
+
+ @Override
+ public byte[] toBytes() {
+ return _opaque.clone();
+ }
+
+ @Override
+ public String toBase64() {
+ if (base64 == null) {
+ base64 = Base64.getEncoder().withoutPadding().encodeToString(_opaque);
+ }
+ return base64;
+ }
+
+ @Override
+ public void putBytes(ByteBuffer buf) {
+ buf.put(_opaque);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(_opaque);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Opaque)) {
+ return false;
+ }
+
+ if (o instanceof OpaqueImpl) {
+ return Arrays.equals(_opaque, ((OpaqueImpl) o)._opaque);
+ } else {
+ return Arrays.equals(_opaque, ((Opaque) o).toBytes());
+ }
+ }
+
+ /**
+ * Returns a (potentially non-stable) debug string.
+ *
+ * @see #toBase64()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + "[" + toBase64() + "]";
+ }
+
+ @Override
+ public int numBytes() {
+ return _opaque.length;
+ }
}
}
diff --git a/core/src/main/java/org/dcache/nfs/v4/FileTracker.java b/core/src/main/java/org/dcache/nfs/v4/FileTracker.java
index 267a6da2..e6e5991c 100644
--- a/core/src/main/java/org/dcache/nfs/v4/FileTracker.java
+++ b/core/src/main/java/org/dcache/nfs/v4/FileTracker.java
@@ -242,7 +242,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
// client explicitly requested write delegation
boolean wantWriteDelegation = (shareAccess & nfs4_prot.OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG) != 0;
- Opaque fileId = new Opaque(inode.getFileId());
+ Opaque fileId = inode.getFileIdKey();
Lock lock = filesLock.get(fileId);
lock.lock();
try {
@@ -376,7 +376,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
public stateid4 downgradeOpen(NFS4Client client, stateid4 stateid, Inode inode, int shareAccess, int shareDeny)
throws ChimeraNFSException {
- Opaque fileId = new Opaque(inode.getFileId());
+ Opaque fileId = inode.getFileIdKey();
Lock lock = filesLock.get(fileId);
lock.lock();
try {
@@ -426,7 +426,7 @@ public stateid4 downgradeOpen(NFS4Client client, stateid4 stateid, Inode inode,
public void delegationReturn(NFS4Client client, stateid4 stateid, Inode inode)
throws ChimeraNFSException {
- Opaque fileId = new Opaque(inode.getFileId());
+ Opaque fileId = inode.getFileIdKey();
Lock lock = filesLock.get(fileId);
lock.lock();
try {
@@ -464,7 +464,7 @@ public void delegationReturn(NFS4Client client, stateid4 stateid, Inode inode)
public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid)
throws ChimeraNFSException {
- Opaque fileId = new Opaque(inode.getFileId());
+ Opaque fileId = inode.getFileIdKey();
Lock lock = filesLock.get(fileId);
lock.lock();
try {
@@ -527,7 +527,7 @@ public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid)
*/
void removeOpen(Inode inode, stateid4 stateid) {
- Opaque fileId = new Opaque(inode.getFileId());
+ Opaque fileId = inode.getFileIdKey();
Lock lock = filesLock.get(fileId);
lock.lock();
try {
@@ -565,7 +565,7 @@ void removeOpen(Inode inode, stateid4 stateid) {
public Map> getOpenFiles() {
return files.entrySet().stream()
.collect(Collectors.toMap(
- e -> Inode.forFile(e.getKey().getOpaque()),
+ e -> Inode.forFileIdKey(e.getKey()),
e -> e.getValue().stream().map(OpenState::getClient).collect(Collectors.toSet())));
}
@@ -578,7 +578,7 @@ public Map> getOpenFiles() {
public Map> getDelegations() {
return delegations.entrySet().stream()
.collect(Collectors.toMap(
- e -> Inode.forFile(e.getKey().getOpaque()),
+ e -> Inode.forFileIdKey(e.getKey()),
e -> e.getValue().stream().map(DelegationState::client).collect(Collectors.toSet())));
}
}
diff --git a/core/src/main/java/org/dcache/nfs/v4/NFS4Client.java b/core/src/main/java/org/dcache/nfs/v4/NFS4Client.java
index 6da6871e..4afbd04b 100644
--- a/core/src/main/java/org/dcache/nfs/v4/NFS4Client.java
+++ b/core/src/main/java/org/dcache/nfs/v4/NFS4Client.java
@@ -628,7 +628,7 @@ public boolean isCallbackNeede() {
public synchronized StateOwner getOrCreateOwner(byte[] owner, seqid4 seq) throws BadSeqidException {
StateOwner stateOwner;
if (_minorVersion == 0) {
- Opaque k = new Opaque(owner);
+ Opaque k = Opaque.forBytes(owner);
stateOwner = _owners.get(k);
if (stateOwner == null) {
state_owner4 so = new state_owner4();
@@ -655,7 +655,7 @@ public synchronized StateOwner getOrCreateOwner(byte[] owner, seqid4 seq) throws
* @param owner client unique state owner
*/
public synchronized void releaseOwner(byte[] owner) throws StaleClientidException {
- Opaque k = new Opaque(owner);
+ Opaque k = Opaque.forBytes(owner);
StateOwner stateOwner = _owners.remove(k);
if (stateOwner == null) {
throw new StaleClientidException();
diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationLOCK.java b/core/src/main/java/org/dcache/nfs/v4/OperationLOCK.java
index f410321a..d3b9575b 100644
--- a/core/src/main/java/org/dcache/nfs/v4/OperationLOCK.java
+++ b/core/src/main/java/org/dcache/nfs/v4/OperationLOCK.java
@@ -118,11 +118,11 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
NlmLock lock = new NlmLock(lockOwner, _args.oplock.locktype, _args.oplock.offset.value,
_args.oplock.length.value);
- context.getLm().lock(inode.getFileId(), lock);
+ context.getLm().lock(inode.getLockKey(), lock);
// ensure, that on close locks will be released
lock_state.addDisposeListener(s -> {
- context.getLm().unlockIfExists(inode.getFileId(), lock);
+ context.getLm().unlockIfExists(inode.getLockKey(), lock);
});
// FIXME: we might run into race condition, thus updating sedid must be fenced!
diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationLOCKT.java b/core/src/main/java/org/dcache/nfs/v4/OperationLOCKT.java
index d4432b76..fc15e2ec 100644
--- a/core/src/main/java/org/dcache/nfs/v4/OperationLOCKT.java
+++ b/core/src/main/java/org/dcache/nfs/v4/OperationLOCKT.java
@@ -85,7 +85,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws IOExcepti
NlmLock lock = new NlmLock(lockOwner, _args.oplockt.locktype, _args.oplockt.offset.value,
_args.oplockt.length.value);
- context.getLm().test(inode.getFileId(), lock);
+ context.getLm().test(inode.getLockKey(), lock);
result.oplockt.status = nfsstat.NFS_OK;
diff --git a/core/src/main/java/org/dcache/nfs/v4/OperationLOCKU.java b/core/src/main/java/org/dcache/nfs/v4/OperationLOCKU.java
index 601106f5..bbe7223f 100644
--- a/core/src/main/java/org/dcache/nfs/v4/OperationLOCKU.java
+++ b/core/src/main/java/org/dcache/nfs/v4/OperationLOCKU.java
@@ -76,7 +76,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
NlmLock lock = new NlmLock(lockOwner, _args.oplocku.locktype, _args.oplocku.offset.value,
_args.oplocku.length.value);
try {
- context.getLm().unlock(inode.getFileId(), lock);
+ context.getLm().unlock(inode.getLockKey(), lock);
} catch (LockRangeUnavailabeException e) {
// posix locks allows unlocking of not locked regions
}
diff --git a/core/src/main/java/org/dcache/nfs/v4/nlm/AbstractLockManager.java b/core/src/main/java/org/dcache/nfs/v4/nlm/AbstractLockManager.java
index 97a172f8..3993065b 100644
--- a/core/src/main/java/org/dcache/nfs/v4/nlm/AbstractLockManager.java
+++ b/core/src/main/java/org/dcache/nfs/v4/nlm/AbstractLockManager.java
@@ -26,6 +26,7 @@
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;
+import org.dcache.nfs.util.Opaque;
import org.dcache.nfs.v4.xdr.nfs4_prot;
/**
@@ -42,7 +43,7 @@ public abstract class AbstractLockManager implements LockManager {
* @param objId object id.
* @return exclusive lock.
*/
- abstract protected Lock getObjectLock(byte[] objId);
+ protected abstract Lock getObjectLock(Opaque objId);
/**
* Get collection of currently used active locks on the object.
@@ -50,7 +51,7 @@ public abstract class AbstractLockManager implements LockManager {
* @param objId object id.
* @return collection of active locks.
*/
- abstract protected Collection getActiveLocks(byte[] objId);
+ protected abstract Collection getActiveLocks(Opaque objId);
/**
* Add {@code lock} to an object.
@@ -58,7 +59,7 @@ public abstract class AbstractLockManager implements LockManager {
* @param objId object id.
* @param lock lock to add.
*/
- abstract protected void add(byte[] objId, NlmLock lock);
+ protected abstract void add(Opaque objId, NlmLock lock);
/**
* Remove a lock for the given object.
@@ -67,15 +68,15 @@ public abstract class AbstractLockManager implements LockManager {
* @param lock lock to remove.
* @return true, if specified lock was removed.
*/
- abstract protected boolean remove(byte[] objId, NlmLock lock);
+ protected abstract boolean remove(Opaque objId, NlmLock lock);
/**
* Add all locks from a given collection of locks
*
- * @param objId
- * @param locks
+ * @param objId object id.
+ * @param locks locks to add.
*/
- abstract protected void addAll(byte[] objId, Collection locks);
+ protected abstract void addAll(Opaque objId, Collection locks);
/**
* Remove all locks specified by {@code locks} associated with the given object.
@@ -83,10 +84,10 @@ public abstract class AbstractLockManager implements LockManager {
* @param objId object id.
* @param locks collections of locks to remove.
*/
- abstract protected void removeAll(byte[] objId, Collection locks);
+ protected abstract void removeAll(Opaque objId, Collection locks);
@Override
- public void lock(byte[] objId, NlmLock lock) throws LockException {
+ public void lock(Opaque objId, NlmLock lock) throws LockException {
Lock dlmLock = getObjectLock(objId);
dlmLock.lock();
try {
@@ -123,7 +124,7 @@ public void lock(byte[] objId, NlmLock lock) throws LockException {
}
@Override
- public void unlock(byte[] objId, NlmLock lock) throws LockException {
+ public void unlock(Opaque objId, NlmLock lock) throws LockException {
Lock dlmLock = getObjectLock(objId);
dlmLock.lock();
try {
@@ -162,7 +163,7 @@ public void unlock(byte[] objId, NlmLock lock) throws LockException {
}
@Override
- public void test(byte[] objId, NlmLock lock) throws LockException {
+ public void test(Opaque objId, NlmLock lock) throws LockException {
Lock dlmLock = getObjectLock(objId);
dlmLock.lock();
try {
@@ -178,7 +179,7 @@ public void test(byte[] objId, NlmLock lock) throws LockException {
}
@Override
- public void unlockIfExists(byte[] objId, NlmLock lock) {
+ public void unlockIfExists(Opaque objId, NlmLock lock) {
Lock dlmLock = getObjectLock(objId);
dlmLock.lock();
try {
@@ -187,5 +188,4 @@ public void unlockIfExists(byte[] objId, NlmLock lock) {
dlmLock.unlock();
}
}
-
}
diff --git a/core/src/main/java/org/dcache/nfs/v4/nlm/LockManager.java b/core/src/main/java/org/dcache/nfs/v4/nlm/LockManager.java
index ec3309d2..253b3390 100644
--- a/core/src/main/java/org/dcache/nfs/v4/nlm/LockManager.java
+++ b/core/src/main/java/org/dcache/nfs/v4/nlm/LockManager.java
@@ -19,6 +19,8 @@
*/
package org.dcache.nfs.v4.nlm;
+import org.dcache.nfs.util.Opaque;
+
/**
*/
public interface LockManager {
@@ -31,7 +33,7 @@ public interface LockManager {
* @throws LockDeniedException if a conflicting lock is detected.
* @throws LockException if locking fails.
*/
- void lock(byte[] objId, NlmLock lock) throws LockException;
+ void lock(Opaque objId, NlmLock lock) throws LockException;
/**
* Unlock byte range of an {@code objId}.
@@ -41,7 +43,7 @@ public interface LockManager {
* @throws LockRangeUnavailabeException if no matching lock found.
* @throws LockException if locking fails.
*/
- void unlock(byte[] objId, NlmLock lock) throws LockException;
+ void unlock(Opaque objId, NlmLock lock) throws LockException;
/**
* Test byte range lock existence for an {@code objId}. Same as {@link #lock}, except that a new lock is not
@@ -52,13 +54,13 @@ public interface LockManager {
* @throws LockDeniedException if a conflicting lock is detected.
* @throws LockException if locking fails.
*/
- void test(byte[] objId, NlmLock lock) throws LockException;
+ void test(Opaque objId, NlmLock lock) throws LockException;
/**
- * Like {@link #unlock(byte[], org.dcache.nfs.v4.nlm.NlmLock)}, but does not fail if lock does not exists.
+ * Like {@link #unlock(Opaque, org.dcache.nfs.v4.nlm.NlmLock)}, but does not fail if lock does not exists.
*
* @param objId
* @param lock
*/
- void unlockIfExists(byte[] objId, NlmLock lock);
+ void unlockIfExists(Opaque objId, NlmLock lock);
}
diff --git a/core/src/main/java/org/dcache/nfs/v4/nlm/SimpleLm.java b/core/src/main/java/org/dcache/nfs/v4/nlm/SimpleLm.java
index 580fc23b..0d1aa92d 100644
--- a/core/src/main/java/org/dcache/nfs/v4/nlm/SimpleLm.java
+++ b/core/src/main/java/org/dcache/nfs/v4/nlm/SimpleLm.java
@@ -20,13 +20,14 @@
package org.dcache.nfs.v4.nlm;
import java.util.ArrayList;
-import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
+import org.dcache.nfs.util.Opaque;
+
import com.google.common.util.concurrent.Striped;
/**
@@ -60,26 +61,26 @@ public SimpleLm(int concurrency) {
private final ConcurrentHashMap> locks = new ConcurrentHashMap<>();
@Override
- protected Lock getObjectLock(byte[] objId) {
+ protected Lock getObjectLock(Opaque objId) {
String key = toKey(objId);
return objLock.get(key);
}
@Override
- protected Collection getActiveLocks(byte[] objId) {
+ protected Collection getActiveLocks(Opaque objId) {
String key = toKey(objId);
return locks.getOrDefault(key, Collections.emptyList());
}
@Override
- protected void add(byte[] objId, NlmLock lock) {
+ protected void add(Opaque objId, NlmLock lock) {
String key = toKey(objId);
Collection l = locks.computeIfAbsent(key, k -> new ArrayList<>());
l.add(lock);
}
@Override
- protected boolean remove(byte[] objId, NlmLock lock) {
+ protected boolean remove(Opaque objId, NlmLock lock) {
String key = toKey(objId);
Collection l = locks.get(key);
boolean isRemoved = false;
@@ -93,14 +94,14 @@ protected boolean remove(byte[] objId, NlmLock lock) {
}
@Override
- protected void addAll(byte[] objId, Collection locks) {
+ protected void addAll(Opaque objId, Collection locks) {
String key = toKey(objId);
Collection l = this.locks.computeIfAbsent(key, k -> new ArrayList<>());
l.addAll(locks);
}
@Override
- protected void removeAll(byte[] objId, Collection locks) {
+ protected void removeAll(Opaque objId, Collection locks) {
String key = toKey(objId);
Collection l = this.locks.get(key);
if (l != null) {
@@ -111,8 +112,8 @@ protected void removeAll(byte[] objId, Collection locks) {
}
}
- private final String toKey(byte[] objId) {
- return Base64.getEncoder().encodeToString(objId);
+ private final String toKey(Opaque objId) {
+ return objId.toBase64();
}
}
diff --git a/core/src/main/java/org/dcache/nfs/vfs/FsCache.java b/core/src/main/java/org/dcache/nfs/vfs/FsCache.java
index 1dbebaf4..cf8de09b 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/FsCache.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/FsCache.java
@@ -52,8 +52,7 @@ private static class FileChannelSupplier extends CacheLoader
@Override
public FileChannel load(Inode inode) throws IOException {
- byte[] fid = inode.getFileId();
- String id = BaseEncoding.base16().lowerCase().encode(fid);
+ String id = inode.getLockKey().toBase64();
File dir = getAndCreateDirectory(id);
File f = new File(dir, id);
return new RandomAccessFile(f, "rw").getChannel();
diff --git a/core/src/main/java/org/dcache/nfs/vfs/Inode.java b/core/src/main/java/org/dcache/nfs/vfs/Inode.java
index 09f3d079..788804a7 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/Inode.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/Inode.java
@@ -23,6 +23,8 @@
import java.nio.ByteOrder;
import java.util.Arrays;
+import org.dcache.nfs.util.Opaque;
+
import com.google.common.io.BaseEncoding;
/**
@@ -48,9 +50,10 @@ public class Inode {
private final int generation;
private final int exportIdx;
private final int type;
- private final byte[] fs_opaque;
private final byte[] nfsHandle;
+ private final Opaque opaqueKey;
+
@Deprecated(forRemoval = true)
public Inode(FileHandle fh) {
this(fh.bytes());
@@ -66,12 +69,16 @@ public Inode(FileHandle fh) {
*/
@Deprecated
public Inode(int generation, int exportIdx, int type, byte[] fs_opaque) {
+ this(generation, exportIdx, type, Opaque.forBytes(fs_opaque));
+ }
+
+ private Inode(int generation, int exportIdx, int type, Opaque fileIdKey) {
this.version = VERSION;
this.magic = MAGIC;
this.generation = generation;
this.exportIdx = exportIdx;
this.type = type;
- this.fs_opaque = fs_opaque;
+ this.opaqueKey = fileIdKey;
this.nfsHandle = buildNfsHandle();
}
@@ -106,8 +113,7 @@ public Inode(byte[] bytes) {
exportIdx = b.getInt();
type = (int) b.get();
int olen = (int) b.get();
- fs_opaque = new byte[olen];
- b.get(fs_opaque);
+ this.opaqueKey = Opaque.forBytes(b, olen);
this.nfsHandle = bytes.clone();
}
@@ -140,8 +146,40 @@ public static Inode forFile(byte[] bytes) {
return new Inode(0, 0, 0, bytes);
}
+ public static Inode forFileIdKey(Opaque key) {
+ return new Inode(0, 0, 0, key);
+ }
+
+ public static Inode innerInode(Inode outerInode) {
+ return forFileIdKey(outerInode.getFileIdKey());
+ }
+
+ @Deprecated(forRemoval = true)
public byte[] getFileId() {
- return fs_opaque;
+ return opaqueKey.toBytes();
+ }
+
+ /**
+ * Returns a locking-key referring to the underlying inode/file referred to by this instance, or a superset,
+ * suitable for {@link org.dcache.nfs.v4.nlm.LockManager} etc.
+ *
+ * This may or may not be equal to {@link #getFileIdKey()}.
+ *
+ * @return The locking key for file referred to by this inode.
+ */
+ public Opaque getLockKey() {
+ return opaqueKey;
+ }
+
+ /**
+ * Returns a key suitable for identifying the underlying inode/file referred to by this instance, providing a
+ * {@link Object#equals(Object)} and {@link Object#hashCode()} implementation that may or may not be different from
+ * {@link Inode#equals(Object)} and {@link Inode#hashCode()}.
+ *
+ * @return The fileId key.
+ */
+ public Opaque getFileIdKey() {
+ return opaqueKey;
}
public byte[] toNfsHandle() {
@@ -149,7 +187,12 @@ public byte[] toNfsHandle() {
}
private byte[] buildNfsHandle() {
- int len = fs_opaque.length + MIN_LEN;
+ int opaqueLen = opaqueKey.numBytes();
+ if (opaqueLen < 0 || opaqueLen > 255) {
+ throw new IllegalStateException("Invalid opaque key length");
+ }
+
+ int len = MIN_LEN + opaqueLen;
byte[] bytes = new byte[len];
ByteBuffer b = ByteBuffer.wrap(bytes);
b.order(ByteOrder.BIG_ENDIAN);
@@ -158,8 +201,8 @@ private byte[] buildNfsHandle() {
b.putInt(generation);
b.putInt(exportIdx);
b.put((byte) type);
- b.put((byte) fs_opaque.length);
- b.put(fs_opaque);
+ b.put((byte) opaqueLen);
+ opaqueKey.putBytes(b);
return bytes;
}
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 f7ecf18d..3b00a18e 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/PseudoFs.java
@@ -746,8 +746,17 @@ private boolean inheritUidGid(Inode inode) {
*
* @param inode The {@link Inode} as passed from and to the NFS client.
* @return The {@link Inode} as passed from {@link PseudoFs} to the underlying {@link VirtualFileSystem}.
+ * @throws IOException on error.
*/
- private Inode innerInode(Inode inode) {
- return Inode.forFile(inode.getFileId());
+ private Inode innerInode(Inode inode) throws IOException {
+ if (inode.isPseudoInode()) {
+ Inode innerRootInode = _inner.getRootInode();
+ if (innerRootInode.getFileIdKey().equals(inode.getFileIdKey())) {
+ return innerRootInode;
+ } else {
+ throw new BadHandleException();
+ }
+ }
+ return Inode.innerInode(inode);
}
}
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 60bde95b..2ccab223 100644
--- a/core/src/main/java/org/dcache/nfs/vfs/VfsCache.java
+++ b/core/src/main/java/org/dcache/nfs/vfs/VfsCache.java
@@ -219,7 +219,7 @@ private void updateLookupCache(Inode parent, String path, Inode inode) {
* @param inode The inode for which cached state value should be invalidated.
*/
public void invalidateStatCache(final Inode inode) {
- _statCache.invalidate(new Opaque(inode.getFileId()));
+ _statCache.invalidate(inode.getFileIdKey());
}
private void updateParentCache(Inode inode, Inode parent) {
@@ -246,7 +246,7 @@ private Inode lookupFromCacheOrLoad(final Inode parent, final String path) throw
private Stat statFromCacheOrLoad(final Inode inode) throws IOException {
try {
- return _statCache.get(new Opaque(inode.getFileId()), () -> _inner.getattr(inode));
+ return _statCache.get(inode.getFileIdKey(), () -> _inner.getattr(inode));
} catch (ExecutionException e) {
Throwable t = e.getCause();
Throwables.throwIfInstanceOf(t, IOException.class);
diff --git a/core/src/test/java/org/dcache/nfs/v4/nlm/SimpleLmTest.java b/core/src/test/java/org/dcache/nfs/v4/nlm/SimpleLmTest.java
index 6bd5dd89..cb4fe772 100644
--- a/core/src/test/java/org/dcache/nfs/v4/nlm/SimpleLmTest.java
+++ b/core/src/test/java/org/dcache/nfs/v4/nlm/SimpleLmTest.java
@@ -2,6 +2,7 @@
import java.nio.charset.StandardCharsets;
+import org.dcache.nfs.util.Opaque;
import org.dcache.nfs.v4.StateOwner;
import org.dcache.nfs.v4.xdr.clientid4;
import org.dcache.nfs.v4.xdr.nfs4_prot;
@@ -16,14 +17,14 @@
public class SimpleLmTest {
private LockManager nlm;
- private byte[] file1;
- private byte[] file2;
+ private Opaque file1;
+ private Opaque file2;
@Before
public void setUp() throws Exception {
nlm = new SimpleLm();
- file1 = "file1".getBytes(StandardCharsets.UTF_8);
- file2 = "file2".getBytes(StandardCharsets.UTF_8);
+ file1 = Opaque.forBytes("file1".getBytes(StandardCharsets.UTF_8));
+ file2 = Opaque.forBytes("file2".getBytes(StandardCharsets.UTF_8));
}
@Test
diff --git a/dlm/src/main/java/org/dcache/nfs/v4/nlm/DistributedLockManager.java b/dlm/src/main/java/org/dcache/nfs/v4/nlm/DistributedLockManager.java
index ae60b7c8..595ceabd 100644
--- a/dlm/src/main/java/org/dcache/nfs/v4/nlm/DistributedLockManager.java
+++ b/dlm/src/main/java/org/dcache/nfs/v4/nlm/DistributedLockManager.java
@@ -19,12 +19,13 @@
*/
package org.dcache.nfs.v4.nlm;
-import java.util.Base64;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
+import org.dcache.nfs.util.Opaque;
+
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.multimap.MultiMap;
@@ -61,7 +62,7 @@ public DistributedLockManager(HazelcastInstance hz, String name) {
}
@Override
- protected Lock getObjectLock(byte[] objId) {
+ protected Lock getObjectLock(Opaque objId) {
String key = objIdToKey(objId);
return new Lock() {
@Override
@@ -103,40 +104,37 @@ public Condition newCondition() {
* @return collection of active locks.
*/
@Override
- protected Collection getActiveLocks(byte[] objId) {
+ protected Collection getActiveLocks(Opaque objId) {
String key = objIdToKey(objId);
return locks.get(key);
}
@Override
- protected void add(byte[] objId, NlmLock lock) {
+ protected void add(Opaque objId, NlmLock lock) {
String key = objIdToKey(objId);
locks.put(key, lock);
}
@Override
- protected boolean remove(byte[] objId, NlmLock lock) {
+ protected boolean remove(Opaque objId, NlmLock lock) {
String key = objIdToKey(objId);
return locks.remove(key, lock);
}
@Override
- protected void addAll(byte[] objId, Collection locks) {
+ protected void addAll(Opaque objId, Collection locks) {
String key = objIdToKey(objId);
locks.forEach(l -> this.locks.put(key, l));
}
@Override
- protected void removeAll(byte[] objId, Collection locks) {
+ protected void removeAll(Opaque objId, Collection locks) {
String key = objIdToKey(objId);
locks.forEach(l -> this.locks.remove(key, l));
}
- private static String objIdToKey(byte[] objId) {
- return Base64
- .getEncoder()
- .withoutPadding()
- .encodeToString(objId);
+ private static String objIdToKey(Opaque objId) {
+ return objId.toBase64();
}
}
diff --git a/dlm/src/test/java/org/dcache/nfs/v4/nlm/DistributedLockManagerTest.java b/dlm/src/test/java/org/dcache/nfs/v4/nlm/DistributedLockManagerTest.java
index 1ff45092..92ab5c87 100644
--- a/dlm/src/test/java/org/dcache/nfs/v4/nlm/DistributedLockManagerTest.java
+++ b/dlm/src/test/java/org/dcache/nfs/v4/nlm/DistributedLockManagerTest.java
@@ -5,6 +5,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import org.dcache.nfs.util.Opaque;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -20,12 +21,12 @@ public class DistributedLockManagerTest {
private HazelcastInstance hzClient;
private LockManager lm1;
private LockManager lm2;
- private byte[] file1;
+ private Opaque file1;
@Before
public void setUp() throws Exception {
- file1 = "file1".getBytes(StandardCharsets.UTF_8);
+ file1 = Opaque.forBytes("file1".getBytes(StandardCharsets.UTF_8));
hzSerrver = Hazelcast.newHazelcastInstance();