@@ -55,6 +55,7 @@ public static RealUseToken createWithExistingFile(Path p, String owner, Cryptor
5555 private volatile Path filePath ;
5656 private volatile FileChannel channel ;
5757 private volatile boolean closed ;
58+ private volatile long lastModified ;
5859
5960 RealUseToken (Path filePath , String owner , Cryptor cryptor , ConcurrentMap <Path , RealUseToken > useTokens , Executor tokenPersistor , OpenOption openMode ) {
6061 var delayedExecutor = CompletableFuture .delayedExecutor (Constants .INUSE_DELAY_MILLIS , TimeUnit .MILLISECONDS , tokenPersistor );
@@ -96,9 +97,18 @@ void refresh() {
9697 if (closed || channel == null ) {
9798 return ;
9899 }
100+ var currentLastModfied = Files .getLastModifiedTime (filePath ).toMillis ();
101+ if (currentLastModfied != lastModified ) {
102+ throw new ModifiedFileException (); //someone edited _our_ file.
103+ }
99104 writeInUseFile ();
105+ } catch (ModifiedFileException e ) {
106+ //TODO: event? we have no access to the cleartext!
107+ LOG .debug ("Failed to refresh in-use file {}." , filePath , e );
108+ close (false );
100109 } catch (IOException e ) {
101110 LOG .debug ("Failed to refresh in-use file {}." , filePath , e );
111+ close ();
102112 } finally {
103113 fileCreationSync .unlock ();
104114 }
@@ -116,7 +126,8 @@ int writeInUseFile() throws IOException {
116126 prop .store (rawInfo , null );
117127 bytesWritten = encChannel .write (ByteBuffer .wrap (rawInfo .toByteArray ()));
118128 }
119- channel .force (false );
129+ channel .force (true );
130+ lastModified = Files .getLastModifiedTime (filePath ).toMillis ();
120131 return bytesWritten ;
121132 }
122133
@@ -136,6 +147,7 @@ void moveToInternal(Path newFilePath) {
136147 useTokens .compute (newFilePath , (_ , _ ) -> {
137148 try {
138149 if (channel != null ) {
150+ //TODO: does this affect the lastModified file?
139151 Files .move (filePath , newFilePath , StandardCopyOption .REPLACE_EXISTING );
140152 }
141153 return this ;
@@ -160,6 +172,10 @@ public boolean isClosed() {
160172
161173 @ Override
162174 public void close () {
175+ close (true );
176+ }
177+
178+ void close (boolean deleteFile ) {
163179 fileCreationSync .lock ();
164180 try {
165181 if (closed ) {
@@ -171,7 +187,9 @@ public void close() {
171187 if (channel != null ) {
172188 try {
173189 channel .close ();
174- Files .deleteIfExists (filePath );
190+ if (deleteFile ) {
191+ Files .deleteIfExists (filePath );
192+ }
175193 } catch (IOException e ) {
176194 //ignore
177195 LOG .warn ("Failed to delete inUse File {}. Must be deleted manually." , path );
@@ -184,6 +202,8 @@ public void close() {
184202 }
185203 }
186204
205+ //--- glue code ---
206+
187207 interface EncryptionDecorator {
188208
189209 WritableByteChannel wrapWithEncryption (ByteChannel ch , Cryptor c );
@@ -211,4 +231,8 @@ public int read(ByteBuffer dst) throws IOException {
211231 return delegate .read (dst );
212232 }
213233 }
234+
235+ static class ModifiedFileException extends RuntimeException {
236+
237+ }
214238}
0 commit comments