12 // untar is a shared helper for untarring an archive. The reader should provide
13 // an uncompressed view of the tar archive.
14 func untar(input io.Reader, dst, src string, dir bool) error {
15 tarR := tar.NewReader(input)
17 dirHdrs := []*tar.Header{}
20 hdr, err := tarR.Next()
24 return fmt.Errorf("empty archive: %s", src)
33 if hdr.Typeflag == tar.TypeXGlobalHeader || hdr.Typeflag == tar.TypeXHeader {
34 // don't unpack extended headers as files
40 // Disallow parent traversal
41 if containsDotDot(hdr.Name) {
42 return fmt.Errorf("entry contains '..': %s", hdr.Name)
45 path = filepath.Join(path, hdr.Name)
48 if hdr.FileInfo().IsDir() {
50 return fmt.Errorf("expected a single file: %s", src)
53 // A directory, just make the directory and continue unarchiving...
54 if err := os.MkdirAll(path, 0755); err != nil {
58 // Record the directory information so that we may set its attributes
59 // after all files have been extracted
60 dirHdrs = append(dirHdrs, hdr)
64 // There is no ordering guarantee that a file in a directory is
65 // listed before the directory
66 dstPath := filepath.Dir(path)
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 {
76 // We have a file. If we already decoded, then it is an error
78 return fmt.Errorf("expected a single file, got multiple: %s", src)
81 // Mark that we're done so future in single file mode errors
84 // Open the file for writing
85 dstF, err := os.Create(path)
89 _, err = io.Copy(dstF, tarR)
96 if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil {
100 // Set the access and modification time if valid, otherwise default to current time
103 if hdr.AccessTime.Unix() > 0 {
104 aTime = hdr.AccessTime
106 if hdr.ModTime.Unix() > 0 {
109 if err := os.Chtimes(path, aTime, mTime); err != nil {
114 // Perform a final pass over extracted directories to update metadata
115 for _, dirHdr := range dirHdrs {
116 path := filepath.Join(dst, dirHdr.Name)
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 {
121 // Set the mtime/atime attributes since they would have been changed during extraction
124 if dirHdr.AccessTime.Unix() > 0 {
125 aTime = dirHdr.AccessTime
127 if dirHdr.ModTime.Unix() > 0 {
128 mTime = dirHdr.ModTime
130 if err := os.Chtimes(path, aTime, mTime); err != nil {
138 // tarDecompressor is an implementation of Decompressor that can
140 type tarDecompressor struct{}
142 func (d *tarDecompressor) Decompress(dst, src string, dir bool) error {
143 // If we're going into a directory we should make that first
146 mkdir = filepath.Dir(dst)
148 if err := os.MkdirAll(mkdir, 0755); err != nil {
153 f, err := os.Open(src)
159 return untar(f, dst, src, dir)