//go:build windows package safeio import ( "os" "path/filepath" "testing" "golang.org/x/sys/windows" ) const expectedWindowsRetainedHandleShareMode = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE // Keep this contract compile-enforced so Windows cross-test compilation catches a future // FILE_SHARE_DELETE regression even when the tests are compiled on a non-Windows host. var _ [windowsRetainedHandleShareMode - expectedWindowsRetainedHandleShareMode]struct{} var _ [expectedWindowsRetainedHandleShareMode - windowsRetainedHandleShareMode]struct{} func TestOpenWindowsComponentBlocksMutationWhileHandleIsRetained(t *testing.T) { t.Run("parent rename", func(t *testing.T) { parent := filepath.Join(t.TempDir(), "parent") if err := os.Mkdir(parent, 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(parent, "secret"), []byte("secret"), 0o600); err != nil { t.Fatal(err) } handle, err := openWindowsComponent(parent, true) if err != nil { t.Fatal(err) } renamed := parent + "-renamed" if err := os.Rename(parent, renamed); err == nil { windows.CloseHandle(handle) t.Fatal("parent rename succeeded while its safe-I/O handle was retained") } if err := windows.CloseHandle(handle); err != nil { t.Fatal(err) } if err := os.Rename(parent, renamed); err != nil { t.Fatalf("parent rename after closing its safe-I/O handle: %v", err) } }) t.Run("final delete", func(t *testing.T) { path := filepath.Join(t.TempDir(), "secret") if err := os.WriteFile(path, []byte("secret"), 0o600); err != nil { t.Fatal(err) } handle, err := openWindowsComponent(path, false) if err != nil { t.Fatal(err) } if err := os.Remove(path); err == nil { windows.CloseHandle(handle) t.Fatal("final-file deletion succeeded while its safe-I/O handle was retained") } if err := windows.CloseHandle(handle); err != nil { t.Fatal(err) } if err := os.Remove(path); err != nil { t.Fatalf("final-file deletion after closing its safe-I/O handle: %v", err) } }) }