Skip to content

mem: ReadFile returns an error when filename is a directory#633

Open
amitmishra11 wants to merge 1 commit into
spf13:masterfrom
amitmishra11:fix/memmapfs-readfile-on-directory-201
Open

mem: ReadFile returns an error when filename is a directory#633
amitmishra11 wants to merge 1 commit into
spf13:masterfrom
amitmishra11:fix/memmapfs-readfile-on-directory-201

Conversation

@amitmishra11

Copy link
Copy Markdown

Fixes #201

Bug

afero.ReadFile on a MemMapFs silently succeeds with empty content when the path is a directory, instead of returning an error like the real os.ReadFile does (EISDIR).

fs := afero.NewMemMapFs()
fs.MkdirAll("/somedir", 0755)
data, err := afero.ReadFile(fs, "/somedir")
// data == "", err == nil  (expected: err != nil)

Root cause

An in-memory directory's FileData.data buffer is empty, the same as an empty regular file. mem.File.ReadAt had no check for fileData.dir, so reading a directory returned (0, io.EOF) exactly like reading an empty file would. File.Read and afero.ReadFile's underlying readAll treat a first io.EOF as a normal empty read (not an error), so the whole call chain returns ("", nil) with no indication anything was wrong.

Readdir already has the equivalent inverse check (if !f.fileData.dir { return error }), so this brings ReadAt in line with that existing convention.

Fix

Added a directory check at the top of mem.File.ReadAt that returns &os.PathError{Op: "read", Path: ..., Err: syscall.EISDIR}, matching the real os.File's behavior when Read is called on a directory.

Scoped to the read path only (matching the issue title/report) — did not touch Write/WriteAt, which is a separate question not raised by this issue.

Tests

Added TestReadFileOnDirectory in ioutil_test.go, following the existing TestReadFile pattern: creates a directory via MkdirAll, calls Afero.ReadFile on it, and asserts an error is returned.

I verified the test actually catches the regression: temporarily removed the new directory check in ReadAt and reran the test — it failed with ReadFile somedir: error expected, got nil (contents=""), reproducing the exact symptom from the issue. Restoring the fix makes it pass again.

Verification

  • go build ./... — clean
  • go vet ./... — clean
  • go test ./... — all pass except TestLstatIfPossible/TestSymlinkIfPossible/TestReadlinkIfPossible, which fail identically on unmodified master in my environment (Windows, no privilege to create symlinks) — confirmed pre-existing and unrelated by stashing my changes and rerunning.

MemMapFs's in-memory directory entries have an empty data buffer, the
same as an empty regular file. File.ReadAt had no directory check, so
reading a directory returned (0, io.EOF) just like reading an empty
file would, and ReadFile (which treats Read's first EOF as a normal
empty read, not an error) silently returned ("", nil) instead of an
error.

Add a directory check to File.ReadAt that returns a PathError wrapping
syscall.EISDIR, matching the real os.File's behavior of erroring when
Read is called on a directory.

Added TestReadFileOnDirectory, which calls Afero.ReadFile on a path
created with MkdirAll and asserts an error is returned. Verified the
test catches the regression by temporarily removing the directory
check: the test fails exactly as described in the issue (no error,
empty contents).

Fixes spf13#201
@CLAassistant

CLAassistant commented Jun 27, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

afero.ReadFile doesn't return error when filename is a directory when fs is memmapfs

2 participants