Files
ThothII/tools/tht/internal/backup/restore_file_windows_test.go

84 lines
2.5 KiB
Go

//go:build windows
package backup
import (
"errors"
"os"
"path/filepath"
"testing"
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
)
func TestReplaceRestoreFileWindowsCreatesAndReplacesOwnerOnlyRegular(t *testing.T) {
parent := filepath.Join(t.TempDir(), "private")
if err := os.Mkdir(parent, 0o700); err != nil {
t.Fatal(err)
}
if err := safeio.ProtectPrivateDirectory(parent); err != nil {
t.Fatal(err)
}
target := filepath.Join(parent, "auth.yaml")
if err := replaceRestoreFile(target, []byte("created"), 0o600); err != nil {
t.Fatal(err)
}
if err := safeio.ValidatePrivateRegular(target); err != nil {
t.Fatalf("created restore file is not owner-only: %v", err)
}
if err := replaceRestoreFile(target, []byte("replacement"), 0o600); err != nil {
t.Fatal(err)
}
if err := safeio.ValidatePrivateRegular(target); err != nil {
t.Fatalf("replacement restore file is not owner-only: %v", err)
}
contents, err := os.ReadFile(target)
if err != nil || string(contents) != "replacement" {
t.Fatalf("replacement contents = %q, error = %v", contents, err)
}
}
func TestReplaceRestoreFileWindowsPinsParentAndRejectsReparsePath(t *testing.T) {
root := t.TempDir()
parent := filepath.Join(root, "private")
if err := os.Mkdir(parent, 0o700); err != nil {
t.Fatal(err)
}
if err := safeio.ProtectPrivateDirectory(parent); err != nil {
t.Fatal(err)
}
target := filepath.Join(parent, "users.yaml")
if err := os.WriteFile(target, []byte("old"), 0o600); err != nil {
t.Fatal(err)
}
if err := safeio.ProtectPrivateRegular(target); err != nil {
t.Fatal(err)
}
renamed := parent + "-renamed"
var hookCalled bool
restoreHook := safeio.SetPrivateDirectoryTestHookForTest(func(stage string) {
if stage != "after-restore-parent-open" {
return
}
hookCalled = true
if err := os.Rename(parent, renamed); err == nil {
t.Fatal("parent rename succeeded while restore retained its directory handles")
}
})
defer restoreHook()
if err := replaceRestoreFile(target, []byte("new"), 0o600); err != nil {
t.Fatal(err)
}
if !hookCalled {
t.Fatal("parent-pinning race hook was not reached")
}
link := filepath.Join(root, "reparse-parent")
if err := os.Symlink(parent, link); err != nil {
t.Skipf("Windows host does not permit symlink creation: %v", err)
}
if err := replaceRestoreFile(filepath.Join(link, "users.yaml"), []byte("unsafe"), 0o600); !errors.Is(err, safeio.ErrUnsafeFile) {
t.Fatalf("reparse-parent restore error = %v, want ErrUnsafeFile", err)
}
}