9

In java, a symbolic link in a Unix environment can be detected by comparing the file's canonical and absolute path. However, this trick does not work on windows. If I execute

mkdir c:\foo
mklink /j c:\bar

from the command line and then execute the following lines in java

File f = new File("C:/bar");
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());

the output is

C:\bar
C:\bar

Is there any pre-Java 7 way of detecting a junction in windows?

Jherico
  • 28,584
  • 8
  • 61
  • 87

4 Answers4

8

There doesn't appear to be any cross platform mechanism for this in Java 6 or earlier, though its a fairly simple task using JNA

interface Kernel32 extends Library {
  public int GetFileAttributesW(WString fileName);
}

static Kernel32 lib = null;
public static int getWin32FileAttributes(File f) throws IOException { 
  if (lib == null) {
    synchronized (Kernel32.class) {
      lib = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
    }
  }
  return lib.GetFileAttributesW(new WString(f.getCanonicalPath()));
}

public static boolean isJunctionOrSymlink(File f) throws IOException {
  if (!f.exists()) { return false; }
  int attributes = getWin32FileAttributes(f);
  if (-1 == attributes) { return false; }
  return ((0x400 & attributes) != 0);
}

EDIT: updated per comment about possible error return by getWin32FileAttributes()

Jherico
  • 28,584
  • 8
  • 61
  • 87
  • Just an FYI, `GetFileAttributesW()` returns `0xFFFFFFFF` (`-1` as an `int`) if it fails. `isJunctionOrSymlink()` needs to check for that condition before it then checks for the `0x400` flag, otherwise it will return a false positive. Since `GetFileAttributesW()` will fail if the file does not exist, the check for `f.exists()` is redundant. – Remy Lebeau Dec 05 '12 at 22:45
1

The answer is 'no'. Junction points and symbolic links are not the same sort of thing. The JRE doesn't check for them, and so the functions you cite don't differentiate them.

Having said this, you might accomplish something with the following:

If the junctioned directory has contents, then the result of getting the canonical pathname of something down below it might be 'surprising' and reveal the situation, since it is likely to be a pathname under the target of the junction. This only works if the directory pointed to by the junction is, of course, non-empty.

bmargulies
  • 97,814
  • 39
  • 186
  • 310
  • 1
    A junction point in windows is a file system entity that redirects you to another location (possibly on another volume) and is transparent to all the file system API's for opening and reading files. How is that not exactly like a (POSIX) symbolic link? Other than that on windows its called a junction if its for a directory and a symlink if its for a file? – Jherico Jul 19 '10 at 17:39
  • The point of my question is that unlink the JVM on linux, the JVM on windows will NOT return different canonical and absolute values for the junction (or a directory symlink or any of the contents therein). – Jherico Jul 19 '10 at 18:27
  • In a Linux file system, there is a backpointer in the inode to the real parent, not to any symlink. Similiarly in NTFS. What a junction point is like is a *mount point*. – bmargulies Jul 20 '10 at 15:46
0

With nio it is possible to detect a junction/reparse point or symlink even without reflection or use of JNA:

public void isReparsePointOrSymlink(Path path) {
    final var attribute = Files.getAttribute(path, "dos:attributes", LinkOption.NOFOLLOW_LINKS);
    if (attribute instanceof Integer value) {
        final boolean isJunctionOrSymlink = (value & 0x400) != 0;
        return isJunctionOrSymlink;
    }
    return Files.isSymbolicLink(path);
}

Note, that Files.isSymbolicLink(path) only returns true for the symbolic link, not for the reparse point. If you need to distinguish between both and isReparsePointOrSymlink returns true, check also Files.isSymbolicLink.

Thomas S.
  • 5,804
  • 5
  • 37
  • 72
-1

You can try this dirty hack for windows

    if (f.isDirectory() || f.isFile()) {
        System.out.println("File or Directory");
    } else {
        System.out.println("Link");
    }
Manish Singh
  • 3,463
  • 22
  • 21
  • 1
    This does nothing useful. isDirectory() will return true for a normal directory, a directory symbolic link (made with mklink /d) or a junction (made with mklink /j). Any other kind of link or shortcut will return true for isFile(). Your confusion may arise from the fact that if you create a shortcut to a directory or file it shows up as 'Foo' but on disk its represented as 'Foo.lnk', therefore isDirectory() and isFile() will return false for 'Foo' (but then so will exists()). Shortcuts are at any rate, a windows explorer artifact only and have nothing to do with what I was asking. – Jherico Jul 19 '10 at 18:22