rclone/vendor/github.com/calebcase/tmpfile/tmpfile_windows.go
2020-05-12 15:56:50 +00:00

94 lines
2.4 KiB
Go

// Copyright 2010 The Go Authors. All rights reserved. Use of this source code
// is governed by a BSD-style license that can be found in the LICENSE.golang
// file.
//
// This is a copied and modified version of the code provided in:
// https://golang.org/src/io/ioutil/tempfile.go
package tmpfile
import (
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/sys/windows"
)
// 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:]
}
// New creates a new temporary file in the directory dir using the same method
// as ioutil.TempFile and then unlinks the file with os.Remove to ensure the
// file is deleted when the calling process exists.
func New(dir, pattern string) (f *os.File, err error) {
if dir == "" {
dir = os.TempDir()
}
var prefix, suffix string
if pos := strings.LastIndex(pattern, "*"); pos != -1 {
prefix, suffix = pattern[:pos], pattern[pos+1:]
} else {
prefix = pattern
}
nconflict := 0
for i := 0; i < 10000; i++ {
name := filepath.Join(dir, prefix+nextRandom()+suffix)
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
handle, err := windows.CreateFile(
windows.StringToUTF16Ptr(name), // File Name
windows.GENERIC_READ|windows.GENERIC_WRITE|windows.DELETE, // Desired Access
windows.FILE_SHARE_DELETE, // Share Mode
nil, // Security Attributes
windows.CREATE_NEW, // Create Disposition
windows.FILE_ATTRIBUTE_TEMPORARY|windows.FILE_FLAG_DELETE_ON_CLOSE, // Flags & Attributes
0, // Template File
)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randmu.Unlock()
}
continue
}
f = os.NewFile(uintptr(handle), name)
break
}
err = os.Remove(f.Name())
if err != nil {
return
}
return f, nil
}