Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion core/src/main/java/org/dcache/nfs/util/Opaque.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
package org.dcache.nfs.util;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;

import org.dcache.oncrpc4j.util.Bytes;

/**
* Describes something that can be used as a key for {@link java.util.HashMap} and that can be converted to a
* {@code byte[]} and a Base64 string representation.
Expand Down Expand Up @@ -85,6 +88,9 @@ static Opaque forBytes(ByteBuffer buf, int length) {
* @see #toImmutableOpaque()
*/
static Opaque forMutableByteBuffer(ByteBuffer buf, int index, int length) {
if (buf.order() != ByteOrder.BIG_ENDIAN) {
buf = buf.duplicate();
}
return new OpaqueBufferImpl(buf, index, length);
}

Expand Down Expand Up @@ -174,7 +180,23 @@ default void putBytes(ByteBuffer buf) {
@Override
boolean equals(Object o);

class OpaqueImpl implements Opaque {
/**
* Returns the byte stored at the given position.
*
* @param byteOffset The byte offset
* @return The byte.
*/
byte byteAt(int byteOffset);

/**
* Returns the {@code long} stored at the given position, using big-endian byte order.
*
* @param byteOffset The byte offset
* @return The long.
*/
long longAt(int byteOffset);

public class OpaqueImpl implements Opaque {
final byte[] _opaque;

OpaqueImpl(byte[] opaque) {
Expand Down Expand Up @@ -253,6 +275,16 @@ public int numBytes() {
public Opaque toImmutableOpaque() {
return Opaque.forBytes(_opaque);
}

@Override
public byte byteAt(int position) {
return _opaque[position];
}

@Override
public long longAt(int byteOffset) {
return Bytes.getLong(_opaque, byteOffset);
}
}

final class OpaqueImmutableImpl extends OpaqueImpl {
Expand Down Expand Up @@ -368,5 +400,15 @@ public boolean equals(Object o) {
public String toString() {
return super.toString() + "[" + toBase64() + "]";
}

@Override
public byte byteAt(int position) {
return buf.get(position);
}

@Override
public long longAt(int byteOffset) {
return buf.getLong(byteOffset);
}
}
}
29 changes: 17 additions & 12 deletions core/src/main/java/org/dcache/nfs/v4/FileTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,9 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
os.shareDenySeen |= 1 << ((shareDeny & nfs4_prot.OPEN4_SHARE_ACCESS_BOTH) - 1);
}

os.stateid.seqid++;
os.stateid.bumpSeqid();
// we need to return copy to avoid modification by concurrent opens
var openStateid = new stateid4(os.stateid.other, os.stateid.seqid);
var openStateid = os.stateid.clone();

// yet another open from the same client. Let's check if we can delegate.
if (canDelegateRead && (os.shareAccess
Expand All @@ -342,10 +342,10 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
OpenState openState = new OpenState(client, owner, stateid, shareAccess, shareDeny);
opens.add(openState);
state.addDisposeListener(s -> removeOpen(inode, stateid));
stateid.seqid++;
stateid.bumpSeqid();

// we need to return copy to avoid modification by concurrent opens
var openStateid = new stateid4(stateid.other, stateid.seqid);
var openStateid = stateid.clone();

// REVISIT: currently only read-delegations are supported
if (canDelegateRead && (wantReadDelegation || adlHeuristic.shouldDelegate(client, inode))) {
Expand Down Expand Up @@ -408,9 +408,9 @@ public stateid4 downgradeOpen(NFS4Client client, stateid4 stateid, Inode inode,
os.shareAccess = shareAccess;
os.shareDeny = shareDeny;

os.stateid.seqid++;
os.stateid.bumpSeqid();
// we need to return copy to avoid modification by concurrent opens
return new stateid4(os.stateid.other, os.stateid.seqid);
return os.stateid.clone();
} finally {
lock.unlock();
}
Expand Down Expand Up @@ -463,16 +463,21 @@ public void delegationReturn(NFS4Client client, stateid4 stateid, Inode inode)
*/
public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid)
throws ChimeraNFSException {
Opaque stateIdOpaque = stateid.getOpaque();
return getShareAccess(client, inode, stateIdOpaque);
}

public int getShareAccess(NFS4Client client, Inode inode, Opaque stateid)
throws ChimeraNFSException {
Opaque fileId = inode.getFileIdKey();
Lock lock = filesLock.get(fileId);
lock.lock();
try {

switch (stateid.other[11]) {
switch (stateid4.getType(stateid)) {
case Stateids.LOCK_STATE_ID:
NFS4State lockState = client.state(stateid);
stateid = lockState.getOpenState().stateid();
stateid = lockState.getOpenState().stateid().getOpaque();
// fall through
case Stateids.OPEN_STATE_ID: {
final List<OpenState> opens = files.get(fileId);
Expand All @@ -481,10 +486,10 @@ public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid)
throw new BadStateidException("no matching open");
}

final stateid4 openStateid = stateid;
final Opaque openStateid = stateid;
return opens.stream()
.filter(s -> client.getId() == s.client.getId())
.filter(s -> s.stateid.equals(openStateid))
.filter(s -> s.stateid.getOpaque().equals(openStateid))
.mapToInt(OpenState::getShareAccess)
.findAny()
.orElseThrow(BadStateidException::new);
Expand All @@ -496,11 +501,11 @@ public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid)
throw new BadStateidException("no delegation found");
}

stateid4 delegationStateid = stateid;
Opaque delegationStateid = stateid;

var delegation = fileDelegations.stream()
.filter(d -> d.client().getId().equals(client.getId()))
.filter(d -> d.delegationStateid().stateid().equals(delegationStateid))
.filter(d -> d.delegationStateid().stateid().getOpaque().equals(delegationStateid))
.findAny()
.orElseThrow(BadStateidException::new);

Expand Down
31 changes: 19 additions & 12 deletions core/src/main/java/org/dcache/nfs/v4/NFS4Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public class NFS4Client {
*/
private int _sessionSequence = 1;

private final Map<stateid4, NFS4State> _clientStates = new ConcurrentHashMap<>();
private final Map<Opaque, NFS4State> _clientStates = new ConcurrentHashMap<>();

/**
* sessions associated with the client
Expand Down Expand Up @@ -334,18 +334,20 @@ private NFS4State createState(StateOwner stateOwner, byte type, NFS4State openSt

NFS4State state = new NFS4State(openState, stateOwner, _stateHandler.createStateId(this, type, _stateIdCounter
.incrementAndGet()));
stateid4 stateId = state.stateid();
Opaque opaque = stateId.getOpaque();
if (openState != null) {
openState.addDisposeListener(s -> {
// remove and dispose derived states.
NFS4State nfsState = _clientStates.get(state.stateid());
NFS4State nfsState = _clientStates.get(opaque);
if (nfsState != null) {
_log.debug("removing derived state {}", nfsState);
nfsState.tryDispose();
}
_clientStates.remove(state.stateid());
_clientStates.remove(opaque);
});
}
_clientStates.put(state.stateid(), state);
_clientStates.put(opaque, state);
return state;
}

Expand Down Expand Up @@ -410,29 +412,34 @@ public NFS4State createServerSideCopyState(StateOwner stateOwner, NFS4State open
}

public void releaseState(stateid4 stateid) throws ChimeraNFSException {
Opaque opaque = stateid.getOpaque();

NFS4State state = _clientStates.get(stateid);
NFS4State state = _clientStates.get(opaque);
if (state == null) {
throw new BadStateidException("State not known to the client: " + stateid);
}
state.disposeIgnoreFailures();
_clientStates.remove(stateid);
_clientStates.remove(opaque);
}

public void tryReleaseState(stateid4 stateid) throws ChimeraNFSException {

NFS4State state = _clientStates.get(stateid);
NFS4State state = _clientStates.get(stateid.getOpaque());
if (state == null) {
throw new BadStateidException("State not known to the client: " + stateid);
}
state.tryDispose();
_clientStates.remove(stateid);
_clientStates.remove(stateid.getOpaque());
}

public NFS4State state(stateid4 stateid) throws ChimeraNFSException {
NFS4State state = _clientStates.get(stateid);
return state(stateid.getOpaque());
}

public NFS4State state(Opaque stateidOpaque) throws ChimeraNFSException {
NFS4State state = _clientStates.get(stateidOpaque);
if (state == null) {
throw new BadStateidException("State not known to the client: " + stateid);
throw new BadStateidException("State not known to the client: " + stateidOpaque);
}
return state;
}
Expand Down Expand Up @@ -534,7 +541,7 @@ public boolean hasState() {
* @param state to attach
*/
public void attachState(NFS4State state) {
_clientStates.put(state.stateid(), state);
_clientStates.put(state.stateid().getOpaque(), state);
}

/**
Expand All @@ -543,7 +550,7 @@ public void attachState(NFS4State state) {
* @param state to detach
*/
public void detachState(NFS4State state) {
_clientStates.remove(state.stateid());
_clientStates.remove(state.stateid().getOpaque());
}

@GuardedBy("this")
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/dcache/nfs/v4/NFS4State.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public NFS4State(NFS4State openState, StateOwner owner, stateid4 stateid) {
}

public void bumpSeqid() {
++_stateid.seqid;
_stateid.bumpSeqid();
}

public stateid4 stateid() {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/dcache/nfs/v4/NFSServerV41.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public COMPOUND4res NFSPROC4_COMPOUND_4(RpcCall call$, COMPOUND4args arg1) {
}
res.resarray = new ArrayList<>(arg1.argarray.length);

VirtualFileSystem fs = new PseudoFs(_fs, call$, _exportTable);
VirtualFileSystem fs = new PseudoFs(_fs, call$, _exportTable, _statHandler);

CompoundContextBuilder builder = new CompoundContextBuilder()
.withMinorversion(arg1.minorversion.value)
Expand Down
13 changes: 11 additions & 2 deletions core/src/main/java/org/dcache/nfs/v4/NFSv4StateHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,22 @@ public NFS4Client getClient(clientid4 clientid) throws StaleClientidException {
}
}

public NFS4Client getClientIfExists(long clientId) {
_readLock.lock();
try {
return _clientsByServerId.get(new clientid4(clientId));
} finally {
_readLock.unlock();
}
}

public NFS4Client getClientIdByStateId(stateid4 stateId) throws ChimeraNFSException {

_readLock.lock();
try {
checkState(_running, "NFS state handler not running");

clientid4 clientId = new clientid4(Bytes.getLong(stateId.other, 0));
clientid4 clientId = new clientid4(stateId.getClientId());
NFS4Client client = _clientsByServerId.get(clientId);
if (client == null) {
throw new BadStateidException("no client for stateid: " + stateId);
Expand Down Expand Up @@ -439,7 +448,7 @@ public int getInstanceId() {
* @return state hander id.
*/
public static int getInstanceId(stateid4 stateid) {
long clientid = Bytes.getLong(stateid.other, 0);
long clientid = stateid.getClientId();
return (int) (clientid >> 16) & 0xFFFF;
}

Expand Down
7 changes: 6 additions & 1 deletion core/src/main/java/org/dcache/nfs/v4/OperationCLOSE.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.dcache.nfs.v4.xdr.nfs_resop4;
import org.dcache.nfs.v4.xdr.stateid4;
import org.dcache.nfs.vfs.Inode;
import org.dcache.nfs.vfs.VirtualFileSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -59,6 +60,11 @@ public void process(CompoundContext context, nfs_resop4 result)
NFS4State nfsState = client.state(stateid);
Stateids.checkStateId(nfsState.stateid(), stateid);

VirtualFileSystem fs = context.getFs();
if (fs != null) {
fs.close(stateid.getOpaque());
}

if (context.getMinorversion() == 0) {
nfsState.getStateOwner().acceptAsNextSequence(_args.opclose.seqid);
client.updateLeaseTime();
Expand All @@ -68,6 +74,5 @@ public void process(CompoundContext context, nfs_resop4 result)

res.open_stateid = Stateids.invalidStateId();
res.status = nfsstat.NFS_OK;

}
}
6 changes: 5 additions & 1 deletion core/src/main/java/org/dcache/nfs/v4/OperationCOMMIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.dcache.nfs.v4.xdr.nfs_argop4;
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.Inode;

public class OperationCOMMIT extends AbstractNFSv4Operation {
Expand All @@ -42,8 +43,11 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
final COMMIT4res res = result.opcommit;
Inode inode = context.currentInode();

stateid4 stateid = Stateids.getCurrentStateidIfNeeded(context, _args.opwrite.stateid);

_args.opcommit.offset.checkOverflow(_args.opcommit.count.value, "offset + length overflow");
context.getFs().commit(inode, _args.opcommit.offset.value, _args.opcommit.count.value);
context.getFs().commit(stateid.getOpaque(), inode, _args.opcommit.offset.value,
_args.opcommit.count.value);

res.resok4 = new COMMIT4resok();
res.resok4.writeverf = context.getRebootVerifier();
Expand Down
10 changes: 6 additions & 4 deletions core/src/main/java/org/dcache/nfs/v4/OperationLOCK.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
import org.dcache.nfs.status.InvalException;
import org.dcache.nfs.status.OpenModeException;
import org.dcache.nfs.status.ServerFaultException;
import org.dcache.nfs.util.Opaque;
import org.dcache.nfs.v4.nlm.LockDeniedException;
import org.dcache.nfs.v4.nlm.LockException;
import org.dcache.nfs.v4.nlm.LockManager;
import org.dcache.nfs.v4.nlm.NlmLock;
import org.dcache.nfs.v4.xdr.LOCK4denied;
import org.dcache.nfs.v4.xdr.LOCK4resok;
Expand Down Expand Up @@ -118,12 +120,12 @@ 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.getLockKey(), lock);
Opaque lockKey = inode.getLockKey();
LockManager lm = context.getLm();
lm.lock(lockKey, lock);

// ensure, that on close locks will be released
lock_state.addDisposeListener(s -> {
context.getLm().unlockIfExists(inode.getLockKey(), lock);
});
lock_state.addDisposeListener(s -> lm.unlockIfExists(lockKey, lock));

// FIXME: we might run into race condition, thus updating sedid must be fenced!
lock_state.bumpSeqid();
Expand Down
Loading