From 5dc3220ee0d92c5830b5e4df9f8ad5dafe374e9c Mon Sep 17 00:00:00 2001 From: Aryan Date: Sat, 17 Oct 2026 00:12:18 +0530 Subject: [PATCH] memfs: Reject ReadDir calls on regular files Returning an empty directory listing for a regular file hides invalid paths from callers and differs from the OS-backed filesystem. Check the target mode after resolving symlinks and return a PathError wrapping ENOTDIR. Add a shared filesystem regression and memfs coverage for symlinks to files and directories. Assisted-by: OpenAI Codex (GPT-6) Signed-off-by: Aryan --- memfs/memory.go | 3 +++ memfs/memory_test.go | 16 ++++++++++++++++ test/dir_test.go | 15 +++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/memfs/memory.go b/memfs/memory.go index 3c700d6..9798cc5 100644 --- a/memfs/memory.go +++ b/memfs/memory.go @@ -171,6 +171,9 @@ func (fs *Memory) ReadDir(path string) ([]gofs.DirEntry, error) { return fs.ReadDir(target) } } + if !f.mode.IsDir() { + return nil, &os.PathError{Op: "readdir", Path: path, Err: syscall.ENOTDIR} + } } else { return nil, &os.PathError{Op: "open", Path: path, Err: syscall.ENOENT} } diff --git a/memfs/memory_test.go b/memfs/memory_test.go index c07d52f..4fab871 100644 --- a/memfs/memory_test.go +++ b/memfs/memory_test.go @@ -137,6 +137,22 @@ func TestNotFound(t *testing.T) { assert.ErrorIs(t, err, os.ErrNotExist) } +func TestReadDirSymlink(t *testing.T) { + fs := New() + require.NoError(t, util.WriteFile(fs, "file", nil, 0o644)) + require.NoError(t, fs.MkdirAll("dir", 0o755)) + require.NoError(t, fs.Symlink("file", "file-link")) + require.NoError(t, fs.Symlink("dir", "dir-link")) + + entries, err := fs.ReadDir("file-link") + require.ErrorIs(t, err, syscall.ENOTDIR) + assert.Nil(t, entries) + + entries, err = fs.ReadDir("dir-link") + require.NoError(t, err) + assert.Empty(t, entries) +} + func TestTruncateAppend(t *testing.T) { fs := New() err := util.WriteFile(fs, "truncate_append", []byte("file-content"), 0o666) diff --git a/test/dir_test.go b/test/dir_test.go index 0f98f26..1b0bcac 100644 --- a/test/dir_test.go +++ b/test/dir_test.go @@ -169,6 +169,21 @@ func TestDir_ReadDir(t *testing.T) { }) } +func TestDir_ReadDirFile(t *testing.T) { + eachDirFS(t, func(t *testing.T, fs dirFS) { + t.Helper() + err := util.WriteFile(fs, "dir/file", []byte("content"), 0o644) + require.NoError(t, err) + + entries, err := fs.ReadDir("dir/file") + require.Error(t, err) + assert.Nil(t, entries) + + var pathErr *os.PathError + require.ErrorAs(t, err, &pathErr) + }) +} + func TestDir_ReadDirNested(t *testing.T) { eachDirFS(t, func(t *testing.T, fs dirFS) { t.Helper()