mirror of https://github.com/restic/restic.git
Simplify and comment TempFile implementation for windows
This commit is contained in:
parent
9e34c791c9
commit
9a3f1a9703
|
@ -0,0 +1,10 @@
|
|||
Enhancement: Improve handling of temporary files on Windows
|
||||
|
||||
In some cases restic failed to delete temporary files causing the current
|
||||
command to fail. This has been fixed by ensuring that Windows automatically
|
||||
deletes the file. In addition, temporary files are only written to disk if
|
||||
necessary to reduce disk writes.
|
||||
|
||||
https://github.com/restic/restic/issues/3465
|
||||
https://github.com/restic/restic/issues/1551
|
||||
https://github.com/restic/restic/pull/3610
|
|
@ -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,23 +34,39 @@ 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)
|
||||
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)
|
||||
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := 0; i < 10000; i++ {
|
||||
path := filepath.Join(dir, prefix+nextRandom())
|
||||
randSuffix := strconv.Itoa(int(1e9 + rnd.Intn(1e9)%1e9))[1:]
|
||||
path := filepath.Join(dir, prefix+randSuffix)
|
||||
|
||||
h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), access, 0, nil, creation, flags, 0)
|
||||
if err == nil {
|
||||
return os.NewFile(uintptr(h), path), nil
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue