mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #30139 from Microsoft/jjh/sequential
Windows: Use sequential file access
This commit is contained in:
commit
062f2a3eae
4 changed files with 79 additions and 5 deletions
|
@ -3,16 +3,19 @@ package command
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyToFile writes the content of the reader to the specified file
|
// CopyToFile writes the content of the reader to the specified file
|
||||||
func CopyToFile(outfile string, r io.Reader) error {
|
func CopyToFile(outfile string, r io.Reader) error {
|
||||||
tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
|
// We use sequential file access here to avoid depleting the standby list
|
||||||
|
// on Windows. On Linux, this is a call directly to ioutil.TempFile
|
||||||
|
tmpFile, err := system.TempFileSequential(filepath.Dir(outfile), ".docker_temp_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -825,14 +825,16 @@ func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser
|
||||||
var f *os.File
|
var f *os.File
|
||||||
// Open the file while holding the Windows backup privilege. This ensures that the
|
// Open the file while holding the Windows backup privilege. This ensures that the
|
||||||
// file can be opened even if the caller does not actually have access to it according
|
// file can be opened even if the caller does not actually have access to it according
|
||||||
// to the security descriptor.
|
// to the security descriptor. Also use sequential file access to avoid depleting the
|
||||||
|
// standby list - Microsoft VSO Bug Tracker #9900466
|
||||||
err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
|
err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
|
||||||
path := longpath.AddPrefix(filepath.Join(fg.path, filename))
|
path := longpath.AddPrefix(filepath.Join(fg.path, filename))
|
||||||
p, err := syscall.UTF16FromString(path)
|
p, err := syscall.UTF16FromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
|
||||||
|
h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|fileFlagSequentialScan, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &os.PathError{Op: "open", Path: path, Err: err}
|
return &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +25,7 @@ func IsAbs(path string) bool {
|
||||||
return filepath.IsAbs(path)
|
return filepath.IsAbs(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The functions below here are wrappers for the equivalents in the os package.
|
// The functions below here are wrappers for the equivalents in the os and ioutils packages.
|
||||||
// They are passthrough on Unix platforms, and only relevant on Windows.
|
// They are passthrough on Unix platforms, and only relevant on Windows.
|
||||||
|
|
||||||
// CreateSequential creates the named file with mode 0666 (before umask), truncating
|
// CreateSequential creates the named file with mode 0666 (before umask), truncating
|
||||||
|
@ -52,3 +53,16 @@ func OpenSequential(name string) (*os.File, error) {
|
||||||
func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
|
func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
return os.OpenFile(name, flag, perm)
|
return os.OpenFile(name, flag, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TempFileSequential creates a new temporary file in the directory dir
|
||||||
|
// with a name beginning with prefix, opens the file for reading
|
||||||
|
// and writing, and returns the resulting *os.File.
|
||||||
|
// If dir is the empty string, TempFile uses the default directory
|
||||||
|
// for temporary files (see os.TempDir).
|
||||||
|
// Multiple programs calling TempFile simultaneously
|
||||||
|
// will not choose the same file. The caller can use f.Name()
|
||||||
|
// to find the pathname of the file. It is the caller's responsibility
|
||||||
|
// to remove the file when no longer needed.
|
||||||
|
func TempFileSequential(dir, prefix string) (f *os.File, err error) {
|
||||||
|
return ioutil.TempFile(dir, prefix)
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
winio "github.com/Microsoft/go-winio"
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
@ -234,3 +237,55 @@ func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle,
|
||||||
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
||||||
return h, e
|
return h, e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helpers for TempFileSequential
|
||||||
|
var rand uint32
|
||||||
|
var randmu sync.Mutex
|
||||||
|
|
||||||
|
func reseed() uint32 {
|
||||||
|
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
|
||||||
|
}
|
||||||
|
func nextSuffix() 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:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempFileSequential is a copy of ioutil.TempFile, modified to use sequential
|
||||||
|
// file access. Below is the original comment from golang:
|
||||||
|
// TempFile creates a new temporary file in the directory dir
|
||||||
|
// with a name beginning with prefix, opens the file for reading
|
||||||
|
// and writing, and returns the resulting *os.File.
|
||||||
|
// If dir is the empty string, TempFile uses the default directory
|
||||||
|
// for temporary files (see os.TempDir).
|
||||||
|
// Multiple programs calling TempFile simultaneously
|
||||||
|
// will not choose the same file. The caller can use f.Name()
|
||||||
|
// to find the pathname of the file. It is the caller's responsibility
|
||||||
|
// to remove the file when no longer needed.
|
||||||
|
func TempFileSequential(dir, prefix string) (f *os.File, err error) {
|
||||||
|
if dir == "" {
|
||||||
|
dir = os.TempDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
nconflict := 0
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
name := filepath.Join(dir, prefix+nextSuffix())
|
||||||
|
f, err = OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
if nconflict++; nconflict > 10 {
|
||||||
|
randmu.Lock()
|
||||||
|
rand = reseed()
|
||||||
|
randmu.Unlock()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue