Skip to content
This repository was archived by the owner on Oct 13, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions daemon/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,13 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
if driver.Base(resolvedPath) == "." {
resolvedPath += string(driver.Separator()) + "."
}
sourceDir, sourceBase := driver.Dir(resolvedPath), driver.Base(resolvedPath)

sourceDir := resolvedPath
sourceBase := "."

if stat.Mode&os.ModeDir == 0 { // not dir
sourceDir, sourceBase = driver.Split(resolvedPath)
}
opts := archive.TarResourceRebaseOpts(sourceBase, driver.Base(absPath))

data, err := archivePath(driver, sourceDir, opts, container.BaseFS.Path())
Expand Down Expand Up @@ -426,9 +432,6 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
d, f := driver.Split(basePath)
basePath = d
filter = []string{f}
} else {
filter = []string{driver.Base(basePath)}
basePath = driver.Dir(basePath)
}
archive, err := archivePath(driver, basePath, &archive.TarOptions{
Compression: archive.Uncompressed,
Expand Down
97 changes: 97 additions & 0 deletions integration/container/copy_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package container // import "github.com/docker/docker/integration/container"

import (
"archive/tar"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"testing"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/internal/test/fakecontext"
"github.com/docker/docker/pkg/jsonmessage"
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
"gotest.tools/skip"
Expand Down Expand Up @@ -64,3 +71,93 @@ func TestCopyToContainerPathIsNotDir(t *testing.T) {
err := apiclient.CopyToContainer(ctx, cid, "/etc/passwd/", nil, types.CopyToContainerOptions{})
assert.Assert(t, is.ErrorContains(err, "not a directory"))
}

func TestCopyFromContainer(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
defer setupTest(t)()

ctx := context.Background()
apiClient := testEnv.APIClient()

dir, err := ioutil.TempDir("", t.Name())
assert.NilError(t, err)
defer os.RemoveAll(dir)

buildCtx := fakecontext.New(t, dir, fakecontext.WithFile("foo", "hello"), fakecontext.WithFile("baz", "world"), fakecontext.WithDockerfile(`
FROM busybox
COPY foo /foo
COPY baz /bar/quux/baz
RUN ln -s notexist /bar/notarget && ln -s quux/baz /bar/filesymlink && ln -s quux /bar/dirsymlink && ln -s / /bar/root
CMD /fake
`))
defer buildCtx.Close()

resp, err := apiClient.ImageBuild(ctx, buildCtx.AsTarReader(t), types.ImageBuildOptions{})
assert.NilError(t, err)
defer resp.Body.Close()

var imageID string
err = jsonmessage.DisplayJSONMessagesStream(resp.Body, ioutil.Discard, 0, false, func(msg jsonmessage.JSONMessage) {
var r types.BuildResult
assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
imageID = r.ID
})
assert.NilError(t, err)
assert.Assert(t, imageID != "")

cid := container.Create(t, ctx, apiClient, container.WithImage(imageID))

for _, x := range []struct {
src string
expect map[string]string
}{
{"/", map[string]string{"/": "", "/foo": "hello", "/bar/quux/baz": "world", "/bar/filesymlink": "", "/bar/dirsymlink": "", "/bar/notarget": ""}},
{"/bar/root", map[string]string{"root": ""}},
{"/bar/root/", map[string]string{"root/": "", "root/foo": "hello", "root/bar/quux/baz": "world", "root/bar/filesymlink": "", "root/bar/dirsymlink": "", "root/bar/notarget": ""}},

{"bar/quux", map[string]string{"quux/": "", "quux/baz": "world"}},
{"bar/quux/", map[string]string{"quux/": "", "quux/baz": "world"}},
{"bar/quux/baz", map[string]string{"baz": "world"}},

{"bar/filesymlink", map[string]string{"filesymlink": ""}},
{"bar/dirsymlink", map[string]string{"dirsymlink": ""}},
{"bar/dirsymlink/", map[string]string{"dirsymlink/": "", "dirsymlink/baz": "world"}},
{"bar/notarget", map[string]string{"notarget": ""}},
} {
t.Run(x.src, func(t *testing.T) {
rdr, _, err := apiClient.CopyFromContainer(ctx, cid, x.src)
assert.NilError(t, err)
defer rdr.Close()

found := make(map[string]bool, len(x.expect))
var numFound int
tr := tar.NewReader(rdr)
for numFound < len(x.expect) {
h, err := tr.Next()
if err == io.EOF {
break
}
assert.NilError(t, err)

expected, exists := x.expect[h.Name]
if !exists {
// this archive will have extra stuff in it since we are copying from root
// and docker adds a bunch of stuff
continue
}

numFound++
found[h.Name] = true

buf, err := ioutil.ReadAll(tr)
if err == nil {
assert.Check(t, is.Equal(string(buf), expected))
}
}

for f := range x.expect {
assert.Check(t, found[f], f+" not found in archive")
}
})
}
}
3 changes: 2 additions & 1 deletion pkg/archive/archive_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"os"
"path/filepath"
"strings"
"syscall"

"github.com/docker/docker/pkg/idtools"
Expand All @@ -26,7 +27,7 @@ func fixVolumePathPrefix(srcPath string) string {
// can't use filepath.Join(srcPath,include) because this will clean away
// a trailing "." or "/" which may be important.
func getWalkRoot(srcPath string, include string) string {
return srcPath + string(filepath.Separator) + include
return strings.TrimSuffix(srcPath, string(filepath.Separator)) + string(filepath.Separator) + include
}

// CanonicalTarNameForPath returns platform-specific filepath
Expand Down