2016-01-29 20:53:56 +01:00
|
|
|
package getter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/tar"
|
|
|
|
"compress/gzip"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TarGzipDecompressor is an implementation of Decompressor that can
|
|
|
|
// decompress tar.gzip files.
|
|
|
|
type TarGzipDecompressor struct{}
|
|
|
|
|
|
|
|
func (d *TarGzipDecompressor) Decompress(dst, src string, dir bool) error {
|
|
|
|
// If we're going into a directory we should make that first
|
|
|
|
mkdir := dst
|
|
|
|
if !dir {
|
|
|
|
mkdir = filepath.Dir(dst)
|
|
|
|
}
|
|
|
|
if err := os.MkdirAll(mkdir, 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// File first
|
|
|
|
f, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
// Gzip compression is second
|
|
|
|
gzipR, err := gzip.NewReader(f)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error opening a gzip reader for %s: %s", src, err)
|
|
|
|
}
|
|
|
|
defer gzipR.Close()
|
|
|
|
|
|
|
|
// Once gzip decompressed we have a tar format
|
|
|
|
tarR := tar.NewReader(gzipR)
|
|
|
|
done := false
|
|
|
|
for {
|
|
|
|
hdr, err := tarR.Next()
|
|
|
|
if err == io.EOF {
|
|
|
|
if !done {
|
|
|
|
// Empty archive
|
|
|
|
return fmt.Errorf("empty archive: %s", src)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := dst
|
|
|
|
if dir {
|
|
|
|
path = filepath.Join(path, hdr.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if hdr.FileInfo().IsDir() {
|
2016-03-20 23:01:11 +01:00
|
|
|
if !dir {
|
2016-01-29 20:53:56 +01:00
|
|
|
return fmt.Errorf("expected a single file: %s", src)
|
|
|
|
}
|
|
|
|
|
|
|
|
// A directory, just make the directory and continue unarchiving...
|
|
|
|
if err := os.MkdirAll(path, 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a file. If we already decoded, then it is an error
|
|
|
|
if !dir && done {
|
|
|
|
return fmt.Errorf("expected a single file, got multiple: %s", src)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark that we're done so future in single file mode errors
|
|
|
|
done = true
|
|
|
|
|
|
|
|
// Open the file for writing
|
|
|
|
dstF, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = io.Copy(dstF, tarR)
|
|
|
|
dstF.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Chmod the file
|
|
|
|
if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|