Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java, Linux: how to detect whether two java.io.Files refer to the same physical file

I'm looking for an efficient way to detect whether two java.io.Files refer to the same physical file. According to the docs, File.equals() should do the job:

Tests this abstract pathname for equality with the given object. Returns true if and only if the argument is not null and is an abstract pathname that denotes the same file or directory as this abstract pathname.

However, given a FAT32 partition (actually a TrueCrypt container) which is mounted at /media/truecrypt1:

new File("/media/truecrypt1/File").equals(new File("/media/truecrypt1/file")) == false

Would you say that this conforms to the specification? And in this case, how to work around that problem?

Update: Thanks to commenters, for Java 7 I've found java.io.Files.isSameFile() which works for me.

like image 635
mstrap Avatar asked May 04 '11 12:05

mstrap


1 Answers

The answer in @Joachim's comment is normally correct. The way to determine if two File object refer to the same OS file is to use getCanonicalFile() or getCanonicalPath(). The javadoc says this:

"A canonical pathname is both absolute and unique. [...] Every pathname that denotes an existing file or directory has a unique canonical form."

So the following should work:

File f1 = new File("/media/truecrypt1/File");  // different capitalization ...
File f2 = new File("/media/truecrypt1/file");  // ... but same OS file (on Windows)
if (f1.getCanonicalPath().equals(f2.getCanonicalPath())) {
    System.out.println("Files are equal ... no kittens need to die.");
}

However, it would appear that you are viewing a FAT32 file system mounted on UNIX / Linux. AFAIK, Java does not know that this is happening, and is just applying the generic UNIX / Linux rules for file names ... which give the wrong answer in this scenario.

If this is what is really happening, I don't think there is a reliable solution in pure Java 6. However,

  • You could do some hairy JNI stuff; e.g. get the file descriptor numbers and then in native code, use the fstat(2) system call to get hold of the two files' device and inode numbers and comparing those.

  • Java 7 java.nio.file.Path.equals(Object) looks like it might give the right answer if you call resolve() on the paths first to resolve symlinks. (It is a little unclear from the javadoc whether each mounted filesystem on Linux will correspond to a distinct FileSystem object.)

  • The Java 7 tutorials have this section on seeing if two Path objects are for the same file ... which recommends using java.nio.file.Files.isSameFile(Path, Path)


Would you say that this conforms to the specification?

No and yes.

  • No in the sense that the getCanonicalPath() method is not returning the same value for each existing OS file ... which is what you'd expect from reading the javadoc.

  • Yes in the technical sense that the Java codebase (not the javadoc) is the ultimate specification ... both in theory and in practice.

like image 90
Stephen C Avatar answered Nov 10 '22 04:11

Stephen C