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();