Issue
On Windows, when using the [email protected]
class FileUtils
to copy a directory (e.g. FileUtils.copyDirectory(...)
, the behavior differs from that of href="https://docs.microsoft.com/troubleshoot/windows-client/windows-security/permissions-on-copying-moving-files" rel="nofollow noreferrer">Windows default copy behavior, quoting (microsoft.com):
"By default, an object inherits permissions from its parent object, either at the time of creation or when it is copied or moved to its parent folder. The only exception to this rule occurs when you move an object to a different folder on the same volume. In this case, the original permissions are retained."
Expected behavior:
- Files copied to destination inherit parent permissions
Actual behavior:
- Files copied to destination do NOT inherit parent permissions
The reason this is important is that Windows historically manages these; setting them manually would be prone to even more potential errors. Thus, without the ability to inherit target permissions, this method does not serve the most common-use purpose on Windows.
How do I restore the old behavior of inheriting the target permissions without downgrading the commons-io
library?
This seems to be a regression in behavior:
Version | Status |
---|---|
[email protected] (2005) |
✅ Inherits permissions of destination |
[email protected] (2013) |
✅ Inherits permissions of destination |
[email protected] (2016) |
✅ Inherits permissions of destination |
[email protected] (2017) |
✅ Inherits permissions of destination |
[email protected] (2020) |
✅ Inherits permissions of destination |
[email protected] (2020) |
✅ Inherits permissions of destination |
[email protected] (2021) |
🚫 Does not inherit permissions of destination |
[email protected] (2021) |
🚫 Does not inherit permissions of destination |
[email protected] (2021) |
🚫 Does not inherit permissions of destination |
For example, if copying to C:\Program Files
Version | Permissions (icals <file> ) |
---|---|
[email protected] |
NT AUTHORITY\SYSTEM:(I)(F) BUILTIN\Administrators:(I)(F) BUILTIN\Users:(I)(RX) APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:(I)(RX) APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES:(I)(RX) |
[email protected] |
NT AUTHORITY\SYSTEM:(F) BUILTIN\Administrators:(F) WIN10ARM\owner:(F) |
Sample code:
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
public class Main {
private static final String PROGRAM_FILES = System.getenv().get("ProgramFiles");
public static void main(String ... args) throws IOException {
// Prepare our folder
File folder = new File("MyApp");
folder.mkdirs();
File file = new File(folder, "test.txt");
file.createNewFile();
// Use FileUtils to copy it elsewhere
File parent = new File(PROGRAM_FILES);
File dest = new File(parent, "MyApp");
System.out.println(String.format("Creating parent folder %s...", dest));
dest.mkdirs();
System.out.println(String.format("Copying contents %s to %s...", folder, dest));
FileUtils.copyDirectory(folder, dest);
// To test permissions call:
// icacls "%ProgramFiles%\MyApp\test.txt"
}
}
To compile the code:
javac -cp lib\commons-io-2.8.jar src\Main.java
To run the code (open a CMD as administrator):
java -cp lib/commons-io-2.8.jar;src Main
To test permissions:
icacls "%ProgramFiles%\MyApp\test.txt"
Then manually delete %ProgramFiles%\MyApp
and repeat for commons-io-2.9.0.jar
.
Similar questions have been asked before, but I could not find an explanation for this behavior.
Apache commons-io
Mailing list discussion:
Related:
- How to preserve file permissions when using FileUtils.copyDirectory?
- Java File copy - how to inherit permissions of destination directory
A possible workaround, but much more complex:
Solution
The workaround for commons-io
2.9.0 and higher is:
- FileUtils.copyDirectory(folder, dest);
+ FileUtils.copyDirectory(folder, dest, false);
// HERE -----^
Or alternately...
- FileUtils.copyDirectory(folder, dest);
+ PathUtils.copyDirectory(folder.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
//^------ HERE
Quoting some exchange with the Apache Commons mailing list:
I think this is the problematic line: https://github.com/apache/commons-io/blob/f22a4227401855ecbfdf8184bbe37275c3aeb5c3/src/main/java/org/apache/commons/io/FileUtils.java#L701
From what I'm reading, toggling off
preserveFileDate
will inadvertently and unobviously fix this, but at this point. This fix seems like undesired, undocumented and prone-to-break-later behavior, at least for Windows environments.I'm curious what reasoning was used to associate a file's modified date with the rest of its attributes. Perhaps this was a backward-compat shortcut to maintain the modified date using the new NIO API? Regardless, this introduces permissions changes which can and will break standard file copy operations for environments which expect inherited permissions, e.g. Windows.
Ideally, the file modified date could stay without clobbering the default NIO behavior.
For now a viable workaround is:
FileUtils.copyDirectory(folder, dest, false /* <--- change to FALSE */);
... however this does have the disadvantage of (potentially) losing timestamp information, which is still a regression, albeit smaller.
... however local tests show that timestamp information (files, not folders) is preserved by default on Windows (even with this flag off), so this second concern may not be as... well... concerning. The impact of this change on Unixes is yet to be tested.
Answered By - tresf
Answer Checked By - Willingham (JavaFixing Volunteer)