diff --git a/afero_test.go b/afero_test.go index a6cb05c4..5cac6f0d 100644 --- a/afero_test.go +++ b/afero_test.go @@ -45,6 +45,8 @@ func testDir(fs Fs) string { } func tmpFile(fs Fs) File { + // Ensure the temp directory hierarchy exists in the filesystem. + fs.MkdirAll(os.TempDir(), 0o700) x, err := TempFile(fs, "", "afero") if err != nil { panic(fmt.Sprint("unable to work with temp file", err)) diff --git a/ioutil_test.go b/ioutil_test.go index edeaf453..35bb109c 100644 --- a/ioutil_test.go +++ b/ioutil_test.go @@ -16,6 +16,7 @@ package afero import ( + "os" "path/filepath" "strings" "testing" @@ -33,6 +34,7 @@ func checkSizePath(t *testing.T, path string, size int64) { func TestReadFile(t *testing.T) { testFS = &MemMapFs{} + testFS.MkdirAll(os.TempDir(), 0o700) fsutil := &Afero{Fs: testFS} testFS.Create("this_exists.go") @@ -53,6 +55,7 @@ func TestReadFile(t *testing.T) { func TestWriteFile(t *testing.T) { testFS = &MemMapFs{} + testFS.MkdirAll(os.TempDir(), 0o700) fsutil := &Afero{Fs: testFS} f, err := fsutil.TempFile("", "ioutil-test") if err != nil { @@ -83,6 +86,7 @@ func TestWriteFile(t *testing.T) { func TestReadDir(t *testing.T) { testFS = &MemMapFs{} + testFS.MkdirAll(os.TempDir(), 0o700) testFS.Mkdir("/i-am-a-dir", 0o777) testFS.Create("/this_exists.go") dirname := "rumpelstilzchen" @@ -162,7 +166,9 @@ func TestTempFile(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - file, err := TempFile(NewMemMapFs(), tt.args.dir, tt.args.pattern) + fs := NewMemMapFs() + fs.MkdirAll(os.TempDir(), 0o700) + file, err := TempFile(fs, tt.args.dir, tt.args.pattern) if err != nil { t.Errorf("TempFile() error = %v, none expected", err) return diff --git a/lstater_test.go b/lstater_test.go index cb5585f9..b4e5b6f0 100644 --- a/lstater_test.go +++ b/lstater_test.go @@ -51,6 +51,7 @@ func TestLstatIfPossible(t *testing.T) { pathFileMem := filepath.Join(memWorkDir, "aferom.txt") WriteFile(osFs, filepath.Join(workDir, "afero.txt"), []byte("Hi, Afero!"), 0o777) + memFs.MkdirAll(memWorkDir, 0o777) WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0o777) os.Chdir(workDir) diff --git a/memmap.go b/memmap.go index ed92f564..bc398c22 100644 --- a/memmap.go +++ b/memmap.go @@ -247,6 +247,15 @@ func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, erro return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileExists} } if os.IsNotExist(err) && (flag&os.O_CREATE > 0) { + // Check that the parent directory exists before creating the file, + // matching os.OpenFile semantics. + parentDir := normalizePath(filepath.Dir(normalizePath(name))) + m.mu.RLock() + _, parentExists := m.getData()[parentDir] + m.mu.RUnlock() + if !parentExists { + return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} + } file, err = m.Create(name) chmod = true } diff --git a/memmap_test.go b/memmap_test.go index 873cd904..7921ccb1 100644 --- a/memmap_test.go +++ b/memmap_test.go @@ -918,3 +918,34 @@ func TestMemMapFsRename(t *testing.T) { } } } + +func TestOpenFileNonExistentDirectory(t *testing.T) { + // Verify that OpenFile with O_CREATE returns an error when the parent + // directory does not exist, matching os.OpenFile behavior. + // See https://github.com/spf13/afero/issues/270 + fs := NewMemMapFs() + + err := WriteFile(fs, "/nonexistent/dir/file.txt", []byte("content"), 0o644) + if err == nil { + t.Fatal("expected error when writing to a non-existent directory, got nil") + } + if !os.IsNotExist(err) { + t.Fatalf("expected os.ErrNotExist, got: %v", err) + } + + // Verify it works when the directory is created first. + fs.MkdirAll("/existing/dir", 0o755) + err = WriteFile(fs, "/existing/dir/file.txt", []byte("content"), 0o644) + if err != nil { + t.Fatalf("expected no error when writing to an existing directory, got: %v", err) + } + + // Verify the written content. + data, err := ReadFile(fs, "/existing/dir/file.txt") + if err != nil { + t.Fatalf("failed to read back written file: %v", err) + } + if string(data) != "content" { + t.Fatalf("expected 'content', got %q", string(data)) + } +} diff --git a/util_test.go b/util_test.go index a8e7212f..033355b1 100644 --- a/util_test.go +++ b/util_test.go @@ -67,6 +67,7 @@ func TestDirExists(t *testing.T) { func TestIsDir(t *testing.T) { testFS = new(MemMapFs) + testFS.MkdirAll(os.TempDir(), 0o700) type test struct { input string @@ -90,6 +91,7 @@ func TestIsDir(t *testing.T) { func TestIsEmpty(t *testing.T) { testFS = new(MemMapFs) + testFS.MkdirAll(os.TempDir(), 0o700) zeroSizedFile, _ := createZeroSizedFileInTempDir() defer deleteFileInTempDir(zeroSizedFile)