JDK-6203387 : File.canRead() reports wrong value (win)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 1.4.2,5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic,windows_xp
  • CPU: generic,x86
  • Submitted: 2004-12-02
  • Updated: 2013-07-19
  • Resolved: 2010-11-20
Related Reports
Relates :  
Relates :  
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)

Windows XP

java.io.File::canRead reports wrong value

create new file "c:/xxxxx.txt ". Deny all permissions on this file for current user.

compile & execute following code

is file:true
can read:false

IOException on read attempt.
is file:true
can read:true
Exception in thread "main" java.io.FileNotFoundException: c:\xxxxx.txt (Access is denied)
	at java.io.FileInputStream.open(Native Method)
	at java.io.FileInputStream.<init>(FileInputStream.java:106)
	at Test.main(Test.java:11)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:78)

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Test {
    public static void main( String[] args ) throws IOException {
        File file = new File("c:\\xxxxx.txt");
        System.out.println( "exists:"  + file.exists() );
        System.out.println( "is file:"  + file.isFile() );
        System.out.println( "can read:" + file.canRead() );
        FileInputStream fileInputStream = new FileInputStream(file);


---------- END SOURCE ----------

Seems like a windows XP only problem. On solaris we get the expected behavior: - --------------------------- Solaris --------------------------- > ls -al /tmp/test.txt -r-------- 1 root other 0 Dec 2 12:56 /tmp/test.txt > java Test exists:true is file:true can read:false Exception in thread "main" java.io.FileNotFoundException: /tmp/test.txt (Permission denied) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:106) at Test.main(Test.java:11) ------------------------- Windows XP ------------------------ H:\tmpjava\343795>java Test exists:true is file:true can read:true <==== should be false Exception in thread "main" java.io.FileNotFoundException: c:\a.txt (Access is d nied) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:106) at Test.main(Test.java:11) Additionally, when I double click the file to read it the Windows OS correctly throws an access denied error. 2004-12-02 14:21:55.687 ###@###.### ###@###.### 2004-12-02 21:20:22 GMT

The cool thing about OpenJDK is that this is all open source, including the history, so we can do some research on it. File.canWrite() boils down to a native method FileSystem.checkAccess on the filesystem- and OS-specific class, which I believe is here: http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/9b6070690e50/src/windows/native/java/io/WinNTFileSystem_md.c at lines 384ff. This method calls GetFileAttributesW and checks some bits on it, including, yes, FILE_ATTRIBUTE_READONLY. But according to the Microsoft docs, this seems to be the most appropriate thing to check: http://msdn.microsoft.com/en-us/library/windows/desktop/gg258117%28v=vs.85%29.aspx But the WinNTFileSystem code in this area was changed significantly by this changeset: http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/f570cbc8d4ff which itself is a fix to bug JDK-4939819. I don't know what the right answer is, but maybe by looking through all this history we can figure out some solution to the problem.

(The following note is from Jordan Brown.) The Java org ... decided that Windows File.canWrite( ) was somehow defined to test the read-only bit (and nothing else), instead of being defined in its platform-neutral meaning "can I write to this file". Not that it's relevant to my complaint with Java, but the reason that I ran into this is that we have an internal customer who writes files on our server from UNIX and reads them from Windows (or something like that). On a NetApp server, setting a UNIX read-only permission set like 0644 causes the read-only bit to appear set to Windows clients. We didn't choose to handle the interoperation that way, because we don't think it's a good match for Windows semantics. (Windows has the r/o bit and the ACL as separate properties, and we tie the UNIX permissions to the ACL, so tying the r/o bit to the UNIX permissions would tie it to the ACL and violate Windows semantics.) The customer's Windows Java application calls File.canWrite() on the file...

EVALUATION JDK7 didn't end up including checkAccess; you can use Files.isWritable(f.toPath()).

EVALUATION At this time, we don't have any plans to change the canXXX methods to check the file ACLs. For jdk7, applications can replace code using canXXX with f.toPath().checkAccess(XXX). The checkAccess method will check that the application has the required access to the file, taking into account the effective access that the ACL grants, the DOS readonly attribute, and in the case of write access, that the volume is not read-only.

EVALUATION -- An alternative solution (for canRead and canWrite) is to open the file (via CreateFile) for the desired access. If the open succeeds then canXXX will return true. If the open fails then the error can be examined to determine if canXXX returns true or false. This "solution" isn't going to work for canExecute - that will requires examination of the ACEs.

EVALUATION Here's the sad story of canRead() on Windows: The win32 API provides _access() and _waccess(), which seem to exactly match the requirements for implementing canRead() and canWrite(). Unfortunately, the Windows implementers, in their infinite wisdom, decided not to teach these functions about the new NTFS security mechanism, and so they claim that *ALL* files are readable (because they were so on MS-DOS) and for writing, only the one bit in the file attributes is checked. So this is simply a Windows bug. We can work around it as follows: 1. check if on NT and file is NTFS. If not, _waccess works. 2. If so, we need to call the complex NT function AccessCheck http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/accesscheck.asp to determine whether the file is readable/writable. Probably File.canWrite() is also broken in the presence of NTFS ACLs. ###@###.### 2004-12-03 21:29:01 GMT