Merge pull request #20130 from Microsoft/sjw/windows_save_fix
Fixing 'docker save' on Windows.
This commit is contained in:
commit
01a1925792
7 changed files with 57 additions and 73 deletions
|
@ -6,7 +6,6 @@ import (
|
|||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"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 {
|
||||
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())
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
err = copySysFiles(filepath.Join(d.info.HomeDir, "sysfile-backups", id), tempFolder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return tempFolder, func() error {
|
||||
// TODO: activate layers and release here?
|
||||
_, folderName := filepath.Split(tempFolder)
|
||||
return hcsshim.DestroyLayer(d.info, folderName)
|
||||
}, 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/layer"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/reference"
|
||||
)
|
||||
|
||||
|
@ -160,7 +161,7 @@ func (s *saveSession) save(outStream io.Writer) error {
|
|||
if err := f.Close(); err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +178,7 @@ func (s *saveSession) save(outStream io.Writer) error {
|
|||
if err := f.Close(); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -233,7 +234,7 @@ func (s *saveSession) saveImage(id image.ID) error {
|
|||
if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -290,7 +291,7 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
|
|||
|
||||
for _, fname := range []string{"", legacyVersionFileName, legacyConfigFileName, legacyLayerFileName} {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
func max(x, y int) int {
|
||||
|
@ -87,7 +89,7 @@ func createSampleDir(t *testing.T, root string) {
|
|||
|
||||
if info.filetype != Symlink {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +291,7 @@ func mutateSampleDir(t *testing.T, root string) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -333,7 +335,7 @@ func mutateSampleDir(t *testing.T, root string) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,5 +43,10 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Take platform specific action for setting create time.
|
||||
if err := setCTime(name, mtime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
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