From afcb7e66f38a395a33b2858445d69f1c0ba12510 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Wed, 2 Sep 2026 11:37:36 +0200 Subject: [PATCH] fix: only enforce O_EXCL on MemMapFs OpenFile when O_CREATE is set Per POSIX, O_EXCL is meaningful only together with O_CREATE. MemMapFs OpenFile returned ErrFileExists whenever O_EXCL was set on an existing file, so reopening a file with O_RDWR|O_EXCL failed even though no creation was requested. Guard the existence check with O_CREATE so that O_EXCL without O_CREATE opens the existing file, matching os.OpenFile. Fixes #415 --- memmap.go | 4 +++- memmap_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/memmap.go b/memmap.go index 30fff0f..424a178 100644 --- a/memmap.go +++ b/memmap.go @@ -263,7 +263,9 @@ func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, erro perm &= chmodBits chmod := false file, err := m.openWrite(name) - if err == nil && (flag&os.O_EXCL > 0) { + // O_EXCL is only meaningful together with O_CREATE (POSIX); without + // O_CREATE it must not prevent opening an existing file. + if err == nil && flag&os.O_EXCL > 0 && flag&os.O_CREATE > 0 { return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileExists} } if os.IsNotExist(err) && (flag&os.O_CREATE > 0) { diff --git a/memmap_test.go b/memmap_test.go index e6a3f3d..31b9000 100644 --- a/memmap_test.go +++ b/memmap_test.go @@ -135,6 +135,35 @@ func TestOpenFileExcl(t *testing.T) { checkPathError(t, err, "Open") } +// Ensure os.O_EXCL only rejects an existing file when os.O_CREATE is also +// requested. Per POSIX, O_EXCL is meaningful only in combination with +// O_CREATE, so opening an existing file with O_RDWR|O_EXCL must succeed. +func TestOpenFileExclWithoutCreate(t *testing.T) { + const fileName = "/myExclNoCreate" + const fileMode = os.FileMode(0o644) + + fs := NewMemMapFs() + + // Create the file up front. + f, err := fs.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_EXCL, fileMode) + if err != nil { + t.Fatalf("initial create failed: %s", err) + } + f.Close() + + // Reopening with O_EXCL but without O_CREATE must succeed. + f, err = fs.OpenFile(fileName, os.O_RDWR|os.O_EXCL, fileMode) + if err != nil { + t.Fatalf("OpenFile(O_RDWR|O_EXCL) on existing file should succeed, got: %s", err) + } + f.Close() + + // O_CREATE|O_EXCL on an existing file must still fail. + if _, err := fs.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_EXCL, fileMode); err == nil { + t.Fatalf("OpenFile(O_CREATE|O_EXCL) on existing file should fail, but it didn't") + } +} + // Ensure Permissions are set on OpenFile/Mkdir/MkdirAll func TestPermSet(t *testing.T) { const fileName = "/myFileTest"