diff --git a/internal/fs/file_windows.go b/internal/fs/file_windows.go index 8a4d01fb0..423492fa5 100644 --- a/internal/fs/file_windows.go +++ b/internal/fs/file_windows.go @@ -1,10 +1,41 @@ package fs import ( - "io/ioutil" "os" "path/filepath" "strings" + "syscall" + "strconv" + "sync" + "time" +) + +// Random number state. +// We generate random temporary file names so that there's a good +// chance the file doesn't exist yet - keeps the number of tries in +// TempFile to a minimum. +var rand uint32 +var randmu sync.Mutex + +func reseed() uint32 { + return uint32(time.Now().UnixNano() + int64(os.Getpid())) +} + +func nextRandom() string { + randmu.Lock() + r := rand + if r == 0 { + r = reseed() + } + r = r*1664525 + 1013904223 // constants from Numerical Recipes + rand = r + randmu.Unlock() + return strconv.Itoa(int(1e9 + r%1e9))[1:] +} + +const ( + FILE_ATTRIBUTE_TEMPORARY = 0x00000100 + FILE_FLAG_DELETE_ON_CLOSE = 0x04000000 ) // fixpath returns an absolute path on windows, so restic can open long file @@ -32,7 +63,25 @@ func fixpath(name string) string { // TempFile creates a temporary file. func TempFile(dir, prefix string) (f *os.File, err error) { - return ioutil.TempFile(dir, prefix) + if dir == "" { + dir = os.TempDir() + } + + access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE) + creation := uint32(syscall.CREATE_NEW) + flags := uint32(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE) + + for i := 0; i < 10000; i++ { + path := filepath.Join(dir, prefix+nextRandom()) + + h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), access, 0, nil, creation, flags, 0) + if err == nil { + return os.NewFile(uintptr(h), path), nil + } + } + + // Proper error handling is still to do + return nil, os.ErrExist } // Chmod changes the mode of the named file to mode.