marschall / memoryfilesystem

An in memory implementation of a JSR-203 file system
284 stars 36 forks source link

Incorrect symlink target existence in subfolder via Files.exists #129

Closed mgrafl closed 3 years ago

mgrafl commented 3 years ago

Similar to #128, existence check for the target of a symbolic link in a subfolder gives wrong result.

Reproduction:

  1. Create a directory on in-memory filesystem.
  2. Create a file inside that directory.
  3. Create a symbolic link to that file (also in the subfolder).
  4. Check existence of file by following links via Files.exists.

Expected behavior:

Files.exists(symLink) should return true.

Actual behavior: Files.exists(symLink) returns false. However, Files.exists(Files.readSymbolicLink(symLink)) returns true.

Unit test:

@Test
public void testFileExistenceInSubfolderWithFollowingLinks() throws IOException {
    FileSystem fileSystem = MemoryFileSystemBuilder.newLinux().build();
    // In contrast to https://github.com/marschall/memoryfilesystem/issues/128, work inside a subfolder:
    Path dir = Files.createDirectories(fileSystem.getPath("dir"));
    Path realFile = Files.writeString(dir.resolve("realFile"), "Test");
    Path symLink = Files.createSymbolicLink(dir.resolve("symLink"), realFile);

    Assert.assertTrue("Real file should exist", Files.exists(realFile));
    Assert.assertTrue("Symlink file should exist without following links", Files.exists(symLink, LinkOption.NOFOLLOW_LINKS));
    Assert.assertTrue("Target of symlink file should exist", Files.exists(Files.readSymbolicLink(symLink)));
    Assert.assertEquals("Target of symlink file should be a real file", realFile, Files.readSymbolicLink(symLink));

    // The following assertion fails:
    Assert.assertTrue("Symlink file target in subfolder should exist when following links", Files.exists(symLink));
}
marschall commented 3 years ago

You seem to have a bad time using this library, I'm sorry for this.

This one I'm less sure about. If I understand correctly you're doing the equivalent of

mkdir dir
touch dir/realFile
ln -s dir/realFile dir/symLink

It is my current understanding that relative symlinks are relative to the parent directory of the symbolic link which means the link links to dir/dir/realFile not dir/realFile because it is already in the dir folder.

Based on this I believe it is correct that this assertion fails:

Assert.assertTrue("Symlink file target in subfolder should exist when following links", Files.exists(symLink));

Instead I believe the following assertion, which currently succeeds, should fail:

Assert.assertTrue("Target of symlink file should exist", Files.exists(Files.readSymbolicLink(symLink)));
marschall commented 3 years ago

I had the time to do further analysis and believe the current behavior is correct. Here's why:

mkdir dir
touch dir/realFile
ln -s dir/realFile dir/symLink
readlink -fv symLink 
readlink: symLink: No such file or directory

In addition I ran the test provided by you with

FileSystem fileSystem = FileSystems.getDefault();

instead of

FileSystem fileSystem = MemoryFileSystemBuilder.newLinux().build();

The behavior is the same and the last assertion fails for both.

This leads me to believe that the current behavior is correct. Do you agree?

mgrafl commented 3 years ago

You are absolutely right. I should have checked the behavior against the actual Linux file system implementation. I am sorry.

And, by the way, thanks for the awesome library! It is the best tool for unit testing non-trivial file handling.

marschall commented 3 years ago

No problem, no harm done. It gave me the opportunity to revisit relative symlinks.

Thank you for the kind words.