diff --git a/internal/wslstate/command.go b/internal/wslstate/command.go index 6cdcf7d..609f256 100644 --- a/internal/wslstate/command.go +++ b/internal/wslstate/command.go @@ -12,6 +12,7 @@ import ( "os" "sort" "strings" + "syscall" "github.com/AviBackToBlack/container-bin/internal/hostenv" "github.com/AviBackToBlack/container-bin/internal/policy" @@ -367,6 +368,9 @@ func inspectProjectPath(recorded string, lstat func(string) (os.FileInfo, error) } return "MANAGED", nil } + if errors.Is(err, syscall.ENOTDIR) { + return "UNSAFE", nil + } if !errors.Is(err, fs.ErrNotExist) { return "", fmt.Errorf("inspect recorded native WSL project path %s: %w", recorded, err) } diff --git a/internal/wslstate/command_test.go b/internal/wslstate/command_test.go index 8343947..233055c 100644 --- a/internal/wslstate/command_test.go +++ b/internal/wslstate/command_test.go @@ -7,6 +7,7 @@ import ( "io/fs" "os" "strings" + "syscall" "testing" "time" @@ -195,6 +196,18 @@ func TestMissingProjectWithUnsafeBoundaryProofIsNeverOrphaned(t *testing.T) { } } +func TestProjectPathWithNonDirectoryAncestorIsUnsafe(t *testing.T) { + status, err := inspectProjectPath("/work/file/gone", func(string) (os.FileInfo, error) { + return nil, &os.PathError{Op: "lstat", Path: "/work/file/gone", Err: syscall.ENOTDIR} + }, func(string) error { + t.Fatal("boundary proof ran after a non-directory ancestor was already proven") + return nil + }) + if err != nil || status != "UNSAFE" { + t.Fatalf("non-directory ancestor classification = (%q, %v)", status, err) + } +} + func TestGCRechecksOrphanImmediatelyBeforeRemoval(t *testing.T) { deps, scope := testDependencies(t) orphan, _ := scope.Project("node24", "modules", "/work/restored")