Browse code

Fixing 'docker save' on Windows.

Save was failing file integrity checksums due to bugs in both
Windows and Docker. This commit includes fixes to file time handling
in tarexport and system.chtimes that are necessary along with
the Windows platform fixes to correctly support save. With this
change, sysfile_backups for windowsfilter driver are no longer
needed, so that code is removed.

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>

Stefan J. Wernli authored on 2016/02/09 08:40:12
Showing 7 changed files
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"crypto/sha512"
7 7
 	"encoding/json"
8 8
 	"fmt"
9
-	"io"
10 9
 	"io/ioutil"
11 10
 	"os"
12 11
 	"path/filepath"
... ...
@@ -501,10 +500,6 @@ func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPat
501 501
 	if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
502 502
 		return
503 503
 	}
504
-	err = copySysFiles(tempFolder, filepath.Join(d.info.HomeDir, "sysfile-backups", id))
505
-	if err != nil {
506
-		return
507
-	}
508 504
 	logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
509 505
 
510 506
 	if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
... ...
@@ -602,69 +597,9 @@ func (d *Driver) DiffPath(id string) (path string, release func() error, err err
602 602
 		return
603 603
 	}
604 604
 
605
-	err = copySysFiles(filepath.Join(d.info.HomeDir, "sysfile-backups", id), tempFolder)
606
-	if err != nil {
607
-		return
608
-	}
609
-
610 605
 	return tempFolder, func() error {
611 606
 		// TODO: activate layers and release here?
612 607
 		_, folderName := filepath.Split(tempFolder)
613 608
 		return hcsshim.DestroyLayer(d.info, folderName)
614 609
 	}, nil
615 610
 }
616
-
617
-var sysFileWhiteList = []string{
618
-	"Hives\\*",
619
-	"Files\\BOOTNXT",
620
-	"tombstones.txt",
621
-}
622
-
623
-// note this only handles files
624
-func copySysFiles(src string, dest string) error {
625
-	if err := os.MkdirAll(dest, 0700); err != nil {
626
-		return err
627
-	}
628
-	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
629
-		rel, err := filepath.Rel(src, path)
630
-		if err != nil {
631
-			return err
632
-		}
633
-		for _, sysfile := range sysFileWhiteList {
634
-			if matches, err := filepath.Match(sysfile, rel); err != nil || !matches {
635
-				continue
636
-			}
637
-
638
-			fi, err := os.Lstat(path)
639
-			if err != nil {
640
-				return err
641
-			}
642
-
643
-			if !fi.Mode().IsRegular() {
644
-				continue
645
-			}
646
-
647
-			targetPath := filepath.Join(dest, rel)
648
-			if err = os.MkdirAll(filepath.Dir(targetPath), 0700); err != nil {
649
-				return err
650
-			}
651
-
652
-			in, err := os.Open(path)
653
-			if err != nil {
654
-				return err
655
-			}
656
-			out, err := os.Create(targetPath)
657
-			if err != nil {
658
-				in.Close()
659
-				return err
660
-			}
661
-			_, err = io.Copy(out, in)
662
-			in.Close()
663
-			out.Close()
664
-			if err != nil {
665
-				return err
666
-			}
667
-		}
668
-		return nil
669
-	})
670
-}
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"github.com/docker/docker/image/v1"
15 15
 	"github.com/docker/docker/layer"
16 16
 	"github.com/docker/docker/pkg/archive"
17
+	"github.com/docker/docker/pkg/system"
17 18
 	"github.com/docker/docker/reference"
18 19
 )
19 20
 
... ...
@@ -160,7 +161,7 @@ func (s *saveSession) save(outStream io.Writer) error {
160 160
 		if err := f.Close(); err != nil {
161 161
 			return err
162 162
 		}
163
-		if err := os.Chtimes(reposFile, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
163
+		if err := system.Chtimes(reposFile, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
164 164
 			return err
165 165
 		}
166 166
 	}
... ...
@@ -177,7 +178,7 @@ func (s *saveSession) save(outStream io.Writer) error {
177 177
 	if err := f.Close(); err != nil {
178 178
 		return err
179 179
 	}
180
-	if err := os.Chtimes(manifestFileName, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
180
+	if err := system.Chtimes(manifestFileName, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
181 181
 		return err
182 182
 	}
183 183
 
... ...
@@ -233,7 +234,7 @@ func (s *saveSession) saveImage(id image.ID) error {
233 233
 	if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
234 234
 		return err
235 235
 	}
236
-	if err := os.Chtimes(configFile, img.Created, img.Created); err != nil {
236
+	if err := system.Chtimes(configFile, img.Created, img.Created); err != nil {
237 237
 		return err
238 238
 	}
239 239
 
... ...
@@ -290,7 +291,7 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
290 290
 
291 291
 	for _, fname := range []string{"", legacyVersionFileName, legacyConfigFileName, legacyLayerFileName} {
292 292
 		// todo: maybe save layer created timestamp?
293
-		if err := os.Chtimes(filepath.Join(outDir, fname), createdTime, createdTime); err != nil {
293
+		if err := system.Chtimes(filepath.Join(outDir, fname), createdTime, createdTime); err != nil {
294 294
 			return err
295 295
 		}
296 296
 	}
... ...
@@ -8,6 +8,8 @@ import (
8 8
 	"sort"
9 9
 	"testing"
10 10
 	"time"
11
+
12
+	"github.com/docker/docker/pkg/system"
11 13
 )
12 14
 
13 15
 func max(x, y int) int {
... ...
@@ -87,7 +89,7 @@ func createSampleDir(t *testing.T, root string) {
87 87
 
88 88
 		if info.filetype != Symlink {
89 89
 			// Set a consistent ctime, atime for all files and dirs
90
-			if err := os.Chtimes(p, now, now); err != nil {
90
+			if err := system.Chtimes(p, now, now); err != nil {
91 91
 				t.Fatal(err)
92 92
 			}
93 93
 		}
... ...
@@ -289,7 +291,7 @@ func mutateSampleDir(t *testing.T, root string) {
289 289
 	}
290 290
 
291 291
 	// Touch file
292
-	if err := os.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
292
+	if err := system.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
293 293
 		t.Fatal(err)
294 294
 	}
295 295
 
... ...
@@ -333,7 +335,7 @@ func mutateSampleDir(t *testing.T, root string) {
333 333
 	}
334 334
 
335 335
 	// Touch dir
336
-	if err := os.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
336
+	if err := system.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
337 337
 		t.Fatal(err)
338 338
 	}
339 339
 }
... ...
@@ -43,5 +43,10 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
43 43
 		return err
44 44
 	}
45 45
 
46
+	// Take platform specific action for setting create time.
47
+	if err := setCTime(name, mtime); err != nil {
48
+		return err
49
+	}
50
+
46 51
 	return nil
47 52
 }
48 53
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+// +build !windows
1
+
2
+package system
3
+
4
+import (
5
+	"time"
6
+)
7
+
8
+//setCTime will set the create time on a file. On Unix, the create
9
+//time is updated as a side effect of setting the modified time, so
10
+//no action is required.
11
+func setCTime(path string, ctime time.Time) error {
12
+	return nil
13
+}
... ...
@@ -1,4 +1,4 @@
1
-// +build linux freebsd
1
+// +build !windows
2 2
 
3 3
 package system
4 4
 
5 5
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+// +build windows
1
+
2
+package system
3
+
4
+import (
5
+	"syscall"
6
+	"time"
7
+)
8
+
9
+//setCTime will set the create time on a file. On Windows, this requires
10
+//calling SetFileTime and explicitly including the create time.
11
+func setCTime(path string, ctime time.Time) error {
12
+	ctimespec := syscall.NsecToTimespec(ctime.UnixNano())
13
+	pathp, e := syscall.UTF16PtrFromString(path)
14
+	if e != nil {
15
+		return e
16
+	}
17
+	h, e := syscall.CreateFile(pathp,
18
+		syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil,
19
+		syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
20
+	if e != nil {
21
+		return e
22
+	}
23
+	defer syscall.Close(h)
24
+	c := syscall.NsecToFiletime(syscall.TimespecToNsec(ctimespec))
25
+	return syscall.SetFileTime(h, &c, nil, nil)
26
+}