Browse code

daemon/archive.go: Fix copy routines to preserve UID.

This changes the long-standing bug of copy operations not preserving the
UID/GID information after the files arrive to the container.

Signed-off-by: Erik Hollensbe <github@hollensbe.org>

Erik Hollensbe authored on 2016/11/14 22:37:08
Showing 14 changed files
... ...
@@ -27,7 +27,7 @@ type copyBackend interface {
27 27
 	ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
28 28
 	ContainerCopy(name string, res string) (io.ReadCloser, error)
29 29
 	ContainerExport(name string, out io.Writer) error
30
-	ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error
30
+	ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
31 31
 	ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
32 32
 }
33 33
 
... ...
@@ -112,5 +112,7 @@ func (s *containerRouter) putContainersArchive(ctx context.Context, w http.Respo
112 112
 	}
113 113
 
114 114
 	noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
115
-	return s.backend.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body)
115
+	copyUIDGID := httputils.BoolValue(r, "copyUIDGID")
116
+
117
+	return s.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body)
116 118
 }
... ...
@@ -97,6 +97,7 @@ type ContainerStartOptions struct {
97 97
 // about files to copy into a container
98 98
 type CopyToContainerOptions struct {
99 99
 	AllowOverwriteDirWithFile bool
100
+	CopyUIDGID                bool
100 101
 }
101 102
 
102 103
 // EventsOptions holds parameters to filter events with.
... ...
@@ -20,6 +20,7 @@ type copyOptions struct {
20 20
 	source      string
21 21
 	destination string
22 22
 	followLink  bool
23
+	copyUIDGID  bool
23 24
 }
24 25
 
25 26
 type copyDirection int
... ...
@@ -66,6 +67,7 @@ func NewCopyCommand(dockerCli *command.DockerCli) *cobra.Command {
66 66
 	flags := cmd.Flags()
67 67
 
68 68
 	flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
69
+	flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)")
69 70
 
70 71
 	return cmd
71 72
 }
... ...
@@ -92,7 +94,7 @@ func runCopy(dockerCli *command.DockerCli, opts copyOptions) error {
92 92
 	case fromContainer:
93 93
 		return copyFromContainer(ctx, dockerCli, srcContainer, srcPath, dstPath, cpParam)
94 94
 	case toContainer:
95
-		return copyToContainer(ctx, dockerCli, srcPath, dstContainer, dstPath, cpParam)
95
+		return copyToContainer(ctx, dockerCli, srcPath, dstContainer, dstPath, cpParam, opts.copyUIDGID)
96 96
 	case acrossContainers:
97 97
 		// Copying between containers isn't supported.
98 98
 		return errors.New("copying between containers is not supported")
... ...
@@ -175,7 +177,7 @@ func copyFromContainer(ctx context.Context, dockerCli *command.DockerCli, srcCon
175 175
 	return archive.CopyTo(preArchive, srcInfo, dstPath)
176 176
 }
177 177
 
178
-func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
178
+func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig, copyUIDGID bool) (err error) {
179 179
 	if srcPath != "-" {
180 180
 		// Get an absolute source path.
181 181
 		srcPath, err = resolveLocalPath(srcPath)
... ...
@@ -265,6 +267,7 @@ func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath,
265 265
 
266 266
 	options := types.CopyToContainerOptions{
267 267
 		AllowOverwriteDirWithFile: false,
268
+		CopyUIDGID:                copyUIDGID,
268 269
 	}
269 270
 
270 271
 	return dockerCli.Client().CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)
... ...
@@ -38,6 +38,10 @@ func (cli *Client) CopyToContainer(ctx context.Context, container, path string,
38 38
 		query.Set("noOverwriteDirNonDir", "true")
39 39
 	}
40 40
 
41
+	if options.CopyUIDGID {
42
+		query.Set("copyUIDGID", "true")
43
+	}
44
+
41 45
 	apiPath := fmt.Sprintf("/containers/%s/archive", container)
42 46
 
43 47
 	response, err := cli.putRaw(ctx, apiPath, query, content, nil)
... ...
@@ -83,7 +83,7 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io
83 83
 // be ErrExtractPointNotDirectory. If noOverwriteDirNonDir is true then it will
84 84
 // be an error if unpacking the given content would cause an existing directory
85 85
 // to be replaced with a non-directory and vice versa.
86
-func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error {
86
+func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error {
87 87
 	container, err := daemon.GetContainer(name)
88 88
 	if err != nil {
89 89
 		return err
... ...
@@ -94,7 +94,7 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNon
94 94
 		return err
95 95
 	}
96 96
 
97
-	return daemon.containerExtractToDir(container, path, noOverwriteDirNonDir, content)
97
+	return daemon.containerExtractToDir(container, path, copyUIDGID, noOverwriteDirNonDir, content)
98 98
 }
99 99
 
100 100
 // containerStatPath stats the filesystem resource at the specified path in this
... ...
@@ -196,7 +196,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
196 196
 // noOverwriteDirNonDir is true then it will be an error if unpacking the
197 197
 // given content would cause an existing directory to be replaced with a non-
198 198
 // directory and vice versa.
199
-func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) {
199
+func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) (err error) {
200 200
 	container.Lock()
201 201
 	defer container.Unlock()
202 202
 
... ...
@@ -279,13 +279,18 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
279 279
 		return ErrRootFSReadOnly
280 280
 	}
281 281
 
282
-	uid, gid := daemon.GetRemappedUIDGID()
283
-	options := &archive.TarOptions{
284
-		NoOverwriteDirNonDir: noOverwriteDirNonDir,
285
-		ChownOpts: &archive.TarChownOptions{
286
-			UID: uid, GID: gid, // TODO: should all ownership be set to root (either real or remapped)?
287
-		},
282
+	options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir)
283
+
284
+	if copyUIDGID {
285
+		var err error
286
+		// tarCopyOptions will appropriately pull in the right uid/gid for the
287
+		// user/group and will set the options.
288
+		options, err = daemon.tarCopyOptions(container, noOverwriteDirNonDir)
289
+		if err != nil {
290
+			return err
291
+		}
288 292
 	}
293
+
289 294
 	if err := chrootarchive.Untar(content, resolvedPath, options); err != nil {
290 295
 		return err
291 296
 	}
292 297
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+package daemon
1
+
2
+import (
3
+	"github.com/docker/docker/pkg/archive"
4
+)
5
+
6
+// defaultTarCopyOptions is the setting that is used when unpacking an archive
7
+// for a copy API event.
8
+func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
9
+	uidMaps, gidMaps := daemon.GetUIDGIDMaps()
10
+	return &archive.TarOptions{
11
+		NoOverwriteDirNonDir: noOverwriteDirNonDir,
12
+		UIDMaps:              uidMaps,
13
+		GIDMaps:              gidMaps,
14
+	}
15
+}
0 16
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+// +build !windows
1
+
2
+package daemon
3
+
4
+import (
5
+	"github.com/docker/docker/container"
6
+	"github.com/docker/docker/pkg/archive"
7
+	"github.com/docker/docker/pkg/idtools"
8
+)
9
+
10
+func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwriteDirNonDir bool) (*archive.TarOptions, error) {
11
+	if container.Config.User == "" {
12
+		return daemon.defaultTarCopyOptions(noOverwriteDirNonDir), nil
13
+	}
14
+
15
+	user, err := idtools.LookupUser(container.Config.User)
16
+	if err != nil {
17
+		return nil, err
18
+	}
19
+
20
+	return &archive.TarOptions{
21
+		NoOverwriteDirNonDir: noOverwriteDirNonDir,
22
+		ChownOpts: &archive.TarChownOptions{
23
+			UID: user.Uid,
24
+			GID: user.Gid,
25
+		},
26
+	}, nil
27
+}
0 28
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+// +build windows
1
+
2
+package daemon
3
+
4
+import (
5
+	"github.com/docker/docker/container"
6
+	"github.com/docker/docker/pkg/archive"
7
+)
8
+
9
+func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwriteDirNonDir bool) (*archive.TarOptions, error) {
10
+	return daemon.defaultTarCopyOptions(noOverwriteDirNonDir), nil
11
+}
... ...
@@ -29,7 +29,7 @@ func (s *DockerSuite) TestCpFromErrSrcNotExists(c *check.C) {
29 29
 	tmpDir := getTestDir(c, "test-cp-from-err-src-not-exists")
30 30
 	defer os.RemoveAll(tmpDir)
31 31
 
32
-	err := runDockerCp(c, containerCpPath(containerID, "file1"), tmpDir)
32
+	err := runDockerCp(c, containerCpPath(containerID, "file1"), tmpDir, nil)
33 33
 	c.Assert(err, checker.NotNil)
34 34
 
35 35
 	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
... ...
@@ -44,7 +44,7 @@ func (s *DockerSuite) TestCpFromErrSrcNotDir(c *check.C) {
44 44
 	tmpDir := getTestDir(c, "test-cp-from-err-src-not-dir")
45 45
 	defer os.RemoveAll(tmpDir)
46 46
 
47
-	err := runDockerCp(c, containerCpPathTrailingSep(containerID, "file1"), tmpDir)
47
+	err := runDockerCp(c, containerCpPathTrailingSep(containerID, "file1"), tmpDir, nil)
48 48
 	c.Assert(err, checker.NotNil)
49 49
 
50 50
 	c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err))
... ...
@@ -65,7 +65,7 @@ func (s *DockerSuite) TestCpFromErrDstParentNotExists(c *check.C) {
65 65
 	srcPath := containerCpPath(containerID, "/file1")
66 66
 	dstPath := cpPath(tmpDir, "notExists", "file1")
67 67
 
68
-	err := runDockerCp(c, srcPath, dstPath)
68
+	err := runDockerCp(c, srcPath, dstPath, nil)
69 69
 	c.Assert(err, checker.NotNil)
70 70
 
71 71
 	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
... ...
@@ -73,7 +73,7 @@ func (s *DockerSuite) TestCpFromErrDstParentNotExists(c *check.C) {
73 73
 	// Try with a directory source.
74 74
 	srcPath = containerCpPath(containerID, "/dir1")
75 75
 
76
-	err = runDockerCp(c, srcPath, dstPath)
76
+	err = runDockerCp(c, srcPath, dstPath, nil)
77 77
 	c.Assert(err, checker.NotNil)
78 78
 
79 79
 	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
... ...
@@ -94,7 +94,7 @@ func (s *DockerSuite) TestCpFromErrDstNotDir(c *check.C) {
94 94
 	srcPath := containerCpPath(containerID, "/file1")
95 95
 	dstPath := cpPathTrailingSep(tmpDir, "file1")
96 96
 
97
-	err := runDockerCp(c, srcPath, dstPath)
97
+	err := runDockerCp(c, srcPath, dstPath, nil)
98 98
 	c.Assert(err, checker.NotNil)
99 99
 
100 100
 	c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err))
... ...
@@ -102,7 +102,7 @@ func (s *DockerSuite) TestCpFromErrDstNotDir(c *check.C) {
102 102
 	// Try with a directory source.
103 103
 	srcPath = containerCpPath(containerID, "/dir1")
104 104
 
105
-	err = runDockerCp(c, srcPath, dstPath)
105
+	err = runDockerCp(c, srcPath, dstPath, nil)
106 106
 	c.Assert(err, checker.NotNil)
107 107
 
108 108
 	c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err))
... ...
@@ -124,7 +124,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
124 124
 	srcPath := containerCpPath(containerID, "/file2")
125 125
 	dstPath := cpPath(tmpDir, "symlinkToFile1")
126 126
 
127
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
127
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
128 128
 
129 129
 	// The symlink should not have been modified.
130 130
 	c.Assert(symlinkTargetEquals(c, dstPath, "file1"), checker.IsNil)
... ...
@@ -136,7 +136,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
136 136
 	// should copy the file into the symlink target directory.
137 137
 	dstPath = cpPath(tmpDir, "symlinkToDir1")
138 138
 
139
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
139
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
140 140
 
141 141
 	// The symlink should not have been modified.
142 142
 	c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
... ...
@@ -149,7 +149,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
149 149
 	// the contents of the source file.
150 150
 	dstPath = cpPath(tmpDir, "brokenSymlinkToFileX")
151 151
 
152
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
152
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
153 153
 
154 154
 	// The symlink should not have been modified.
155 155
 	c.Assert(symlinkTargetEquals(c, dstPath, "fileX"), checker.IsNil)
... ...
@@ -163,7 +163,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
163 163
 	srcPath = containerCpPath(containerID, "/dir2")
164 164
 	dstPath = cpPath(tmpDir, "symlinkToDir1")
165 165
 
166
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
166
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
167 167
 
168 168
 	// The symlink should not have been modified.
169 169
 	c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
... ...
@@ -177,7 +177,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
177 177
 	// should not modify the symlink.
178 178
 	dstPath = cpPath(tmpDir, "brokenSymlinkToDirX")
179 179
 
180
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
180
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
181 181
 
182 182
 	// The symlink should not have been modified.
183 183
 	c.Assert(symlinkTargetEquals(c, dstPath, "dirX"), checker.IsNil)
... ...
@@ -217,7 +217,7 @@ func (s *DockerSuite) TestCpFromCaseA(c *check.C) {
217 217
 	srcPath := containerCpPath(containerID, "/root/file1")
218 218
 	dstPath := cpPath(tmpDir, "itWorks.txt")
219 219
 
220
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
220
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
221 221
 
222 222
 	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
223 223
 }
... ...
@@ -235,7 +235,7 @@ func (s *DockerSuite) TestCpFromCaseB(c *check.C) {
235 235
 	srcPath := containerCpPath(containerID, "/file1")
236 236
 	dstDir := cpPathTrailingSep(tmpDir, "testDir")
237 237
 
238
-	err := runDockerCp(c, srcPath, dstDir)
238
+	err := runDockerCp(c, srcPath, dstDir, nil)
239 239
 	c.Assert(err, checker.NotNil)
240 240
 
241 241
 	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err))
... ...
@@ -260,7 +260,7 @@ func (s *DockerSuite) TestCpFromCaseC(c *check.C) {
260 260
 	// Ensure the local file starts with different content.
261 261
 	c.Assert(fileContentEquals(c, dstPath, "file2\n"), checker.IsNil)
262 262
 
263
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
263
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
264 264
 
265 265
 	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
266 266
 }
... ...
@@ -285,7 +285,7 @@ func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
285 285
 	_, err := os.Stat(dstPath)
286 286
 	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("did not expect dstPath %q to exist", dstPath))
287 287
 
288
-	c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
288
+	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
289 289
 
290 290
 	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
291 291
 
... ...
@@ -299,7 +299,7 @@ func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
299 299
 
300 300
 	dstDir = cpPathTrailingSep(tmpDir, "dir1")
301 301
 
302
-	c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
302
+	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
303 303
 
304 304
 	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
305 305
 }
... ...
@@ -319,7 +319,7 @@ func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
319 319
 	dstDir := cpPath(tmpDir, "testDir")
320 320
 	dstPath := filepath.Join(dstDir, "file1-1")
321 321
 
322
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
322
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
323 323
 
324 324
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
325 325
 
... ...
@@ -330,7 +330,7 @@ func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
330 330
 
331 331
 	dstDir = cpPathTrailingSep(tmpDir, "testDir")
332 332
 
333
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
333
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
334 334
 
335 335
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
336 336
 }
... ...
@@ -351,7 +351,7 @@ func (s *DockerSuite) TestCpFromCaseF(c *check.C) {
351 351
 	srcDir := containerCpPath(containerID, "/root/dir1")
352 352
 	dstFile := cpPath(tmpDir, "file1")
353 353
 
354
-	err := runDockerCp(c, srcDir, dstFile)
354
+	err := runDockerCp(c, srcDir, dstFile, nil)
355 355
 	c.Assert(err, checker.NotNil)
356 356
 
357 357
 	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
... ...
@@ -376,7 +376,7 @@ func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
376 376
 	resultDir := filepath.Join(dstDir, "dir1")
377 377
 	dstPath := filepath.Join(resultDir, "file1-1")
378 378
 
379
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
379
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
380 380
 
381 381
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
382 382
 
... ...
@@ -390,7 +390,7 @@ func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
390 390
 
391 391
 	dstDir = cpPathTrailingSep(tmpDir, "dir2")
392 392
 
393
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
393
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
394 394
 
395 395
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
396 396
 }
... ...
@@ -410,7 +410,7 @@ func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
410 410
 	dstDir := cpPath(tmpDir, "testDir")
411 411
 	dstPath := filepath.Join(dstDir, "file1-1")
412 412
 
413
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
413
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
414 414
 
415 415
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
416 416
 
... ...
@@ -421,7 +421,7 @@ func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
421 421
 
422 422
 	dstDir = cpPathTrailingSep(tmpDir, "testDir")
423 423
 
424
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
424
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
425 425
 
426 426
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
427 427
 }
... ...
@@ -443,7 +443,7 @@ func (s *DockerSuite) TestCpFromCaseI(c *check.C) {
443 443
 	srcDir := containerCpPathTrailingSep(containerID, "/root/dir1") + "."
444 444
 	dstFile := cpPath(tmpDir, "file1")
445 445
 
446
-	err := runDockerCp(c, srcDir, dstFile)
446
+	err := runDockerCp(c, srcDir, dstFile, nil)
447 447
 	c.Assert(err, checker.NotNil)
448 448
 
449 449
 	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
... ...
@@ -468,7 +468,7 @@ func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
468 468
 	dstDir := cpPath(tmpDir, "dir2")
469 469
 	dstPath := filepath.Join(dstDir, "file1-1")
470 470
 
471
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
471
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
472 472
 
473 473
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
474 474
 
... ...
@@ -482,7 +482,7 @@ func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
482 482
 
483 483
 	dstDir = cpPathTrailingSep(tmpDir, "dir2")
484 484
 
485
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
485
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
486 486
 
487 487
 	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
488 488
 }
... ...
@@ -28,7 +28,7 @@ const (
28 28
 
29 29
 // Ensure that an all-local path case returns an error.
30 30
 func (s *DockerSuite) TestCpLocalOnly(c *check.C) {
31
-	err := runDockerCp(c, "foo", "bar")
31
+	err := runDockerCp(c, "foo", "bar", nil)
32 32
 	c.Assert(err, checker.NotNil)
33 33
 
34 34
 	c.Assert(err.Error(), checker.Contains, "must specify at least one container source")
... ...
@@ -31,7 +31,7 @@ func (s *DockerSuite) TestCpToErrSrcNotExists(c *check.C) {
31 31
 	srcPath := cpPath(tmpDir, "file1")
32 32
 	dstPath := containerCpPath(containerID, "file1")
33 33
 
34
-	err := runDockerCp(c, srcPath, dstPath)
34
+	err := runDockerCp(c, srcPath, dstPath, nil)
35 35
 	c.Assert(err, checker.NotNil)
36 36
 
37 37
 	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
... ...
@@ -50,7 +50,7 @@ func (s *DockerSuite) TestCpToErrSrcNotDir(c *check.C) {
50 50
 	srcPath := cpPathTrailingSep(tmpDir, "file1")
51 51
 	dstPath := containerCpPath(containerID, "testDir")
52 52
 
53
-	err := runDockerCp(c, srcPath, dstPath)
53
+	err := runDockerCp(c, srcPath, dstPath, nil)
54 54
 	c.Assert(err, checker.NotNil)
55 55
 
56 56
 	c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err))
... ...
@@ -71,7 +71,7 @@ func (s *DockerSuite) TestCpToErrDstParentNotExists(c *check.C) {
71 71
 	srcPath := cpPath(tmpDir, "file1")
72 72
 	dstPath := containerCpPath(containerID, "/notExists", "file1")
73 73
 
74
-	err := runDockerCp(c, srcPath, dstPath)
74
+	err := runDockerCp(c, srcPath, dstPath, nil)
75 75
 	c.Assert(err, checker.NotNil)
76 76
 
77 77
 	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
... ...
@@ -79,7 +79,7 @@ func (s *DockerSuite) TestCpToErrDstParentNotExists(c *check.C) {
79 79
 	// Try with a directory source.
80 80
 	srcPath = cpPath(tmpDir, "dir1")
81 81
 
82
-	err = runDockerCp(c, srcPath, dstPath)
82
+	err = runDockerCp(c, srcPath, dstPath, nil)
83 83
 	c.Assert(err, checker.NotNil)
84 84
 
85 85
 	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
... ...
@@ -104,7 +104,7 @@ func (s *DockerSuite) TestCpToErrDstNotDir(c *check.C) {
104 104
 	// The client should encounter an error trying to stat the destination
105 105
 	// and then be unable to copy since the destination is asserted to be a
106 106
 	// directory but does not exist.
107
-	err := runDockerCp(c, srcPath, dstPath)
107
+	err := runDockerCp(c, srcPath, dstPath, nil)
108 108
 	c.Assert(err, checker.NotNil)
109 109
 
110 110
 	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExist error, but got %T: %s", err, err))
... ...
@@ -116,7 +116,7 @@ func (s *DockerSuite) TestCpToErrDstNotDir(c *check.C) {
116 116
 	// then decide to extract to the parent directory instead with a rebased
117 117
 	// name in the source archive, but this directory would overwrite the
118 118
 	// existing file with the same name.
119
-	err = runDockerCp(c, srcPath, dstPath)
119
+	err = runDockerCp(c, srcPath, dstPath, nil)
120 120
 	c.Assert(err, checker.NotNil)
121 121
 
122 122
 	c.Assert(isCannotOverwriteNonDirWithDir(err), checker.True, check.Commentf("expected CannotOverwriteNonDirWithDir error, but got %T: %s", err, err))
... ...
@@ -144,7 +144,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
144 144
 	srcPath := cpPath(testVol, "file2")
145 145
 	dstPath := containerCpPath(containerID, "/vol2/symlinkToFile1")
146 146
 
147
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
147
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
148 148
 
149 149
 	// The symlink should not have been modified.
150 150
 	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"), checker.IsNil)
... ...
@@ -156,7 +156,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
156 156
 	// This should copy the file into the symlink target directory.
157 157
 	dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
158 158
 
159
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
159
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
160 160
 
161 161
 	// The symlink should not have been modified.
162 162
 	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
... ...
@@ -169,7 +169,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
169 169
 	// contents of the source file.
170 170
 	dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToFileX")
171 171
 
172
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
172
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
173 173
 
174 174
 	// The symlink should not have been modified.
175 175
 	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"), checker.IsNil)
... ...
@@ -183,7 +183,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
183 183
 	srcPath = cpPath(testVol, "/dir2")
184 184
 	dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
185 185
 
186
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
186
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
187 187
 
188 188
 	// The symlink should not have been modified.
189 189
 	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
... ...
@@ -197,7 +197,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
197 197
 	// should not modify the symlink.
198 198
 	dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToDirX")
199 199
 
200
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
200
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
201 201
 
202 202
 	// The symlink should not have been modified.
203 203
 	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"), checker.IsNil)
... ...
@@ -238,7 +238,7 @@ func (s *DockerSuite) TestCpToCaseA(c *check.C) {
238 238
 	srcPath := cpPath(tmpDir, "file1")
239 239
 	dstPath := containerCpPath(containerID, "/root/itWorks.txt")
240 240
 
241
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
241
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
242 242
 
243 243
 	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
244 244
 }
... ...
@@ -259,7 +259,7 @@ func (s *DockerSuite) TestCpToCaseB(c *check.C) {
259 259
 	srcPath := cpPath(tmpDir, "file1")
260 260
 	dstDir := containerCpPathTrailingSep(containerID, "testDir")
261 261
 
262
-	err := runDockerCp(c, srcPath, dstDir)
262
+	err := runDockerCp(c, srcPath, dstDir, nil)
263 263
 	c.Assert(err, checker.NotNil)
264 264
 
265 265
 	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err))
... ...
@@ -285,7 +285,7 @@ func (s *DockerSuite) TestCpToCaseC(c *check.C) {
285 285
 	// Ensure the container's file starts with the original content.
286 286
 	c.Assert(containerStartOutputEquals(c, containerID, "file2\n"), checker.IsNil)
287 287
 
288
-	c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
288
+	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
289 289
 
290 290
 	// Should now contain file1's contents.
291 291
 	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
... ...
@@ -312,7 +312,7 @@ func (s *DockerSuite) TestCpToCaseD(c *check.C) {
312 312
 	// Ensure that dstPath doesn't exist.
313 313
 	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
314 314
 
315
-	c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
315
+	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
316 316
 
317 317
 	// Should now contain file1's contents.
318 318
 	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
... ...
@@ -330,7 +330,7 @@ func (s *DockerSuite) TestCpToCaseD(c *check.C) {
330 330
 	// Ensure that dstPath doesn't exist.
331 331
 	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
332 332
 
333
-	c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
333
+	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
334 334
 
335 335
 	// Should now contain file1's contents.
336 336
 	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
... ...
@@ -353,7 +353,7 @@ func (s *DockerSuite) TestCpToCaseE(c *check.C) {
353 353
 	srcDir := cpPath(tmpDir, "dir1")
354 354
 	dstDir := containerCpPath(containerID, "testDir")
355 355
 
356
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
356
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
357 357
 
358 358
 	// Should now contain file1-1's contents.
359 359
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -367,7 +367,7 @@ func (s *DockerSuite) TestCpToCaseE(c *check.C) {
367 367
 
368 368
 	dstDir = containerCpPathTrailingSep(containerID, "testDir")
369 369
 
370
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
370
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
371 371
 
372 372
 	// Should now contain file1-1's contents.
373 373
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -389,7 +389,7 @@ func (s *DockerSuite) TestCpToCaseF(c *check.C) {
389 389
 	srcDir := cpPath(tmpDir, "dir1")
390 390
 	dstFile := containerCpPath(containerID, "/root/file1")
391 391
 
392
-	err := runDockerCp(c, srcDir, dstFile)
392
+	err := runDockerCp(c, srcDir, dstFile, nil)
393 393
 	c.Assert(err, checker.NotNil)
394 394
 
395 395
 	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
... ...
@@ -416,7 +416,7 @@ func (s *DockerSuite) TestCpToCaseG(c *check.C) {
416 416
 	// Ensure that dstPath doesn't exist.
417 417
 	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
418 418
 
419
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
419
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
420 420
 
421 421
 	// Should now contain file1-1's contents.
422 422
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -434,7 +434,7 @@ func (s *DockerSuite) TestCpToCaseG(c *check.C) {
434 434
 	// Ensure that dstPath doesn't exist.
435 435
 	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
436 436
 
437
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
437
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
438 438
 
439 439
 	// Should now contain file1-1's contents.
440 440
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -457,7 +457,7 @@ func (s *DockerSuite) TestCpToCaseH(c *check.C) {
457 457
 	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
458 458
 	dstDir := containerCpPath(containerID, "testDir")
459 459
 
460
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
460
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
461 461
 
462 462
 	// Should now contain file1-1's contents.
463 463
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -471,7 +471,7 @@ func (s *DockerSuite) TestCpToCaseH(c *check.C) {
471 471
 
472 472
 	dstDir = containerCpPathTrailingSep(containerID, "testDir")
473 473
 
474
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
474
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
475 475
 
476 476
 	// Should now contain file1-1's contents.
477 477
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -494,7 +494,7 @@ func (s *DockerSuite) TestCpToCaseI(c *check.C) {
494 494
 	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
495 495
 	dstFile := containerCpPath(containerID, "/root/file1")
496 496
 
497
-	err := runDockerCp(c, srcDir, dstFile)
497
+	err := runDockerCp(c, srcDir, dstFile, nil)
498 498
 	c.Assert(err, checker.NotNil)
499 499
 
500 500
 	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
... ...
@@ -522,7 +522,7 @@ func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
522 522
 	// Ensure that dstPath doesn't exist.
523 523
 	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
524 524
 
525
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
525
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
526 526
 
527 527
 	// Should now contain file1-1's contents.
528 528
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -539,7 +539,7 @@ func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
539 539
 	// Ensure that dstPath doesn't exist.
540 540
 	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
541 541
 
542
-	c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
542
+	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
543 543
 
544 544
 	// Should now contain file1-1's contents.
545 545
 	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
... ...
@@ -563,7 +563,7 @@ func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) {
563 563
 	srcPath := cpPath(tmpDir, "file1")
564 564
 	dstPath := containerCpPath(containerID, "/root/shouldNotExist")
565 565
 
566
-	err := runDockerCp(c, srcPath, dstPath)
566
+	err := runDockerCp(c, srcPath, dstPath, nil)
567 567
 	c.Assert(err, checker.NotNil)
568 568
 
569 569
 	c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrContainerRootfsReadonly error, but got %T: %s", err, err))
... ...
@@ -590,7 +590,7 @@ func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *check.C) {
590 590
 	srcPath := cpPath(tmpDir, "file1")
591 591
 	dstPath := containerCpPath(containerID, "/vol_ro/shouldNotExist")
592 592
 
593
-	err := runDockerCp(c, srcPath, dstPath)
593
+	err := runDockerCp(c, srcPath, dstPath, nil)
594 594
 	c.Assert(err, checker.NotNil)
595 595
 
596 596
 	c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrVolumeReadonly error, but got %T: %s", err, err))
... ...
@@ -14,6 +14,29 @@ import (
14 14
 	"github.com/go-check/check"
15 15
 )
16 16
 
17
+func (s *DockerSuite) TestCpToContainerWithPermissions(c *check.C) {
18
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
19
+
20
+	tmpDir := getTestDir(c, "test-cp-to-host-with-permissions")
21
+	defer os.RemoveAll(tmpDir)
22
+
23
+	makeTestContentInDir(c, tmpDir)
24
+
25
+	containerName := "permtest"
26
+
27
+	_, exc := dockerCmd(c, "create", "--name", containerName, "debian:jessie", "/bin/bash", "-c", "stat -c '%u %g %a' /permdirtest /permdirtest/permtest")
28
+	c.Assert(exc, checker.Equals, 0)
29
+	defer dockerCmd(c, "rm", "-f", containerName)
30
+
31
+	srcPath := cpPath(tmpDir, "permdirtest")
32
+	dstPath := containerCpPath(containerName, "/")
33
+	c.Assert(runDockerCp(c, srcPath, dstPath, []string{"-a"}), checker.IsNil)
34
+
35
+	out, err := startContainerGetOutput(c, containerName)
36
+	c.Assert(err, checker.IsNil, check.Commentf("output: %v", out))
37
+	c.Assert(strings.TrimSpace(out), checker.Equals, "2 2 700\n65534 65534 400", check.Commentf("output: %v", out))
38
+}
39
+
17 40
 // Check ownership is root, both in non-userns and userns enabled modes
18 41
 func (s *DockerSuite) TestCpCheckDestOwnership(c *check.C) {
19 42
 	testRequires(c, DaemonIsLinux, SameHostDaemon)
... ...
@@ -29,7 +52,7 @@ func (s *DockerSuite) TestCpCheckDestOwnership(c *check.C) {
29 29
 	srcPath := cpPath(tmpDir, "file1")
30 30
 	dstPath := containerCpPath(containerID, "/tmpvol", "file1")
31 31
 
32
-	err := runDockerCp(c, srcPath, dstPath)
32
+	err := runDockerCp(c, srcPath, dstPath, nil)
33 33
 	c.Assert(err, checker.IsNil)
34 34
 
35 35
 	stat, err := system.Stat(filepath.Join(tmpVolDir, "file1"))
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"os"
8 8
 	"os/exec"
9 9
 	"path/filepath"
10
+	"runtime"
10 11
 	"strings"
11 12
 
12 13
 	"github.com/docker/docker/integration-cli/checker"
... ...
@@ -26,6 +27,9 @@ type fileData struct {
26 26
 	filetype fileType
27 27
 	path     string
28 28
 	contents string
29
+	uid      int
30
+	gid      int
31
+	mode     int
29 32
 }
30 33
 
31 34
 func (fd fileData) creationCommand() string {
... ...
@@ -55,31 +59,33 @@ func mkFilesCommand(fds []fileData) string {
55 55
 }
56 56
 
57 57
 var defaultFileData = []fileData{
58
-	{ftRegular, "file1", "file1"},
59
-	{ftRegular, "file2", "file2"},
60
-	{ftRegular, "file3", "file3"},
61
-	{ftRegular, "file4", "file4"},
62
-	{ftRegular, "file5", "file5"},
63
-	{ftRegular, "file6", "file6"},
64
-	{ftRegular, "file7", "file7"},
65
-	{ftDir, "dir1", ""},
66
-	{ftRegular, "dir1/file1-1", "file1-1"},
67
-	{ftRegular, "dir1/file1-2", "file1-2"},
68
-	{ftDir, "dir2", ""},
69
-	{ftRegular, "dir2/file2-1", "file2-1"},
70
-	{ftRegular, "dir2/file2-2", "file2-2"},
71
-	{ftDir, "dir3", ""},
72
-	{ftRegular, "dir3/file3-1", "file3-1"},
73
-	{ftRegular, "dir3/file3-2", "file3-2"},
74
-	{ftDir, "dir4", ""},
75
-	{ftRegular, "dir4/file3-1", "file4-1"},
76
-	{ftRegular, "dir4/file3-2", "file4-2"},
77
-	{ftDir, "dir5", ""},
78
-	{ftSymlink, "symlinkToFile1", "file1"},
79
-	{ftSymlink, "symlinkToDir1", "dir1"},
80
-	{ftSymlink, "brokenSymlinkToFileX", "fileX"},
81
-	{ftSymlink, "brokenSymlinkToDirX", "dirX"},
82
-	{ftSymlink, "symlinkToAbsDir", "/root"},
58
+	{ftRegular, "file1", "file1", 0, 0, 0666},
59
+	{ftRegular, "file2", "file2", 0, 0, 0666},
60
+	{ftRegular, "file3", "file3", 0, 0, 0666},
61
+	{ftRegular, "file4", "file4", 0, 0, 0666},
62
+	{ftRegular, "file5", "file5", 0, 0, 0666},
63
+	{ftRegular, "file6", "file6", 0, 0, 0666},
64
+	{ftRegular, "file7", "file7", 0, 0, 0666},
65
+	{ftDir, "dir1", "", 0, 0, 0777},
66
+	{ftRegular, "dir1/file1-1", "file1-1", 0, 0, 0666},
67
+	{ftRegular, "dir1/file1-2", "file1-2", 0, 0, 0666},
68
+	{ftDir, "dir2", "", 0, 0, 0666},
69
+	{ftRegular, "dir2/file2-1", "file2-1", 0, 0, 0666},
70
+	{ftRegular, "dir2/file2-2", "file2-2", 0, 0, 0666},
71
+	{ftDir, "dir3", "", 0, 0, 0666},
72
+	{ftRegular, "dir3/file3-1", "file3-1", 0, 0, 0666},
73
+	{ftRegular, "dir3/file3-2", "file3-2", 0, 0, 0666},
74
+	{ftDir, "dir4", "", 0, 0, 0666},
75
+	{ftRegular, "dir4/file3-1", "file4-1", 0, 0, 0666},
76
+	{ftRegular, "dir4/file3-2", "file4-2", 0, 0, 0666},
77
+	{ftDir, "dir5", "", 0, 0, 0666},
78
+	{ftSymlink, "symlinkToFile1", "file1", 0, 0, 0666},
79
+	{ftSymlink, "symlinkToDir1", "dir1", 0, 0, 0666},
80
+	{ftSymlink, "brokenSymlinkToFileX", "fileX", 0, 0, 0666},
81
+	{ftSymlink, "brokenSymlinkToDirX", "dirX", 0, 0, 0666},
82
+	{ftSymlink, "symlinkToAbsDir", "/root", 0, 0, 0666},
83
+	{ftDir, "permdirtest", "", 2, 2, 0700},
84
+	{ftRegular, "permdirtest/permtest", "perm_test", 65534, 65534, 0400},
83 85
 }
84 86
 
85 87
 func defaultMkContentCommand() string {
... ...
@@ -91,12 +97,16 @@ func makeTestContentInDir(c *check.C, dir string) {
91 91
 		path := filepath.Join(dir, filepath.FromSlash(fd.path))
92 92
 		switch fd.filetype {
93 93
 		case ftRegular:
94
-			c.Assert(ioutil.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(0666)), checker.IsNil)
94
+			c.Assert(ioutil.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(fd.mode)), checker.IsNil)
95 95
 		case ftDir:
96
-			c.Assert(os.Mkdir(path, os.FileMode(0777)), checker.IsNil)
96
+			c.Assert(os.Mkdir(path, os.FileMode(fd.mode)), checker.IsNil)
97 97
 		case ftSymlink:
98 98
 			c.Assert(os.Symlink(fd.contents, path), checker.IsNil)
99 99
 		}
100
+
101
+		if fd.filetype != ftSymlink && runtime.GOOS != "windows" {
102
+			c.Assert(os.Chown(path, fd.uid, fd.gid), checker.IsNil)
103
+		}
100 104
 	}
101 105
 }
102 106
 
... ...
@@ -178,10 +188,16 @@ func containerCpPathTrailingSep(containerID string, pathElements ...string) stri
178 178
 	return fmt.Sprintf("%s/", containerCpPath(containerID, pathElements...))
179 179
 }
180 180
 
181
-func runDockerCp(c *check.C, src, dst string) (err error) {
182
-	c.Logf("running `docker cp %s %s`", src, dst)
181
+func runDockerCp(c *check.C, src, dst string, params []string) (err error) {
182
+	c.Logf("running `docker cp %s %s %s`", strings.Join(params, " "), src, dst)
183
+
184
+	args := []string{"cp"}
185
+
186
+	for _, param := range params {
187
+		args = append(args, param)
188
+	}
183 189
 
184
-	args := []string{"cp", src, dst}
190
+	args = append(args, src, dst)
185 191
 
186 192
 	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
187 193
 	if err != nil {