Windows: Archive package changes for Windows daemon
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
parent
24fd826fc0
commit
f883e81d79
11 changed files with 166 additions and 56 deletions
|
@ -12,8 +12,8 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
@ -291,17 +291,8 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
|
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
|
||||||
mode := uint32(hdr.Mode & 07777)
|
// Handle this is an OS-specific way
|
||||||
switch hdr.Typeflag {
|
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
|
||||||
case tar.TypeBlock:
|
|
||||||
mode |= syscall.S_IFBLK
|
|
||||||
case tar.TypeChar:
|
|
||||||
mode |= syscall.S_IFCHR
|
|
||||||
case tar.TypeFifo:
|
|
||||||
mode |= syscall.S_IFIFO
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,8 +328,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
||||||
return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
|
return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil && Lchown {
|
// Lchown is not supported on Windows
|
||||||
return err
|
if runtime.GOOS != "windows" {
|
||||||
|
if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil && Lchown {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range hdr.Xattrs {
|
for key, value := range hdr.Xattrs {
|
||||||
|
@ -349,20 +343,12 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
||||||
|
|
||||||
// There is no LChmod, so ignore mode for symlink. Also, this
|
// There is no LChmod, so ignore mode for symlink. Also, this
|
||||||
// must happen after chown, as that can modify the file mode
|
// must happen after chown, as that can modify the file mode
|
||||||
if hdr.Typeflag == tar.TypeLink {
|
if err := handleLChmod(hdr, path, hdrInfo); err != nil {
|
||||||
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
return err
|
||||||
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if hdr.Typeflag != tar.TypeSymlink {
|
|
||||||
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
||||||
// syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and
|
// syscall.UtimesNano doesn't support a NOFOLLOW flag atm
|
||||||
if hdr.Typeflag == tar.TypeLink {
|
if hdr.Typeflag == tar.TypeLink {
|
||||||
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
||||||
if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
|
if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
|
||||||
|
@ -531,7 +517,7 @@ loop:
|
||||||
parent := filepath.Dir(hdr.Name)
|
parent := filepath.Dir(hdr.Name)
|
||||||
parentPath := filepath.Join(dest, parent)
|
parentPath := filepath.Join(dest, parent)
|
||||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(parentPath, 0777)
|
err = system.MkdirAll(parentPath, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -651,7 +637,7 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
|
||||||
}
|
}
|
||||||
// Create dst, copy src's content into it
|
// Create dst, copy src's content into it
|
||||||
logrus.Debugf("Creating dest directory: %s", dst)
|
logrus.Debugf("Creating dest directory: %s", dst)
|
||||||
if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
if err := system.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
||||||
|
@ -675,12 +661,12 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||||
if srcSt.IsDir() {
|
if srcSt.IsDir() {
|
||||||
return fmt.Errorf("Can't copy a directory")
|
return fmt.Errorf("Can't copy a directory")
|
||||||
}
|
}
|
||||||
// Clean up the trailing /
|
// Clean up the trailing slash
|
||||||
if dst[len(dst)-1] == '/' {
|
if dst[len(dst)-1] == os.PathSeparator {
|
||||||
dst = path.Join(dst, filepath.Base(src))
|
dst = filepath.Join(dst, filepath.Base(src))
|
||||||
}
|
}
|
||||||
// Create the holding directory if necessary
|
// Create the holding directory if necessary
|
||||||
if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// canonicalTarNameForPath returns platform-specific filepath
|
// canonicalTarNameForPath returns platform-specific filepath
|
||||||
|
@ -51,3 +53,37 @@ func major(device uint64) uint64 {
|
||||||
func minor(device uint64) uint64 {
|
func minor(device uint64) uint64 {
|
||||||
return (device & 0xff) | ((device >> 12) & 0xfff00)
|
return (device & 0xff) | ((device >> 12) & 0xfff00)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
||||||
|
// createTarFile to handle the following types of header: Block; Char; Fifo
|
||||||
|
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||||
|
mode := uint32(hdr.Mode & 07777)
|
||||||
|
switch hdr.Typeflag {
|
||||||
|
case tar.TypeBlock:
|
||||||
|
mode |= syscall.S_IFBLK
|
||||||
|
case tar.TypeChar:
|
||||||
|
mode |= syscall.S_IFCHR
|
||||||
|
case tar.TypeFifo:
|
||||||
|
mode |= syscall.S_IFIFO
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||||
|
if hdr.Typeflag == tar.TypeLink {
|
||||||
|
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
||||||
|
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if hdr.Typeflag != tar.TypeSymlink {
|
||||||
|
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ import (
|
||||||
// path.
|
// path.
|
||||||
func CanonicalTarNameForPath(p string) (string, error) {
|
func CanonicalTarNameForPath(p string) (string, error) {
|
||||||
// windows: convert windows style relative path with backslashes
|
// windows: convert windows style relative path with backslashes
|
||||||
// into forward slashes. since windows does not allow '/' or '\'
|
// into forward slashes. Since windows does not allow '/' or '\'
|
||||||
// in file names, it is mostly safe to replace however we must
|
// in file names, it is mostly safe to replace however we must
|
||||||
// check just in case
|
// check just in case
|
||||||
if strings.Contains(p, "/") {
|
if strings.Contains(p, "/") {
|
||||||
return "", fmt.Errorf("windows path contains forward slash: %s", p)
|
return "", fmt.Errorf("Windows path contains forward slash: %s", p)
|
||||||
}
|
}
|
||||||
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil
|
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil
|
||||||
|
|
||||||
|
@ -38,3 +38,13 @@ func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, st
|
||||||
// do nothing. no notion of Rdev, Inode, Nlink in stat on Windows
|
// do nothing. no notion of Rdev, Inode, Nlink in stat on Windows
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
||||||
|
// createTarFile to handle the following types of header: Block; Char; Fifo
|
||||||
|
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -174,10 +174,6 @@ func (info *FileInfo) path() string {
|
||||||
return filepath.Join(info.parent.path(), info.name)
|
return filepath.Join(info.parent.path(), info.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *FileInfo) isDir() bool {
|
|
||||||
return info.parent == nil || info.stat.Mode()&syscall.S_IFDIR != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
||||||
|
|
||||||
sizeAtEntry := len(*changes)
|
sizeAtEntry := len(*changes)
|
||||||
|
@ -214,13 +210,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
||||||
// be visible when actually comparing the stat fields. The only time this
|
// be visible when actually comparing the stat fields. The only time this
|
||||||
// breaks down is if some code intentionally hides a change by setting
|
// breaks down is if some code intentionally hides a change by setting
|
||||||
// back mtime
|
// back mtime
|
||||||
if oldStat.Mode() != newStat.Mode() ||
|
if statDifferent(oldStat, newStat) ||
|
||||||
oldStat.Uid() != newStat.Uid() ||
|
|
||||||
oldStat.Gid() != newStat.Gid() ||
|
|
||||||
oldStat.Rdev() != newStat.Rdev() ||
|
|
||||||
// Don't look at size for dirs, its not a good measure of change
|
|
||||||
(oldStat.Mode()&syscall.S_IFDIR != syscall.S_IFDIR &&
|
|
||||||
(!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) ||
|
|
||||||
bytes.Compare(oldChild.capability, newChild.capability) != 0 {
|
bytes.Compare(oldChild.capability, newChild.capability) != 0 {
|
||||||
change := Change{
|
change := Change{
|
||||||
Path: newChild.path(),
|
Path: newChild.path(),
|
||||||
|
|
27
archive/changes_unix.go
Normal file
27
archive/changes_unix.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func statDifferent(oldStat *system.Stat_t, newStat *system.Stat_t) bool {
|
||||||
|
// Don't look at size for dirs, its not a good measure of change
|
||||||
|
if oldStat.Mode() != newStat.Mode() ||
|
||||||
|
oldStat.Uid() != newStat.Uid() ||
|
||||||
|
oldStat.Gid() != newStat.Gid() ||
|
||||||
|
oldStat.Rdev() != newStat.Rdev() ||
|
||||||
|
// Don't look at size for dirs, its not a good measure of change
|
||||||
|
(oldStat.Mode()&syscall.S_IFDIR != syscall.S_IFDIR &&
|
||||||
|
(!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *FileInfo) isDir() bool {
|
||||||
|
return info.parent == nil || info.stat.Mode()&syscall.S_IFDIR != 0
|
||||||
|
}
|
20
archive/changes_windows.go
Normal file
20
archive/changes_windows.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func statDifferent(oldStat *system.Stat_t, newStat *system.Stat_t) bool {
|
||||||
|
|
||||||
|
// Don't look at size for dirs, its not a good measure of change
|
||||||
|
if oldStat.ModTime() != newStat.ModTime() ||
|
||||||
|
oldStat.Mode() != newStat.Mode() ||
|
||||||
|
oldStat.Size() != newStat.Size() && !oldStat.IsDir() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *FileInfo) isDir() bool {
|
||||||
|
return info.parent == nil || info.stat.IsDir()
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) {
|
||||||
parent := filepath.Dir(hdr.Name)
|
parent := filepath.Dir(hdr.Name)
|
||||||
parentPath := filepath.Join(dest, parent)
|
parentPath := filepath.Join(dest, parent)
|
||||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(parentPath, 0600)
|
err = system.MkdirAll(parentPath, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,28 @@
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Some explanation for my own sanity, and hopefully maintainers in the
|
||||||
|
// future.
|
||||||
|
//
|
||||||
|
// Lstat calls os.Lstat to get a fileinfo interface back.
|
||||||
|
// This is then copied into our own locally defined structure.
|
||||||
|
// Note the Linux version uses fromStatT to do the copy back,
|
||||||
|
// but that not strictly necessary when already in an OS specific module.
|
||||||
|
|
||||||
func Lstat(path string) (*Stat_t, error) {
|
func Lstat(path string) (*Stat_t, error) {
|
||||||
// should not be called on cli code path
|
fi, err := os.Lstat(path)
|
||||||
return nil, ErrNotSupportedPlatform
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Stat_t{
|
||||||
|
name: fi.Name(),
|
||||||
|
size: fi.Size(),
|
||||||
|
mode: fi.Mode(),
|
||||||
|
modTime: fi.ModTime(),
|
||||||
|
isDir: fi.IsDir()}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
func Mknod(path string, mode uint32, dev int) error {
|
func Mknod(path string, mode uint32, dev int) error {
|
||||||
// should not be called on cli code path
|
|
||||||
return ErrNotSupportedPlatform
|
return ErrNotSupportedPlatform
|
||||||
}
|
}
|
||||||
|
|
||||||
func Mkdev(major int64, minor int64) uint32 {
|
func Mkdev(major int64, minor int64) uint32 {
|
||||||
panic("Mkdev not implemented on windows, should not be called on cli code")
|
panic("Mkdev not implemented on Windows.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -3,15 +3,34 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"os"
|
||||||
"syscall"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fromStatT(s *syscall.Win32FileAttributeData) (*Stat_t, error) {
|
type Stat_t struct {
|
||||||
return nil, errors.New("fromStatT should not be called on windows path")
|
name string
|
||||||
|
size int64
|
||||||
|
mode os.FileMode
|
||||||
|
modTime time.Time
|
||||||
|
isDir bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Stat(path string) (*Stat_t, error) {
|
func (s Stat_t) Name() string {
|
||||||
// should not be called on cli code path
|
return s.name
|
||||||
return nil, ErrNotSupportedPlatform
|
}
|
||||||
|
|
||||||
|
func (s Stat_t) Size() int64 {
|
||||||
|
return s.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Stat_t) Mode() os.FileMode {
|
||||||
|
return s.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Stat_t) ModTime() time.Time {
|
||||||
|
return s.modTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Stat_t) IsDir() bool {
|
||||||
|
return s.isDir
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue