mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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>
This commit is contained in:
parent
ac9d1b7b47
commit
041a9510c6
7 changed files with 57 additions and 73 deletions
|
@ -6,7 +6,6 @@ import (
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -501,10 +500,6 @@ func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPat
|
||||||
if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
|
if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = copySysFiles(tempFolder, filepath.Join(d.info.HomeDir, "sysfile-backups", id))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||||
|
|
||||||
if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
|
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
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = copySysFiles(filepath.Join(d.info.HomeDir, "sysfile-backups", id), tempFolder)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempFolder, func() error {
|
return tempFolder, func() error {
|
||||||
// TODO: activate layers and release here?
|
// TODO: activate layers and release here?
|
||||||
_, folderName := filepath.Split(tempFolder)
|
_, folderName := filepath.Split(tempFolder)
|
||||||
return hcsshim.DestroyLayer(d.info, folderName)
|
return hcsshim.DestroyLayer(d.info, folderName)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var sysFileWhiteList = []string{
|
|
||||||
"Hives\\*",
|
|
||||||
"Files\\BOOTNXT",
|
|
||||||
"tombstones.txt",
|
|
||||||
}
|
|
||||||
|
|
||||||
// note this only handles files
|
|
||||||
func copySysFiles(src string, dest string) error {
|
|
||||||
if err := os.MkdirAll(dest, 0700); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
|
||||||
rel, err := filepath.Rel(src, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, sysfile := range sysFileWhiteList {
|
|
||||||
if matches, err := filepath.Match(sysfile, rel); err != nil || !matches {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := os.Lstat(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !fi.Mode().IsRegular() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
targetPath := filepath.Join(dest, rel)
|
|
||||||
if err = os.MkdirAll(filepath.Dir(targetPath), 0700); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
in, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out, err := os.Create(targetPath)
|
|
||||||
if err != nil {
|
|
||||||
in.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(out, in)
|
|
||||||
in.Close()
|
|
||||||
out.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/docker/docker/image/v1"
|
"github.com/docker/docker/image/v1"
|
||||||
"github.com/docker/docker/layer"
|
"github.com/docker/docker/layer"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -160,7 +161,7 @@ func (s *saveSession) save(outStream io.Writer) error {
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := os.Chtimes(reposFile, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
|
if err := system.Chtimes(reposFile, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +178,7 @@ func (s *saveSession) save(outStream io.Writer) error {
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := os.Chtimes(manifestFileName, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
|
if err := system.Chtimes(manifestFileName, time.Unix(0, 0), time.Unix(0, 0)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +234,7 @@ func (s *saveSession) saveImage(id image.ID) error {
|
||||||
if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
|
if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := os.Chtimes(configFile, img.Created, img.Created); err != nil {
|
if err := system.Chtimes(configFile, img.Created, img.Created); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +291,7 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
|
||||||
|
|
||||||
for _, fname := range []string{"", legacyVersionFileName, legacyConfigFileName, legacyLayerFileName} {
|
for _, fname := range []string{"", legacyVersionFileName, legacyConfigFileName, legacyLayerFileName} {
|
||||||
// todo: maybe save layer created timestamp?
|
// todo: maybe save layer created timestamp?
|
||||||
if err := os.Chtimes(filepath.Join(outDir, fname), createdTime, createdTime); err != nil {
|
if err := system.Chtimes(filepath.Join(outDir, fname), createdTime, createdTime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func max(x, y int) int {
|
func max(x, y int) int {
|
||||||
|
@ -87,7 +89,7 @@ func createSampleDir(t *testing.T, root string) {
|
||||||
|
|
||||||
if info.filetype != Symlink {
|
if info.filetype != Symlink {
|
||||||
// Set a consistent ctime, atime for all files and dirs
|
// Set a consistent ctime, atime for all files and dirs
|
||||||
if err := os.Chtimes(p, now, now); err != nil {
|
if err := system.Chtimes(p, now, now); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +291,7 @@ func mutateSampleDir(t *testing.T, root string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Touch file
|
// Touch file
|
||||||
if err := os.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
|
if err := system.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +335,7 @@ func mutateSampleDir(t *testing.T, root string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Touch dir
|
// Touch dir
|
||||||
if err := os.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
|
if err := system.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,5 +43,10 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Take platform specific action for setting create time.
|
||||||
|
if err := setCTime(name, mtime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
14
pkg/system/chtimes_unix.go
Normal file
14
pkg/system/chtimes_unix.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//setCTime will set the create time on a file. On Unix, the create
|
||||||
|
//time is updated as a side effect of setting the modified time, so
|
||||||
|
//no action is required.
|
||||||
|
func setCTime(path string, ctime time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux freebsd
|
// +build !windows
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
|
|
27
pkg/system/chtimes_windows.go
Normal file
27
pkg/system/chtimes_windows.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//setCTime will set the create time on a file. On Windows, this requires
|
||||||
|
//calling SetFileTime and explicitly including the create time.
|
||||||
|
func setCTime(path string, ctime time.Time) error {
|
||||||
|
ctimespec := syscall.NsecToTimespec(ctime.UnixNano())
|
||||||
|
pathp, e := syscall.UTF16PtrFromString(path)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
h, e := syscall.CreateFile(pathp,
|
||||||
|
syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil,
|
||||||
|
syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
defer syscall.Close(h)
|
||||||
|
c := syscall.NsecToFiletime(syscall.TimespecToNsec(ctimespec))
|
||||||
|
return syscall.SetFileTime(h, &c, nil, nil)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue