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>
This commit is contained in:
Erik Hollensbe 2016-11-14 05:37:08 -08:00
parent 8d96619e5a
commit 8a7ff5ff74
14 changed files with 209 additions and 99 deletions

View file

@ -27,7 +27,7 @@ type copyBackend interface {
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
ContainerCopy(name string, res string) (io.ReadCloser, error)
ContainerExport(name string, out io.Writer) error
ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
}

View file

@ -112,5 +112,7 @@ func (s *containerRouter) putContainersArchive(ctx context.Context, w http.Respo
}
noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
return s.backend.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body)
copyUIDGID := httputils.BoolValue(r, "copyUIDGID")
return s.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body)
}

View file

@ -97,6 +97,7 @@ type ContainerStartOptions struct {
// about files to copy into a container
type CopyToContainerOptions struct {
AllowOverwriteDirWithFile bool
CopyUIDGID bool
}
// EventsOptions holds parameters to filter events with.

View file

@ -20,6 +20,7 @@ type copyOptions struct {
source string
destination string
followLink bool
copyUIDGID bool
}
type copyDirection int
@ -66,6 +67,7 @@ func NewCopyCommand(dockerCli *command.DockerCli) *cobra.Command {
flags := cmd.Flags()
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)")
return cmd
}
@ -92,7 +94,7 @@ func runCopy(dockerCli *command.DockerCli, opts copyOptions) error {
case fromContainer:
return copyFromContainer(ctx, dockerCli, srcContainer, srcPath, dstPath, cpParam)
case toContainer:
return copyToContainer(ctx, dockerCli, srcPath, dstContainer, dstPath, cpParam)
return copyToContainer(ctx, dockerCli, srcPath, dstContainer, dstPath, cpParam, opts.copyUIDGID)
case acrossContainers:
// Copying between containers isn't supported.
return errors.New("copying between containers is not supported")
@ -175,7 +177,7 @@ func copyFromContainer(ctx context.Context, dockerCli *command.DockerCli, srcCon
return archive.CopyTo(preArchive, srcInfo, dstPath)
}
func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig, copyUIDGID bool) (err error) {
if srcPath != "-" {
// Get an absolute source path.
srcPath, err = resolveLocalPath(srcPath)
@ -265,6 +267,7 @@ func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath,
options := types.CopyToContainerOptions{
AllowOverwriteDirWithFile: false,
CopyUIDGID: copyUIDGID,
}
return dockerCli.Client().CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)

View file

@ -38,6 +38,10 @@ func (cli *Client) CopyToContainer(ctx context.Context, container, path string,
query.Set("noOverwriteDirNonDir", "true")
}
if options.CopyUIDGID {
query.Set("copyUIDGID", "true")
}
apiPath := fmt.Sprintf("/containers/%s/archive", container)
response, err := cli.putRaw(ctx, apiPath, query, content, nil)

View file

@ -83,7 +83,7 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io
// be ErrExtractPointNotDirectory. If noOverwriteDirNonDir is true then it will
// be an error if unpacking the given content would cause an existing directory
// to be replaced with a non-directory and vice versa.
func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error {
func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error {
container, err := daemon.GetContainer(name)
if err != nil {
return err
@ -94,7 +94,7 @@ func (daemon *Daemon) ContainerExtractToDir(name, path string, noOverwriteDirNon
return err
}
return daemon.containerExtractToDir(container, path, noOverwriteDirNonDir, content)
return daemon.containerExtractToDir(container, path, copyUIDGID, noOverwriteDirNonDir, content)
}
// containerStatPath stats the filesystem resource at the specified path in this
@ -196,7 +196,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
// noOverwriteDirNonDir is true then it will be an error if unpacking the
// given content would cause an existing directory to be replaced with a non-
// directory and vice versa.
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, noOverwriteDirNonDir bool, content io.Reader) (err error) {
func (daemon *Daemon) containerExtractToDir(container *container.Container, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) (err error) {
container.Lock()
defer container.Unlock()
@ -279,13 +279,18 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
return ErrRootFSReadOnly
}
uid, gid := daemon.GetRemappedUIDGID()
options := &archive.TarOptions{
NoOverwriteDirNonDir: noOverwriteDirNonDir,
ChownOpts: &archive.TarChownOptions{
UID: uid, GID: gid, // TODO: should all ownership be set to root (either real or remapped)?
},
options := daemon.defaultTarCopyOptions(noOverwriteDirNonDir)
if copyUIDGID {
var err error
// tarCopyOptions will appropriately pull in the right uid/gid for the
// user/group and will set the options.
options, err = daemon.tarCopyOptions(container, noOverwriteDirNonDir)
if err != nil {
return err
}
}
if err := chrootarchive.Untar(content, resolvedPath, options); err != nil {
return err
}

View file

@ -0,0 +1,16 @@
package daemon
import (
"github.com/docker/docker/pkg/archive"
)
// defaultTarCopyOptions is the setting that is used when unpacking an archive
// for a copy API event.
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
uidMaps, gidMaps := daemon.GetUIDGIDMaps()
return &archive.TarOptions{
NoOverwriteDirNonDir: noOverwriteDirNonDir,
UIDMaps: uidMaps,
GIDMaps: gidMaps,
}
}

View file

@ -0,0 +1,28 @@
// +build !windows
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
)
func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwriteDirNonDir bool) (*archive.TarOptions, error) {
if container.Config.User == "" {
return daemon.defaultTarCopyOptions(noOverwriteDirNonDir), nil
}
user, err := idtools.LookupUser(container.Config.User)
if err != nil {
return nil, err
}
return &archive.TarOptions{
NoOverwriteDirNonDir: noOverwriteDirNonDir,
ChownOpts: &archive.TarChownOptions{
UID: user.Uid,
GID: user.Gid,
},
}, nil
}

View file

@ -0,0 +1,12 @@
// +build windows
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/archive"
)
func (daemon *Daemon) tarCopyOptions(container *container.Container, noOverwriteDirNonDir bool) (*archive.TarOptions, error) {
return daemon.defaultTarCopyOptions(noOverwriteDirNonDir), nil
}

View file

@ -29,7 +29,7 @@ func (s *DockerSuite) TestCpFromErrSrcNotExists(c *check.C) {
tmpDir := getTestDir(c, "test-cp-from-err-src-not-exists")
defer os.RemoveAll(tmpDir)
err := runDockerCp(c, containerCpPath(containerID, "file1"), tmpDir)
err := runDockerCp(c, containerCpPath(containerID, "file1"), tmpDir, nil)
c.Assert(err, checker.NotNil)
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) {
tmpDir := getTestDir(c, "test-cp-from-err-src-not-dir")
defer os.RemoveAll(tmpDir)
err := runDockerCp(c, containerCpPathTrailingSep(containerID, "file1"), tmpDir)
err := runDockerCp(c, containerCpPathTrailingSep(containerID, "file1"), tmpDir, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := containerCpPath(containerID, "/file1")
dstPath := cpPath(tmpDir, "notExists", "file1")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
// Try with a directory source.
srcPath = containerCpPath(containerID, "/dir1")
err = runDockerCp(c, srcPath, dstPath)
err = runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := containerCpPath(containerID, "/file1")
dstPath := cpPathTrailingSep(tmpDir, "file1")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
// Try with a directory source.
srcPath = containerCpPath(containerID, "/dir1")
err = runDockerCp(c, srcPath, dstPath)
err = runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := containerCpPath(containerID, "/file2")
dstPath := cpPath(tmpDir, "symlinkToFile1")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, dstPath, "file1"), checker.IsNil)
@ -136,7 +136,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
// should copy the file into the symlink target directory.
dstPath = cpPath(tmpDir, "symlinkToDir1")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
@ -149,7 +149,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
// the contents of the source file.
dstPath = cpPath(tmpDir, "brokenSymlinkToFileX")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, dstPath, "fileX"), checker.IsNil)
@ -163,7 +163,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
srcPath = containerCpPath(containerID, "/dir2")
dstPath = cpPath(tmpDir, "symlinkToDir1")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
@ -177,7 +177,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
// should not modify the symlink.
dstPath = cpPath(tmpDir, "brokenSymlinkToDirX")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, dstPath, "dirX"), checker.IsNil)
@ -217,7 +217,7 @@ func (s *DockerSuite) TestCpFromCaseA(c *check.C) {
srcPath := containerCpPath(containerID, "/root/file1")
dstPath := cpPath(tmpDir, "itWorks.txt")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
}
@ -235,7 +235,7 @@ func (s *DockerSuite) TestCpFromCaseB(c *check.C) {
srcPath := containerCpPath(containerID, "/file1")
dstDir := cpPathTrailingSep(tmpDir, "testDir")
err := runDockerCp(c, srcPath, dstDir)
err := runDockerCp(c, srcPath, dstDir, nil)
c.Assert(err, checker.NotNil)
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) {
// Ensure the local file starts with different content.
c.Assert(fileContentEquals(c, dstPath, "file2\n"), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
}
@ -285,7 +285,7 @@ func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
_, err := os.Stat(dstPath)
c.Assert(os.IsNotExist(err), checker.True, check.Commentf("did not expect dstPath %q to exist", dstPath))
c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
@ -299,7 +299,7 @@ func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
dstDir = cpPathTrailingSep(tmpDir, "dir1")
c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
}
@ -319,7 +319,7 @@ func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
dstDir := cpPath(tmpDir, "testDir")
dstPath := filepath.Join(dstDir, "file1-1")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
@ -330,7 +330,7 @@ func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
dstDir = cpPathTrailingSep(tmpDir, "testDir")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
}
@ -351,7 +351,7 @@ func (s *DockerSuite) TestCpFromCaseF(c *check.C) {
srcDir := containerCpPath(containerID, "/root/dir1")
dstFile := cpPath(tmpDir, "file1")
err := runDockerCp(c, srcDir, dstFile)
err := runDockerCp(c, srcDir, dstFile, nil)
c.Assert(err, checker.NotNil)
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) {
resultDir := filepath.Join(dstDir, "dir1")
dstPath := filepath.Join(resultDir, "file1-1")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
@ -390,7 +390,7 @@ func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
dstDir = cpPathTrailingSep(tmpDir, "dir2")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
}
@ -410,7 +410,7 @@ func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
dstDir := cpPath(tmpDir, "testDir")
dstPath := filepath.Join(dstDir, "file1-1")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
@ -421,7 +421,7 @@ func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
dstDir = cpPathTrailingSep(tmpDir, "testDir")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
}
@ -443,7 +443,7 @@ func (s *DockerSuite) TestCpFromCaseI(c *check.C) {
srcDir := containerCpPathTrailingSep(containerID, "/root/dir1") + "."
dstFile := cpPath(tmpDir, "file1")
err := runDockerCp(c, srcDir, dstFile)
err := runDockerCp(c, srcDir, dstFile, nil)
c.Assert(err, checker.NotNil)
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) {
dstDir := cpPath(tmpDir, "dir2")
dstPath := filepath.Join(dstDir, "file1-1")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
@ -482,7 +482,7 @@ func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
dstDir = cpPathTrailingSep(tmpDir, "dir2")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
}

View file

@ -28,7 +28,7 @@ const (
// Ensure that an all-local path case returns an error.
func (s *DockerSuite) TestCpLocalOnly(c *check.C) {
err := runDockerCp(c, "foo", "bar")
err := runDockerCp(c, "foo", "bar", nil)
c.Assert(err, checker.NotNil)
c.Assert(err.Error(), checker.Contains, "must specify at least one container source")

View file

@ -31,7 +31,7 @@ func (s *DockerSuite) TestCpToErrSrcNotExists(c *check.C) {
srcPath := cpPath(tmpDir, "file1")
dstPath := containerCpPath(containerID, "file1")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := cpPathTrailingSep(tmpDir, "file1")
dstPath := containerCpPath(containerID, "testDir")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := cpPath(tmpDir, "file1")
dstPath := containerCpPath(containerID, "/notExists", "file1")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
// Try with a directory source.
srcPath = cpPath(tmpDir, "dir1")
err = runDockerCp(c, srcPath, dstPath)
err = runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
// The client should encounter an error trying to stat the destination
// and then be unable to copy since the destination is asserted to be a
// directory but does not exist.
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
// then decide to extract to the parent directory instead with a rebased
// name in the source archive, but this directory would overwrite the
// existing file with the same name.
err = runDockerCp(c, srcPath, dstPath)
err = runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := cpPath(testVol, "file2")
dstPath := containerCpPath(containerID, "/vol2/symlinkToFile1")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"), checker.IsNil)
@ -156,7 +156,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
// This should copy the file into the symlink target directory.
dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
@ -169,7 +169,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
// contents of the source file.
dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToFileX")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"), checker.IsNil)
@ -183,7 +183,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
srcPath = cpPath(testVol, "/dir2")
dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
@ -197,7 +197,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
// should not modify the symlink.
dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToDirX")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// The symlink should not have been modified.
c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"), checker.IsNil)
@ -238,7 +238,7 @@ func (s *DockerSuite) TestCpToCaseA(c *check.C) {
srcPath := cpPath(tmpDir, "file1")
dstPath := containerCpPath(containerID, "/root/itWorks.txt")
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
}
@ -259,7 +259,7 @@ func (s *DockerSuite) TestCpToCaseB(c *check.C) {
srcPath := cpPath(tmpDir, "file1")
dstDir := containerCpPathTrailingSep(containerID, "testDir")
err := runDockerCp(c, srcPath, dstDir)
err := runDockerCp(c, srcPath, dstDir, nil)
c.Assert(err, checker.NotNil)
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) {
// Ensure the container's file starts with the original content.
c.Assert(containerStartOutputEquals(c, containerID, "file2\n"), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
// Should now contain file1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
@ -312,7 +312,7 @@ func (s *DockerSuite) TestCpToCaseD(c *check.C) {
// Ensure that dstPath doesn't exist.
c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
// Should now contain file1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
@ -330,7 +330,7 @@ func (s *DockerSuite) TestCpToCaseD(c *check.C) {
// Ensure that dstPath doesn't exist.
c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
// Should now contain file1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
@ -353,7 +353,7 @@ func (s *DockerSuite) TestCpToCaseE(c *check.C) {
srcDir := cpPath(tmpDir, "dir1")
dstDir := containerCpPath(containerID, "testDir")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -367,7 +367,7 @@ func (s *DockerSuite) TestCpToCaseE(c *check.C) {
dstDir = containerCpPathTrailingSep(containerID, "testDir")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -389,7 +389,7 @@ func (s *DockerSuite) TestCpToCaseF(c *check.C) {
srcDir := cpPath(tmpDir, "dir1")
dstFile := containerCpPath(containerID, "/root/file1")
err := runDockerCp(c, srcDir, dstFile)
err := runDockerCp(c, srcDir, dstFile, nil)
c.Assert(err, checker.NotNil)
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) {
// Ensure that dstPath doesn't exist.
c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -434,7 +434,7 @@ func (s *DockerSuite) TestCpToCaseG(c *check.C) {
// Ensure that dstPath doesn't exist.
c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -457,7 +457,7 @@ func (s *DockerSuite) TestCpToCaseH(c *check.C) {
srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
dstDir := containerCpPath(containerID, "testDir")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -471,7 +471,7 @@ func (s *DockerSuite) TestCpToCaseH(c *check.C) {
dstDir = containerCpPathTrailingSep(containerID, "testDir")
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -494,7 +494,7 @@ func (s *DockerSuite) TestCpToCaseI(c *check.C) {
srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
dstFile := containerCpPath(containerID, "/root/file1")
err := runDockerCp(c, srcDir, dstFile)
err := runDockerCp(c, srcDir, dstFile, nil)
c.Assert(err, checker.NotNil)
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) {
// Ensure that dstPath doesn't exist.
c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -539,7 +539,7 @@ func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
// Ensure that dstPath doesn't exist.
c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir), checker.IsNil)
c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
// Should now contain file1-1's contents.
c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
@ -563,7 +563,7 @@ func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) {
srcPath := cpPath(tmpDir, "file1")
dstPath := containerCpPath(containerID, "/root/shouldNotExist")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
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) {
srcPath := cpPath(tmpDir, "file1")
dstPath := containerCpPath(containerID, "/vol_ro/shouldNotExist")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.NotNil)
c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrVolumeReadonly error, but got %T: %s", err, err))

View file

@ -14,6 +14,29 @@ import (
"github.com/go-check/check"
)
func (s *DockerSuite) TestCpToContainerWithPermissions(c *check.C) {
testRequires(c, SameHostDaemon, DaemonIsLinux)
tmpDir := getTestDir(c, "test-cp-to-host-with-permissions")
defer os.RemoveAll(tmpDir)
makeTestContentInDir(c, tmpDir)
containerName := "permtest"
_, exc := dockerCmd(c, "create", "--name", containerName, "debian:jessie", "/bin/bash", "-c", "stat -c '%u %g %a' /permdirtest /permdirtest/permtest")
c.Assert(exc, checker.Equals, 0)
defer dockerCmd(c, "rm", "-f", containerName)
srcPath := cpPath(tmpDir, "permdirtest")
dstPath := containerCpPath(containerName, "/")
c.Assert(runDockerCp(c, srcPath, dstPath, []string{"-a"}), checker.IsNil)
out, err := startContainerGetOutput(c, containerName)
c.Assert(err, checker.IsNil, check.Commentf("output: %v", out))
c.Assert(strings.TrimSpace(out), checker.Equals, "2 2 700\n65534 65534 400", check.Commentf("output: %v", out))
}
// Check ownership is root, both in non-userns and userns enabled modes
func (s *DockerSuite) TestCpCheckDestOwnership(c *check.C) {
testRequires(c, DaemonIsLinux, SameHostDaemon)
@ -29,7 +52,7 @@ func (s *DockerSuite) TestCpCheckDestOwnership(c *check.C) {
srcPath := cpPath(tmpDir, "file1")
dstPath := containerCpPath(containerID, "/tmpvol", "file1")
err := runDockerCp(c, srcPath, dstPath)
err := runDockerCp(c, srcPath, dstPath, nil)
c.Assert(err, checker.IsNil)
stat, err := system.Stat(filepath.Join(tmpVolDir, "file1"))

View file

@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/docker/docker/integration-cli/checker"
@ -26,6 +27,9 @@ type fileData struct {
filetype fileType
path string
contents string
uid int
gid int
mode int
}
func (fd fileData) creationCommand() string {
@ -55,31 +59,33 @@ func mkFilesCommand(fds []fileData) string {
}
var defaultFileData = []fileData{
{ftRegular, "file1", "file1"},
{ftRegular, "file2", "file2"},
{ftRegular, "file3", "file3"},
{ftRegular, "file4", "file4"},
{ftRegular, "file5", "file5"},
{ftRegular, "file6", "file6"},
{ftRegular, "file7", "file7"},
{ftDir, "dir1", ""},
{ftRegular, "dir1/file1-1", "file1-1"},
{ftRegular, "dir1/file1-2", "file1-2"},
{ftDir, "dir2", ""},
{ftRegular, "dir2/file2-1", "file2-1"},
{ftRegular, "dir2/file2-2", "file2-2"},
{ftDir, "dir3", ""},
{ftRegular, "dir3/file3-1", "file3-1"},
{ftRegular, "dir3/file3-2", "file3-2"},
{ftDir, "dir4", ""},
{ftRegular, "dir4/file3-1", "file4-1"},
{ftRegular, "dir4/file3-2", "file4-2"},
{ftDir, "dir5", ""},
{ftSymlink, "symlinkToFile1", "file1"},
{ftSymlink, "symlinkToDir1", "dir1"},
{ftSymlink, "brokenSymlinkToFileX", "fileX"},
{ftSymlink, "brokenSymlinkToDirX", "dirX"},
{ftSymlink, "symlinkToAbsDir", "/root"},
{ftRegular, "file1", "file1", 0, 0, 0666},
{ftRegular, "file2", "file2", 0, 0, 0666},
{ftRegular, "file3", "file3", 0, 0, 0666},
{ftRegular, "file4", "file4", 0, 0, 0666},
{ftRegular, "file5", "file5", 0, 0, 0666},
{ftRegular, "file6", "file6", 0, 0, 0666},
{ftRegular, "file7", "file7", 0, 0, 0666},
{ftDir, "dir1", "", 0, 0, 0777},
{ftRegular, "dir1/file1-1", "file1-1", 0, 0, 0666},
{ftRegular, "dir1/file1-2", "file1-2", 0, 0, 0666},
{ftDir, "dir2", "", 0, 0, 0666},
{ftRegular, "dir2/file2-1", "file2-1", 0, 0, 0666},
{ftRegular, "dir2/file2-2", "file2-2", 0, 0, 0666},
{ftDir, "dir3", "", 0, 0, 0666},
{ftRegular, "dir3/file3-1", "file3-1", 0, 0, 0666},
{ftRegular, "dir3/file3-2", "file3-2", 0, 0, 0666},
{ftDir, "dir4", "", 0, 0, 0666},
{ftRegular, "dir4/file3-1", "file4-1", 0, 0, 0666},
{ftRegular, "dir4/file3-2", "file4-2", 0, 0, 0666},
{ftDir, "dir5", "", 0, 0, 0666},
{ftSymlink, "symlinkToFile1", "file1", 0, 0, 0666},
{ftSymlink, "symlinkToDir1", "dir1", 0, 0, 0666},
{ftSymlink, "brokenSymlinkToFileX", "fileX", 0, 0, 0666},
{ftSymlink, "brokenSymlinkToDirX", "dirX", 0, 0, 0666},
{ftSymlink, "symlinkToAbsDir", "/root", 0, 0, 0666},
{ftDir, "permdirtest", "", 2, 2, 0700},
{ftRegular, "permdirtest/permtest", "perm_test", 65534, 65534, 0400},
}
func defaultMkContentCommand() string {
@ -91,12 +97,16 @@ func makeTestContentInDir(c *check.C, dir string) {
path := filepath.Join(dir, filepath.FromSlash(fd.path))
switch fd.filetype {
case ftRegular:
c.Assert(ioutil.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(0666)), checker.IsNil)
c.Assert(ioutil.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(fd.mode)), checker.IsNil)
case ftDir:
c.Assert(os.Mkdir(path, os.FileMode(0777)), checker.IsNil)
c.Assert(os.Mkdir(path, os.FileMode(fd.mode)), checker.IsNil)
case ftSymlink:
c.Assert(os.Symlink(fd.contents, path), checker.IsNil)
}
if fd.filetype != ftSymlink && runtime.GOOS != "windows" {
c.Assert(os.Chown(path, fd.uid, fd.gid), checker.IsNil)
}
}
}
@ -178,10 +188,16 @@ func containerCpPathTrailingSep(containerID string, pathElements ...string) stri
return fmt.Sprintf("%s/", containerCpPath(containerID, pathElements...))
}
func runDockerCp(c *check.C, src, dst string) (err error) {
c.Logf("running `docker cp %s %s`", src, dst)
func runDockerCp(c *check.C, src, dst string, params []string) (err error) {
c.Logf("running `docker cp %s %s %s`", strings.Join(params, " "), src, dst)
args := []string{"cp", src, dst}
args := []string{"cp"}
for _, param := range params {
args = append(args, param)
}
args = append(args, src, dst)
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
if err != nil {