Name: mt13159 Date: 02/28/2001
A problem has arisen in conjunction with File's canWrite method and
members of group User under Windows 2000.
Like NT, Windows 2000 has an Administrator group. Win2K also has a
Power Users group, which is akin to a standard user under NT. New to
Win2K is the Users group, which is severely restricted...
Members of group User can read the entire Registry, but can only
cause updates to occur in HKEY_CURRENT_USER.
Members of group User cannot write to C:\WinNT or C:\Program Files
or to subdirectories of C:\WinNT or C:\Program Files, unless the
Administrator has specifically granted such privileges.
To use a generic example, let's say a user gets a new machine with
Windows 2000 installed. The machine is missing Netscape. A member
of group User cannot install Netscape as it modifies Registry entries
outside of HKEY_CURRENT_USER, and typically installs to the C drive's
Program Files directory. An Administrator would have to install the
software, and configure it for the User.
Now for the Java problem. Class File contains the method canWrite,
which determines write accessibility for a file or directory. In the
underlying C code, canWrite calls access. access checks the file or
directory's permissions, and returns 0 for success or -1 for failure.
The problem is that access *ONLY* checks the file or directory's
permissions. It knows nothing about system security.
By default, C:\Program Files has the A and R attributes set. R says
the directory is not writable. Calling canWrite on this directory
returns false, as the directory's permissions do not allow writing.
But the C:\WinNT directory, and most of the directories inside of
C:\Program Files, do not have the R attribute set, meaning they are
available for writing. Calling canWrite on these directories returns
true, suggesting they're available for writing when, in fact they are
not because of the additional layer of Windows 2000 security.
Example...
import java.io.File;
public class FileTest
{
public static void main(String[] args)
{
if (args.length != 1) {
System.out.println("usage: java FileTest filename");
System.exit(1);
}
File file = new File(args[0]);
if (file.exists() == true) {
System.out.println("exists");
if (file.canWrite() == true) {
System.out.println("can write");
} else {
System.out.println("can't write");
}
} else {
System.out.println("doesn't exist");
}
}
}
If the above code is run by a member of group User, as follows, the
results are incorrect...
java FileTest c:\winnt
exists
can write
This is wrong. A member of group User does not have write access to
C:\WinNT.
java FileTest "c:\program files"
exists
can't write
This is correct, only because the of the R attribute on the Program
Files directory.
java FileTest "c:\program files\common files"
exists
can write
This is wrong. A member of group User does not have write access to
this directory.
Here's an example specific to C...
#include <stdio.h>
void main(int argc, char **argv)
{
if (argc != 2) {
printf("usage: filetest filename\n");
exit(1);
}
if (access(argv[1], 0) == 0) {
printf("exists\n");
if (access(argv[1], 2) == 0) {
printf("can write\n");
} else {
printf("can't write\n");
}
} else {
printf("doesn't exist\n");
}
}
As above, the results of this code are wrong. The test for write
accessibility doesn't work, as access simply doesn't know to test for
system security.
I recommend testing this with a fresh version of Windows 2000. This
way you'll be testing with a system that hasn't been altered by the
Administrator, giving write access to members of group User.
After performing the install, create a new user. Add this person to
the Users group, log out, log back in, and run your tests.
(Review ID: 117807)
======================================================================
Additional information provided by licensee:
The workaround I provided turns out to not be a complete fix. There
are a number of other issues that I came across after filling the bug.
First, the Windows specific code snippet I provided is only capable of
determining whether the user has write permission based upon system
security. It doesn't look at file permissions, which may differ from
system security.
In other words, the Windows specific code may determine a file system
is writable, but it can't tell whether or not the file is read only.
So it's still necessary to test file permissions using access().
Additionally, there are issues not only with canWrite, but also the
other functions such as canDelete. This is a little weird, so bear
with me.
If you set a Windows directory to be read only, you would expect the
contents of the directory to not be writable. Unfortunately, this
isn't the case. "attrib +r directory" only prevents the directory
from being renamed, moved or deleted. It doesn't prevent a user from
creating, modifying or deleting files from within the directory.
As a result, canWrite and canDelete may not return proper results.
Overall, the classes for testing files or file systems don't exactly
work properly on Windows due to quirks in the OS.
A problem has arisen in conjunction with File's canWrite method and
members of group User under Windows 2000.
Like NT, Windows 2000 has an Administrator group. Win2K also has a
Power Users group, which is akin to a standard user under NT. New to
Win2K is the Users group, which is severely restricted...
Members of group User can read the entire Registry, but can only
cause updates to occur in HKEY_CURRENT_USER.
Members of group User cannot write to C:\WinNT or C:\Program Files
or to subdirectories of C:\WinNT or C:\Program Files, unless the
Administrator has specifically granted such privileges.
To use a generic example, let's say a user gets a new machine with
Windows 2000 installed. The machine is missing Netscape. A member
of group User cannot install Netscape as it modifies Registry entries
outside of HKEY_CURRENT_USER, and typically installs to the C drive's
Program Files directory. An Administrator would have to install the
software, and configure it for the User.
Now for the Java problem. Class File contains the method canWrite,
which determines write accessibility for a file or directory. In the
underlying C code, canWrite calls access. access checks the file or
directory's permissions, and returns 0 for success or -1 for failure.
The problem is that access *ONLY* checks the file or directory's
permissions. It knows nothing about system security.
By default, C:\Program Files has the A and R attributes set. R says
the directory is not writable. Calling canWrite on this directory
returns false, as the directory's permissions do not allow writing.
But the C:\WinNT directory, and most of the directories inside of
C:\Program Files, do not have the R attribute set, meaning they are
available for writing. Calling canWrite on these directories returns
true, suggesting they're available for writing when, in fact they are
not because of the additional layer of Windows 2000 security.
Example...
import java.io.File;
public class FileTest
{
public static void main(String[] args)
{
if (args.length != 1) {
System.out.println("usage: java FileTest filename");
System.exit(1);
}
File file = new File(args[0]);
if (file.exists() == true) {
System.out.println("exists");
if (file.canWrite() == true) {
System.out.println("can write");
} else {
System.out.println("can't write");
}
} else {
System.out.println("doesn't exist");
}
}
}
If the above code is run by a member of group User, as follows, the
results are incorrect...
java FileTest c:\winnt
exists
can write
This is wrong. A member of group User does not have write access to
C:\WinNT.
java FileTest "c:\program files"
exists
can't write
This is correct, only because the of the R attribute on the Program
Files directory.
java FileTest "c:\program files\common files"
exists
can write
This is wrong. A member of group User does not have write access to
this directory.
Here's an example specific to C...
#include <stdio.h>
void main(int argc, char **argv)
{
if (argc != 2) {
printf("usage: filetest filename\n");
exit(1);
}
if (access(argv[1], 0) == 0) {
printf("exists\n");
if (access(argv[1], 2) == 0) {
printf("can write\n");
} else {
printf("can't write\n");
}
} else {
printf("doesn't exist\n");
}
}
As above, the results of this code are wrong. The test for write
accessibility doesn't work, as access simply doesn't know to test for
system security.
I recommend testing this with a fresh version of Windows 2000. This
way you'll be testing with a system that hasn't been altered by the
Administrator, giving write access to members of group User.
After performing the install, create a new user. Add this person to
the Users group, log out, log back in, and run your tests.
(Review ID: 117807)
======================================================================
Additional information provided by licensee:
The workaround I provided turns out to not be a complete fix. There
are a number of other issues that I came across after filling the bug.
First, the Windows specific code snippet I provided is only capable of
determining whether the user has write permission based upon system
security. It doesn't look at file permissions, which may differ from
system security.
In other words, the Windows specific code may determine a file system
is writable, but it can't tell whether or not the file is read only.
So it's still necessary to test file permissions using access().
Additionally, there are issues not only with canWrite, but also the
other functions such as canDelete. This is a little weird, so bear
with me.
If you set a Windows directory to be read only, you would expect the
contents of the directory to not be writable. Unfortunately, this
isn't the case. "attrib +r directory" only prevents the directory
from being renamed, moved or deleted. It doesn't prevent a user from
creating, modifying or deleting files from within the directory.
As a result, canWrite and canDelete may not return proper results.
Overall, the classes for testing files or file systems don't exactly
work properly on Windows due to quirks in the OS.
- relates to
-
JDK-6368018 File.can{Execute,Read}(),set{Read,Execut}able() return incorrect result for the file on mapped drive
-
- Open
-