diff --git a/components/engine/daemon/cluster/configs.go b/components/engine/daemon/cluster/configs.go index 6b373e618b..f44adb284e 100644 --- a/components/engine/daemon/cluster/configs.go +++ b/components/engine/daemon/cluster/configs.go @@ -7,6 +7,7 @@ import ( types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/convert" swarmapi "github.com/docker/swarmkit/api" + "google.golang.org/grpc" ) // GetConfig returns a config from a managed swarm cluster @@ -44,7 +45,8 @@ func (c *Cluster) GetConfigs(options apitypes.ConfigListOptions) ([]types.Config defer cancel() r, err := state.controlClient.ListConfigs(ctx, - &swarmapi.ListConfigsRequest{Filters: filters}) + &swarmapi.ListConfigsRequest{Filters: filters}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse)) if err != nil { return nil, err } diff --git a/components/engine/layer/layer_store.go b/components/engine/layer/layer_store.go index bc3e8719fc..1601465c04 100644 --- a/components/engine/layer/layer_store.go +++ b/components/engine/layer/layer_store.go @@ -253,13 +253,14 @@ func (ls *layerStore) applyTar(tx *fileMetadataTransaction, ts io.Reader, parent } applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, rdr) + // discard trailing data but ensure metadata is picked up to reconstruct stream + // unconditionally call io.Copy here before checking err to ensure the resources + // allocated by NewInputTarStream above are always released + io.Copy(ioutil.Discard, rdr) // ignore error as reader may be closed if err != nil { return err } - // Discard trailing data but ensure metadata is picked up to reconstruct stream - io.Copy(ioutil.Discard, rdr) // ignore error as reader may be closed - layer.size = applySize layer.diffID = DiffID(digester.Digest()) diff --git a/components/engine/pkg/archive/copy.go b/components/engine/pkg/archive/copy.go index d0f13ca79b..57fddac078 100644 --- a/components/engine/pkg/archive/copy.go +++ b/components/engine/pkg/archive/copy.go @@ -336,6 +336,14 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read return } + // srcContent tar stream, as served by TarWithOptions(), is + // definitely in PAX format, but tar.Next() mistakenly guesses it + // as USTAR, which creates a problem: if the newBase is >100 + // characters long, WriteHeader() returns an error like + // "archive/tar: cannot encode header: Format specifies USTAR; and USTAR cannot encode Name=...". + // + // To fix, set the format to PAX here. See docker/for-linux issue #484. + hdr.Format = tar.FormatPAX hdr.Name = strings.Replace(hdr.Name, oldBase, newBase, 1) if hdr.Typeflag == tar.TypeLink { hdr.Linkname = strings.Replace(hdr.Linkname, oldBase, newBase, 1) diff --git a/components/engine/pkg/archive/copy_unix_test.go b/components/engine/pkg/archive/copy_unix_test.go index 739ad0e3ef..ca0f4653af 100644 --- a/components/engine/pkg/archive/copy_unix_test.go +++ b/components/engine/pkg/archive/copy_unix_test.go @@ -257,6 +257,30 @@ func TestCopyErrDstNotDir(t *testing.T) { } } +// Test to check if CopyTo works with a long (>100 characters) destination file name. +// This is a regression (see https://github.com/docker/for-linux/issues/484). +func TestCopyLongDstFilename(t *testing.T) { + const longName = "a_very_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_long_filename_that_is_101_characters" + tmpDirA, tmpDirB := getTestTempDirs(t) + defer removeAllPaths(tmpDirA, tmpDirB) + + // Load A with some sample files and directories. + createSampleDir(t, tmpDirA) + + srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} + + content, err := TarResource(srcInfo) + if err != nil { + t.Fatalf("unexpected error %T: %s", err, err) + } + defer content.Close() + + err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, longName)) + if err != nil { + t.Fatalf("unexpected error %T: %s", err, err) + } +} + // Possibilities are reduced to the remaining 10 cases: // // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action