github.com/kekek/gb@v0.4.5-0.20170222120241-d4ba64b0b297/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 } 52 53 func untarfile(dest string, h *tar.Header, r io.Reader) error { 54 path := filepath.Join(dest, h.Name) 55 switch h.Typeflag { 56 case tar.TypeDir: 57 return os.Mkdir(path, os.FileMode(h.Mode)) 58 case tar.TypeReg: 59 return writefile(path, r, os.FileMode(h.Mode)) 60 case tar.TypeXGlobalHeader: 61 // ignore PAX headers 62 return nil 63 case tar.TypeSymlink: 64 // symlinks are not supported by the go tool or windows so 65 // cannot be part of a valie package. Any symlinks in the tarball 66 // will be in parts of the release that we can safely ignore. 67 return nil 68 default: 69 return errors.Errorf("unsupported header type: %c", rune(h.Typeflag)) 70 } 71 } 72 73 func writefile(path string, r io.Reader, mode os.FileMode) error { 74 dir, _ := filepath.Split(path) 75 if err := os.MkdirAll(dir, mode); err != nil { 76 return errors.Wrap(err, "mkdirall failed") 77 } 78 79 w, err := os.Create(path) 80 if err != nil { 81 return errors.Wrap(err, "could not create destination") 82 } 83 if _, err := io.Copy(w, r); err != nil { 84 w.Close() 85 return err 86 } 87 return w.Close() 88 } 89 90 func exists(path string) bool { 91 _, err := os.Stat(path) 92 return err == nil || !os.IsNotExist(err) 93 }