Skip to content

Commit d8893d9

Browse files
committed
nfsv4.0: fix handling of SETCLIENTID operation
fix corner cases of SETCLIENTID operation as specified in rfc7530 Acked-by: Karen Hoyos Target: master
1 parent 69ee7a4 commit d8893d9

File tree

5 files changed

+59
-22
lines changed

5 files changed

+59
-22
lines changed

core/src/main/java/org/dcache/nfs/v4/NFS4Client.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,14 @@ public class NFS4Client {
8989
private final byte[] _ownerId;
9090

9191
/**
92-
* Verifier that is used to detect client reboots.
92+
* Client side generated verifier that is used to detect client reboots.
9393
*/
94-
private final verifier4 _verifier;
94+
private final verifier4 _clientVerifier;
95+
96+
/**
97+
* Server side generated verifier that is used to detect retry.
98+
*/
99+
private verifier4 _serverVerifier;
95100

96101
/**
97102
* The RPCSEC_GSS principal sent via the RPC headers.
@@ -194,7 +199,8 @@ public NFS4Client(NFSv4StateHandler stateHandler, clientid4 clientId, int minorV
194199
_stateHandler = stateHandler;
195200
_clock = _stateHandler.getClock();
196201
_ownerId = Arrays.copyOf(ownerID, ownerID.length);
197-
_verifier = verifier;
202+
_clientVerifier = verifier;
203+
_serverVerifier = verifier4.valueOf(System.currentTimeMillis());
198204
_principal = principal;
199205
_clientId = clientId;
200206

@@ -236,8 +242,8 @@ public byte[] getOwnerId() {
236242
*
237243
* @return client generated verifier
238244
*/
239-
public verifier4 verifier() {
240-
return _verifier;
245+
public verifier4 serverGeneratedVerifier() {
246+
return _serverVerifier;
241247
}
242248

243249
/**
@@ -248,10 +254,15 @@ public clientid4 getId() {
248254
return _clientId;
249255
}
250256

251-
public boolean verifierEquals(verifier4 verifier) {
252-
return _verifier.equals(verifier);
257+
public boolean clientGeneratedVerifierEquals(verifier4 verifier) {
258+
return _clientVerifier.equals(verifier);
253259
}
254260

261+
public boolean serverGeneratedVerifierEquals(verifier4 verifier) {
262+
return _serverVerifier.equals(verifier);
263+
}
264+
265+
255266
public synchronized boolean isConfirmed() {
256267
return _isConfirmed;
257268
}
@@ -294,6 +305,7 @@ public void refreshLeaseTime() {
294305
public synchronized void reset() {
295306
refreshLeaseTime();
296307
_isConfirmed = false;
308+
_serverVerifier = verifier4.valueOf(System.currentTimeMillis());
297309
}
298310

299311
/**

core/src/main/java/org/dcache/nfs/v4/OperationEXCHANGE_ID.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
156156
throw new NoEntException("no such client");
157157
}
158158

159-
if (!client.verifierEquals(verifier)) {
159+
if (!client.clientGeneratedVerifierEquals(verifier)) {
160160
_log.debug("case 8: Update but Wrong Verifier");
161161
throw new NotSameException("Update but Wrong Verifier");
162162
}
@@ -181,7 +181,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
181181
} else {
182182

183183
if (client.isConfirmed()) {
184-
if (client.verifierEquals(verifier) && principal.equals(client.principal())) {
184+
if (client.clientGeneratedVerifierEquals(verifier) && principal.equals(client.principal())) {
185185
_log.debug("Case 2: Non-Update on Existing Client ID");
186186
client.refreshLeaseTime();
187187
} else if (principal.equals(client.principal())) {

core/src/main/java/org/dcache/nfs/v4/OperationSETCLIENTID.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,53 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
5555
final byte[] id = _args.opsetclientid.client.id;
5656
NFS4Client client = context.getStateHandler().clientByOwner(id);
5757

58-
if (client != null && client.isConfirmed() && client.isLeaseValid()) {
5958

60-
if (!client.principal().equals(context.getPrincipal())) {
61-
netaddr4 addr = new netaddr4(client.getRemoteAddress());
62-
res.status = nfsstat.NFSERR_CLID_INUSE;
63-
res.client_using = new clientaddr4(addr);
64-
throw new ClidInUseException();
65-
}
66-
client.reset();
59+
if (client == null) {
60+
// new client
61+
client = context.getStateHandler().createClient(
62+
context.getRemoteSocketAddress(),
63+
context.getLocalSocketAddress(),
64+
context.getMinorversion(),
65+
_args.opsetclientid.client.id, _args.opsetclientid.client.verifier,
66+
context.getPrincipal(), false);
67+
} else if (!client.isConfirmed()) {
6768

68-
} else {
69+
// existing client, but not confirmed. either retry or client restarted before confirmation
70+
context.getStateHandler().removeClient(client);
6971
client = context.getStateHandler().createClient(
7072
context.getRemoteSocketAddress(),
7173
context.getLocalSocketAddress(),
7274
context.getMinorversion(),
7375
_args.opsetclientid.client.id, _args.opsetclientid.client.verifier,
7476
context.getPrincipal(), false);
77+
78+
} else if (!client.clientGeneratedVerifierEquals(verifier)) {
79+
80+
// existing client, different verifier. Client rebooted.
81+
// create new record, keep the old one as required by the RFC 7530
82+
client = context.getStateHandler().createClient(
83+
context.getRemoteSocketAddress(),
84+
context.getLocalSocketAddress(),
85+
context.getMinorversion(),
86+
_args.opsetclientid.client.id, _args.opsetclientid.client.verifier,
87+
context.getPrincipal(), false);
88+
89+
} else if (client.isLeaseValid()) {
90+
91+
// can't be reused, if principal have changes and client has state
92+
if (!client.principal().equals(context.getPrincipal()) && client.hasState()) {
93+
netaddr4 addr = new netaddr4(client.getRemoteAddress());
94+
res.status = nfsstat.NFSERR_CLID_INUSE;
95+
res.client_using = new clientaddr4(addr);
96+
throw new ClidInUseException();
97+
}
98+
99+
client.reset();
75100
}
76101

77102
res.resok4 = new SETCLIENTID4resok();
78103
res.resok4.clientid = client.getId();
79-
res.resok4.setclientid_confirm = client.verifier();
104+
res.resok4.setclientid_confirm = client.serverGeneratedVerifier();
80105
res.status = nfsstat.NFS_OK;
81106
}
82107
}

core/src/main/java/org/dcache/nfs/v4/OperationSETCLIENTID_CONFIRM.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2017 Deutsches Elektronen-Synchroton,
2+
* Copyright (c) 2009 - 2025 Deutsches Elektronen-Synchroton,
33
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
44
*
55
* This library is free software; you can redistribute it and/or modify
@@ -49,7 +49,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
4949
NFS4Client client = context.getStateHandler().getClient(_args.opsetclientid_confirm.clientid);
5050

5151
res.status = nfsstat.NFSERR_INVAL;
52-
if (client.verifierEquals(_args.opsetclientid_confirm.setclientid_confirm)) {
52+
if (client.serverGeneratedVerifierEquals(_args.opsetclientid_confirm.setclientid_confirm)) {
5353
res.status = nfsstat.NFS_OK;
5454
client.setConfirmed();
5555
}

full-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ services:
2424
sleep 10
2525
/run-nfs4.0.sh --xml=/report/xunit-report-v40.xml --maketree nfs4j:/data all nochar nosocket noblock nofifo \
2626
noACC2a noACC2b noACC2c noACC2d noACC2f noACC2r noACC2s \
27-
noCID1 noCID2 noCID4a noCID4b noCID4c noCID4d noCID4e \
27+
noCID1 noCID4d \
2828
noCLOSE10 noCLOSE12 noCLOSE5 noCLOSE6 noCLOSE8 noCLOSE9 \
2929
noCMT1aa noCMT1b noCMT1c noCMT1d noCMT1e noCMT1f noCMT2a noCMT2b noCMT2c noCMT2d noCMT2f \
3030
noCMT2s noCMT3 noCMT4 noCR12 noLKT1 noLKT2a noLKT2b noLKT2c noLKT2d noLKT2f noLKT2s noLKT3 \

0 commit comments

Comments
 (0)