Skip to content

Commit 008028a

Browse files
authored
Fix UnionPath existence checks with nested union file systems (#53)
Fixes `Files.exists` always returning true for layered/nested UnionFileSystems. The bug happens because of a missing `else` branch in `checkAccess` for non-default and non-zip file systems. Added a test to reproduce the issue, and consolidated the fs-specific code to avoid such issues in the future.
1 parent d2a51e3 commit 008028a

2 files changed

Lines changed: 32 additions & 25 deletions

File tree

src/main/java/cpw/mods/niofs/union/UnionFileSystem.java

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,8 @@ List<Path> getBasePaths() {
233233

234234
private Optional<BasicFileAttributes> getFileAttributes(final Path path) {
235235
try {
236-
if (path.getFileSystem() == FileSystems.getDefault() && !path.toFile().exists()) {
237-
return Optional.empty();
238-
} else if (path.getFileSystem().provider().getScheme().equals("jar") && !zipFsExists(this, path)) {
236+
Boolean fastCheck = tryFastPathExists(this, path);
237+
if (fastCheck != null && !fastCheck) {
239238
return Optional.empty();
240239
} else {
241240
return Optional.of(path.getFileSystem().provider().readAttributes(path, BasicFileAttributes.class));
@@ -245,6 +244,30 @@ private Optional<BasicFileAttributes> getFileAttributes(final Path path) {
245244
}
246245
}
247246

247+
/**
248+
* Checks if a path exists using optimized filesystem-specific code.
249+
* (With a fallback to the regular {@link Files#exists(Path, LinkOption...)}).
250+
*/
251+
private static boolean fastPathExists(UnionFileSystem ufs, Path path) {
252+
Boolean result = tryFastPathExists(ufs, path);
253+
return result != null ? result : Files.exists(path);
254+
}
255+
256+
/**
257+
* Tries to check if a path exists using optimized filesystem-specific code,
258+
* or returns {@code null} if there is no optimized code for that particular filesystem.
259+
*/
260+
@Nullable
261+
private static Boolean tryFastPathExists(UnionFileSystem ufs, Path path) {
262+
if (path.getFileSystem() == FileSystems.getDefault()) {
263+
return path.toFile().exists();
264+
} else if (path.getFileSystem().provider().getScheme().equals("jar")) {
265+
return zipFsExists(ufs, path);
266+
}
267+
268+
return null;
269+
}
270+
248271
private static boolean zipFsExists(UnionFileSystem ufs, Path path) {
249272
try {
250273
if (Optional.ofNullable(ufs.embeddedFileSystems.get(path.getFileSystem())).filter(efs -> !efs.fsCh.isOpen()).isPresent())
@@ -270,15 +293,7 @@ private Optional<Path> findFirstFiltered(final UnionPath unionPath) {
270293
final Path realPath = toRealPath(p, unionPath);
271294
// Test if the real path exists and matches the filter of this file system
272295
if (testFilter(realPath, p)) {
273-
if (realPath.getFileSystem() == FileSystems.getDefault()) {
274-
if (realPath.toFile().exists()) {
275-
return Optional.of(realPath);
276-
}
277-
} else if (realPath.getFileSystem().provider().getScheme().equals("jar")) {
278-
if (zipFsExists(this, realPath)) {
279-
return Optional.of(realPath);
280-
}
281-
} else if (Files.exists(realPath)) {
296+
if (fastPathExists(this, realPath)) {
282297
return Optional.of(realPath);
283298
}
284299
}
@@ -320,14 +335,8 @@ public void checkAccess(final UnionPath p, final AccessMode... modes) throws IOE
320335
findFirstFiltered(p).ifPresentOrElse(path -> {
321336
try {
322337
if (modes.length == 0) {
323-
if (path.getFileSystem() == FileSystems.getDefault()) {
324-
if (!path.toFile().exists()) {
325-
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
326-
}
327-
} else if (path.getFileSystem().provider().getScheme().equals("jar")) {
328-
if (!zipFsExists(this, path)) {
329-
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
330-
}
338+
if (!fastPathExists(this, path)) {
339+
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
331340
}
332341
} else {
333342
path.getFileSystem().provider().checkAccess(path, modes);
@@ -377,11 +386,7 @@ public DirectoryStream<Path> newDirStream(final UnionPath path, final DirectoryS
377386
Stream<Path> stream = Stream.empty();
378387
for (final var bp : basepaths) {
379388
final var dir = toRealPath(bp, path);
380-
if (dir.getFileSystem() == FileSystems.getDefault() && !dir.toFile().exists()) {
381-
continue;
382-
} else if (dir.getFileSystem().provider().getScheme().equals("jar") && !zipFsExists(this, dir)) {
383-
continue;
384-
} else if (Files.notExists(dir)) {
389+
if (!fastPathExists(this, dir)) {
385390
continue;
386391
}
387392
final var isSimple = embeddedFileSystems.containsKey(bp);

src/test/java/cpw/mods/niofs/union/TestUnionFS.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ void testNested() {
202202
var npath = Paths.get(uri);
203203
var input = assertDoesNotThrow(() -> Files.newInputStream(npath));
204204
var data = assertDoesNotThrow(() -> input.readAllBytes());
205+
206+
assertFalse(Files.exists(outer.getPath("definitely", "does", "not", "exist")));
205207
}
206208

207209
@Test

0 commit comments

Comments
 (0)