Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -3,16 +3,19 @@ package command |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io" |
| 6 |
- "io/ioutil" |
|
| 7 | 6 |
"os" |
| 8 | 7 |
"path/filepath" |
| 9 | 8 |
"runtime" |
| 10 | 9 |
"strings" |
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/pkg/system" |
|
| 11 | 12 |
) |
| 12 | 13 |
|
| 13 | 14 |
// CopyToFile writes the content of the reader to the specified file |
| 14 | 15 |
func CopyToFile(outfile string, r io.Reader) error {
|
| 15 |
- tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_") |
|
| 16 |
+ // We use sequential file access here to avoid depleting the standby list |
|
| 17 |
+ // on Windows. On Linux, this is a call directly to ioutil.TempFile |
|
| 18 |
+ tmpFile, err := system.TempFileSequential(filepath.Dir(outfile), ".docker_temp_") |
|
| 16 | 19 |
if err != nil {
|
| 17 | 20 |
return err |
| 18 | 21 |
} |
| ... | ... |
@@ -825,14 +825,16 @@ func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser |
| 825 | 825 |
var f *os.File |
| 826 | 826 |
// Open the file while holding the Windows backup privilege. This ensures that the |
| 827 | 827 |
// file can be opened even if the caller does not actually have access to it according |
| 828 |
- // to the security descriptor. |
|
| 828 |
+ // to the security descriptor. Also use sequential file access to avoid depleting the |
|
| 829 |
+ // standby list - Microsoft VSO Bug Tracker #9900466 |
|
| 829 | 830 |
err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
|
| 830 | 831 |
path := longpath.AddPrefix(filepath.Join(fg.path, filename)) |
| 831 | 832 |
p, err := syscall.UTF16FromString(path) |
| 832 | 833 |
if err != nil {
|
| 833 | 834 |
return err |
| 834 | 835 |
} |
| 835 |
- h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) |
|
| 836 |
+ const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN |
|
| 837 |
+ h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS|fileFlagSequentialScan, 0) |
|
| 836 | 838 |
if err != nil {
|
| 837 | 839 |
return &os.PathError{Op: "open", Path: path, Err: err}
|
| 838 | 840 |
} |
| ... | ... |
@@ -3,6 +3,7 @@ |
| 3 | 3 |
package system |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "io/ioutil" |
|
| 6 | 7 |
"os" |
| 7 | 8 |
"path/filepath" |
| 8 | 9 |
) |
| ... | ... |
@@ -24,7 +25,7 @@ func IsAbs(path string) bool {
|
| 24 | 24 |
return filepath.IsAbs(path) |
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 |
-// The functions below here are wrappers for the equivalents in the os package. |
|
| 27 |
+// The functions below here are wrappers for the equivalents in the os and ioutils packages. |
|
| 28 | 28 |
// They are passthrough on Unix platforms, and only relevant on Windows. |
| 29 | 29 |
|
| 30 | 30 |
// CreateSequential creates the named file with mode 0666 (before umask), truncating |
| ... | ... |
@@ -52,3 +53,16 @@ func OpenSequential(name string) (*os.File, error) {
|
| 52 | 52 |
func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
|
| 53 | 53 |
return os.OpenFile(name, flag, perm) |
| 54 | 54 |
} |
| 55 |
+ |
|
| 56 |
+// TempFileSequential creates a new temporary file in the directory dir |
|
| 57 |
+// with a name beginning with prefix, opens the file for reading |
|
| 58 |
+// and writing, and returns the resulting *os.File. |
|
| 59 |
+// If dir is the empty string, TempFile uses the default directory |
|
| 60 |
+// for temporary files (see os.TempDir). |
|
| 61 |
+// Multiple programs calling TempFile simultaneously |
|
| 62 |
+// will not choose the same file. The caller can use f.Name() |
|
| 63 |
+// to find the pathname of the file. It is the caller's responsibility |
|
| 64 |
+// to remove the file when no longer needed. |
|
| 65 |
+func TempFileSequential(dir, prefix string) (f *os.File, err error) {
|
|
| 66 |
+ return ioutil.TempFile(dir, prefix) |
|
| 67 |
+} |
| ... | ... |
@@ -6,8 +6,11 @@ import ( |
| 6 | 6 |
"os" |
| 7 | 7 |
"path/filepath" |
| 8 | 8 |
"regexp" |
| 9 |
+ "strconv" |
|
| 9 | 10 |
"strings" |
| 11 |
+ "sync" |
|
| 10 | 12 |
"syscall" |
| 13 |
+ "time" |
|
| 11 | 14 |
"unsafe" |
| 12 | 15 |
|
| 13 | 16 |
winio "github.com/Microsoft/go-winio" |
| ... | ... |
@@ -234,3 +237,55 @@ func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, |
| 234 | 234 |
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) |
| 235 | 235 |
return h, e |
| 236 | 236 |
} |
| 237 |
+ |
|
| 238 |
+// Helpers for TempFileSequential |
|
| 239 |
+var rand uint32 |
|
| 240 |
+var randmu sync.Mutex |
|
| 241 |
+ |
|
| 242 |
+func reseed() uint32 {
|
|
| 243 |
+ return uint32(time.Now().UnixNano() + int64(os.Getpid())) |
|
| 244 |
+} |
|
| 245 |
+func nextSuffix() string {
|
|
| 246 |
+ randmu.Lock() |
|
| 247 |
+ r := rand |
|
| 248 |
+ if r == 0 {
|
|
| 249 |
+ r = reseed() |
|
| 250 |
+ } |
|
| 251 |
+ r = r*1664525 + 1013904223 // constants from Numerical Recipes |
|
| 252 |
+ rand = r |
|
| 253 |
+ randmu.Unlock() |
|
| 254 |
+ return strconv.Itoa(int(1e9 + r%1e9))[1:] |
|
| 255 |
+} |
|
| 256 |
+ |
|
| 257 |
+// TempFileSequential is a copy of ioutil.TempFile, modified to use sequential |
|
| 258 |
+// file access. Below is the original comment from golang: |
|
| 259 |
+// TempFile creates a new temporary file in the directory dir |
|
| 260 |
+// with a name beginning with prefix, opens the file for reading |
|
| 261 |
+// and writing, and returns the resulting *os.File. |
|
| 262 |
+// If dir is the empty string, TempFile uses the default directory |
|
| 263 |
+// for temporary files (see os.TempDir). |
|
| 264 |
+// Multiple programs calling TempFile simultaneously |
|
| 265 |
+// will not choose the same file. The caller can use f.Name() |
|
| 266 |
+// to find the pathname of the file. It is the caller's responsibility |
|
| 267 |
+// to remove the file when no longer needed. |
|
| 268 |
+func TempFileSequential(dir, prefix string) (f *os.File, err error) {
|
|
| 269 |
+ if dir == "" {
|
|
| 270 |
+ dir = os.TempDir() |
|
| 271 |
+ } |
|
| 272 |
+ |
|
| 273 |
+ nconflict := 0 |
|
| 274 |
+ for i := 0; i < 10000; i++ {
|
|
| 275 |
+ name := filepath.Join(dir, prefix+nextSuffix()) |
|
| 276 |
+ f, err = OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) |
|
| 277 |
+ if os.IsExist(err) {
|
|
| 278 |
+ if nconflict++; nconflict > 10 {
|
|
| 279 |
+ randmu.Lock() |
|
| 280 |
+ rand = reseed() |
|
| 281 |
+ randmu.Unlock() |
|
| 282 |
+ } |
|
| 283 |
+ continue |
|
| 284 |
+ } |
|
| 285 |
+ break |
|
| 286 |
+ } |
|
| 287 |
+ return |
|
| 288 |
+} |