From e192fca762c2a44743474db33e5c807aab68ce6b Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 7 Jun 2026 01:21:00 +0900 Subject: [PATCH] internal/pkg: check for unclean shutdown This avoids running into nasty surprises opening a cache that suffered unclean shutdown due to power loss. All other parts of the cache are not prone to inconsistent state. Signed-off-by: Ophestra --- internal/pkg/pkg.go | 31 +++++++++++++++++++++++++++++ internal/pkg/pkg_test.go | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go index b8a997d8..9a6d5201 100644 --- a/internal/pkg/pkg.go +++ b/internal/pkg/pkg.go @@ -2443,6 +2443,37 @@ func open( c.unlock = func() {} } + for _, name := range []string{ + dirWork, + dirTemp, + } { + dents, err := os.ReadDir(base.Append(name).String()) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + continue + } + c.unlock() + return nil, err + } + if len(dents) != 0 { + c.unlock() + return nil, fmt.Errorf( + "%s is not empty, scrub likely required", + name, + ) + } + } + + if _, err := os.ReadDir(base.Append( + dirExecScratch, + ).String()); !errors.Is(err, os.ErrNotExist) { + c.unlock() + if err != nil { + return nil, err + } + return nil, errors.New(dirExecScratch + " is present, scrub likely required") + } + variantPath := base.Append(fileVariant).String() if p, err := os.ReadFile(variantPath); err != nil { if !errors.Is(err, os.ErrNotExist) { diff --git a/internal/pkg/pkg_test.go b/internal/pkg/pkg_test.go index ce11fa90..9a397898 100644 --- a/internal/pkg/pkg_test.go +++ b/internal/pkg/pkg_test.go @@ -1852,6 +1852,49 @@ func TestOpen(t *testing.T) { t.Errorf("Open: error = %#v, want %#v", err, wantErr) } }) + + t.Run("dirty", func(t *testing.T) { + t.Parallel() + + tempDir := check.MustAbs(t.TempDir()) + if err := os.MkdirAll(tempDir.Append( + "cache", + "work", + "dirty", + ).String(), 0755); err != nil { + t.Fatal(err) + } + + wantErr := errors.New("work is not empty, scrub likely required") + if _, err := pkg.Open( + t.Context(), + message.New(nil), + 0, 0, 0, tempDir.Append("cache"), + ); !reflect.DeepEqual(err, wantErr) { + t.Errorf("Open: error = %#v, want %#v", err, wantErr) + } + }) + + t.Run("scratch", func(t *testing.T) { + t.Parallel() + + tempDir := check.MustAbs(t.TempDir()) + if err := os.MkdirAll(tempDir.Append( + "cache", + "scratch", + ).String(), 0755); err != nil { + t.Fatal(err) + } + + wantErr := errors.New("scratch is present, scrub likely required") + if _, err := pkg.Open( + t.Context(), + message.New(nil), + 0, 0, 0, tempDir.Append("cache"), + ); !reflect.DeepEqual(err, wantErr) { + t.Errorf("Open: error = %#v, want %#v", err, wantErr) + } + }) } func TestExtensionRegister(t *testing.T) {