mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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>
This commit is contained in:
parent
49088b0b89
commit
21278efaee
11 changed files with 16 additions and 20 deletions
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue