Skip to content

Commit d22e64b

Browse files
committed
nfs4: introduce stateid type
Motivation: nfs spec describes multiple types of states: open, lock, delegation, etc. The operations that accepts stateis as arguments should validate them. For example: CLOSE should accept open-stateid, and reject any other state types. To reduce bookkeeping on the server side, it makes sense to encode state type into stateid. Modification: - introduce one byte (8 bit) stateid types, which gives as possibility to encode 256 different states. - encode type into stateid. The stated is constructed by clientid + counter + type. Only 24 bits of counter is used, which gives 16777216 states that can be used at any moment (should be enough). - add create{Open,Lock,Layout,Delegation ...}State methods to NFSv4StateHandler - update codebase to use the appropriate createXxxState methods. Result: Better state handling. Acked-by: Marina Sahakyan Target: master
1 parent 532f71e commit d22e64b

File tree

10 files changed

+181
-32
lines changed

10 files changed

+181
-32
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public NFS4Client getClient() {
136136
/**
137137
* Record associated with open-delegation.
138138
* @param client
139-
* @param stateid
139+
* @param delegationStateid
140140
* @param delegationType
141141
*/
142142
record DelegationState(NFS4Client client, NFS4State delegationStateid, int delegationType) {
@@ -251,7 +251,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
251251
}
252252
}
253253

254-
NFS4State state = client.createState(owner);
254+
NFS4State state = client.createOpenState(owner);
255255
stateid = state.stateid();
256256
OpenState openState = new OpenState(client, owner, stateid, shareAccess, shareDeny);
257257
opens.add(openState);
@@ -264,7 +264,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
264264
// REVISIT: currently only read-delegations are supported
265265
if (wantReadDelegation && canDelegateRead) {
266266
// REVISIT: currently only read-delegations are supported
267-
var delegationStateid = client.createState(state.getStateOwner(), state);
267+
var delegationStateid = client.createDelegationState(state.getStateOwner());
268268
delegations.computeIfAbsent(fileId, x -> new ArrayList<>(1))
269269
.add(new DelegationState(client, delegationStateid, open_delegation_type4.OPEN_DELEGATE_READ));
270270
return new OpenRecord(openStateid, delegationStateid.stateid(), true);

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

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2023 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
@@ -26,6 +26,7 @@
2626
import java.time.Clock;
2727
import java.time.Duration;
2828
import java.time.Instant;
29+
2930
import org.slf4j.Logger;
3031
import org.slf4j.LoggerFactory;
3132

@@ -331,9 +332,9 @@ public int currentSeqID() {
331332
return _sessionSequence;
332333
}
333334

334-
public NFS4State createState(StateOwner stateOwner, NFS4State openState) throws ChimeraNFSException {
335+
private NFS4State createState(StateOwner stateOwner, byte type, NFS4State openState) throws ChimeraNFSException {
335336

336-
NFS4State state = new NFS4State(openState, stateOwner, _stateHandler.createStateId(this, _stateIdCounter.incrementAndGet()));
337+
NFS4State state = new NFS4State(openState, stateOwner, _stateHandler.createStateId(this, type, _stateIdCounter.incrementAndGet()));
337338
if (openState != null) {
338339
openState.addDisposeListener(s -> {
339340
// remove and dispose derived states.
@@ -349,8 +350,59 @@ public NFS4State createState(StateOwner stateOwner, NFS4State openState) throws
349350
return state;
350351
}
351352

352-
public NFS4State createState(StateOwner stateOwner) throws ChimeraNFSException {
353-
return createState(stateOwner, null);
353+
/**
354+
* Create a new open state.
355+
* @param stateOwner state owner
356+
* @return new open state.
357+
* @throws ChimeraNFSException
358+
*/
359+
public NFS4State createOpenState(StateOwner stateOwner) throws ChimeraNFSException {
360+
return createState(stateOwner, Stateids.OPEN_STATE_ID, null);
361+
}
362+
363+
/**
364+
* Create a new lock state.
365+
* @param stateOwner state owner
366+
* @param openState open state to derive from
367+
* @return new lock state.
368+
* @throws ChimeraNFSException
369+
*/
370+
public NFS4State createLockState(StateOwner stateOwner, NFS4State openState) throws ChimeraNFSException {
371+
return createState(stateOwner, Stateids.LOCK_STATE_ID, openState);
372+
}
373+
374+
/**
375+
* Create a new layout state.
376+
* @param stateOwner state owner
377+
* @return new layout state.
378+
* @throws ChimeraNFSException
379+
*/
380+
public NFS4State createLayoutState(StateOwner stateOwner) throws ChimeraNFSException {
381+
return createState(stateOwner, Stateids.LAYOUT_STATE_ID, null);
382+
}
383+
384+
/**
385+
* Create a new delegation state.
386+
* @param stateOwner state owner.
387+
* @return new delegation state.
388+
* @throws ChimeraNFSException
389+
*/
390+
public NFS4State createDelegationState(StateOwner stateOwner) throws ChimeraNFSException {
391+
return createState(stateOwner, Stateids.DELEGATION_STATE_ID, null);
392+
}
393+
394+
/**
395+
* Create a new directory delegation state.
396+
* @param stateOwner state owner.
397+
* @return new directory delegation state.
398+
* @throws ChimeraNFSException
399+
*/
400+
public NFS4State createDirDelegationState(StateOwner stateOwner) throws ChimeraNFSException {
401+
return createState(stateOwner, Stateids.DIR_DELEGATION_STATE_ID, null);
402+
}
403+
404+
public NFS4State createServerSideCopyState(StateOwner stateOwner, NFS4State openState) throws ChimeraNFSException {
405+
return createState(stateOwner, Stateids.SSC_STATE_ID, openState);
354406
}
355407

356408
public void releaseState(stateid4 stateid) throws ChimeraNFSException {

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,18 +452,22 @@ private clientid4 nextClientId() {
452452
/**
453453
* Generate new state id associated with a given {@code client}.
454454
*
455-
* we construct 'other' fileld of state IDs as following:
455+
* we construct 'other' field of state IDs as following:
456456
* |0 - 7| : client id
457-
* |8 - 11| : clients state counter
457+
* |8 - 10| : clients state counter
458+
* |11| : type of state id
458459
*
459460
* @param client nfs client for which state is generated.
461+
* @param type the type of state id.
460462
* @param count the count of already generated state ids for give client.
461463
* @return new state id.
462464
*/
463-
public stateid4 createStateId(NFS4Client client, int count) {
465+
public stateid4 createStateId(NFS4Client client, byte type, int count) {
464466
byte[] other = new byte[12];
465467
Bytes.putLong(other, 0, client.getId().value);
466-
Bytes.putInt(other, 8, count);
468+
// we eat the first 8 bits if the counter, however, we don't expect 16M states be active at the same time,
469+
// thus the probability of a collision is too low
470+
Bytes.putInt(other, 8, count << 8 | (type & 0xFF));
467471
return new stateid4(other, STATE_INITIAL_SEQUENCE);
468472
}
469473

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2018 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
@@ -54,6 +54,7 @@ public void process(CompoundContext context, nfs_resop4 result)
5454
client = context.getStateHandler().getClientIdByStateId(stateid);
5555
}
5656

57+
Stateids.checkOpenStateid(stateid);
5758
NFS4State nfsState = client.state(stateid);
5859
Stateids.checkStateId(nfsState.stateid(), stateid);
5960

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021 - 2022 Deutsches Elektronen-Synchroton,
2+
* Copyright (c) 2021 - 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
@@ -146,7 +146,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
146146

147147
private stateid4 notifyWhenComplete(NFS4Client client, Inode dstInode, verifier4 verifier, CompletableFuture<Long> copyFuture) throws ChimeraNFSException {
148148
var openState = client.state(_args.opcopy.ca_src_stateid);
149-
var copyState = client.createState(openState.getStateOwner(), openState).stateid();
149+
var copyState = client.createServerSideCopyState(openState.getStateOwner(), openState).stateid();
150150

151151
copyFuture.handle((n, t) -> {
152152

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2023 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
@@ -83,7 +83,7 @@ public void process(CompoundContext context, nfs_resop4 result) throws ChimeraNF
8383
}
8484

8585
lockOwner = client.getOrCreateOwner(_args.oplock.locker.open_owner.lock_owner.owner, _args.oplock.locker.open_owner.lock_seqid);
86-
lock_state = client.createState(lockOwner, openState);
86+
lock_state = client.createLockState(lockOwner, openState);
8787

8888
// lock states do not requires extra confirmation
8989
lock_state.confirm();

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2020 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
@@ -27,6 +27,31 @@
2727

2828
public class Stateids {
2929

30+
/**
31+
* OPEN state ID.
32+
*/
33+
final static byte OPEN_STATE_ID = 0x01;
34+
/**
35+
* Byte-range locks state ID.
36+
*/
37+
final static byte LOCK_STATE_ID = 0x02;
38+
/**
39+
* Layout state ID.
40+
*/
41+
final static byte LAYOUT_STATE_ID = 0x03;
42+
/**
43+
* Delegation state ID.
44+
*/
45+
final static byte DELEGATION_STATE_ID = 0x04;
46+
/**
47+
* Directory delegation state ID.
48+
*/
49+
final static byte DIR_DELEGATION_STATE_ID = 0x04;
50+
/**
51+
* Server Side Copy state ID.
52+
*/
53+
final static byte SSC_STATE_ID = 0x05;
54+
3055
private Stateids() {
3156
}
3257
private final static stateid4 CURRENT_STATEID =
@@ -84,4 +109,40 @@ public static stateid4 getCurrentStateidIfNeeded(CompoundContext context, statei
84109
}
85110
return stateid;
86111
}
112+
113+
public static void checkOpenStateid(stateid4 stateid) throws BadStateidException {
114+
if (stateid.other[11] != OPEN_STATE_ID) {
115+
throw new BadStateidException("Not an open stateid");
116+
}
117+
}
118+
119+
public static void checkLockStateid(stateid4 stateid) throws BadStateidException {
120+
if (stateid.other[11] != LOCK_STATE_ID) {
121+
throw new BadStateidException("Not a lock stateid");
122+
}
123+
}
124+
125+
public static void checkDelegationStateid(stateid4 stateid) throws BadStateidException {
126+
if (stateid.other[11] != DELEGATION_STATE_ID) {
127+
throw new BadStateidException("Not a delegation stateid");
128+
}
129+
}
130+
131+
public static void checkDirDelegationStateid(stateid4 stateid) throws BadStateidException {
132+
if (stateid.other[11] != DIR_DELEGATION_STATE_ID) {
133+
throw new BadStateidException("Not a directory delegation stateid");
134+
}
135+
}
136+
137+
public static void checkServerSiderCopyStateid(stateid4 stateid) throws BadStateidException {
138+
if (stateid.other[11] != SSC_STATE_ID) {
139+
throw new BadStateidException("Not a server-side copy stateid");
140+
}
141+
}
142+
143+
public static void checkLayoutStateid(stateid4 stateid) throws BadStateidException {
144+
if (stateid.other[11] != LAYOUT_STATE_ID) {
145+
throw new BadStateidException("Not a layout stateid");
146+
}
147+
}
87148
}

core/src/test/java/org/dcache/nfs/v4/FileTrackerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public void shouldFailToGetAccessModeWithBadStateid() throws Exception {
200200
Inode inode = Inode.forFile(fh.value);
201201

202202
tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_READ, 0);
203-
tracker.getShareAccess(client1, inode, client1.createState(stateOwner1).stateid());
203+
tracker.getShareAccess(client1, inode, client1.createOpenState(stateOwner1).stateid());
204204
}
205205

206206
@Test

core/src/test/java/org/dcache/nfs/v4/NFS4ClientTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2022 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
@@ -75,7 +75,7 @@ public void testStateCleanOnOpenCloseV41() throws Exception {
7575
nfs_resop4 result;
7676

7777
NFSv41Session session = nfsClient.createSession(1, 2, 1, 8, 8);
78-
NFS4State state = nfsClient.createState(owner);
78+
NFS4State state = nfsClient.createOpenState(owner);
7979

8080
nfs_argop4 close_args = new CompoundBuilder()
8181
.withClose(state.stateid(), 1)
@@ -102,7 +102,7 @@ public void testStateCleanOnOpenCloseV40() throws Exception {
102102
CompoundContext context;
103103
nfs_resop4 result;
104104

105-
NFS4State state = nfsClient.createState(owner);
105+
NFS4State state = nfsClient.createOpenState(owner);
106106

107107
nfs_argop4 close_args = new CompoundBuilder()
108108
.withClose(state.stateid(), 1)
@@ -145,7 +145,7 @@ public void testAttacheDetachState() throws ChimeraNFSException {
145145

146146
@Test
147147
public void testCreateState() throws ChimeraNFSException {
148-
NFS4State state = nfsClient.createState(owner);
148+
NFS4State state = nfsClient.createOpenState(owner);
149149
assertTrue(nfsClient.hasState());
150150
}
151151

@@ -176,7 +176,7 @@ public void testCreateSessionWrongSequence() throws ChimeraNFSException {
176176
public void testClientDisposeCleansState() throws ChimeraNFSException {
177177
AtomicBoolean isDisposed = new AtomicBoolean(false);
178178

179-
NFS4State state = nfsClient.createState(owner);
179+
NFS4State state = nfsClient.createOpenState(owner);
180180
state.addDisposeListener(s -> isDisposed.set(true));
181181

182182
nfsClient.tryDispose();

0 commit comments

Comments
 (0)