73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
//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
|
|
|
|
const expectedWindowsReplaceMoveFlags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH
|
|
|
|
// 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{}
|
|
var _ [windowsReplaceMoveFlags - expectedWindowsReplaceMoveFlags]struct{}
|
|
var _ [expectedWindowsReplaceMoveFlags - windowsReplaceMoveFlags]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)
|
|
}
|
|
})
|
|
}
|