Odd result with canonical paths with intermixed usage of java.io.File and java.nio.file.Files APIs (original) (raw)

Jaikiran Pai jai.forums2013 at gmail.com
Mon Dec 11 14:06:37 UTC 2017


I have been debugging an issue with canonical file path and it seems that I either have run into a bug or am incorrectly intermixing the use of java.io.File and java.nio.file.Files APIs. I'm on MacOS and this issue is reproducible with both Java 8 and Java 9:

Java 8:

java version "1.8.0_152" Java(TM) SE Runtime Environment (build 1.8.0_152-b16) Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

Java 9:

java version "9.0.1" Java(TM) SE Runtime Environment (build 9.0.1+11) Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)

The code that reproduces this issue is as simple as this:

public static void main(final String[] args) throws Exception {     final Path symlinkPath = Paths.get("/tmp/foosymlink");

    final Path targetPath = Paths.get("/tmp/existing-text-file.txt");     // create a symbolic link     java.nio.file.Files.createSymbolicLink(symlinkPath, targetPath);

    System.out.println("Canonical path on first symlink creation " + symlinkPath.toFile().getCanonicalPath());

    // delete the link (but not the target)     Files.delete(symlinkPath);

    // recreate the link to point to some other target file     final Path someOtherTargetPath = Paths.get("/tmp/other-existing-text-file.txt");     java.nio.file.Files.createSymbolicLink(symlinkPath, someOtherTargetPath);

    System.out.println("Canonical path on symlink recreation " + symlinkPath.toFile().getCanonicalPath()); }

So what that code does is:

    1. Create a symlink with the link pointing to some existing file     2. Invoke getCanonicalPath() on the symlink File object. This is important to reproduce the issue (for reasons explained later)     3. Delete the symlink (not the target of the symlink) using java.nio.file.Files.delete(Path) API.     4. Recreate the symlink (at the same path) but pointing it to a different (existent) target file     5. Invoke the getCanonicalPath() on the symlink File object and evaluate the returned value.

Step#5 incorrectly returns the canonical path which points to the outdated target path (the link to which was broken in step#3). However, if step#5 is invoked after a delay of around 30 seconds from step#3, then it returns the correct canonical path which points to the target file of the link which was recreated in step#4.

Looking into the implementation of getCanonicalPath and the underlying java.io.FileSystem, on my setup the java.io.UnixFileSystem gets picked up which internally maintains a canonical path cache (which expires its entries with a 30 second duration). So it's pretty clear why this behaves the way it does. There's a system property which controls disabling this cache, so this in itself isn't an issue.

However, if I switch the call (in step#3 above) from:

    Files.delete(symlinkPath)

to     symlinkPath.toFile().delete()

then the step#5 (immediately) returns the right canonical path which points to the target file of the recreated link. Looking at the implementation of java.io.File.delete() it triggers the java.io.UnixFileSystem.delete() which clears off the canonical path cache[1] and thus doesn't end up returning stale path values on calls to getCanonicalPath().

So a few related questions that I have are:

    1. Is this inconsistency an expected behaviour or is this a bug?     2. If this is an expected behaviour, then would it be a better idea (as an application developer) to use Path.toRealPath[2] instead of using the File.getCanonicalPath()? Are these 2 APIs semantically equivalent? The File.getCanonicalPath() talks about the canonical path being "unique" paths but the Path.toRealPath has no such mentions.

[1] http://hg.openjdk.java.net/jdk/jdk/file/31febb3f66f7/src/java.base/unix/classes/java/io/UnixFileSystem.java#l256 [2] https://docs.oracle.com/javase/9/docs/api/java/nio/file/Path.html#toRealPath-java.nio.file.LinkOption...-

-Jaikiran



More information about the discuss mailing list