Commit 20b7f4e2 authored by Tigran Mkrtchyan's avatar Tigran Mkrtchyan
Browse files

nfsv41: fix expired client session recovery

observed with RHEL 6.2

at some point client sends late SEQUENCE.
Server reply with NFS4ERR_EXPIRED. Client
attempts to establish a new session, but uses
old client id. Due to a bug in recovery procedure
This ends up with EXPIRED as well.
parent 21a41b0c
...@@ -210,8 +210,8 @@ public class NFS4Client { ...@@ -210,8 +210,8 @@ public class NFS4Client {
_isConfirmed = true; _isConfirmed = true;
} }
public long leaseTime() { public boolean isLeaseValid() {
return _cl_time; return (System.currentTimeMillis() - _cl_time) > _leaseTime;
} }
/** /**
......
...@@ -190,22 +190,23 @@ public class OperationEXCHANGE_ID extends AbstractNFSv4Operation { ...@@ -190,22 +190,23 @@ public class OperationEXCHANGE_ID extends AbstractNFSv4Operation {
if (client.isConfirmed()) { if (client.isConfirmed()) {
if (client.verifierEquals(verifier) && principal.equals(client.principal())) { if (client.verifierEquals(verifier) && principal.equals(client.principal())) {
_log.debug("Case 2: Non-Update on Existing Client ID"); _log.debug("Case 2: Non-Update on Existing Client ID");
if (!client.hasState()) {
client.refreshLeaseTime();
}
} else if (principal.equals(client.principal())) { } else if (principal.equals(client.principal())) {
_log.debug("case 5: Client Restart"); _log.debug("case 5: Client Restart");
context.getStateHandler().removeClient(client); context.getStateHandler().removeClient(client);
client = context.getStateHandler().createClient( client = context.getStateHandler().createClient(
remoteSocketAddress, localSocketAddress, remoteSocketAddress, localSocketAddress,
clientOwner, _args.opexchange_id.eia_clientowner.co_verifier, principal); clientOwner, _args.opexchange_id.eia_clientowner.co_verifier, principal);
} else { } else {
if ((!client.hasState()) || (System.currentTimeMillis() - client.leaseTime()) > (NFSv4Defaults.NFS4_LEASE_TIME * 1000)) { _log.debug("Case 3b: Client Collision");
_log.debug("case 3a: Client Collision is equivalent to case 1 (the new Owner ID)"); if ((!client.hasState()) || !client.isLeaseValid()) {
context.getStateHandler().removeClient(client); context.getStateHandler().removeClient(client);
client = context.getStateHandler().createClient( client = context.getStateHandler().createClient(
remoteSocketAddress, localSocketAddress, remoteSocketAddress, localSocketAddress,
clientOwner, _args.opexchange_id.eia_clientowner.co_verifier, principal); clientOwner, _args.opexchange_id.eia_clientowner.co_verifier, principal);
} else { } else {
_log.debug("Case 3b: Client Collision");
throw new ChimeraNFSException(nfsstat.NFSERR_CLID_INUSE, "Principal Missmatch"); throw new ChimeraNFSException(nfsstat.NFSERR_CLID_INUSE, "Principal Missmatch");
} }
} }
......
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
package org.dcache.chimera.nfs.v4; package org.dcache.chimera.nfs.v4;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.dcache.chimera.nfs.nfsstat; import org.dcache.chimera.nfs.nfsstat;
import org.dcache.chimera.nfs.v4.client.CreateSessionStub; import org.dcache.chimera.nfs.v4.client.CreateSessionStub;
import org.dcache.chimera.nfs.v4.client.ExchangeIDStub; import org.dcache.chimera.nfs.v4.client.ExchangeIDStub;
import org.dcache.chimera.nfs.v4.client.SequenceStub;
import org.dcache.chimera.nfs.v4.xdr.*; import org.dcache.chimera.nfs.v4.xdr.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -33,7 +35,7 @@ public class OperationEXCHANGE_IDTest { ...@@ -33,7 +35,7 @@ public class OperationEXCHANGE_IDTest {
@Before @Before
public void setUp() { public void setUp() {
stateHandler = new NFSv4StateHandler(); stateHandler = new NFSv4StateHandler(2000);
clientId = UUID.randomUUID().toString(); clientId = UUID.randomUUID().toString();
} }
...@@ -136,4 +138,46 @@ public class OperationEXCHANGE_IDTest { ...@@ -136,4 +138,46 @@ public class OperationEXCHANGE_IDTest {
result = nfs_resop4.resopFor(nfs_opnum4.OP_EXCHANGE_ID); result = nfs_resop4.resopFor(nfs_opnum4.OP_EXCHANGE_ID);
AssertNFS.assertNFS(EXCHANGE_ID, context, result, nfsstat.NFS_OK); AssertNFS.assertNFS(EXCHANGE_ID, context, result, nfsstat.NFS_OK);
} }
@Test
public void testResendConfirmedLate() throws Exception {
CompoundContext context;
nfs_resop4 result;
nfs_argop4 exchangeid_args = ExchangeIDStub.normal(domain, name, clientId, 0, state_protect_how4.SP4_NONE);
OperationEXCHANGE_ID EXCHANGE_ID = new OperationEXCHANGE_ID(exchangeid_args, 0);
result = nfs_resop4.resopFor(nfs_opnum4.OP_EXCHANGE_ID);
context = new CompoundContextBuilder()
.withStateHandler(stateHandler)
.withOpCount(1)
.build();
AssertNFS.assertNFS(EXCHANGE_ID, context, result, nfsstat.NFS_OK);
nfs_argop4 cretaesession_args = CreateSessionStub.standard(
result.opexchange_id.eir_resok4.eir_clientid, result.opexchange_id.eir_resok4.eir_sequenceid);
OperationCREATE_SESSION CREATE_SESSION = new OperationCREATE_SESSION(cretaesession_args);
result = nfs_resop4.resopFor(nfs_opnum4.OP_CREATE_SESSION);
context = new CompoundContextBuilder()
.withStateHandler(stateHandler)
.withOpCount(1)
.build();
AssertNFS.assertNFS(CREATE_SESSION, context, result, nfsstat.NFS_OK);
TimeUnit.SECONDS.sleep(3);
nfs_argop4 sequence_args = SequenceStub.generateRequest(false,
result.opcreate_session.csr_resok4.csr_sessionid.value, 0, 0, 0);
OperationSEQUENCE SEQUENCE = new OperationSEQUENCE(sequence_args);
result = nfs_resop4.resopFor(nfs_opnum4.OP_SEQUENCE);
AssertNFS.assertNFS(SEQUENCE, context, result, nfsstat.NFSERR_EXPIRED);
EXCHANGE_ID = new OperationEXCHANGE_ID(exchangeid_args, 0);
result = nfs_resop4.resopFor(nfs_opnum4.OP_EXCHANGE_ID);
AssertNFS.assertNFS(EXCHANGE_ID, context, result, nfsstat.NFS_OK);
}
} }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment