forked from DataDog/nikos
/
tarball.go
92 lines (81 loc) · 2.47 KB
/
tarball.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package extract
import (
"archive/tar"
"compress/bzip2"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/jeffweiss/nikos/types"
"github.com/DataDog/zstd"
"github.com/xi2/xz"
)
type onlyWriter struct {
io.Writer
}
func ExtractTarball(reader io.Reader, filename, directory string, logger types.Logger) error {
var compressedTarReader io.Reader
var err error
switch filepath.Ext(filename) {
case ".xz":
compressedTarReader, err = xz.NewReader(reader, 0)
case ".gz", ".tgz":
compressedTarReader, err = gzip.NewReader(reader)
case ".bz2":
compressedTarReader = bzip2.NewReader(reader)
case ".zst":
zstdReader := zstd.NewReader(reader)
defer zstdReader.Close()
compressedTarReader = zstdReader
default:
return fmt.Errorf("failed to extract %s", filename)
}
if err != nil {
return fmt.Errorf("failed to read %s: %w", filename, err)
}
buf := make([]byte, 50)
tarReader := tar.NewReader(compressedTarReader)
for {
hdr, err := tarReader.Next()
if err == io.EOF {
break // End of archive
}
if err != nil {
return fmt.Errorf("failed to read entry from tarball: %w", err)
}
path := filepath.Join(directory, hdr.Name)
// logger.Debugf("Extracting %s to %s", hdr.Name, path)
switch hdr.Typeflag {
case tar.TypeSymlink:
// If the symlink is to an absolute path, prefix it with the download directory
if strings.HasPrefix(hdr.Linkname, "/") {
os.Symlink(filepath.Join(directory, hdr.Linkname), path)
continue
}
// If the symlink is to a relative path, leave it be
os.Symlink(hdr.Linkname, path)
case tar.TypeDir:
os.MkdirAll(path, 0755)
case tar.TypeReg:
output, err := os.Create(path)
if err != nil {
return fmt.Errorf("failed to create output file '%s': %w", path, err)
}
// By default, an os.File implements the io.ReaderFrom interface.
// As a result, CopyBuffer will attempt to use the output.ReadFrom method to perform
// the requested copy, which ends up calling the unbuffered io.Copy function & performing
// a large number of allocations.
// In order to force CopyBuffer to actually utilize the given buffer, we have to ensure
// output does not implement the io.ReaderFrom interface.
if _, err := io.CopyBuffer(onlyWriter{output}, tarReader, buf); err != nil {
return fmt.Errorf("failed to uncompress file %s: %w", hdr.Name, err)
}
output.Close()
default:
logger.Warnf("Unsupported header flag '%d' for '%s'", hdr.Typeflag, hdr.Name)
}
}
return nil
}