Browse code

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>

Vincent Batts authored on 2015/07/27 22:46:20
Showing 5 changed files
... ...
@@ -633,8 +633,20 @@ loop:
633 633
 // The archive may be compressed with one of the following algorithms:
634 634
 //  identity (uncompressed), gzip, bzip2, xz.
635 635
 // FIXME: specify behavior when target path exists vs. doesn't exist.
636
-func Untar(archive io.Reader, dest string, options *TarOptions) error {
637
-	if archive == nil {
636
+func Untar(tarArchive io.Reader, dest string, options *TarOptions) error {
637
+	return untarHandler(tarArchive, dest, options, true)
638
+}
639
+
640
+// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
641
+// and unpacks it into the directory at `dest`.
642
+// The archive must be an uncompressed stream.
643
+func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) error {
644
+	return untarHandler(tarArchive, dest, options, false)
645
+}
646
+
647
+// Handler for teasing out the automatic decompression
648
+func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error {
649
+	if tarArchive == nil {
638 650
 		return fmt.Errorf("Empty archive")
639 651
 	}
640 652
 	dest = filepath.Clean(dest)
... ...
@@ -644,12 +656,18 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
644 644
 	if options.ExcludePatterns == nil {
645 645
 		options.ExcludePatterns = []string{}
646 646
 	}
647
-	decompressedArchive, err := DecompressStream(archive)
648
-	if err != nil {
649
-		return err
647
+
648
+	var r io.Reader = tarArchive
649
+	if decompress {
650
+		decompressedArchive, err := DecompressStream(tarArchive)
651
+		if err != nil {
652
+			return err
653
+		}
654
+		defer decompressedArchive.Close()
655
+		r = decompressedArchive
650 656
 	}
651
-	defer decompressedArchive.Close()
652
-	return Unpack(decompressedArchive, dest, options)
657
+
658
+	return Unpack(r, dest, options)
653 659
 }
654 660
 
655 661
 func (archiver *Archiver) TarUntar(src, dst string) error {
... ...
@@ -173,10 +173,24 @@ func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) {
173 173
 	return size, nil
174 174
 }
175 175
 
176
-// ApplyLayer parses a diff in the standard layer format from `layer`, and
177
-// applies it to the directory `dest`. Returns the size in bytes of the
178
-// contents of the layer.
176
+// ApplyLayer parses a diff in the standard layer format from `layer`,
177
+// and applies it to the directory `dest`. The stream `layer` can be
178
+// compressed or uncompressed.
179
+// Returns the size in bytes of the contents of the layer.
179 180
 func ApplyLayer(dest string, layer ArchiveReader) (int64, error) {
181
+	return applyLayerHandler(dest, layer, true)
182
+}
183
+
184
+// ApplyUncompressedLayer parses a diff in the standard layer format from
185
+// `layer`, and applies it to the directory `dest`. The stream `layer`
186
+// can only be uncompressed.
187
+// Returns the size in bytes of the contents of the layer.
188
+func ApplyUncompressedLayer(dest string, layer ArchiveReader) (int64, error) {
189
+	return applyLayerHandler(dest, layer, false)
190
+}
191
+
192
+// do the bulk load of ApplyLayer, but allow for not calling DecompressStream
193
+func applyLayerHandler(dest string, layer ArchiveReader, decompress bool) (int64, error) {
180 194
 	dest = filepath.Clean(dest)
181 195
 
182 196
 	// We need to be able to set any perms
... ...
@@ -186,9 +200,11 @@ func ApplyLayer(dest string, layer ArchiveReader) (int64, error) {
186 186
 	}
187 187
 	defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform
188 188
 
189
-	layer, err = DecompressStream(layer)
190
-	if err != nil {
191
-		return 0, err
189
+	if decompress {
190
+		layer, err = DecompressStream(layer)
191
+		if err != nil {
192
+			return 0, err
193
+		}
192 194
 	}
193 195
 	return UnpackLayer(dest, layer)
194 196
 }
... ...
@@ -3,6 +3,7 @@ package chrootarchive
3 3
 import (
4 4
 	"fmt"
5 5
 	"io"
6
+	"io/ioutil"
6 7
 	"os"
7 8
 	"path/filepath"
8 9
 
... ...
@@ -17,6 +18,18 @@ var chrootArchiver = &archive.Archiver{Untar: Untar}
17 17
 // The archive may be compressed with one of the following algorithms:
18 18
 //  identity (uncompressed), gzip, bzip2, xz.
19 19
 func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
20
+	return untarHandler(tarArchive, dest, options, true)
21
+}
22
+
23
+// UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive,
24
+// and unpacks it into the directory at `dest`.
25
+// The archive must be an uncompressed stream.
26
+func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
27
+	return untarHandler(tarArchive, dest, options, false)
28
+}
29
+
30
+// Handler for teasing out the automatic decompression
31
+func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool) error {
20 32
 
21 33
 	if tarArchive == nil {
22 34
 		return fmt.Errorf("Empty archive")
... ...
@@ -35,13 +48,17 @@ func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error
35 35
 		}
36 36
 	}
37 37
 
38
-	decompressedArchive, err := archive.DecompressStream(tarArchive)
39
-	if err != nil {
40
-		return err
38
+	r := ioutil.NopCloser(tarArchive)
39
+	if decompress {
40
+		decompressedArchive, err := archive.DecompressStream(tarArchive)
41
+		if err != nil {
42
+			return err
43
+		}
44
+		defer decompressedArchive.Close()
45
+		r = decompressedArchive
41 46
 	}
42
-	defer decompressedArchive.Close()
43 47
 
44
-	return invokeUnpack(decompressedArchive, dest, options)
48
+	return invokeUnpack(r, dest, options)
45 49
 }
46 50
 
47 51
 // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
... ...
@@ -49,7 +49,7 @@ func untar() {
49 49
 	os.Exit(0)
50 50
 }
51 51
 
52
-func invokeUnpack(decompressedArchive io.ReadCloser, dest string, options *archive.TarOptions) error {
52
+func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions) error {
53 53
 
54 54
 	// We can't pass a potentially large exclude list directly via cmd line
55 55
 	// because we easily overrun the kernel's max argument/environment size
... ...
@@ -65,20 +65,36 @@ func applyLayer() {
65 65
 	os.Exit(0)
66 66
 }
67 67
 
68
-// ApplyLayer parses a diff in the standard layer format from `layer`, and
69
-// applies it to the directory `dest`. Returns the size in bytes of the
70
-// contents of the layer.
68
+// ApplyLayer parses a diff in the standard layer format from `layer`,
69
+// and applies it to the directory `dest`. The stream `layer` can only be
70
+// uncompressed.
71
+// Returns the size in bytes of the contents of the layer.
71 72
 func ApplyLayer(dest string, layer archive.ArchiveReader) (size int64, err error) {
73
+	return applyLayerHandler(dest, layer, true)
74
+}
75
+
76
+// ApplyUncompressedLayer parses a diff in the standard layer format from
77
+// `layer`, and applies it to the directory `dest`. The stream `layer`
78
+// can only be uncompressed.
79
+// Returns the size in bytes of the contents of the layer.
80
+func ApplyUncompressedLayer(dest string, layer archive.ArchiveReader) (int64, error) {
81
+	return applyLayerHandler(dest, layer, false)
82
+}
83
+
84
+func applyLayerHandler(dest string, layer archive.ArchiveReader, decompress bool) (size int64, err error) {
72 85
 	dest = filepath.Clean(dest)
73
-	decompressed, err := archive.DecompressStream(layer)
74
-	if err != nil {
75
-		return 0, err
86
+	if decompress {
87
+		decompressed, err := archive.DecompressStream(layer)
88
+		if err != nil {
89
+			return 0, err
90
+		}
91
+		defer decompressed.Close()
92
+
93
+		layer = decompressed
76 94
 	}
77 95
 
78
-	defer decompressed.Close()
79
-
80 96
 	cmd := reexec.Command("docker-applyLayer", dest)
81
-	cmd.Stdin = decompressed
97
+	cmd.Stdin = layer
82 98
 
83 99
 	outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer)
84 100
 	cmd.Stdout, cmd.Stderr = outBuf, errBuf