github.com/gsquire/gb@v0.4.4-0.20161112235727-3982dc872064/internal/untar/untar.go (about) 1 package untar 2 3 import ( 4 "archive/tar" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 "github.com/pkg/errors" 11 ) 12 13 // Untar extracts the contents of r to the destination dest. 14 // dest must not aleady exist. 15 func Untar(dest string, r io.Reader) error { 16 if exists(dest) { 17 return errors.Errorf("%q must not exist", dest) 18 } 19 parent, _ := filepath.Split(dest) 20 tmpdir, err := ioutil.TempDir(parent, ".untar") 21 if err != nil { 22 return err 23 } 24 25 if err := untar(tmpdir, r); err != nil { 26 os.RemoveAll(tmpdir) 27 return err 28 } 29 30 if err := os.Rename(tmpdir, dest); err != nil { 31 os.RemoveAll(tmpdir) 32 return err 33 } 34 return nil 35 } 36 37 func untar(dest string, r io.Reader) error { 38 tr := tar.NewReader(r) 39 for { 40 h, err := tr.Next() 41 if err == io.EOF { 42 return nil 43 } 44 if err != nil { 45 return err 46 } 47 if err := untarfile(dest, h, tr); err != nil { 48 return err 49 } 50 } 51 return nil 52 } 53 54 func untarfile(dest string, h *tar.Header, r io.Reader) error { 55 path := filepath.Join(dest, h.Name) 56 switch h.Typeflag { 57 case tar.TypeDir: 58 return os.Mkdir(path, os.FileMode(h.Mode)) 59 case tar.TypeReg: 60 return writefile(path, r, os.FileMode(h.Mode)) 61 case tar.TypeXGlobalHeader: 62 // ignore PAX headers 63 return nil 64 case tar.TypeSymlink: 65 // symlinks are not supported by the go tool or windows so 66 // cannot be part of a valie package. Any symlinks in the tarball 67 // will be in parts of the release that we can safely ignore. 68 return nil 69 default: 70 return errors.Errorf("unsupported header type: %c", rune(h.Typeflag)) 71 } 72 } 73 74 func writefile(path string, r io.Reader, mode os.FileMode) error { 75 dir, _ := filepath.Split(path) 76 if err := os.MkdirAll(dir, mode); err != nil { 77 return errors.Wrap(err, "mkdirall failed") 78 } 79 80 w, err := os.Create(path) 81 if err != nil { 82 return errors.Wrap(err, "could not create destination") 83 } 84 if _, err := io.Copy(w, r); err != nil { 85 w.Close() 86 return err 87 } 88 return w.Close() 89 } 90 91 func exists(path string) bool { 92 _, err := os.Stat(path) 93 return err == nil || !os.IsNotExist(err) 94 }