java.nio.file.Files.isWriteable doesn't agree with java.io.File.canWrite()
Asked Answered
D

3

6

I have Java code doing the following:

  1. Create a temporary empty file with ZIP extension using File.createTempFile()
  2. Delete it with File.delete() (we only really wanted it to generate a temp file name)
  3. Copy a "template" ZIP file to the same path with com.google.commons.io.ByteStreams.copy() using a new OutputSupplier given the same filename
  4. Modify the ZIP archive (remove a directory) using TrueZIP 7.4.3

On a specific system, step 4 fails consistently with FsReadOnlyArchiveFileSystemException - "This is a read-only archive file system!" (see http://java.net/projects/truezip/lists/users/archive/2011-05/message/9)

Debugging the TrueZIP code, I noticed the following:

  • There is no open file handle on this file between any of the steps above, and specifically not before step 4
  • Checking the same file with File.canWrite() rather than NIO returns at the exact same timing (using a debugger), it shows that it is writable

Here is what you see in the debugger expressions list:

fn => "C:/myworkdir/temp/myfile4088293380313057223tmp.zip"
java.nio.file.Files.isWritable(java.nio.file.Paths.get(fn)) => false
new java.io.File(fn).canWrite() => true

Using JDK 1.7.04

Any ideas?

Disbursement answered 2/10, 2012 at 10:4 Comment(8)
I would try Java 7 update 7 to see if its a bug which has been fixed.Hungerford
I would avoid using both APIs and instead rely on the exceptions thrown by e.g. new FileOutputStream().Mclellan
i would not use java 7, to begin withAdriell
Does this code fail on any system or just on one specific? Which one?Saskatoon
First, I would update to JDK 7u7 and TrueZIP 7.6.6 to see if the issue persists.Cosmo
Upgraded to Java 1.7.07, didn't make a difference. Didn't upgrade TrueZIP yet but looking at the change log I don't think that would matter (since it doesn't look like an issue in TrueZIP). I'm considering downgrading it to a non-NIO version but god knows what will break when I do that.Disbursement
BTW A reboot didn't help either. It's on one specific machine, on other machines things work fine.Disbursement
it can be an NIO bug. step into isWritable() and see why it returns false.Ias
K
6

There is a bug in java.nio.file.Files.isWritable under windows: it won't take implicit permissions into consideration. java bug #7190897

Kovacev answered 10/7, 2013 at 12:49 Comment(1)
Update: this bug was fixed in Java 8 (b84) and backported to Java 7u40.Kronstadt
M
5

I would avoid using both APIs and instead rely on the exceptions thrown by e.g. new FileOutputStream(). They at least are real, and of real concern. Using the APIs you mention is entirely pointless, and it introduces timing windows and repeated code. You have to catch the IOException anyway: why write all that code twice?

Mclellan answered 2/10, 2012 at 10:21 Comment(4)
That might be true, but your answer doesn't explain why the two give a different result in this case. (see title)Saskatoon
The test is part of TrueZIP code actually and can't be avoided because depending on the result, you may be allowed to write to the virtual file system or not.Cosmo
@Saskatoon Sometimes the answer to a question is to do it another way.Mclellan
@EJP True, I didn't down-vote. But I'm curious myself why the result is different for the 2 APIs. :-) Is there a good reson or is there a bug?Saskatoon
L
5

The end result isn't too surprising:

java.nio.file.Files.isWritable(java.nio.file.Paths.get(fn)) => false
new java.io.File(fn).canWrite() => true

File.canWrite doesn't pay attention to ACLs at all and only checks the MS-DOS read-only attribute.

Files.isWriteable pays attention to ACLs but for whatever reason (to keep broken programs broken?) they left File.canWrite un-fixed. This turns out to be lucky, because in some situations it seems like it can return false even when you can open the file with no problems.

Really, I would summarise the methods like this:

  • File.canWrite sometimes returns true when you can't actually write to the file.
  • Files.isWriteable sometimes returns false when you can actually write to the file.

I'm not sure what the point of either method is right now. Since everyone who uses these ultimately has to write a non-broken equivalent which actually tries to open the file, one wonders why they didn't just open the file to do the check themselves.

Lothaire answered 20/2, 2013 at 5:8 Comment(2)
Since bugs.openjdk.java.net/browse/JDK-7190897 Files.isWritable works as expected.Mystify
I think it's still advisable to simply perform the operation, rather than checking and then performing it, because the result of Files.isWritable is immediately out of date, even if it was correct. Same goes for isReadable, exists, notExists, isRegularFile, isDirectory, isSymbolicLink, and probably more.Lothaire

© 2022 - 2024 — McMap. All rights reserved.