Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -12,8 +12,8 @@ import ( |
| 12 | 12 |
"io/ioutil" |
| 13 | 13 |
"os" |
| 14 | 14 |
"os/exec" |
| 15 |
- "path" |
|
| 16 | 15 |
"path/filepath" |
| 16 |
+ "runtime" |
|
| 17 | 17 |
"strings" |
| 18 | 18 |
"syscall" |
| 19 | 19 |
|
| ... | ... |
@@ -291,17 +291,8 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L |
| 291 | 291 |
file.Close() |
| 292 | 292 |
|
| 293 | 293 |
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: |
| 294 |
- mode := uint32(hdr.Mode & 07777) |
|
| 295 |
- switch hdr.Typeflag {
|
|
| 296 |
- case tar.TypeBlock: |
|
| 297 |
- mode |= syscall.S_IFBLK |
|
| 298 |
- case tar.TypeChar: |
|
| 299 |
- mode |= syscall.S_IFCHR |
|
| 300 |
- case tar.TypeFifo: |
|
| 301 |
- mode |= syscall.S_IFIFO |
|
| 302 |
- } |
|
| 303 |
- |
|
| 304 |
- if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
|
| 294 |
+ // Handle this is an OS-specific way |
|
| 295 |
+ if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
|
|
| 305 | 296 |
return err |
| 306 | 297 |
} |
| 307 | 298 |
|
| ... | ... |
@@ -337,8 +328,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L |
| 337 | 337 |
return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
|
| 338 | 338 |
} |
| 339 | 339 |
|
| 340 |
- if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil && Lchown {
|
|
| 341 |
- return err |
|
| 340 |
+ // Lchown is not supported on Windows |
|
| 341 |
+ if runtime.GOOS != "windows" {
|
|
| 342 |
+ if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil && Lchown {
|
|
| 343 |
+ return err |
|
| 344 |
+ } |
|
| 342 | 345 |
} |
| 343 | 346 |
|
| 344 | 347 |
for key, value := range hdr.Xattrs {
|
| ... | ... |
@@ -349,20 +343,12 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L |
| 349 | 349 |
|
| 350 | 350 |
// There is no LChmod, so ignore mode for symlink. Also, this |
| 351 | 351 |
// must happen after chown, as that can modify the file mode |
| 352 |
- if hdr.Typeflag == tar.TypeLink {
|
|
| 353 |
- if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
|
| 354 |
- if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
|
| 355 |
- return err |
|
| 356 |
- } |
|
| 357 |
- } |
|
| 358 |
- } else if hdr.Typeflag != tar.TypeSymlink {
|
|
| 359 |
- if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
|
| 360 |
- return err |
|
| 361 |
- } |
|
| 352 |
+ if err := handleLChmod(hdr, path, hdrInfo); err != nil {
|
|
| 353 |
+ return err |
|
| 362 | 354 |
} |
| 363 | 355 |
|
| 364 | 356 |
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
| 365 |
- // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and |
|
| 357 |
+ // syscall.UtimesNano doesn't support a NOFOLLOW flag atm |
|
| 366 | 358 |
if hdr.Typeflag == tar.TypeLink {
|
| 367 | 359 |
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
| 368 | 360 |
if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
|
| ... | ... |
@@ -531,7 +517,7 @@ loop: |
| 531 | 531 |
parent := filepath.Dir(hdr.Name) |
| 532 | 532 |
parentPath := filepath.Join(dest, parent) |
| 533 | 533 |
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
| 534 |
- err = os.MkdirAll(parentPath, 0777) |
|
| 534 |
+ err = system.MkdirAll(parentPath, 0777) |
|
| 535 | 535 |
if err != nil {
|
| 536 | 536 |
return err |
| 537 | 537 |
} |
| ... | ... |
@@ -651,7 +637,7 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
|
| 651 | 651 |
} |
| 652 | 652 |
// Create dst, copy src's content into it |
| 653 | 653 |
logrus.Debugf("Creating dest directory: %s", dst)
|
| 654 |
- if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
|
| 654 |
+ if err := system.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
|
| 655 | 655 |
return err |
| 656 | 656 |
} |
| 657 | 657 |
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
| ... | ... |
@@ -675,12 +661,12 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
| 675 | 675 |
if srcSt.IsDir() {
|
| 676 | 676 |
return fmt.Errorf("Can't copy a directory")
|
| 677 | 677 |
} |
| 678 |
- // Clean up the trailing / |
|
| 679 |
- if dst[len(dst)-1] == '/' {
|
|
| 680 |
- dst = path.Join(dst, filepath.Base(src)) |
|
| 678 |
+ // Clean up the trailing slash |
|
| 679 |
+ if dst[len(dst)-1] == os.PathSeparator {
|
|
| 680 |
+ dst = filepath.Join(dst, filepath.Base(src)) |
|
| 681 | 681 |
} |
| 682 | 682 |
// Create the holding directory if necessary |
| 683 |
- if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
|
| 683 |
+ if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
|
| 684 | 684 |
return err |
| 685 | 685 |
} |
| 686 | 686 |
|
| ... | ... |
@@ -7,6 +7,8 @@ import ( |
| 7 | 7 |
"errors" |
| 8 | 8 |
"os" |
| 9 | 9 |
"syscall" |
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/pkg/system" |
|
| 10 | 12 |
) |
| 11 | 13 |
|
| 12 | 14 |
// canonicalTarNameForPath returns platform-specific filepath |
| ... | ... |
@@ -51,3 +53,37 @@ func major(device uint64) uint64 {
|
| 51 | 51 |
func minor(device uint64) uint64 {
|
| 52 | 52 |
return (device & 0xff) | ((device >> 12) & 0xfff00) |
| 53 | 53 |
} |
| 54 |
+ |
|
| 55 |
+// handleTarTypeBlockCharFifo is an OS-specific helper function used by |
|
| 56 |
+// createTarFile to handle the following types of header: Block; Char; Fifo |
|
| 57 |
+func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
|
| 58 |
+ mode := uint32(hdr.Mode & 07777) |
|
| 59 |
+ switch hdr.Typeflag {
|
|
| 60 |
+ case tar.TypeBlock: |
|
| 61 |
+ mode |= syscall.S_IFBLK |
|
| 62 |
+ case tar.TypeChar: |
|
| 63 |
+ mode |= syscall.S_IFCHR |
|
| 64 |
+ case tar.TypeFifo: |
|
| 65 |
+ mode |= syscall.S_IFIFO |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
|
| 69 |
+ return err |
|
| 70 |
+ } |
|
| 71 |
+ return nil |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
|
| 75 |
+ if hdr.Typeflag == tar.TypeLink {
|
|
| 76 |
+ if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
|
| 77 |
+ if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
|
| 78 |
+ return err |
|
| 79 |
+ } |
|
| 80 |
+ } |
|
| 81 |
+ } else if hdr.Typeflag != tar.TypeSymlink {
|
|
| 82 |
+ if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
|
| 83 |
+ return err |
|
| 84 |
+ } |
|
| 85 |
+ } |
|
| 86 |
+ return nil |
|
| 87 |
+} |
| ... | ... |
@@ -14,11 +14,11 @@ import ( |
| 14 | 14 |
// path. |
| 15 | 15 |
func CanonicalTarNameForPath(p string) (string, error) {
|
| 16 | 16 |
// windows: convert windows style relative path with backslashes |
| 17 |
- // into forward slashes. since windows does not allow '/' or '\' |
|
| 17 |
+ // into forward slashes. Since windows does not allow '/' or '\' |
|
| 18 | 18 |
// in file names, it is mostly safe to replace however we must |
| 19 | 19 |
// check just in case |
| 20 | 20 |
if strings.Contains(p, "/") {
|
| 21 |
- return "", fmt.Errorf("windows path contains forward slash: %s", p)
|
|
| 21 |
+ return "", fmt.Errorf("Windows path contains forward slash: %s", p)
|
|
| 22 | 22 |
} |
| 23 | 23 |
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil |
| 24 | 24 |
|
| ... | ... |
@@ -38,3 +38,13 @@ func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, st |
| 38 | 38 |
// do nothing. no notion of Rdev, Inode, Nlink in stat on Windows |
| 39 | 39 |
return |
| 40 | 40 |
} |
| 41 |
+ |
|
| 42 |
+// handleTarTypeBlockCharFifo is an OS-specific helper function used by |
|
| 43 |
+// createTarFile to handle the following types of header: Block; Char; Fifo |
|
| 44 |
+func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
|
| 45 |
+ return nil |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
|
| 49 |
+ return nil |
|
| 50 |
+} |
| ... | ... |
@@ -174,10 +174,6 @@ func (info *FileInfo) path() string {
|
| 174 | 174 |
return filepath.Join(info.parent.path(), info.name) |
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 |
-func (info *FileInfo) isDir() bool {
|
|
| 178 |
- return info.parent == nil || info.stat.Mode()&syscall.S_IFDIR != 0 |
|
| 179 |
-} |
|
| 180 |
- |
|
| 181 | 177 |
func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
| 182 | 178 |
|
| 183 | 179 |
sizeAtEntry := len(*changes) |
| ... | ... |
@@ -214,13 +210,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
| 214 | 214 |
// be visible when actually comparing the stat fields. The only time this |
| 215 | 215 |
// breaks down is if some code intentionally hides a change by setting |
| 216 | 216 |
// back mtime |
| 217 |
- if oldStat.Mode() != newStat.Mode() || |
|
| 218 |
- oldStat.Uid() != newStat.Uid() || |
|
| 219 |
- oldStat.Gid() != newStat.Gid() || |
|
| 220 |
- oldStat.Rdev() != newStat.Rdev() || |
|
| 221 |
- // Don't look at size for dirs, its not a good measure of change |
|
| 222 |
- (oldStat.Mode()&syscall.S_IFDIR != syscall.S_IFDIR && |
|
| 223 |
- (!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) || |
|
| 217 |
+ if statDifferent(oldStat, newStat) || |
|
| 224 | 218 |
bytes.Compare(oldChild.capability, newChild.capability) != 0 {
|
| 225 | 219 |
change := Change{
|
| 226 | 220 |
Path: newChild.path(), |
| 227 | 221 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+// +build !windows |
|
| 1 |
+ |
|
| 2 |
+package archive |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "syscall" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/pkg/system" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func statDifferent(oldStat *system.Stat_t, newStat *system.Stat_t) bool {
|
|
| 11 |
+ // Don't look at size for dirs, its not a good measure of change |
|
| 12 |
+ if oldStat.Mode() != newStat.Mode() || |
|
| 13 |
+ oldStat.Uid() != newStat.Uid() || |
|
| 14 |
+ oldStat.Gid() != newStat.Gid() || |
|
| 15 |
+ oldStat.Rdev() != newStat.Rdev() || |
|
| 16 |
+ // Don't look at size for dirs, its not a good measure of change |
|
| 17 |
+ (oldStat.Mode()&syscall.S_IFDIR != syscall.S_IFDIR && |
|
| 18 |
+ (!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) {
|
|
| 19 |
+ return true |
|
| 20 |
+ } |
|
| 21 |
+ return false |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func (info *FileInfo) isDir() bool {
|
|
| 25 |
+ return info.parent == nil || info.stat.Mode()&syscall.S_IFDIR != 0 |
|
| 26 |
+} |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+package archive |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/docker/docker/pkg/system" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func statDifferent(oldStat *system.Stat_t, newStat *system.Stat_t) bool {
|
|
| 7 |
+ |
|
| 8 |
+ // Don't look at size for dirs, its not a good measure of change |
|
| 9 |
+ if oldStat.ModTime() != newStat.ModTime() || |
|
| 10 |
+ oldStat.Mode() != newStat.Mode() || |
|
| 11 |
+ oldStat.Size() != newStat.Size() && !oldStat.IsDir() {
|
|
| 12 |
+ return true |
|
| 13 |
+ } |
|
| 14 |
+ return false |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func (info *FileInfo) isDir() bool {
|
|
| 18 |
+ return info.parent == nil || info.stat.IsDir() |
|
| 19 |
+} |
| ... | ... |
@@ -47,7 +47,7 @@ func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) {
|
| 47 | 47 |
parent := filepath.Dir(hdr.Name) |
| 48 | 48 |
parentPath := filepath.Join(dest, parent) |
| 49 | 49 |
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
| 50 |
- err = os.MkdirAll(parentPath, 0600) |
|
| 50 |
+ err = system.MkdirAll(parentPath, 0600) |
|
| 51 | 51 |
if err != nil {
|
| 52 | 52 |
return 0, err |
| 53 | 53 |
} |
| ... | ... |
@@ -2,7 +2,28 @@ |
| 2 | 2 |
|
| 3 | 3 |
package system |
| 4 | 4 |
|
| 5 |
+import ( |
|
| 6 |
+ "os" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// Some explanation for my own sanity, and hopefully maintainers in the |
|
| 10 |
+// future. |
|
| 11 |
+// |
|
| 12 |
+// Lstat calls os.Lstat to get a fileinfo interface back. |
|
| 13 |
+// This is then copied into our own locally defined structure. |
|
| 14 |
+// Note the Linux version uses fromStatT to do the copy back, |
|
| 15 |
+// but that not strictly necessary when already in an OS specific module. |
|
| 16 |
+ |
|
| 5 | 17 |
func Lstat(path string) (*Stat_t, error) {
|
| 6 |
- // should not be called on cli code path |
|
| 7 |
- return nil, ErrNotSupportedPlatform |
|
| 18 |
+ fi, err := os.Lstat(path) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return nil, err |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ return &Stat_t{
|
|
| 24 |
+ name: fi.Name(), |
|
| 25 |
+ size: fi.Size(), |
|
| 26 |
+ mode: fi.Mode(), |
|
| 27 |
+ modTime: fi.ModTime(), |
|
| 28 |
+ isDir: fi.IsDir()}, nil |
|
| 8 | 29 |
} |
| ... | ... |
@@ -3,10 +3,9 @@ |
| 3 | 3 |
package system |
| 4 | 4 |
|
| 5 | 5 |
func Mknod(path string, mode uint32, dev int) error {
|
| 6 |
- // should not be called on cli code path |
|
| 7 | 6 |
return ErrNotSupportedPlatform |
| 8 | 7 |
} |
| 9 | 8 |
|
| 10 | 9 |
func Mkdev(major int64, minor int64) uint32 {
|
| 11 |
- panic("Mkdev not implemented on windows, should not be called on cli code")
|
|
| 10 |
+ panic("Mkdev not implemented on Windows.")
|
|
| 12 | 11 |
} |
| ... | ... |
@@ -3,15 +3,34 @@ |
| 3 | 3 |
package system |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "errors" |
|
| 7 |
- "syscall" |
|
| 6 |
+ "os" |
|
| 7 |
+ "time" |
|
| 8 | 8 |
) |
| 9 | 9 |
|
| 10 |
-func fromStatT(s *syscall.Win32FileAttributeData) (*Stat_t, error) {
|
|
| 11 |
- return nil, errors.New("fromStatT should not be called on windows path")
|
|
| 10 |
+type Stat_t struct {
|
|
| 11 |
+ name string |
|
| 12 |
+ size int64 |
|
| 13 |
+ mode os.FileMode |
|
| 14 |
+ modTime time.Time |
|
| 15 |
+ isDir bool |
|
| 12 | 16 |
} |
| 13 | 17 |
|
| 14 |
-func Stat(path string) (*Stat_t, error) {
|
|
| 15 |
- // should not be called on cli code path |
|
| 16 |
- return nil, ErrNotSupportedPlatform |
|
| 18 |
+func (s Stat_t) Name() string {
|
|
| 19 |
+ return s.name |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func (s Stat_t) Size() int64 {
|
|
| 23 |
+ return s.size |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func (s Stat_t) Mode() os.FileMode {
|
|
| 27 |
+ return s.mode |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func (s Stat_t) ModTime() time.Time {
|
|
| 31 |
+ return s.modTime |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func (s Stat_t) IsDir() bool {
|
|
| 35 |
+ return s.isDir |
|
| 17 | 36 |
} |