Simplify and comment TempFile implementation for windows

This commit is contained in:
Michael Eischer
2021-12-29 22:07:17 +01:00
parent 9e34c791c9
commit 9a3f1a9703
2 changed files with 40 additions and 41 deletions

View File

@@ -1,41 +1,14 @@
package fs
import (
"math/rand"
"os"
"path/filepath"
"strings"
"syscall"
"strconv"
"sync"
"strings"
"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
"golang.org/x/sys/windows"
)
// fixpath returns an absolute path on windows, so restic can open long file
@@ -61,25 +34,41 @@ func fixpath(name string) string {
return name
}
// TempFile creates a temporary file.
// TempFile creates a temporary file which is marked as delete-on-close
func TempFile(dir, prefix string) (f *os.File, err error) {
// slightly modified implementation of ioutil.TempFile(dir, prefix) to allow us to add
// the FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE flags.
// These provide two large benefits:
// FILE_ATTRIBUTE_TEMPORARY tells Windows to keep the file in memory only if possible
// which reduces the amount of unnecessary disk writes.
// FILE_FLAG_DELETE_ON_CLOSE instructs Windows to automatically delete the file once
// all file descriptors are closed.
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())
access := uint32(windows.GENERIC_READ | windows.GENERIC_WRITE)
creation := uint32(windows.CREATE_NEW)
share := uint32(0) // prevent other processes from accessing the file
flags := uint32(windows.FILE_ATTRIBUTE_TEMPORARY | windows.FILE_FLAG_DELETE_ON_CLOSE)
h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), access, 0, nil, creation, flags, 0)
if err == nil {
return os.NewFile(uintptr(h), path), nil
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 10000; i++ {
randSuffix := strconv.Itoa(int(1e9 + rnd.Intn(1e9)%1e9))[1:]
path := filepath.Join(dir, prefix+randSuffix)
ptr, err := windows.UTF16PtrFromString(path)
if err != nil {
return nil, err
}
h, err := windows.CreateFile(ptr, access, share, nil, creation, flags, 0)
if os.IsExist(err) {
continue
}
return os.NewFile(uintptr(h), path), err
}
// Proper error handling is still to do
return nil, os.ErrExist
}