FileTracker { public stateid4 addOpen(NFS4Client client, StateOwner owner, Inode inode, int shareAccess, int shareDeny) throws ChimeraNFSException { Opaque fileId = new Opaque(inode.getFileId()); Lock lock = filesLock.get(fileId); lock.lock(); try { final List<OpenState> opens = files.computeIfAbsent(fileId, x -> new ArrayList<>(1)); stateid4 stateid; if (opens.stream() .filter(o -> o.client.isLeaseValid()) .anyMatch(o -> (shareAccess & o.getShareDeny()) != 0|| (shareDeny & o.getShareAccess()) != 0)) { throw new ShareDeniedException("Conflicting share"); } for (OpenState os : opens) { if (os.client.getId() == client.getId() && os.getOwner().equals(owner)) { os.shareAccess |= shareAccess; os.shareDeny |= shareDeny; return os.stateid; } } NFS4State state = client.createState(owner); stateid = state.stateid(); OpenState openState = new OpenState(client, owner, stateid, shareAccess, shareDeny); opens.add(openState); state.addDisposeListener(s -> removeOpen(inode, stateid)); return stateid; } finally { lock.unlock(); } }  stateid4 addOpen(NFS4Client client, StateOwner owner, Inode inode, int shareAccess, int shareDeny); stateid4 downgradeOpen(NFS4Client client, stateid4 stateid, Inode inode, int shareAccess, int shareDeny); int getShareAccess(NFS4Client client, Inode inode, stateid4 stateid);  }
@Test public void shouldAllowNonConflictingOpens() throws Exception { NFS4Client client1 = createClient(sh); StateOwner stateOwner1 = client1.getOrCreateOwner("client1".getBytes(StandardCharsets.UTF_8), new seqid4(0)); nfs_fh4 fh = generateFileHandle(); Inode inode = Inode.forFile(fh.value); tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_READ, 0); tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_WRITE, 0); }
@Test public void shouldReturnSameStateIdForSameClient() throws Exception { NFS4Client client1 = createClient(sh); StateOwner stateOwner1 = client1.getOrCreateOwner("client1".getBytes(StandardCharsets.UTF_8), new seqid4(0)); nfs_fh4 fh = generateFileHandle(); Inode inode = Inode.forFile(fh.value); stateid4 s1 = tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_READ, 0); stateid4 s2 = tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_WRITE, 0); assertEquals("New stateid returned", s1, s2); }
@Test public void shouldReturnDifferentStateIdForDifferentClient() throws Exception { NFS4Client client1 = createClient(sh); StateOwner stateOwner1 = client1.getOrCreateOwner("client1".getBytes(StandardCharsets.UTF_8), new seqid4(0)); NFS4Client client2 = createClient(sh); StateOwner stateOwner2 = client1.getOrCreateOwner("client2".getBytes(StandardCharsets.UTF_8), new seqid4(0)); nfs_fh4 fh = generateFileHandle(); Inode inode = Inode.forFile(fh.value); stateid4 s1 = tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_READ, 0); stateid4 s2 = tracker.addOpen(client2, stateOwner2, inode, OPEN4_SHARE_ACCESS_WRITE, 0); assertNotEquals("Same stateid returned", s1, s2); }
@Test(expected = ShareDeniedException.class) public void shouldRejectConflictingOpens() throws Exception { NFS4Client client1 = createClient(sh); StateOwner stateOwner1 = client1.getOrCreateOwner("client1".getBytes(StandardCharsets.UTF_8), new seqid4(0)); nfs_fh4 fh = generateFileHandle(); Inode inode = Inode.forFile(fh.value); tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_READ, 0); tracker.addOpen(client1, stateOwner1, inode, OPEN4_SHARE_ACCESS_WRITE, OPEN4_SHARE_ACCESS_READ); }