arblant
firefox-vpn-client / core / internal / securefile / securefile_windows_test.go
securefile_windows_test.go46 lines · 1.2 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
//go:build windows

package securefile

import (
	"os"
	"path/filepath"
	"testing"
)

// When Windows refuses the rename (another program holds the file open),
// the old file stays as it was; it is never deleted first.
func TestRefusedRenameKeepsTheOldFile(t *testing.T) {
	path := filepath.Join(t.TempDir(), "config.json")
	if err := os.WriteFile(path, []byte("old"), 0o600); err != nil {
		t.Fatal(err)
	}
	// os.Open shares read and write but not delete, as a scanner may.
	held, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	if err := WritePrivate(path, []byte("new")); err == nil {
		t.Log("the rename went through despite the open handle")
	}
	_ = held.Close()
	data, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("the old file is gone: %v", err)
	}
	if got := string(data); got != "old" && got != "new" {
		t.Fatalf("the file holds %q", got)
	}
	matches, _ := filepath.Glob(path + ".tmp-*")
	if len(matches) != 0 {
		t.Fatalf("temporary files left: %v", matches)
	}

	// Once the file is free, the write goes through.
	if err := WritePrivate(path, []byte("new")); err != nil {
		t.Fatal(err)
	}
	if data, _ := os.ReadFile(path); string(data) != "new" {
		t.Fatalf("after the retry the file holds %q", data)
	}
}