Julien Kronegg
Julien Kronegg

Reputation: 5251

Which methods of java.nio.file.Files follow symbolic links and which don't?

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:

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) :

Which are the methods without LinkOption... parameter that follow symbolic links, and why ?

Upvotes: 3

Views: 1995

Answers (1)

Julien Kronegg
Julien Kronegg

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 Jimfs

Not followed:

  • Files.isSymbolic(Path)

Mostly not followed:

  • Files.isExecutable(Path) : not followed on Windows and Unix, but followed on Jimfs
  • Files.isReadable(Path) : not followed on Windows and Unix, but followed on Jimfs

Fully implementation specific:

  • Files.isWritable(Path) : followed on Windows but not on Unix
  • Files.isHidden(Path) : followed on Windows but not on Unix

You 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 links

For 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 Unix

From 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-systems

This 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-systems

The 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 links

The 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

Related Questions