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>
| ... | ... |
@@ -690,15 +690,15 @@ func TestBuildSixtySteps(t *testing.T) {
|
| 690 | 690 |
func TestBuildAddSingleFileToRoot(t *testing.T) {
|
| 691 | 691 |
name := "testaddimg" |
| 692 | 692 |
defer deleteImages(name) |
| 693 |
- ctx, err := fakeContext(`FROM busybox |
|
| 693 |
+ ctx, err := fakeContext(fmt.Sprintf(`FROM busybox |
|
| 694 | 694 |
RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
| 695 | 695 |
RUN echo 'dockerio:x:1001:' >> /etc/group |
| 696 | 696 |
RUN touch /exists |
| 697 | 697 |
RUN chown dockerio.dockerio /exists |
| 698 | 698 |
ADD test_file / |
| 699 | 699 |
RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ]
|
| 700 |
-RUN [ $(ls -l /test_file | awk '{print $1}') = '-rw-r--r--' ]
|
|
| 701 |
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
|
|
| 700 |
+RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ]
|
|
| 701 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
|
|
| 702 | 702 |
map[string]string{
|
| 703 | 703 |
"test_file": "test1", |
| 704 | 704 |
}) |
| ... | ... |
@@ -1263,7 +1263,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`,
|
| 1263 | 1263 |
func TestBuildAddWholeDirToRoot(t *testing.T) {
|
| 1264 | 1264 |
name := "testaddwholedirtoroot" |
| 1265 | 1265 |
defer deleteImages(name) |
| 1266 |
- ctx, err := fakeContext(`FROM busybox |
|
| 1266 |
+ ctx, err := fakeContext(fmt.Sprintf(`FROM busybox |
|
| 1267 | 1267 |
RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
| 1268 | 1268 |
RUN echo 'dockerio:x:1001:' >> /etc/group |
| 1269 | 1269 |
RUN touch /exists |
| ... | ... |
@@ -1272,8 +1272,8 @@ ADD test_dir /test_dir |
| 1272 | 1272 |
RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ]
|
| 1273 | 1273 |
RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ]
|
| 1274 | 1274 |
RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ]
|
| 1275 |
-RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '-rw-r--r--' ]
|
|
| 1276 |
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
|
|
| 1275 |
+RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ]
|
|
| 1276 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
|
|
| 1277 | 1277 |
map[string]string{
|
| 1278 | 1278 |
"test_dir/test_file": "test1", |
| 1279 | 1279 |
}) |
| ... | ... |
@@ -1336,15 +1336,15 @@ RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`,
|
| 1336 | 1336 |
func TestBuildCopySingleFileToRoot(t *testing.T) {
|
| 1337 | 1337 |
name := "testcopysinglefiletoroot" |
| 1338 | 1338 |
defer deleteImages(name) |
| 1339 |
- ctx, err := fakeContext(`FROM busybox |
|
| 1339 |
+ ctx, err := fakeContext(fmt.Sprintf(`FROM busybox |
|
| 1340 | 1340 |
RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
| 1341 | 1341 |
RUN echo 'dockerio:x:1001:' >> /etc/group |
| 1342 | 1342 |
RUN touch /exists |
| 1343 | 1343 |
RUN chown dockerio.dockerio /exists |
| 1344 | 1344 |
COPY test_file / |
| 1345 | 1345 |
RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ]
|
| 1346 |
-RUN [ $(ls -l /test_file | awk '{print $1}') = '-rw-r--r--' ]
|
|
| 1347 |
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
|
|
| 1346 |
+RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ]
|
|
| 1347 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
|
|
| 1348 | 1348 |
map[string]string{
|
| 1349 | 1349 |
"test_file": "test1", |
| 1350 | 1350 |
}) |
| ... | ... |
@@ -1496,7 +1496,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`,
|
| 1496 | 1496 |
func TestBuildCopyWholeDirToRoot(t *testing.T) {
|
| 1497 | 1497 |
name := "testcopywholedirtoroot" |
| 1498 | 1498 |
defer deleteImages(name) |
| 1499 |
- ctx, err := fakeContext(`FROM busybox |
|
| 1499 |
+ ctx, err := fakeContext(fmt.Sprintf(`FROM busybox |
|
| 1500 | 1500 |
RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd |
| 1501 | 1501 |
RUN echo 'dockerio:x:1001:' >> /etc/group |
| 1502 | 1502 |
RUN touch /exists |
| ... | ... |
@@ -1505,8 +1505,8 @@ COPY test_dir /test_dir |
| 1505 | 1505 |
RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ]
|
| 1506 | 1506 |
RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ]
|
| 1507 | 1507 |
RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ]
|
| 1508 |
-RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '-rw-r--r--' ]
|
|
| 1509 |
-RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`,
|
|
| 1508 |
+RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ]
|
|
| 1509 |
+RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod),
|
|
| 1510 | 1510 |
map[string]string{
|
| 1511 | 1511 |
"test_dir/test_file": "test1", |
| 1512 | 1512 |
}) |
| ... | ... |
@@ -204,6 +204,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
| 204 | 204 |
if err != nil {
|
| 205 | 205 |
return err |
| 206 | 206 |
} |
| 207 |
+ hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) |
|
| 207 | 208 |
|
| 208 | 209 |
name, err = canonicalTarName(name, fi.IsDir()) |
| 209 | 210 |
if err != nil {
|
| ... | ... |
@@ -696,6 +697,8 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
| 696 | 696 |
return err |
| 697 | 697 |
} |
| 698 | 698 |
hdr.Name = filepath.Base(dst) |
| 699 |
+ hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) |
|
| 700 |
+ |
|
| 699 | 701 |
tw := tar.NewWriter(w) |
| 700 | 702 |
defer tw.Close() |
| 701 | 703 |
if err := tw.WriteHeader(hdr); err != nil {
|
| ... | ... |
@@ -4,6 +4,7 @@ package archive |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"errors" |
| 7 |
+ "os" |
|
| 7 | 8 |
"syscall" |
| 8 | 9 |
|
| 9 | 10 |
"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) {
|
| 16 | 16 |
return p, nil // already unix-style |
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 |
+// chmodTarEntry is used to adjust the file permissions used in tar header based |
|
| 20 |
+// on the platform the archival is done. |
|
| 21 |
+ |
|
| 22 |
+func chmodTarEntry(perm os.FileMode) os.FileMode {
|
|
| 23 |
+ return perm // noop for unix as golang APIs provide perm bits correctly |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 19 | 26 |
func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) {
|
| 20 | 27 |
s, ok := stat.(*syscall.Stat_t) |
| 21 | 28 |
|
| ... | ... |
@@ -3,6 +3,7 @@ |
| 3 | 3 |
package archive |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "os" |
|
| 6 | 7 |
"testing" |
| 7 | 8 |
) |
| 8 | 9 |
|
| ... | ... |
@@ -40,3 +41,20 @@ func TestCanonicalTarName(t *testing.T) {
|
| 40 | 40 |
} |
| 41 | 41 |
} |
| 42 | 42 |
} |
| 43 |
+ |
|
| 44 |
+func TestChmodTarEntry(t *testing.T) {
|
|
| 45 |
+ cases := []struct {
|
|
| 46 |
+ in, expected os.FileMode |
|
| 47 |
+ }{
|
|
| 48 |
+ {0000, 0000},
|
|
| 49 |
+ {0777, 0777},
|
|
| 50 |
+ {0644, 0644},
|
|
| 51 |
+ {0755, 0755},
|
|
| 52 |
+ {0444, 0444},
|
|
| 53 |
+ } |
|
| 54 |
+ for _, v := range cases {
|
|
| 55 |
+ if out := chmodTarEntry(v.in); out != v.expected {
|
|
| 56 |
+ t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out)
|
|
| 57 |
+ } |
|
| 58 |
+ } |
|
| 59 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ package archive |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
+ "os" |
|
| 7 | 8 |
"strings" |
| 8 | 9 |
|
| 9 | 10 |
"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) {
|
| 23 | 23 |
return strings.Replace(p, "\\", "/", -1), nil |
| 24 | 24 |
} |
| 25 | 25 |
|
| 26 |
+// chmodTarEntry is used to adjust the file permissions used in tar header based |
|
| 27 |
+// on the platform the archival is done. |
|
| 28 |
+func chmodTarEntry(perm os.FileMode) os.FileMode {
|
|
| 29 |
+ // Clear r/w on grp/others: no precise equivalen of group/others on NTFS. |
|
| 30 |
+ perm &= 0711 |
|
| 31 |
+ // Add the x bit: make everything +x from windows |
|
| 32 |
+ perm |= 0100 |
|
| 33 |
+ |
|
| 34 |
+ return perm |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 26 | 37 |
func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) {
|
| 27 | 38 |
// do nothing. no notion of Rdev, Inode, Nlink in stat on Windows |
| 28 | 39 |
return |
| ... | ... |
@@ -3,6 +3,7 @@ |
| 3 | 3 |
package archive |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "os" |
|
| 6 | 7 |
"testing" |
| 7 | 8 |
) |
| 8 | 9 |
|
| ... | ... |
@@ -46,3 +47,20 @@ func TestCanonicalTarName(t *testing.T) {
|
| 46 | 46 |
} |
| 47 | 47 |
} |
| 48 | 48 |
} |
| 49 |
+ |
|
| 50 |
+func TestChmodTarEntry(t *testing.T) {
|
|
| 51 |
+ cases := []struct {
|
|
| 52 |
+ in, expected os.FileMode |
|
| 53 |
+ }{
|
|
| 54 |
+ {0000, 0100},
|
|
| 55 |
+ {0777, 0711},
|
|
| 56 |
+ {0644, 0700},
|
|
| 57 |
+ {0755, 0711},
|
|
| 58 |
+ {0444, 0500},
|
|
| 59 |
+ } |
|
| 60 |
+ for _, v := range cases {
|
|
| 61 |
+ if out := chmodTarEntry(v.in); out != v.expected {
|
|
| 62 |
+ t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out)
|
|
| 63 |
+ } |
|
| 64 |
+ } |
|
| 65 |
+} |