From c37cd819a93940ad24e7909204b6cf355872112e Mon Sep 17 00:00:00 2001 From: dualfroz Date: Wed, 2 Sep 2026 11:40:32 +0200 Subject: [PATCH] fix: return an error instead of panicking on orphaned MemMapFs Remove unRegisterWithParent called log.Panic when a file's parent directory was no longer registered (for example when the parent was removed first), crashing the whole program from an ordinary Remove call. Return a normal error instead so Remove, RemoveAll and Rename can surface it to the caller. The now-unused log import is dropped. Fixes #388 --- memmap.go | 7 +++++-- memmap_test.go | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/memmap.go b/memmap.go index 30fff0f..4b61a3a 100644 --- a/memmap.go +++ b/memmap.go @@ -16,7 +16,6 @@ package afero import ( "fmt" "io" - "log" "os" "path/filepath" "sort" @@ -71,7 +70,11 @@ func (m *MemMapFs) unRegisterWithParent(fileName string) error { } parent := m.findParent(f) if parent == nil { - log.Panic("parent of ", f.Name(), " is nil") + // The parent directory is no longer registered (for example it was + // removed before this file). There is nothing to unregister from, and + // crashing the whole process is never appropriate, so surface an error + // that callers can handle. + return fmt.Errorf("parent of %s is nil", f.Name()) } parent.Lock() diff --git a/memmap_test.go b/memmap_test.go index e6a3f3d..9d452ca 100644 --- a/memmap_test.go +++ b/memmap_test.go @@ -135,6 +135,29 @@ func TestOpenFileExcl(t *testing.T) { checkPathError(t, err, "Open") } +// Regression test for #388: removing a file whose parent directory is no +// longer registered must return an error instead of panicking. Previously +// unRegisterWithParent called log.Panic, which crashed the whole process. +func TestMemFsRemoveWithUnregisteredParent(t *testing.T) { + fs := NewMemMapFs() + if err := fs.MkdirAll("/foo/bar", 0o755); err != nil { + t.Fatal(err) + } + if err := WriteFile(fs, "/foo/bar/baz.txt", []byte("data"), 0o644); err != nil { + t.Fatal(err) + } + + // Drop the parent directory entry, leaving the file dangling. + if err := fs.Remove("/foo/bar"); err != nil { + t.Fatalf("removing parent dir failed: %v", err) + } + + // Removing the now-orphaned file must not panic; it must return an error. + if err := fs.Remove("/foo/bar/baz.txt"); err == nil { + t.Fatalf("expected an error removing a file with an unregistered parent, got nil") + } +} + // Ensure Permissions are set on OpenFile/Mkdir/MkdirAll func TestPermSet(t *testing.T) { const fileName = "/myFileTest"