github.com/YousefHaggyHeroku/pack@v1.5.5/internal/blob/blob.go (about) 1 package blob 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "io" 7 "os" 8 9 "github.com/docker/docker/pkg/ioutils" 10 "github.com/pkg/errors" 11 12 "github.com/YousefHaggyHeroku/pack/internal/archive" 13 ) 14 15 type Blob interface { 16 Open() (io.ReadCloser, error) 17 } 18 19 type blob struct { 20 path string 21 } 22 23 func NewBlob(path string) Blob { 24 return &blob{path: path} 25 } 26 27 // Open returns an io.ReadCloser whose contents are in tar archive format 28 func (b blob) Open() (r io.ReadCloser, err error) { 29 fi, err := os.Stat(b.path) 30 if err != nil { 31 return nil, errors.Wrapf(err, "read blob at path '%s'", b.path) 32 } 33 if fi.IsDir() { 34 return archive.ReadDirAsTar(b.path, ".", 0, 0, -1, true, nil), nil 35 } 36 37 fh, err := os.Open(b.path) 38 if err != nil { 39 return nil, errors.Wrap(err, "open buildpack archive") 40 } 41 defer func() { 42 if err != nil { 43 fh.Close() 44 } 45 }() 46 47 if ok, err := isGZip(fh); err != nil { 48 return nil, errors.Wrap(err, "check header") 49 } else if !ok { 50 return fh, nil 51 } 52 gzr, err := gzip.NewReader(fh) 53 if err != nil { 54 return nil, errors.Wrap(err, "create gzip reader") 55 } 56 57 rc := ioutils.NewReadCloserWrapper(gzr, func() error { 58 defer fh.Close() 59 return gzr.Close() 60 }) 61 62 return rc, nil 63 } 64 65 func isGZip(file io.ReadSeeker) (bool, error) { 66 b := make([]byte, 3) 67 if _, err := file.Seek(0, 0); err != nil { 68 return false, err 69 } 70 _, err := file.Read(b) 71 if err != nil && err != io.EOF { 72 return false, err 73 } else if err == io.EOF { 74 return false, nil 75 } 76 if _, err := file.Seek(0, 0); err != nil { 77 return false, err 78 } 79 return bytes.Equal(b, []byte("\x1f\x8b\x08")), nil 80 }