瀏覽代碼

pkg/archive: adjust chmod bits on windows

This change modifies the chmod bits of build context archives built on
windows to preserve the execute bit and remove the r/w bits from
grp/others.

Also adjusted integ-cli tests to verify permissions based on the platform
the tests are running.

Fixes #11047.

Signed-off-by: Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
Ahmet Alp Balkan 10 年之前
父節點
當前提交
1a22418f9f

+ 12 - 12
integration-cli/docker_cli_build_test.go

@@ -690,15 +690,15 @@ func TestBuildSixtySteps(t *testing.T) {
 func TestBuildAddSingleFileToRoot(t *testing.T) {
 func TestBuildAddSingleFileToRoot(t *testing.T) {
 	name := "testaddimg"
 	name := "testaddimg"
 	defer deleteImages(name)
 	defer deleteImages(name)
-	ctx, err := fakeContext(`FROM busybox
+	ctx, err := fakeContext(fmt.Sprintf(`FROM busybox
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN touch /exists
 RUN touch /exists
 RUN chown dockerio.dockerio /exists
 RUN chown dockerio.dockerio /exists
 ADD test_file /
 ADD test_file /
 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ]
-RUN [ $(ls -l /test_file | awk '{print $1}') = '-rw-r--r--' ]
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
+RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ]
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
 		map[string]string{
 		map[string]string{
 			"test_file": "test1",
 			"test_file": "test1",
 		})
 		})
@@ -1263,7 +1263,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`,
 func TestBuildAddWholeDirToRoot(t *testing.T) {
 func TestBuildAddWholeDirToRoot(t *testing.T) {
 	name := "testaddwholedirtoroot"
 	name := "testaddwholedirtoroot"
 	defer deleteImages(name)
 	defer deleteImages(name)
-	ctx, err := fakeContext(`FROM busybox
+	ctx, err := fakeContext(fmt.Sprintf(`FROM busybox
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN touch /exists
 RUN touch /exists
@@ -1272,8 +1272,8 @@ ADD test_dir /test_dir
 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ]
 RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ]
 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ]
-RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '-rw-r--r--' ]
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
+RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ]
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
 		map[string]string{
 		map[string]string{
 			"test_dir/test_file": "test1",
 			"test_dir/test_file": "test1",
 		})
 		})
@@ -1336,15 +1336,15 @@ RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`,
 func TestBuildCopySingleFileToRoot(t *testing.T) {
 func TestBuildCopySingleFileToRoot(t *testing.T) {
 	name := "testcopysinglefiletoroot"
 	name := "testcopysinglefiletoroot"
 	defer deleteImages(name)
 	defer deleteImages(name)
-	ctx, err := fakeContext(`FROM busybox
+	ctx, err := fakeContext(fmt.Sprintf(`FROM busybox
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN touch /exists
 RUN touch /exists
 RUN chown dockerio.dockerio /exists
 RUN chown dockerio.dockerio /exists
 COPY test_file /
 COPY test_file /
 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ]
-RUN [ $(ls -l /test_file | awk '{print $1}') = '-rw-r--r--' ]
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
+RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ]
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
 		map[string]string{
 		map[string]string{
 			"test_file": "test1",
 			"test_file": "test1",
 		})
 		})
@@ -1496,7 +1496,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`,
 func TestBuildCopyWholeDirToRoot(t *testing.T) {
 func TestBuildCopyWholeDirToRoot(t *testing.T) {
 	name := "testcopywholedirtoroot"
 	name := "testcopywholedirtoroot"
 	defer deleteImages(name)
 	defer deleteImages(name)
-	ctx, err := fakeContext(`FROM busybox
+	ctx, err := fakeContext(fmt.Sprintf(`FROM busybox
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN echo 'dockerio:x:1001:' >> /etc/group
 RUN touch /exists
 RUN touch /exists
@@ -1505,8 +1505,8 @@ COPY test_dir /test_dir
 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ]
 RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ]
 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ]
 RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ]
-RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '-rw-r--r--' ]
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
+RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ]
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
 		map[string]string{
 		map[string]string{
 			"test_dir/test_file": "test1",
 			"test_dir/test_file": "test1",
 		})
 		})

+ 2 - 0
integration-cli/test_vars_unix.go

@@ -5,4 +5,6 @@ package main
 const (
 const (
 	// identifies if test suite is running on a unix platform
 	// identifies if test suite is running on a unix platform
 	isUnixCli = true
 	isUnixCli = true
+
+	expectedFileChmod = "-rw-r--r--"
 )
 )

+ 3 - 0
integration-cli/test_vars_windows.go

@@ -5,4 +5,7 @@ package main
 const (
 const (
 	// identifies if test suite is running on a unix platform
 	// identifies if test suite is running on a unix platform
 	isUnixCli = false
 	isUnixCli = false
+
+	// this is the expected file permission set on windows: gh#11047
+	expectedFileChmod = "-rwx------"
 )
 )

+ 3 - 0
pkg/archive/archive.go

@@ -204,6 +204,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
 
 
 	name, err = canonicalTarName(name, fi.IsDir())
 	name, err = canonicalTarName(name, fi.IsDir())
 	if err != nil {
 	if err != nil {
@@ -696,6 +697,8 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
 			return err
 			return err
 		}
 		}
 		hdr.Name = filepath.Base(dst)
 		hdr.Name = filepath.Base(dst)
+		hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
+
 		tw := tar.NewWriter(w)
 		tw := tar.NewWriter(w)
 		defer tw.Close()
 		defer tw.Close()
 		if err := tw.WriteHeader(hdr); err != nil {
 		if err := tw.WriteHeader(hdr); err != nil {

+ 8 - 0
pkg/archive/archive_unix.go

@@ -4,6 +4,7 @@ package archive
 
 
 import (
 import (
 	"errors"
 	"errors"
+	"os"
 	"syscall"
 	"syscall"
 
 
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
@@ -16,6 +17,13 @@ func CanonicalTarNameForPath(p string) (string, error) {
 	return p, nil // already unix-style
 	return p, nil // already unix-style
 }
 }
 
 
+// chmodTarEntry is used to adjust the file permissions used in tar header based
+// on the platform the archival is done.
+
+func chmodTarEntry(perm os.FileMode) os.FileMode {
+	return perm // noop for unix as golang APIs provide perm bits correctly
+}
+
 func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) {
 func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) {
 	s, ok := stat.(*syscall.Stat_t)
 	s, ok := stat.(*syscall.Stat_t)
 
 

+ 18 - 0
pkg/archive/archive_unix_test.go

@@ -3,6 +3,7 @@
 package archive
 package archive
 
 
 import (
 import (
+	"os"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -40,3 +41,20 @@ func TestCanonicalTarName(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
+
+func TestChmodTarEntry(t *testing.T) {
+	cases := []struct {
+		in, expected os.FileMode
+	}{
+		{0000, 0000},
+		{0777, 0777},
+		{0644, 0644},
+		{0755, 0755},
+		{0444, 0444},
+	}
+	for _, v := range cases {
+		if out := chmodTarEntry(v.in); out != v.expected {
+			t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out)
+		}
+	}
+}

+ 12 - 0
pkg/archive/archive_windows.go

@@ -4,6 +4,7 @@ package archive
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"os"
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
@@ -23,6 +24,17 @@ func CanonicalTarNameForPath(p string) (string, error) {
 	return strings.Replace(p, "\\", "/", -1), nil
 	return strings.Replace(p, "\\", "/", -1), nil
 }
 }
 
 
+// chmodTarEntry is used to adjust the file permissions used in tar header based
+// on the platform the archival is done.
+func chmodTarEntry(perm os.FileMode) os.FileMode {
+	// Clear r/w on grp/others: no precise equivalen of group/others on NTFS.
+	perm &= 0711
+	// Add the x bit: make everything +x from windows
+	perm |= 0100
+
+	return perm
+}
+
 func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) {
 func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) {
 	// 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

+ 18 - 0
pkg/archive/archive_windows_test.go

@@ -3,6 +3,7 @@
 package archive
 package archive
 
 
 import (
 import (
+	"os"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -46,3 +47,20 @@ func TestCanonicalTarName(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
+
+func TestChmodTarEntry(t *testing.T) {
+	cases := []struct {
+		in, expected os.FileMode
+	}{
+		{0000, 0100},
+		{0777, 0711},
+		{0644, 0700},
+		{0755, 0711},
+		{0444, 0500},
+	}
+	for _, v := range cases {
+		if out := chmodTarEntry(v.in); out != v.expected {
+			t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out)
+		}
+	}
+}