I want to ensure correct permissions in a directory tree (0755 for directories and 644 for files). Here is my class:
package NRF_Utils;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import static java.nio.file.FileVisitResult.*;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.logging.Logger;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
public class DirectoryTreeOperations {
private static final Logger log = Logger.getLogger("spv." + DirectoryTreeOperations.class.getName());
public static boolean setPermissions(String root, String directoryPermissions, String filePermissions) {
boolean result = true;
//declaring the path to delete
final Path path = Paths.get(root);
final Set<PosixFilePermission> dirPermissions = PosixFilePermissions.fromString(directoryPermissions);
final Set<PosixFilePermission> filPermissions = PosixFilePermissions.fromString(filePermissions);
try {
Files.walkFileTree(path, new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
System.out.println("setting dir permission on " + dir);
Files.setPosixFilePermissions(dir, dirPermissions);
return CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
System.out.println("setting file permission on " + file);
Files.setPosixFilePermissions(file, filPermissions);
return CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
log.severe("visitFileFailed failed on " + file + " : " + exc);
return CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir,
IOException exc) throws IOException {
return CONTINUE;
}
});
} catch (IOException e) {
log.severe("setPermissions failed " + e);
result = false;
}
return result;
}
// TODO remove and provide a proper unit test
public static void main(String [] arg) {
System.out.println ("setPermissions returned: " + setPermissions("/tmp/x", "rwxr-xr-x", "rw-r--r--"));
}
}
To perform test, I have done:
mkdir -p /tmp/x/y/z;touch /tmp/x/y/z/f;chmod 000 /tmp/x/y/z
The output is:
setting dir permission on /tmp/x
setting dir permission on /tmp/x/y
setPermissions returned: true
nov. 26, 2017 2:45:00 PM NRF_Utils.DirectoryTreeOperations$1 visitFileFailed
GRAVE: visitFileFailed failed on /tmp/x/y/z : java.nio.file.AccessDeniedException: /tmp/x/y/z
It seems java does not want to explore the directory tree because the permissions are wrong, but my aim was to explore the directory tree to fix permissions. Is the API crazy ?
It does seem strange it would behave this way, so maybe not a crazy API, but the implementation certainly doesn't cater to the specific case of using preVisitDirectory() to make the directory accessible.
The Files.walkFileTree()
doc says:
Where the file is a directory, and the directory could not be opened, then the visitFileFailed method is invoked with the I/O exception, after which, the file tree walk continues, by default, at the next sibling of the directory.
and looking at the implementation, it does actually try to open the directory before it calls visitor.preVisitDirectory()
which explains the issue you are running into.
As a solution, you could grab the FileTreeWalker.java source, and make the minor adjustment in your own local version of the FileTreeWalker - it's straight forward just look for the call to visitor.preVisitDirectory()
and move the open directory code to occur after that. Then change your code to:
new FileTreeWalker(EnumSet.noneOf(FileVisitOption.class), new FileVisitor<Path>() {
...
}, Integer.MAX_VALUE).walk(path);
When you run, you'll get:
setting dir permission on /tmp/x
setting dir permission on /tmp/x/y
setting dir permission on /tmp/x/y/z
setting file permission on /tmp/x/y/z/f
setPermissions returned: true
which shows your of implementation of FileVisitor
is fine.
Hope that helps.
When you do chmod 755 /tmp/x/y/z
from the command line, it does not check the permission on z
itself. As long as you have sufficient access to y
, you can perform chmod
on z
.
OTOH, if you look at the source code of Files.walkFileTree
, it actually tries to access every target file in its own visit
method, to gather file attributes etc., before it calls your visitor methods. Hence the access denied error.
By the look of it, you might have to roll your own file tree walker, where you would just go ahead change the permission without trying to access the target path first.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With