]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/go-getter/decompress_tar.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / go-getter / decompress_tar.go
CommitLineData
15c0b25d
AP
1package getter
2
3import (
4 "archive/tar"
5 "fmt"
6 "io"
7 "os"
8 "path/filepath"
107c1cdb 9 "time"
15c0b25d
AP
10)
11
12// untar is a shared helper for untarring an archive. The reader should provide
13// an uncompressed view of the tar archive.
14func untar(input io.Reader, dst, src string, dir bool) error {
15 tarR := tar.NewReader(input)
16 done := false
17 dirHdrs := []*tar.Header{}
107c1cdb 18 now := time.Now()
15c0b25d
AP
19 for {
20 hdr, err := tarR.Next()
21 if err == io.EOF {
22 if !done {
23 // Empty archive
24 return fmt.Errorf("empty archive: %s", src)
25 }
26
27 break
28 }
29 if err != nil {
30 return err
31 }
32
33 if hdr.Typeflag == tar.TypeXGlobalHeader || hdr.Typeflag == tar.TypeXHeader {
34 // don't unpack extended headers as files
35 continue
36 }
37
38 path := dst
39 if dir {
40 // Disallow parent traversal
41 if containsDotDot(hdr.Name) {
42 return fmt.Errorf("entry contains '..': %s", hdr.Name)
43 }
44
45 path = filepath.Join(path, hdr.Name)
46 }
47
48 if hdr.FileInfo().IsDir() {
49 if !dir {
50 return fmt.Errorf("expected a single file: %s", src)
51 }
52
53 // A directory, just make the directory and continue unarchiving...
54 if err := os.MkdirAll(path, 0755); err != nil {
55 return err
56 }
57
58 // Record the directory information so that we may set its attributes
59 // after all files have been extracted
60 dirHdrs = append(dirHdrs, hdr)
61
62 continue
63 } else {
64 // There is no ordering guarantee that a file in a directory is
65 // listed before the directory
66 dstPath := filepath.Dir(path)
67
68 // Check that the directory exists, otherwise create it
69 if _, err := os.Stat(dstPath); os.IsNotExist(err) {
70 if err := os.MkdirAll(dstPath, 0755); err != nil {
71 return err
72 }
73 }
74 }
75
76 // We have a file. If we already decoded, then it is an error
77 if !dir && done {
78 return fmt.Errorf("expected a single file, got multiple: %s", src)
79 }
80
81 // Mark that we're done so future in single file mode errors
82 done = true
83
84 // Open the file for writing
85 dstF, err := os.Create(path)
86 if err != nil {
87 return err
88 }
89 _, err = io.Copy(dstF, tarR)
90 dstF.Close()
91 if err != nil {
92 return err
93 }
94
95 // Chmod the file
96 if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil {
97 return err
98 }
99
107c1cdb
ND
100 // Set the access and modification time if valid, otherwise default to current time
101 aTime := now
102 mTime := now
103 if hdr.AccessTime.Unix() > 0 {
104 aTime = hdr.AccessTime
105 }
106 if hdr.ModTime.Unix() > 0 {
107 mTime = hdr.ModTime
108 }
109 if err := os.Chtimes(path, aTime, mTime); err != nil {
15c0b25d
AP
110 return err
111 }
112 }
113
107c1cdb 114 // Perform a final pass over extracted directories to update metadata
15c0b25d
AP
115 for _, dirHdr := range dirHdrs {
116 path := filepath.Join(dst, dirHdr.Name)
107c1cdb
ND
117 // Chmod the directory since they might be created before we know the mode flags
118 if err := os.Chmod(path, dirHdr.FileInfo().Mode()); err != nil {
119 return err
120 }
121 // Set the mtime/atime attributes since they would have been changed during extraction
122 aTime := now
123 mTime := now
124 if dirHdr.AccessTime.Unix() > 0 {
125 aTime = dirHdr.AccessTime
126 }
127 if dirHdr.ModTime.Unix() > 0 {
128 mTime = dirHdr.ModTime
129 }
130 if err := os.Chtimes(path, aTime, mTime); err != nil {
15c0b25d
AP
131 return err
132 }
133 }
134
135 return nil
136}
137
138// tarDecompressor is an implementation of Decompressor that can
139// unpack tar files.
140type tarDecompressor struct{}
141
142func (d *tarDecompressor) Decompress(dst, src string, dir bool) error {
143 // If we're going into a directory we should make that first
144 mkdir := dst
145 if !dir {
146 mkdir = filepath.Dir(dst)
147 }
148 if err := os.MkdirAll(mkdir, 0755); err != nil {
149 return err
150 }
151
152 // File first
153 f, err := os.Open(src)
154 if err != nil {
155 return err
156 }
157 defer f.Close()
158
159 return untar(f, dst, src, dir)
160}