Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
133a324
refactor: split OpenExternalFileAction into OpenSingleExternalFileAct…
Muskan244 Jul 8, 2025
91da0cf
Merge branch 'main' into fix-for-issue-13431
Muskan244 Jul 8, 2025
bcf3b76
Cleaned up constant name, dialog text, and removed extra comment
Muskan244 Jul 8, 2025
a8a1235
Merge branch 'main' into fix-for-issue-13431
koppor Jul 8, 2025
6ccf927
Add null checks to OpenSingleExternalFileAction constructor
Muskan244 Jul 8, 2025
4dd6694
Add @NonNull annotations and fix localization key warnings
Muskan244 Jul 8, 2025
90233e4
Merge branch 'main' into fix-for-issue-13431
Muskan244 Jul 8, 2025
dc0d67f
fix submodules
Muskan244 Jul 8, 2025
dd9ca53
Use database latest context, update button text, and add comments exp…
Muskan244 Jul 10, 2025
87b7ca9
Merge branch 'main' into fix-for-issue-13431
Muskan244 Jul 10, 2025
6c654b6
Bump org.junit.platform:junit-platform-launcher in /versions (#13505)
dependabot[bot] Jul 7, 2025
77fd7a1
Fix: clarify fallback logic, update dialog labels, and avoid null Opt…
Muskan244 Jul 11, 2025
e717e07
Fix: Make FileUtil.relativize symlink-aware for robust relative path …
Muskan244 Jul 18, 2025
22522dc
remove unnecessary comments and make test more clearer and maintainable
Muskan244 Jul 19, 2025
9f38281
Merge branch 'main' into fix-for-issue-12995
Muskan244 Jul 19, 2025
8c9257d
Refactor to avoid using exceptions for normal control flow in tryReal…
Muskan244 Jul 19, 2025
bb3b3b2
Merge branch 'fix-for-issue-12995' of https://github.com/Muskan244/ja…
Muskan244 Jul 19, 2025
b394a3c
Merge branch 'main' into fix-for-issue-12995
Muskan244 Jul 20, 2025
1d236aa
Refactored symlink tests for independence, used Files.writeString for…
Muskan244 Jul 20, 2025
5124b5f
Fix code style issues with OpenRewrite and update symlink tests
Muskan244 Jul 20, 2025
ad92c0d
Fix submodules
Muskan244 Jul 20, 2025
cb314a2
Fix submodules
Muskan244 Jul 20, 2025
c60919c
Merge branch 'main' into fix-for-issue-12995
Muskan244 Jul 24, 2025
b7f7587
Fix typo
koppor Aug 4, 2025
4270261
Refactor tests
koppor Aug 4, 2025
18fa797
Add comments to explain the logic to not use directory.relativize()
Muskan244 Aug 17, 2025
e90cafb
Merge branch 'fix-for-issue-12995' of https://github.com/Muskan244/ja…
Muskan244 Aug 17, 2025
9e1952d
Merge branch 'main' into fix-for-issue-12995
Muskan244 Aug 17, 2025
618e12a
Removed unneccesary comment
Muskan244 Aug 17, 2025
3f88a1d
Merge branch 'fix-for-issue-12995' of https://github.com/Muskan244/ja…
Muskan244 Aug 17, 2025
19d7cb3
Add ignored test for symlink escape case (#12995 comment)
Muskan244 Aug 18, 2025
bf94ce8
Format complete file
koppor Aug 19, 2025
77985e1
Add CHANGELOG.md entry
koppor Aug 19, 2025
9814427
Simplify code
koppor Aug 19, 2025
da97e5f
Have the simple case first
koppor Aug 19, 2025
5f12089
Consistent method naming
koppor Aug 19, 2025
824734f
Warn samehow if real path conversion fails
koppor Aug 19, 2025
cf2bc0f
Refine link
koppor Aug 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,40 @@ public static Path relativize(Path file, List<Path> directories) {
if (!file.isAbsolute()) {
return file;
}
Optional<Path> realFileOpt = tryRealPath(file);

for (Path directory : directories) {
Optional<Path> realDirOpt = tryRealPath(directory);

if (realFileOpt.isPresent() && realDirOpt.isPresent()) {
Path realFile = realFileOpt.get();
Path realDir = realDirOpt.get();
if (realFile.startsWith(realDir)) {
int nameCountToDrop = realDir.getNameCount();
Path relativePart = realFile.subpath(nameCountToDrop, realFile.getNameCount());
return relativePart;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why directory.relativize(file); is not used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try using directory.relativize(file); at first, but I ran into problems for some of the symlink edge cases. If the canonical path doesn’t match the original directory or file prefix exactly, relativize doesn’t always work especially with nested or chained symlinks. That’s why I switched to the current approach, since it seemed to pass those tricky test cases. If there’s a more standard way to handle this, I’d really appreciate any suggestions. Also, should I add comments explaining the logic in the code?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Muskan244, comments would be helpful.
We'll get done reviewing this soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll add the comments explaining the logic of the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me and ChatGPT share the same oppionion:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there was an edge case, there should be a JUnit test for this.

}
}

if (file.startsWith(directory)) {
return directory.relativize(file);
}
}
return file;
}

private static Optional<Path> tryRealPath(Path path) {
if (Files.exists(path)) {
try {
return Optional.of(path.toRealPath());
} catch (IOException e) {
return Optional.empty();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method catches IOException and returns Optional.empty() instead of letting the exception propagate. This uses exceptions for control flow which is against best practices.

} else {
return Optional.of(path.toAbsolutePath());
}
}

/**
* Converts an absolute file to a relative one, if possible. Returns the parameter file itself if no shortening is
* possible.
Expand Down
26 changes: 26 additions & 0 deletions jablib/src/test/java/org/jabref/logic/util/io/FileUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,32 @@ public void cTemp() {
}
}

@Test
void relativizeHandlesSymlinks(@TempDir Path tempDir) throws IOException {
assertFalse(OS.WINDOWS);

Path realDir = tempDir.resolve("real");
Files.createDirectory(realDir);

Path symlinkDir = tempDir.resolve("symlinked");
Files.createSymbolicLink(symlinkDir, realDir);

Path fileInRealDir = realDir.resolve("paper.pdf");
Files.write(fileInRealDir, "dummy".getBytes(StandardCharsets.UTF_8));

Path rel = FileUtil.relativize(fileInRealDir, List.of(symlinkDir));
assertEquals(Path.of("paper.pdf"), rel, "Should resolve to simple filename when accessed through symlink");
Path fileViaSymlink = symlinkDir.resolve("paper.pdf");

Path rel2 = FileUtil.relativize(fileViaSymlink, List.of(symlinkDir));
assertEquals(Path.of("paper.pdf"), rel2, "Should resolve to simple filename for file via symlink-path");
Path outsideFile = tempDir.resolve("elsewhere.pdf");
Files.write(outsideFile, "foo".getBytes(StandardCharsets.UTF_8));

Path rel3 = FileUtil.relativize(outsideFile, List.of(symlinkDir));
assertEquals(outsideFile, rel3, "Unrelated files should not relativize");
}

/**
* @implNote Tests inspired by {@link org.jabref.model.database.BibDatabaseContextTest#getFileDirectoriesWithRelativeMetadata}
*/
Expand Down