|
27 | 27 | import java.util.Iterator;
|
28 | 28 | import java.util.List;
|
29 | 29 | import java.util.Map;
|
| 30 | +import java.util.Objects; |
30 | 31 | import java.util.concurrent.ConcurrentHashMap;
|
31 | 32 | import java.util.concurrent.locks.Lock;
|
32 | 33 | import java.util.stream.Collectors;
|
33 | 34 | import org.dcache.nfs.ChimeraNFSException;
|
34 | 35 | import org.dcache.nfs.status.BadStateidException;
|
35 | 36 | import org.dcache.nfs.status.DelayException;
|
| 37 | +import org.dcache.nfs.status.DelegRevokedException; |
36 | 38 | import org.dcache.nfs.status.InvalException;
|
37 | 39 | import org.dcache.nfs.status.ShareDeniedException;
|
38 | 40 | import org.dcache.nfs.status.StaleException;
|
@@ -134,12 +136,67 @@ public NFS4Client getClient() {
|
134 | 136 | }
|
135 | 137 |
|
136 | 138 | /**
|
137 |
| - * Record associated with open-delegation. |
138 |
| - * @param client |
139 |
| - * @param delegationStateid |
140 |
| - * @param delegationType |
| 139 | + * Open-delegation record |
141 | 140 | */
|
142 |
| - record DelegationState(NFS4Client client, NFS4State delegationStateid, int delegationType) { |
| 141 | + static final class DelegationState { |
| 142 | + private final NFS4Client client; |
| 143 | + private final NFS4State delegationStateid; |
| 144 | + private final int delegationType; |
| 145 | + private boolean revoked; |
| 146 | + |
| 147 | + /** |
| 148 | + * @param client |
| 149 | + * @param delegationStateid |
| 150 | + * @param delegationType |
| 151 | + */ |
| 152 | + DelegationState(NFS4Client client, NFS4State delegationStateid, int delegationType) { |
| 153 | + this.client = client; |
| 154 | + this.delegationStateid = delegationStateid; |
| 155 | + this.delegationType = delegationType; |
| 156 | + this.revoked = false; |
| 157 | + } |
| 158 | + |
| 159 | + public NFS4Client client() { |
| 160 | + return client; |
| 161 | + } |
| 162 | + |
| 163 | + public NFS4State delegationStateid() { |
| 164 | + return delegationStateid; |
| 165 | + } |
| 166 | + |
| 167 | + public int delegationType() { |
| 168 | + return delegationType; |
| 169 | + } |
| 170 | + |
| 171 | + public boolean revoked() { |
| 172 | + return revoked; |
| 173 | + } |
| 174 | + |
| 175 | + @Override |
| 176 | + public boolean equals(Object obj) { |
| 177 | + if (obj == this) return true; |
| 178 | + if (obj == null || obj.getClass() != this.getClass()) return false; |
| 179 | + var that = (DelegationState) obj; |
| 180 | + return Objects.equals(this.client, that.client) && |
| 181 | + Objects.equals(this.delegationStateid, that.delegationStateid) && |
| 182 | + this.delegationType == that.delegationType && |
| 183 | + Objects.equals(this.revoked, that.revoked); |
| 184 | + } |
| 185 | + |
| 186 | + @Override |
| 187 | + public int hashCode() { |
| 188 | + return Objects.hash(client, delegationStateid, delegationType, revoked); |
| 189 | + } |
| 190 | + |
| 191 | + @Override |
| 192 | + public String toString() { |
| 193 | + return "DelegationState[" + |
| 194 | + "client=" + client + ", " + |
| 195 | + "delegationStateid=" + delegationStateid + ", " + |
| 196 | + "delegationType=" + delegationType + ", " + |
| 197 | + "revoked=" + revoked + ']'; |
| 198 | + } |
| 199 | + |
143 | 200 |
|
144 | 201 | }
|
145 | 202 |
|
@@ -215,6 +272,7 @@ public OpenRecord addOpen(NFS4Client client, StateOwner owner, Inode inode, int
|
215 | 272 | .reduce(0, (c, d) -> {
|
216 | 273 | try {
|
217 | 274 | d.client().getCB().cbDelegationRecall(fh, d.delegationStateid().stateid(), false);
|
| 275 | + d.revoked = true; |
218 | 276 | return c + 1;
|
219 | 277 | } catch (IOException e) {
|
220 | 278 | LOG.warn("Failed to recall delegation from {} : {}", d.client(), e.toString());
|
@@ -367,6 +425,71 @@ public void delegationReturn(NFS4Client client, stateid4 stateid, Inode inode)
|
367 | 425 | }
|
368 | 426 | }
|
369 | 427 |
|
| 428 | + /** |
| 429 | + * Get access mode for a given files, client and stateid. The state is must be either an open, |
| 430 | + * lock or delegation stateid. |
| 431 | + * |
| 432 | + * @param client nfs client who returns the delegation. |
| 433 | + * @param inode the inode of the delegated file. |
| 434 | + * @param stateid open or delegation stateid |
| 435 | + */ |
| 436 | + public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid) |
| 437 | + throws ChimeraNFSException { |
| 438 | + |
| 439 | + Opaque fileId = new Opaque(inode.getFileId()); |
| 440 | + Lock lock = filesLock.get(fileId); |
| 441 | + lock.lock(); |
| 442 | + try { |
| 443 | + |
| 444 | + switch (stateid.other[11]) { |
| 445 | + case Stateids.LOCK_STATE_ID: |
| 446 | + NFS4State lockState = client.state(stateid); |
| 447 | + stateid = lockState.getOpenState().stateid(); |
| 448 | + // fall through |
| 449 | + case Stateids.OPEN_STATE_ID: { |
| 450 | + final List<OpenState> opens = files.get(fileId); |
| 451 | + |
| 452 | + if (opens == null) { |
| 453 | + throw new BadStateidException("no matching open"); |
| 454 | + } |
| 455 | + |
| 456 | + final stateid4 openStateid = stateid; |
| 457 | + return opens.stream() |
| 458 | + .filter(s -> client.getId() == s.client.getId()) |
| 459 | + .filter(s -> s.stateid.equals(openStateid)) |
| 460 | + .mapToInt(OpenState::getShareAccess) |
| 461 | + .findAny() |
| 462 | + .orElseThrow(BadStateidException::new); |
| 463 | + } |
| 464 | + case Stateids.DELEGATION_STATE_ID: { |
| 465 | + |
| 466 | + var fileDelegations = delegations.get(fileId); |
| 467 | + if (fileDelegations == null) { |
| 468 | + throw new BadStateidException("no delegation found"); |
| 469 | + } |
| 470 | + |
| 471 | + stateid4 delegationStateid = stateid; |
| 472 | + |
| 473 | + var delegation = fileDelegations.stream() |
| 474 | + .filter(d -> d.client().getId().equals(client.getId())) |
| 475 | + .filter(d -> d.delegationStateid().stateid().equals(delegationStateid)) |
| 476 | + .findAny() |
| 477 | + .orElseThrow(BadStateidException::new); |
| 478 | + |
| 479 | + if (delegation.revoked()) { |
| 480 | + throw new DelegRevokedException(); |
| 481 | + } |
| 482 | + // NOTE: as delegation types match access modes we don't convert the values. |
| 483 | + return delegation.delegationType(); |
| 484 | + } |
| 485 | + |
| 486 | + default: |
| 487 | + throw new BadStateidException(); |
| 488 | + } |
| 489 | + } finally { |
| 490 | + lock.unlock(); |
| 491 | + } |
| 492 | + } |
370 | 493 |
|
371 | 494 | /**
|
372 | 495 | * Remove an open from the list.
|
@@ -404,37 +527,6 @@ void removeOpen(Inode inode, stateid4 stateid) {
|
404 | 527 | }
|
405 | 528 | }
|
406 | 529 |
|
407 |
| - /** |
408 |
| - * Get open access type used by opened file. |
409 |
| - * @param client nfs client which performs the request. |
410 |
| - * @param inode of the opened file |
411 |
| - * @param stateid associated with the open. |
412 |
| - * @return share access typed used. |
413 |
| - * @throws BadStateidException if no open file associated with provided state id. |
414 |
| - */ |
415 |
| - public int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid) throws BadStateidException { |
416 |
| - |
417 |
| - Opaque fileId = new Opaque(inode.getFileId()); |
418 |
| - Lock lock = filesLock.get(fileId); |
419 |
| - lock.lock(); |
420 |
| - try { |
421 |
| - final List<OpenState> opens = files.get(fileId); |
422 |
| - |
423 |
| - if (opens == null) { |
424 |
| - throw new BadStateidException("no matching open"); |
425 |
| - } |
426 |
| - |
427 |
| - return opens.stream() |
428 |
| - .filter(s -> client.getId() == s.client.getId()) |
429 |
| - .filter(s -> s.stateid.equals(stateid)) |
430 |
| - .mapToInt(OpenState::getShareAccess) |
431 |
| - .findFirst() |
432 |
| - .orElseThrow(BadStateidException::new); |
433 |
| - } finally { |
434 |
| - lock.unlock(); |
435 |
| - } |
436 |
| - } |
437 |
| - |
438 | 530 | /**
|
439 | 531 | * Get all currently open files with associated clients. The resulting map contains file's inodes
|
440 | 532 | * as key and collection of nfs clients that have this file opened as a value.
|
|
0 commit comments