archive, chrootarchive: split out decompression

In `ApplyLayer` and `Untar`, the stream is magically decompressed. Since
this is not able to be toggled, rather than break this ./pkg/ API, add
an `ApplyUncompressedLayer` and `UntarUncompressed` that does not
magically decompress the layer stream.

Signed-off-by: Vincent Batts <vbatts@redhat.com>
This commit is contained in:
Vincent Batts 2015-07-27 09:46:20 -04:00
parent 65b22e7a78
commit f0512440f7
5 changed files with 95 additions and 28 deletions

View file

@ -633,8 +633,20 @@ loop:
// The archive may be compressed with one of the following algorithms: // The archive may be compressed with one of the following algorithms:
// identity (uncompressed), gzip, bzip2, xz. // identity (uncompressed), gzip, bzip2, xz.
// FIXME: specify behavior when target path exists vs. doesn't exist. // FIXME: specify behavior when target path exists vs. doesn't exist.
func Untar(archive io.Reader, dest string, options *TarOptions) error { func Untar(tarArchive io.Reader, dest string, options *TarOptions) error {
if archive == nil { return untarHandler(tarArchive, dest, options, true)
}
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
// and unpacks it into the directory at `dest`.
// The archive must be an uncompressed stream.
func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) error {
return untarHandler(tarArchive, dest, options, false)
}
// Handler for teasing out the automatic decompression
func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error {
if tarArchive == nil {
return fmt.Errorf("Empty archive") return fmt.Errorf("Empty archive")
} }
dest = filepath.Clean(dest) dest = filepath.Clean(dest)
@ -644,12 +656,18 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
if options.ExcludePatterns == nil { if options.ExcludePatterns == nil {
options.ExcludePatterns = []string{} options.ExcludePatterns = []string{}
} }
decompressedArchive, err := DecompressStream(archive)
if err != nil { var r io.Reader = tarArchive
return err if decompress {
decompressedArchive, err := DecompressStream(tarArchive)
if err != nil {
return err
}
defer decompressedArchive.Close()
r = decompressedArchive
} }
defer decompressedArchive.Close()
return Unpack(decompressedArchive, dest, options) return Unpack(r, dest, options)
} }
func (archiver *Archiver) TarUntar(src, dst string) error { func (archiver *Archiver) TarUntar(src, dst string) error {

View file

@ -173,10 +173,24 @@ func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) {
return size, nil return size, nil
} }
// ApplyLayer parses a diff in the standard layer format from `layer`, and // ApplyLayer parses a diff in the standard layer format from `layer`,
// applies it to the directory `dest`. Returns the size in bytes of the // and applies it to the directory `dest`. The stream `layer` can be
// contents of the layer. // compressed or uncompressed.
// Returns the size in bytes of the contents of the layer.
func ApplyLayer(dest string, layer ArchiveReader) (int64, error) { func ApplyLayer(dest string, layer ArchiveReader) (int64, error) {
return applyLayerHandler(dest, layer, true)
}
// ApplyUncompressedLayer parses a diff in the standard layer format from
// `layer`, and applies it to the directory `dest`. The stream `layer`
// can only be uncompressed.
// Returns the size in bytes of the contents of the layer.
func ApplyUncompressedLayer(dest string, layer ArchiveReader) (int64, error) {
return applyLayerHandler(dest, layer, false)
}
// do the bulk load of ApplyLayer, but allow for not calling DecompressStream
func applyLayerHandler(dest string, layer ArchiveReader, decompress bool) (int64, error) {
dest = filepath.Clean(dest) dest = filepath.Clean(dest)
// We need to be able to set any perms // We need to be able to set any perms
@ -186,9 +200,11 @@ func ApplyLayer(dest string, layer ArchiveReader) (int64, error) {
} }
defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform
layer, err = DecompressStream(layer) if decompress {
if err != nil { layer, err = DecompressStream(layer)
return 0, err if err != nil {
return 0, err
}
} }
return UnpackLayer(dest, layer) return UnpackLayer(dest, layer)
} }

View file

@ -3,6 +3,7 @@ package chrootarchive
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -17,6 +18,18 @@ var chrootArchiver = &archive.Archiver{Untar: Untar}
// The archive may be compressed with one of the following algorithms: // The archive may be compressed with one of the following algorithms:
// identity (uncompressed), gzip, bzip2, xz. // identity (uncompressed), gzip, bzip2, xz.
func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error { func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
return untarHandler(tarArchive, dest, options, true)
}
// UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive,
// and unpacks it into the directory at `dest`.
// The archive must be an uncompressed stream.
func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
return untarHandler(tarArchive, dest, options, false)
}
// Handler for teasing out the automatic decompression
func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool) error {
if tarArchive == nil { if tarArchive == nil {
return fmt.Errorf("Empty archive") return fmt.Errorf("Empty archive")
@ -35,13 +48,17 @@ func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error
} }
} }
decompressedArchive, err := archive.DecompressStream(tarArchive) r := ioutil.NopCloser(tarArchive)
if err != nil { if decompress {
return err decompressedArchive, err := archive.DecompressStream(tarArchive)
if err != nil {
return err
}
defer decompressedArchive.Close()
r = decompressedArchive
} }
defer decompressedArchive.Close()
return invokeUnpack(decompressedArchive, dest, options) return invokeUnpack(r, dest, options)
} }
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other. // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.

View file

@ -49,7 +49,7 @@ func untar() {
os.Exit(0) os.Exit(0)
} }
func invokeUnpack(decompressedArchive io.ReadCloser, dest string, options *archive.TarOptions) error { func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions) error {
// We can't pass a potentially large exclude list directly via cmd line // We can't pass a potentially large exclude list directly via cmd line
// because we easily overrun the kernel's max argument/environment size // because we easily overrun the kernel's max argument/environment size

View file

@ -65,20 +65,36 @@ func applyLayer() {
os.Exit(0) os.Exit(0)
} }
// ApplyLayer parses a diff in the standard layer format from `layer`, and // ApplyLayer parses a diff in the standard layer format from `layer`,
// applies it to the directory `dest`. Returns the size in bytes of the // and applies it to the directory `dest`. The stream `layer` can only be
// contents of the layer. // uncompressed.
// Returns the size in bytes of the contents of the layer.
func ApplyLayer(dest string, layer archive.ArchiveReader) (size int64, err error) { func ApplyLayer(dest string, layer archive.ArchiveReader) (size int64, err error) {
return applyLayerHandler(dest, layer, true)
}
// ApplyUncompressedLayer parses a diff in the standard layer format from
// `layer`, and applies it to the directory `dest`. The stream `layer`
// can only be uncompressed.
// Returns the size in bytes of the contents of the layer.
func ApplyUncompressedLayer(dest string, layer archive.ArchiveReader) (int64, error) {
return applyLayerHandler(dest, layer, false)
}
func applyLayerHandler(dest string, layer archive.ArchiveReader, decompress bool) (size int64, err error) {
dest = filepath.Clean(dest) dest = filepath.Clean(dest)
decompressed, err := archive.DecompressStream(layer) if decompress {
if err != nil { decompressed, err := archive.DecompressStream(layer)
return 0, err if err != nil {
return 0, err
}
defer decompressed.Close()
layer = decompressed
} }
defer decompressed.Close()
cmd := reexec.Command("docker-applyLayer", dest) cmd := reexec.Command("docker-applyLayer", dest)
cmd.Stdin = decompressed cmd.Stdin = layer
outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer) outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer)
cmd.Stdout, cmd.Stderr = outBuf, errBuf cmd.Stdout, cmd.Stderr = outBuf, errBuf