restic/internal/fs/file_windows.go

91 lines
2 KiB
Go
Raw Normal View History

package fs
import (
"os"
"path/filepath"
"strings"
2021-10-12 00:15:38 +00:00
"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
// names.
func fixpath(name string) string {
abspath, err := filepath.Abs(name)
if err == nil {
// Check if \\?\UNC\ already exist
if strings.HasPrefix(abspath, `\\?\UNC\`) {
return abspath
}
// Check if \\?\ already exist
if strings.HasPrefix(abspath, `\\?\`) {
return abspath
}
// Check if path starts with \\
if strings.HasPrefix(abspath, `\\`) {
return strings.Replace(abspath, `\\`, `\\?\UNC\`, 1)
}
// Normal path
return `\\?\` + abspath
}
return name
}
2017-05-10 17:48:22 +00:00
// TempFile creates a temporary file.
func TempFile(dir, prefix string) (f *os.File, err error) {
2021-10-12 00:15:38 +00:00
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
2017-05-10 17:48:22 +00:00
}
// Chmod changes the mode of the named file to mode.
func Chmod(name string, mode os.FileMode) error {
return os.Chmod(fixpath(name), mode)
}