...
1 package compression
2
3 import (
4 "archive/tar"
5 "fmt"
6 "io"
7 "log"
8 "os"
9 "path/filepath"
10
11 "github.com/xi2/xz"
12 )
13
14
15
16 func DecompressTarXz(txzFilePath, outputPath string) error {
17 tarFile, err := os.Open(txzFilePath)
18 if err != nil {
19 return errorUnableToExtract(txzFilePath, outputPath)
20 }
21
22 defer func() {
23 if err := tarFile.Close(); err != nil {
24 log.Printf("could not close txz tar file: %s, error %v\n", txzFilePath, err)
25 }
26 }()
27
28 xzReader, err := xz.NewReader(tarFile, 0)
29 if err != nil {
30 return errorUnableToExtract(txzFilePath, outputPath)
31 }
32
33 readNext, reader := defaultTarReader(xzReader)
34
35 for {
36 header, err := readNext()
37
38 if err == io.EOF {
39 return nil
40 }
41
42 if err != nil {
43 return errorExtractingPostgres(err)
44 }
45
46 targetPath := filepath.Join(outputPath, header.Name)
47
48 if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
49 return errorExtractingPostgres(err)
50 }
51
52 switch header.Typeflag {
53 case tar.TypeReg:
54 fsMode := os.FileMode(header.Mode)
55 outFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, fsMode)
56 if err != nil {
57 return errorExtractingPostgres(err)
58 }
59
60 if _, err := io.Copy(outFile, reader()); err != nil {
61 return errorExtractingPostgres(err)
62 }
63
64 if err := outFile.Close(); err != nil {
65 return errorExtractingPostgres(err)
66 }
67 case tar.TypeSymlink:
68 if err := os.RemoveAll(targetPath); err != nil {
69 return errorExtractingPostgres(err)
70 }
71
72 if err := os.Symlink(header.Linkname, targetPath); err != nil {
73 return errorExtractingPostgres(err)
74 }
75 }
76 }
77 }
78
79 func defaultTarReader(xzReader *xz.Reader) (func() (*tar.Header, error), func() io.Reader) {
80 tarReader := tar.NewReader(xzReader)
81
82 return func() (*tar.Header, error) {
83 return tarReader.Next()
84 }, func() io.Reader {
85 return tarReader
86 }
87 }
88
89 func errorUnableToExtract(cacheLocation, binariesPath string) error {
90 return fmt.Errorf("unable to extract postgres archive %s to %s, if running parallel tests, configure RuntimePath to isolate testing directories", cacheLocation, binariesPath)
91 }
92
93 func errorExtractingPostgres(err error) error {
94 return fmt.Errorf("unable to extract postgres archive: %s", err)
95 }
96
View as plain text