From 5b18bb726c461fefa42890e2477d20c4489b8b3e Mon Sep 17 00:00:00 2001 From: andreatp Date: Fri, 30 Oct 2026 16:15:08 +0000 Subject: [PATCH] fix: symlink loops throw FileSystemException The reason matches what the JDK reports for ELOOP, so callers such as a WASI layer can map it the same way for ZeroFs and the default file system. --- .../java/io/roastedroot/zerofs/FileTree.java | 3 ++- .../zerofs/ZeroFsUnixLikeFileSystemTest.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/io/roastedroot/zerofs/FileTree.java b/core/src/main/java/io/roastedroot/zerofs/FileTree.java index c9c972230..9b9b8ff4c 100644 --- a/core/src/main/java/io/roastedroot/zerofs/FileTree.java +++ b/core/src/main/java/io/roastedroot/zerofs/FileTree.java @@ -1,6 +1,7 @@ package io.roastedroot.zerofs; import java.io.IOException; +import java.nio.file.FileSystemException; import java.nio.file.LinkOption; import java.nio.file.NoSuchFileException; import java.util.Iterator; @@ -166,7 +167,7 @@ private DirectoryEntry lookUpLast( private DirectoryEntry followSymbolicLink(File dir, SymbolicLink link, int linkDepth) throws IOException { if (linkDepth >= MAX_SYMBOLIC_LINK_DEPTH) { - throw new IOException("too many levels of symbolic links"); + throw new FileSystemException(null, null, "Too many levels of symbolic links"); } return lookUp(dir, link.target(), Options.FOLLOW_LINKS, linkDepth + 1); diff --git a/core/src/test/java/io/roastedroot/zerofs/ZeroFsUnixLikeFileSystemTest.java b/core/src/test/java/io/roastedroot/zerofs/ZeroFsUnixLikeFileSystemTest.java index 1ba48d1f9..fcb480321 100644 --- a/core/src/test/java/io/roastedroot/zerofs/ZeroFsUnixLikeFileSystemTest.java +++ b/core/src/test/java/io/roastedroot/zerofs/ZeroFsUnixLikeFileSystemTest.java @@ -24,6 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -1270,19 +1271,16 @@ public void testSymbolicLinks_symlinkCycle() throws IOException { Files.createSymbolicLink(path("/foo/bar"), path("baz")); Files.createSymbolicLink(path("/foo/baz"), path("bar")); - try { - Files.createFile(path("/foo/bar/file")); - fail(); - } catch (IOException expected) { - assertTrue(expected.getMessage().contains("symbolic link")); - } + FileSystemException expected = + assertThrows( + FileSystemException.class, () -> Files.createFile(path("/foo/bar/file"))); + assertEquals("Too many levels of symbolic links", expected.getReason()); - try { - Files.write(path("/foo/bar"), preFilledBytes(10)); - fail(); - } catch (IOException expected) { - assertTrue(expected.getMessage().contains("symbolic link")); - } + expected = + assertThrows( + FileSystemException.class, + () -> Files.write(path("/foo/bar"), preFilledBytes(10))); + assertEquals("Too many levels of symbolic links", expected.getReason()); } @Test