From 0dbf118974e0aa3bbbfd0501135d61672817da53 Mon Sep 17 00:00:00 2001 From: Martin Hutchinson Date: Thu, 25 Jun 2026 11:34:06 +0000 Subject: [PATCH] [POSIX] Clean up temp files in error cases --- storage/posix/file_ops.go | 31 +++++++++++++++++++++++++++---- storage/posix/files_test.go | 21 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/storage/posix/file_ops.go b/storage/posix/file_ops.go index 534e7e20d..cf217bef6 100644 --- a/storage/posix/file_ops.go +++ b/storage/posix/file_ops.go @@ -151,10 +151,19 @@ func overwrite(name string, d []byte) error { if err != nil { return fmt.Errorf("failed to create temp file: %w", err) } + success := false + defer func() { + if !success { + if err := os.Remove(tmpName); err != nil { + slog.WarnContext(context.Background(), "Failed to remove temporary file", slog.String("tmpname", tmpName), slog.Any("error", err)) + } + } + }() if err := os.Rename(tmpName, name); err != nil { return fmt.Errorf("failed to rename temporary file to target %q: %w", name, err) } + success = true return nil }) } @@ -186,17 +195,31 @@ func createTemp(prefix string, d []byte) (name string, err error) { } } + tmpName := name + success := false + defer func() { + if !success { + if err := os.Remove(tmpName); err != nil { + slog.WarnContext(context.Background(), "Failed to remove temporary file", slog.String("tmpname", tmpName), slog.Any("error", err)) + } + name = "" + } + }() defer func() { - if errC := f.Close(); errC != nil && err == nil { - err = errC + if errC := f.Close(); errC != nil { + if err == nil { + err = errC + } + success = false } }() - if n, err := f.Write(d); err != nil { - return "", fmt.Errorf("failed to write to temporary file %q: %w", name, err) + if n, writeErr := f.Write(d); writeErr != nil { + return "", fmt.Errorf("failed to write to temporary file %q: %w", name, writeErr) } else if l := len(d); n < l { return "", fmt.Errorf("short write on %q, %d < %d", name, n, l) } + success = true return name, nil } diff --git a/storage/posix/files_test.go b/storage/posix/files_test.go index aa3c9ef32..6edecdd05 100644 --- a/storage/posix/files_test.go +++ b/storage/posix/files_test.go @@ -655,3 +655,24 @@ func TestFlockLockingCorrectness(t *testing.T) { t.Fatalf("expected output file to contain %q, got %q (serialization failed)", expected, string(content)) } } + +func TestOverwrite_CleanupOnRenameFailure(t *testing.T) { + tmpDir := t.TempDir() + targetDir := filepath.Join(tmpDir, "target_dir") + if err := os.Mkdir(targetDir, 0755); err != nil { + t.Fatal(err) + } + err := overwrite(targetDir, []byte("some data")) + if err == nil { + t.Fatal("expected overwrite to fail when target is a directory") + } + entries, err := os.ReadDir(tmpDir) + if err != nil { + t.Fatal(err) + } + for _, entry := range entries { + if entry.Name() != "target_dir" { + t.Errorf("found unexpected file/dir after failure: %s", entry.Name()) + } + } +}