Reputation: 5251
The Java helper class java.nio.file.Files
has methods to access file attributes. Some of the class methods follow symbolic links (all methods with a LinkOption
parameter), while for some other methods it is not clear wether symbolic links are followed or not (methods without LinkOption
parameter).
These are some methods that follow symbolic links:
Files.isDirectory(Path, LinkOption...)
Files.isRegular(Path, LinkOption...)
Files.getAttribute(Path, String, LinkOption...)
Files.getLastModified(Path, LinkOption...)
Files.getOwner(Path, LinkOption...)
Files.getPosixFilePermission(Path, LinkOption...)
For some other methods, it is no obvious to determine if they follow symbolic links or not (no LinkOption...
parameter and no mention of symbolic links in the javadoc) :
Files.isSymbolicLink(Path)
Files.isExecutable(Path)
Files.isReadable(Path)
Files.isWritable(Path)
Files.isHidden(Path)
Files.size(Path)
Files.getFileStore(Path)
Which are the methods without LinkOption...
parameter that follow symbolic links, and why ?
Upvotes: 3
Views: 1995
Reputation: 5251
TLDR: in most cases this seems to a FileSystemProvider
implementation choice to follow symbolic links or not (this may answers the "why" question). Symbolic links are:
Followed:
Files.size(Path)
Mostly followed:
Files.getFileStore(Path)
: followed on Windows and Linux, not followed on JimfsNot followed:
Files.isSymbolic(Path)
Mostly not followed:
Files.isExecutable(Path)
: not followed on Windows and Unix, but followed on JimfsFiles.isReadable(Path)
: not followed on Windows and Unix, but followed on JimfsFully implementation specific:
Files.isWritable(Path)
: followed on Windows but not on UnixFiles.isHidden(Path)
: followed on Windows but not on UnixYou can be sure about whether symbolic links are followed or not by calling Files.readAttributes(Path, Class, LinkOption...)
and using the returned attributes.
Files.isSymbolic(Path)
does not follow symbolic linksFor Files.isSymbolic(Path)
, the reason is obvious: if the method had followed the symbolic links by default, it would have always returned false
.
Files.isHidden(Path)
follow symbolic links on Windows but not on UnixFrom the method signature, we may think that the method does not follow symbolic links (because there is no LinkOption...
parameter). However, this is not so evident.
The Files.isHidden(Path)
method delegate to the implementation of the java.nio.file.spi.FileSystemProvider.isHidden(Path)
and the javadoc does not specify wether the method follows symbolic links or not.
On Windows, it is implemented by following symbolic links, see line 465 (the true
parameter in WindowsFileAttributes.get(file, true)
call tells to follow symbolic links):
@Override
public boolean isHidden(Path obj) throws IOException {
WindowsPath file = WindowsPath.toWindowsPath(obj);
file.checkRead();
WindowsFileAttributes attrs = null;
try {
attrs = WindowsFileAttributes.get(file, true);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
}
// DOS hidden attribute not meaningful when set on directories
if (attrs.isDirectory())
return false;
return attrs.isHidden();
}
On Unix, this method is implemented without following symbolic links (it only checks that the file starts with a "."):
@Override
public boolean isHidden(Path obj) {
UnixPath file = UnixPath.toUnixPath(obj);
file.checkRead();
UnixPath name = file.getFileName();
if (name == null)
return false;
return (name.asByteArray()[0] == '.');
}
Thus, we can conclude that this is implementation specific.
Files.isExecutable(Path)
don't follow symbolic links in most file-systemsThis method delegate to Files.isAccessible(Path, AccessMode.EXECUTE)
, which delegates to the FileSystemProvider.checkAccess(Path, AccessMode...)
method.
On Windows, the WindowsFileSystemProvider.checkAccess(Path, AccessMode...)
method delegates to the java.lang.SecurityManager
which decides whether the file is executable or not. AFAIK, the SecurityManager
does not follow symbolic links, so we can assume that Files.isExecutable(Path)
does not follow symbolic links on Windows.
On Unix, the UnixFileSystemProvider.checkAccess(Path, AccessMode...)
method also delegates to the SecurityManager
, we can assume that Files.isExecutable(Path)
does not follow symbolic links on Unix too.
On Jimfs (in-memory file-system from Google), the call delegates to com.google.common.jimfs.FileSystemView.checkAccess(JimfsPath)
which follows symbolic links (even if Jimfs does not support access control):
public void checkAccess(JimfsPath path) throws IOException {
// just check that the file exists
lookUpWithLock(path, Options.FOLLOW_LINKS).requireExists(path);
}
Thus, we can conclude that Files.isExecutable(Path)
may follow symbolic links depending on the file-system, but won't in a majority of the cases (Unix+Windows).
Files.isReadable(Path)
does not follow symbolic links on most file-systemsThe implementation for Files.isReadable(Path)
is very similar to the one of isExecutable(Path)
: don't follow links on Unix and Windows, but follow links on Jimfs.
Files.isWritable(Path)
As for Files.isExecutable(Path)
, the isWritable(Path)
method delegates to the FileSystemProvider.checkAccess(Path)
.
On Windows, this requires determining if the file has a read-only attribute, which is done by following links (see WindowsFileSystemProvider
code above).
On Unix, this is apparently done without following symbolic links (see UnixFileSystemProvider
above).
Thus, we can conclude that this is implementation specific.
Files.size(Path)
follow symbolic linksThe implementation delegates to readAttributes
, thus it follows symbolic links for all file-system implementations:
public static long size(Path path) throws IOException {
return readAttributes(path, BasicFileAttributes.class).size();
}
Files.getFileStore(Path)
The method delegates to the FileSystemProvider.getFileStore(Path)
method.
On Windows, it uses WindowsFileStore.create(Path)
which follow symbolic links (see true
parameter):
static WindowsFileStore create(WindowsPath file) throws IOException {
try {
// if the file is a link then GetVolumePathName returns the
// volume that the link is on so we need to call it with the
// final target
String target = WindowsLinkSupport.getFinalPath(file, true);
...
On Unix, the FileSystemProvider.getFileStore(Path)
method is abstract and implemented by subclasses, e.g. [LinuxFileSystem][3]
:
@Override
LinuxFileStore getFileStore(UnixPath path) throws IOException {
return new LinuxFileStore(path);
}
This classes constructs a UnixFileStore
by getting the attributes with link following (true
parameter in UnixFileAttributes.get()
call):
private static long devFor(UnixPath file) throws IOException {
try {
return UnixFileAttributes.get(file, true).dev();
} catch (UnixException x) {
x.rethrowAsIOException(file);
return 0L; // keep compiler happy
}
}
In Jimfs, the FileStore
seems to be attached to the file at creation time, thus, it looks that links are not followed.
Thus, we can conclude that Files.getFileStore(Path)
uses symbolic link following in most file-system implementations.
Upvotes: 2