Skip to content

Commit

Permalink
Make TarStream return an io.ReadCloser
Browse files Browse the repository at this point in the history
Currently, the resources associated with the io.Reader returned by
TarStream are only freed when it is read until EOF. This means that
partial uploads or exports (for example, in the case of a full disk or
severed connection) can leak a goroutine and open file. This commit
changes TarStream to return an io.ReadCloser. Resources are freed when
Close is called.

Signed-off-by: Aaron Lehmann <[email protected]>
  • Loading branch information
aaronlehmann committed Nov 26, 2015
1 parent 49088b0 commit 21278ef
Show file tree
Hide file tree
Showing 11 changed files with 16 additions and 20 deletions.
1 change: 1 addition & 0 deletions daemon/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func (daemon *Daemon) exportContainerRw(container *Container) (archive.Archive,
return nil, err
}
return ioutils.NewReadCloserWrapper(archive, func() error {
archive.Close()
return daemon.layerStore.Unmount(container.ID)
}),
nil
Expand Down
1 change: 1 addition & 0 deletions distribution/push_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ func (p *v1Pusher) pushImage(v1Image v1Image, ep string) (checksum string, err e
if err != nil {
return "", err
}
defer arch.Close()

// don't care if this fails; best effort
size, _ := l.Size()
Expand Down
1 change: 1 addition & 0 deletions distribution/push_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ func (p *v2Pusher) pushV2Layer(bs distribution.BlobService, l layer.Layer) (dige
if err != nil {
return "", err
}
defer arch.Close()

// Send the layer
layerUpload, err := bs.Create(context.Background())
Expand Down
2 changes: 2 additions & 0 deletions image/tarexport/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
if err != nil {
return err
}
defer arch.Close()

if _, err := io.Copy(tarFile, arch); err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions layer/empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"archive/tar"
"bytes"
"io"
"io/ioutil"
)

// DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file -
Expand All @@ -15,11 +16,11 @@ type emptyLayer struct{}
// EmptyLayer is a layer that corresponds to empty tar.
var EmptyLayer = &emptyLayer{}

func (el *emptyLayer) TarStream() (io.Reader, error) {
func (el *emptyLayer) TarStream() (io.ReadCloser, error) {
buf := new(bytes.Buffer)
tarWriter := tar.NewWriter(buf)
tarWriter.Close()
return buf, nil
return ioutil.NopCloser(buf), nil
}

func (el *emptyLayer) ChainID() ChainID {
Expand Down
2 changes: 1 addition & 1 deletion layer/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (diffID DiffID) String() string {
type TarStreamer interface {
// TarStream returns a tar archive stream
// for the contents of a layer.
TarStream() (io.Reader, error)
TarStream() (io.ReadCloser, error)
}

// Layer represents a read only layer
Expand Down
2 changes: 1 addition & 1 deletion layer/layer_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ func (ls *layerStore) Changes(name string) ([]archive.Change, error) {
return ls.driver.Changes(m.mountID, pid)
}

func (ls *layerStore) assembleTar(graphID string, metadata io.ReadCloser, size *int64) (io.Reader, error) {
func (ls *layerStore) assembleTar(graphID string, metadata io.ReadCloser, size *int64) (io.ReadCloser, error) {
type diffPathDriver interface {
DiffPath(string) (string, func() error, error)
}
Expand Down
2 changes: 2 additions & 0 deletions layer/layer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
if err != nil {
return nil, err
}
defer ts.Close()

layer, err := ls.Register(ts, parent)
if err != nil {
Expand Down Expand Up @@ -521,6 +522,7 @@ func assertLayerDiff(t *testing.T, expected []byte, layer Layer) {
if err != nil {
t.Fatal(err)
}
defer ts.Close()

actual, err := ioutil.ReadAll(ts)
if err != nil {
Expand Down
16 changes: 2 additions & 14 deletions layer/mounted_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ func (ml *mountedLayer) cacheParent() string {
return ""
}

func (ml *mountedLayer) TarStream() (io.Reader, error) {
func (ml *mountedLayer) TarStream() (io.ReadCloser, error) {
archiver, err := ml.layerStore.driver.Diff(ml.mountID, ml.cacheParent())
if err != nil {
return nil, err
}
return autoClosingReader{archiver}, nil
return archiver, nil
}

func (ml *mountedLayer) Path() (string, error) {
Expand All @@ -50,15 +50,3 @@ func (ml *mountedLayer) Parent() Layer {
func (ml *mountedLayer) Size() (int64, error) {
return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
}

type autoClosingReader struct {
source io.ReadCloser
}

func (r autoClosingReader) Read(p []byte) (n int, err error) {
n, err = r.source.Read(p)
if err != nil {
r.source.Close()
}
return
}
2 changes: 1 addition & 1 deletion layer/ro_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type roLayer struct {
references map[Layer]struct{}
}

func (rl *roLayer) TarStream() (io.Reader, error) {
func (rl *roLayer) TarStream() (io.ReadCloser, error) {
r, err := rl.layerStore.store.TarSplitReader(rl.chainID)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion migrate/v1/migratev1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ type mockLayer struct {
parent *mockLayer
}

func (l *mockLayer) TarStream() (io.Reader, error) {
func (l *mockLayer) TarStream() (io.ReadCloser, error) {
return nil, nil
}

Expand Down

0 comments on commit 21278ef

Please sign in to comment.