A DESCRIPTION OF THE PROBLEM :
On DOS file systems, if the `dos:readonly` attribute is set to true for a given file `p`, `Files.delete(p)` fails with `java.nio.file.AccessDeniedException`.
This is inconsistent with the following, both of which delete the file successfully:
* `p.toFile().delete()`
* `Files.delete(q)`, where `q` denotes a read-only file on a POSIX file system (i.e., a file for which `Files.getPosixFilePermissions(p)` only returns `*_READ` permissions)
Given these inconsistencies and the fact that the API does not provide an elegant way to forcefully delete such read-only files (e.g., by providing a `DeleteOption` interface, analogous to the `OpenOption` and `CopyOption` interfaces), I believe this is a bug, rather than intended behavior.
As a workaround, one could use something like the provided code (see `Test Case Code`), but that code is problematic:
* it is unable to do the unsetting of the attribute and the deletion in a single atomic operation
* the unsetting is always attempted, even for files that are not affected (and removing the `unsetDosReadOnly(p);` from the try-block might have the opposite effect, where the code constantly falls into the catch-block)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
* create a read-only file, named `readonly.txt`, on a DOS file system (e.g., NTFS partition on a Windows PC)
* run `jshell` in the directory that contains the file
* run `Files.delete(Path.of("readonly.txt"))`
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
the file is deleted
ACTUAL -
the following exception is thrown:
| Exception java.nio.file.AccessDeniedException: readonly.txt
| at WindowsException.translateToIOException (WindowsException.java:89)
| at WindowsException.rethrowAsIOException (WindowsException.java:103)
| at WindowsException.rethrowAsIOException (WindowsException.java:108)
| at WindowsFileSystemProvider.implDelete (WindowsFileSystemProvider.java:269)
| at AbstractFileSystemProvider.delete (AbstractFileSystemProvider.java:105)
| at Files.delete (Files.java:1052)
| at (#4:1)
---------- BEGIN SOURCE ----------
void delete(Path p) throws IOException {
try {
unsetDosReadOnly(p);
Files.delete(p);
} catch(AccessDeniedException e) {
unsetDosReadOnly(p);
Files.delete(p);
}
}
void unsetDosReadOnly(Path p) throws IOException {
var attrs = Files.getFileAttributeView(p, java.nio.file.attribute.DosFileAttributeView.class);
if(attrs != null) {
attrs.setReadOnly(false);
}
}
---------- END SOURCE ----------
On DOS file systems, if the `dos:readonly` attribute is set to true for a given file `p`, `Files.delete(p)` fails with `java.nio.file.AccessDeniedException`.
This is inconsistent with the following, both of which delete the file successfully:
* `p.toFile().delete()`
* `Files.delete(q)`, where `q` denotes a read-only file on a POSIX file system (i.e., a file for which `Files.getPosixFilePermissions(p)` only returns `*_READ` permissions)
Given these inconsistencies and the fact that the API does not provide an elegant way to forcefully delete such read-only files (e.g., by providing a `DeleteOption` interface, analogous to the `OpenOption` and `CopyOption` interfaces), I believe this is a bug, rather than intended behavior.
As a workaround, one could use something like the provided code (see `Test Case Code`), but that code is problematic:
* it is unable to do the unsetting of the attribute and the deletion in a single atomic operation
* the unsetting is always attempted, even for files that are not affected (and removing the `unsetDosReadOnly(p);` from the try-block might have the opposite effect, where the code constantly falls into the catch-block)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
* create a read-only file, named `readonly.txt`, on a DOS file system (e.g., NTFS partition on a Windows PC)
* run `jshell` in the directory that contains the file
* run `Files.delete(Path.of("readonly.txt"))`
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
the file is deleted
ACTUAL -
the following exception is thrown:
| Exception java.nio.file.AccessDeniedException: readonly.txt
| at WindowsException.translateToIOException (WindowsException.java:89)
| at WindowsException.rethrowAsIOException (WindowsException.java:103)
| at WindowsException.rethrowAsIOException (WindowsException.java:108)
| at WindowsFileSystemProvider.implDelete (WindowsFileSystemProvider.java:269)
| at AbstractFileSystemProvider.delete (AbstractFileSystemProvider.java:105)
| at Files.delete (Files.java:1052)
| at (#4:1)
---------- BEGIN SOURCE ----------
void delete(Path p) throws IOException {
try {
unsetDosReadOnly(p);
Files.delete(p);
} catch(AccessDeniedException e) {
unsetDosReadOnly(p);
Files.delete(p);
}
}
void unsetDosReadOnly(Path p) throws IOException {
var attrs = Files.getFileAttributeView(p, java.nio.file.attribute.DosFileAttributeView.class);
if(attrs != null) {
attrs.setReadOnly(false);
}
}
---------- END SOURCE ----------