Browse code

Windows: Archive package changes for Windows daemon

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2015/05/08 07:14:11
Showing 11 changed files
... ...
@@ -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
 }
... ...
@@ -1,3 +1,5 @@
1
+// +build !windows
2
+
1 3
 package system
2 4
 
3 5
 import (
... ...
@@ -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
 }