Merge pull request #3477 from creack/fix_tar_leaks

Make CopyFileWithTar use a pipe instead of a buffer. Save more than 1Gb of ram for a 500Mb file.
This commit is contained in:
Michael Crosby 2014-01-06 18:04:44 -08:00
commit fca83b4cfb

View file

@ -3,8 +3,8 @@ package archive
import ( import (
"archive/tar" "archive/tar"
"bytes" "bytes"
"compress/gzip"
"compress/bzip2" "compress/bzip2"
"compress/gzip"
"fmt" "fmt"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
"io" "io"
@ -299,7 +299,7 @@ func CopyWithTar(src, dst string) error {
// //
// If `dst` ends with a trailing slash '/', the final destination path // If `dst` ends with a trailing slash '/', the final destination path
// will be `dst/base(src)`. // will be `dst/base(src)`.
func CopyFileWithTar(src, dst string) error { func CopyFileWithTar(src, dst string) (err error) {
utils.Debugf("CopyFileWithTar(%s, %s)", src, dst) utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
srcSt, err := os.Stat(src) srcSt, err := os.Stat(src)
if err != nil { if err != nil {
@ -316,25 +316,38 @@ func CopyFileWithTar(src, dst string) error {
if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) { if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
return err return err
} }
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf) r, w := io.Pipe()
hdr, err := tar.FileInfoHeader(srcSt, "") errC := utils.Go(func() error {
if err != nil { defer w.Close()
return err
} srcF, err := os.Open(src)
hdr.Name = filepath.Base(dst) if err != nil {
if err := tw.WriteHeader(hdr); err != nil { return err
return err }
} defer srcF.Close()
srcF, err := os.Open(src)
if err != nil { tw := tar.NewWriter(w)
return err hdr, err := tar.FileInfoHeader(srcSt, "")
} if err != nil {
if _, err := io.Copy(tw, srcF); err != nil { return err
return err }
} hdr.Name = filepath.Base(dst)
tw.Close() if err := tw.WriteHeader(hdr); err != nil {
return Untar(buf, filepath.Dir(dst), nil) return err
}
if _, err := io.Copy(tw, srcF); err != nil {
return err
}
tw.Close()
return nil
})
defer func() {
if er := <-errC; err != nil {
err = er
}
}()
return Untar(r, filepath.Dir(dst), nil)
} }
// CmdStream executes a command, and returns its stdout as a stream. // CmdStream executes a command, and returns its stdout as a stream.