Skip to content

Commit 2f92e2b

Browse files
committed
Merge branch 'master' of https://github.com/dCache/nfs4j into ck/Inode
Signed-off-by: Christian Kohlschütter <[email protected]>
2 parents 7c38ce8 + 8734b9a commit 2f92e2b

File tree

10 files changed

+142
-117
lines changed

10 files changed

+142
-117
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.dcache.nfs.benchmarks;
2+
3+
import com.sun.security.auth.UnixNumericGroupPrincipal;
4+
import com.sun.security.auth.UnixNumericUserPrincipal;
5+
import java.security.Principal;
6+
import java.util.Set;
7+
import javax.security.auth.Subject;
8+
import org.openjdk.jmh.annotations.Benchmark;
9+
import org.openjdk.jmh.annotations.BenchmarkMode;
10+
11+
import org.openjdk.jmh.annotations.Mode;
12+
import org.openjdk.jmh.annotations.Scope;
13+
import org.openjdk.jmh.annotations.State;
14+
15+
@BenchmarkMode(Mode.Throughput)
16+
@State(Scope.Benchmark)
17+
public class SubjectBenchmark {
18+
19+
20+
@Benchmark
21+
public Subject subjectByAddingToSet() {
22+
Subject subject = new Subject();
23+
subject.getPrincipals().add(new UnixNumericUserPrincipal(0));
24+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(0, true));
25+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(1, false));
26+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(2, false));
27+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(3, false));
28+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(4, false));
29+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(5, false));
30+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(6, false));
31+
subject.setReadOnly();
32+
return subject;
33+
}
34+
35+
36+
@Benchmark
37+
public Subject subjectByPassingSet() {
38+
39+
Principal[] principals = new Principal[]{
40+
new UnixNumericUserPrincipal(0),
41+
new UnixNumericGroupPrincipal(0, true),
42+
new UnixNumericGroupPrincipal(1, false),
43+
new UnixNumericGroupPrincipal(2, false),
44+
new UnixNumericGroupPrincipal(3, false),
45+
new UnixNumericGroupPrincipal(4, false),
46+
new UnixNumericGroupPrincipal(5, false),
47+
new UnixNumericGroupPrincipal(6, false)
48+
};
49+
50+
return new Subject(true, Set.of(principals), Set.of(), Set.of());
51+
}
52+
}
53+
54+

core/src/main/java/org/dcache/nfs/util/UnixSubjects.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,11 @@ public static boolean hasGid(Subject subject, long gid) {
9595
* @return subject with given uid, gid.
9696
*/
9797
public static Subject toSubject(long uid, long gid) {
98-
return new Subject(false,
99-
Set.of(new UnixNumericUserPrincipal(uid), new UnixNumericGroupPrincipal(gid, true)),
100-
Set.of(),
101-
Set.of());
98+
var subject = new Subject();
99+
subject.getPrincipals().add(new UnixNumericUserPrincipal(uid));
100+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(gid, true));
101+
102+
return subject;
102103
}
103104

104105
/**
@@ -110,12 +111,10 @@ public static Subject toSubject(long uid, long gid) {
110111
* @return subject with given uid, gid and gids.
111112
*/
112113
public static Subject toSubject(long uid, long gid, long... gids) {
113-
Subject subject = toSubject(uid, gid);
114-
subject.getPrincipals()
115-
.addAll(
116-
Arrays.stream(gids)
117-
.mapToObj(l -> new UnixNumericGroupPrincipal(l, false))
118-
.collect(Collectors.toSet()));
114+
var subject = toSubject(uid, gid);
115+
for (long aux : gids) {
116+
subject.getPrincipals().add(new UnixNumericGroupPrincipal(aux, false));
117+
}
119118
return subject;
120119
}
121120

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
}

core/src/main/java/org/dcache/nfs/vfs/FileHandle.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
/*
2+
* Copyright (c) 2012 - 2025 Deutsches Elektronen-Synchroton,
3+
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
4+
*
25
* This library is free software; you can redistribute it and/or modify
36
* it under the terms of the GNU Library General Public License as
47
* published by the Free Software Foundation; either version 2 of the

core/src/main/java/org/dcache/nfs/vfs/Inode.java

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009 - 2024 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
@@ -42,8 +42,6 @@ public class Inode {
4242
private final static int MIN_LEN = 14;
4343
private final static int VERSION = 1;
4444
private final static int MAGIC = 0xCAFFEE;
45-
private final static byte[] FH_V0_REG = new byte[] {0x30, 0x3a};
46-
private final static byte[] FH_V0_PFS = new byte[] {0x32, 0x35, 0x35, 0x3a};
4745

4846
private final int version;
4947
private final int magic;
@@ -94,46 +92,24 @@ public Inode(byte[] bytes) {
9492

9593
int magic_version = b.getInt();
9694
int geussVersion = (magic_version & 0xFF000000) >>> 24;
97-
if (geussVersion == VERSION) {
98-
version = geussVersion;
99-
magic = magic_version & 0x00FFFFFF;
100-
if (magic != MAGIC) {
101-
throw new IllegalArgumentException("Bad magic number");
102-
}
103-
104-
generation = b.getInt();
105-
exportIdx = b.getInt();
106-
type = (int) b.get();
107-
int olen = (int) b.get();
108-
fs_opaque = new byte[olen];
109-
b.get(fs_opaque);
110-
111-
this.nfsHandle = bytes.clone();
112-
} else if (arrayEquals(bytes, FH_V0_REG, FH_V0_REG.length)
113-
|| arrayEquals(bytes, FH_V0_PFS, FH_V0_PFS.length)) {
114-
magic = MAGIC;
115-
generation = 0;
116-
type = bytes[1] == FH_V0_REG[1] ? 0 : 1;
117-
if (type == 1) {
118-
/*
119-
* convert pseudo inode into real one: '255:' => '0:' NOTICE: the converted handle will present himself
120-
* as version 1
121-
*/
122-
version = 1;
123-
exportIdx = 0;
124-
fs_opaque = new byte[bytes.length - 2];
125-
System.arraycopy(bytes, 2, fs_opaque, 0, fs_opaque.length);
126-
fs_opaque[0] = 0x30;
127-
} else {
128-
version = 0;
129-
exportIdx = -1;
130-
fs_opaque = bytes;
131-
}
132-
133-
this.nfsHandle = buildNfsHandle();
134-
} else {
95+
if (geussVersion != VERSION) {
13596
throw new IllegalArgumentException("Unsupported version: " + geussVersion);
13697
}
98+
99+
version = geussVersion;
100+
magic = magic_version & 0x00FFFFFF;
101+
if (magic != MAGIC) {
102+
throw new IllegalArgumentException("Bad magic number");
103+
}
104+
105+
generation = b.getInt();
106+
exportIdx = b.getInt();
107+
type = (int) b.get();
108+
int olen = (int) b.get();
109+
fs_opaque = new byte[olen];
110+
b.get(fs_opaque);
111+
112+
this.nfsHandle = bytes.clone();
137113
}
138114

139115
@Deprecated(forRemoval = true)
@@ -156,17 +132,6 @@ public String toString() {
156132
return BaseEncoding.base16().lowerCase().encode(nfsHandle);
157133
}
158134

159-
private static boolean arrayEquals(byte[] a1, byte[] a2, int len) {
160-
if (a1.length < len || a2.length < len)
161-
return false;
162-
for (int i = 0; i < len; i++) {
163-
if (a1[i] != a2[i]) {
164-
return false;
165-
}
166-
}
167-
return true;
168-
}
169-
170135
public static Inode forNfsHandle(byte[] bytes) {
171136
return new Inode(bytes);
172137
}

core/src/test/java/org/dcache/nfs/vfs/FileHandleTest.java

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -89,37 +89,4 @@ public void testFileHandleConstructor() {
8989
"01caffee00000000ea15b996002e303a494e4f44453a3030303043333732333331373433393234353645423833453434383434453844323844363a30"),
9090
inode.toNfsHandle());
9191
}
92-
93-
@Test
94-
public void testValidHandleV0Regular() {
95-
String oldId = "0:INODE:0000C37233174392456EB83E44844E8D28D6:0";
96-
97-
byte[] bytes = oldId.getBytes(US_ASCII);
98-
FileHandle fh = new FileHandle(bytes);
99-
100-
assertEquals(0, fh.getVersion());
101-
assertEquals(0xCAFFEE, fh.getMagic());
102-
assertEquals(0, fh.getGeneration());
103-
byte[] opaque = fh.getFsOpaque();
104-
assertEquals(-1, fh.getExportIdx());
105-
assertEquals(0, fh.getType());
106-
assertEquals(oldId, new String(opaque, US_ASCII));
107-
}
108-
109-
@Test
110-
public void testValidHandleV0Pseudo() {
111-
String oldIdPseudo = "255:INODE:0000C37233174392456EB83E44844E8D28D6:0";
112-
String oldIdReg = "0:INODE:0000C37233174392456EB83E44844E8D28D6:0";
113-
114-
byte[] bytes = oldIdPseudo.getBytes(US_ASCII);
115-
FileHandle fh = new FileHandle(bytes);
116-
117-
assertEquals(1, fh.getVersion());
118-
assertEquals(0xCAFFEE, fh.getMagic());
119-
assertEquals(0, fh.getGeneration());
120-
byte[] opaque = fh.getFsOpaque();
121-
assertEquals(0, fh.getExportIdx());
122-
assertEquals(1, fh.getType());
123-
assertEquals(oldIdReg, new String(opaque, US_ASCII));
124-
}
12592
}

0 commit comments

Comments
 (0)