github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/thirdparty/tar/extractor.go (about) 1 package tar 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io" 7 "os" 8 gopath "path" 9 fp "path/filepath" 10 "strings" 11 ) 12 13 type Extractor struct { 14 Path string 15 } 16 17 func (te *Extractor) Extract(reader io.Reader) error { 18 tarReader := tar.NewReader(reader) 19 20 // Check if the output path already exists, so we know whether we should 21 // create our output with that name, or if we should put the output inside 22 // a preexisting directory 23 rootExists := true 24 rootIsDir := false 25 if stat, err := os.Stat(te.Path); err != nil && os.IsNotExist(err) { 26 rootExists = false 27 } else if err != nil { 28 return err 29 } else if stat.IsDir() { 30 rootIsDir = true 31 } 32 33 // files come recursively in order (i == 0 is root directory) 34 for i := 0; ; i++ { 35 header, err := tarReader.Next() 36 if err != nil && err != io.EOF { 37 return err 38 } 39 if header == nil || err == io.EOF { 40 break 41 } 42 43 switch header.Typeflag { 44 case tar.TypeDir: 45 if err := te.extractDir(header, i); err != nil { 46 return err 47 } 48 case tar.TypeReg: 49 if err := te.extractFile(header, tarReader, i, rootExists, rootIsDir); err != nil { 50 return err 51 } 52 case tar.TypeSymlink: 53 if err := te.extractSymlink(header); err != nil { 54 return err 55 } 56 default: 57 return fmt.Errorf("unrecognized tar header type: %d", header.Typeflag) 58 } 59 } 60 return nil 61 } 62 63 // outputPath returns the path at whicht o place tarPath 64 func (te *Extractor) outputPath(tarPath string) string { 65 elems := strings.Split(tarPath, "/") // break into elems 66 elems = elems[1:] // remove original root 67 68 path := fp.Join(elems...) // join elems 69 path = fp.Join(te.Path, path) // rebase on extractor root 70 return path 71 } 72 73 func (te *Extractor) extractDir(h *tar.Header, depth int) error { 74 path := te.outputPath(h.Name) 75 76 if depth == 0 { 77 // if this is the root root directory, use it as the output path for remaining files 78 te.Path = path 79 } 80 81 err := os.MkdirAll(path, 0755) 82 if err != nil { 83 return err 84 } 85 86 return nil 87 } 88 89 func (te *Extractor) extractSymlink(h *tar.Header) error { 90 return os.Symlink(h.Linkname, te.outputPath(h.Name)) 91 } 92 93 func (te *Extractor) extractFile(h *tar.Header, r *tar.Reader, depth int, rootExists bool, rootIsDir bool) error { 94 path := te.outputPath(h.Name) 95 96 if depth == 0 { // if depth is 0, this is the only file (we aren't 'ipfs get'ing a directory) 97 if rootExists && rootIsDir { 98 // putting file inside of a root dir. 99 fnameo := gopath.Base(h.Name) 100 fnamen := fp.Base(path) 101 // add back original name if lost. 102 if fnameo != fnamen { 103 path = fp.Join(path, fnameo) 104 } 105 } // else if old file exists, just overwrite it. 106 } 107 108 file, err := os.Create(path) 109 if err != nil { 110 return err 111 } 112 defer file.Close() 113 114 _, err = io.Copy(file, r) 115 if err != nil { 116 return err 117 } 118 119 return nil 120 }