Преглед на файлове

Make TarStream return an io.ReadCloser

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 <aaron.lehmann@docker.com>
Aaron Lehmann преди 9 години
родител
ревизия
21278efaee
променени са 11 файла, в които са добавени 16 реда и са изтрити 20 реда
  1. 1 0
      daemon/commit.go
  2. 1 0
      distribution/push_v1.go
  3. 1 0
      distribution/push_v2.go
  4. 2 0
      image/tarexport/save.go
  5. 3 2
      layer/empty.go
  6. 1 1
      layer/layer.go
  7. 1 1
      layer/layer_store.go
  8. 2 0
      layer/layer_test.go
  9. 2 14
      layer/mounted_layer.go
  10. 1 1
      layer/ro_layer.go
  11. 1 1
      migrate/v1/migratev1_test.go

+ 1 - 0
daemon/commit.go

@@ -155,6 +155,7 @@ func (daemon *Daemon) exportContainerRw(container *Container) (archive.Archive,
 		return nil, err
 		return nil, err
 	}
 	}
 	return ioutils.NewReadCloserWrapper(archive, func() error {
 	return ioutils.NewReadCloserWrapper(archive, func() error {
+			archive.Close()
 			return daemon.layerStore.Unmount(container.ID)
 			return daemon.layerStore.Unmount(container.ID)
 		}),
 		}),
 		nil
 		nil

+ 1 - 0
distribution/push_v1.go

@@ -429,6 +429,7 @@ func (p *v1Pusher) pushImage(v1Image v1Image, ep string) (checksum string, err e
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+	defer arch.Close()
 
 
 	// don't care if this fails; best effort
 	// don't care if this fails; best effort
 	size, _ := l.Size()
 	size, _ := l.Size()

+ 1 - 0
distribution/push_v2.go

@@ -365,6 +365,7 @@ func (p *v2Pusher) pushV2Layer(bs distribution.BlobService, l layer.Layer) (dige
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
+	defer arch.Close()
 
 
 	// Send the layer
 	// Send the layer
 	layerUpload, err := bs.Create(context.Background())
 	layerUpload, err := bs.Create(context.Background())

+ 2 - 0
image/tarexport/save.go

@@ -287,6 +287,8 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	defer arch.Close()
+
 	if _, err := io.Copy(tarFile, arch); err != nil {
 	if _, err := io.Copy(tarFile, arch); err != nil {
 		return err
 		return err
 	}
 	}

+ 3 - 2
layer/empty.go

@@ -4,6 +4,7 @@ import (
 	"archive/tar"
 	"archive/tar"
 	"bytes"
 	"bytes"
 	"io"
 	"io"
+	"io/ioutil"
 )
 )
 
 
 // DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file -
 // DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file -
@@ -15,11 +16,11 @@ type emptyLayer struct{}
 // EmptyLayer is a layer that corresponds to empty tar.
 // EmptyLayer is a layer that corresponds to empty tar.
 var EmptyLayer = &emptyLayer{}
 var EmptyLayer = &emptyLayer{}
 
 
-func (el *emptyLayer) TarStream() (io.Reader, error) {
+func (el *emptyLayer) TarStream() (io.ReadCloser, error) {
 	buf := new(bytes.Buffer)
 	buf := new(bytes.Buffer)
 	tarWriter := tar.NewWriter(buf)
 	tarWriter := tar.NewWriter(buf)
 	tarWriter.Close()
 	tarWriter.Close()
-	return buf, nil
+	return ioutil.NopCloser(buf), nil
 }
 }
 
 
 func (el *emptyLayer) ChainID() ChainID {
 func (el *emptyLayer) ChainID() ChainID {

+ 1 - 1
layer/layer.go

@@ -67,7 +67,7 @@ func (diffID DiffID) String() string {
 type TarStreamer interface {
 type TarStreamer interface {
 	// TarStream returns a tar archive stream
 	// TarStream returns a tar archive stream
 	// for the contents of a layer.
 	// for the contents of a layer.
-	TarStream() (io.Reader, error)
+	TarStream() (io.ReadCloser, error)
 }
 }
 
 
 // Layer represents a read only layer
 // Layer represents a read only layer

+ 1 - 1
layer/layer_store.go

@@ -581,7 +581,7 @@ func (ls *layerStore) Changes(name string) ([]archive.Change, error) {
 	return ls.driver.Changes(m.mountID, pid)
 	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 {
 	type diffPathDriver interface {
 		DiffPath(string) (string, func() error, error)
 		DiffPath(string) (string, func() error, error)
 	}
 	}

+ 2 - 0
layer/layer_test.go

@@ -100,6 +100,7 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+	defer ts.Close()
 
 
 	layer, err := ls.Register(ts, parent)
 	layer, err := ls.Register(ts, parent)
 	if err != nil {
 	if err != nil {
@@ -521,6 +522,7 @@ func assertLayerDiff(t *testing.T, expected []byte, layer Layer) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	defer ts.Close()
 
 
 	actual, err := ioutil.ReadAll(ts)
 	actual, err := ioutil.ReadAll(ts)
 	if err != nil {
 	if err != nil {

+ 2 - 14
layer/mounted_layer.go

@@ -22,12 +22,12 @@ func (ml *mountedLayer) cacheParent() string {
 	return ""
 	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())
 	archiver, err := ml.layerStore.driver.Diff(ml.mountID, ml.cacheParent())
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return autoClosingReader{archiver}, nil
+	return archiver, nil
 }
 }
 
 
 func (ml *mountedLayer) Path() (string, error) {
 func (ml *mountedLayer) Path() (string, error) {
@@ -50,15 +50,3 @@ func (ml *mountedLayer) Parent() Layer {
 func (ml *mountedLayer) Size() (int64, error) {
 func (ml *mountedLayer) Size() (int64, error) {
 	return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
 	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
-}

+ 1 - 1
layer/ro_layer.go

@@ -14,7 +14,7 @@ type roLayer struct {
 	references     map[Layer]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)
 	r, err := rl.layerStore.store.TarSplitReader(rl.chainID)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 1 - 1
migrate/v1/migratev1_test.go

@@ -357,7 +357,7 @@ type mockLayer struct {
 	parent  *mockLayer
 	parent  *mockLayer
 }
 }
 
 
-func (l *mockLayer) TarStream() (io.Reader, error) {
+func (l *mockLayer) TarStream() (io.ReadCloser, error) {
 	return nil, nil
 	return nil, nil
 }
 }