github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/builder/remotecontext/tarsum.go (about) 1 package remotecontext 2 3 import ( 4 "io" 5 "os" 6 "path/filepath" 7 8 "github.com/docker/docker/builder" 9 "github.com/docker/docker/pkg/archive" 10 "github.com/docker/docker/pkg/chrootarchive" 11 "github.com/docker/docker/pkg/ioutils" 12 "github.com/docker/docker/pkg/symlink" 13 "github.com/docker/docker/pkg/tarsum" 14 "github.com/pkg/errors" 15 ) 16 17 type tarSumContext struct { 18 root string 19 sums tarsum.FileInfoSums 20 } 21 22 func (c *tarSumContext) Close() error { 23 return os.RemoveAll(c.root) 24 } 25 26 func convertPathError(err error, cleanpath string) error { 27 if err, ok := err.(*os.PathError); ok { 28 err.Path = cleanpath 29 return err 30 } 31 return err 32 } 33 34 type modifiableContext interface { 35 builder.Source 36 // Remove deletes the entry specified by `path`. 37 // It is usual for directory entries to delete all its subentries. 38 Remove(path string) error 39 } 40 41 // MakeTarSumContext returns a build Context from a tar stream. 42 // 43 // It extracts the tar stream to a temporary folder that is deleted as soon as 44 // the Context is closed. 45 // As the extraction happens, a tarsum is calculated for every file, and the set of 46 // all those sums then becomes the source of truth for all operations on this Context. 47 // 48 // Closing tarStream has to be done by the caller. 49 func MakeTarSumContext(tarStream io.Reader) (builder.Source, error) { 50 root, err := ioutils.TempDir("", "docker-builder") 51 if err != nil { 52 return nil, err 53 } 54 55 tsc := &tarSumContext{root: root} 56 57 // Make sure we clean-up upon error. In the happy case the caller 58 // is expected to manage the clean-up 59 defer func() { 60 if err != nil { 61 tsc.Close() 62 } 63 }() 64 65 decompressedStream, err := archive.DecompressStream(tarStream) 66 if err != nil { 67 return nil, err 68 } 69 70 sum, err := tarsum.NewTarSum(decompressedStream, true, tarsum.Version1) 71 if err != nil { 72 return nil, err 73 } 74 75 err = chrootarchive.Untar(sum, root, nil) 76 if err != nil { 77 return nil, err 78 } 79 80 tsc.sums = sum.GetSums() 81 82 return tsc, nil 83 } 84 85 func (c *tarSumContext) Root() string { 86 return c.root 87 } 88 89 func (c *tarSumContext) Remove(path string) error { 90 _, fullpath, err := normalize(path, c.root) 91 if err != nil { 92 return err 93 } 94 return os.RemoveAll(fullpath) 95 } 96 97 func (c *tarSumContext) Hash(path string) (string, error) { 98 cleanpath, fullpath, err := normalize(path, c.root) 99 if err != nil { 100 return "", err 101 } 102 103 rel, err := filepath.Rel(c.root, fullpath) 104 if err != nil { 105 return "", convertPathError(err, cleanpath) 106 } 107 108 // Use the checksum of the followed path(not the possible symlink) because 109 // this is the file that is actually copied. 110 if tsInfo := c.sums.GetFile(filepath.ToSlash(rel)); tsInfo != nil { 111 return tsInfo.Sum(), nil 112 } 113 // We set sum to path by default for the case where GetFile returns nil. 114 // The usual case is if relative path is empty. 115 return path, nil // backwards compat TODO: see if really needed 116 } 117 118 func normalize(path, root string) (cleanPath, fullPath string, err error) { 119 cleanPath = filepath.Clean(string(os.PathSeparator) + path)[1:] 120 fullPath, err = symlink.FollowSymlinkInScope(filepath.Join(root, path), root) 121 if err != nil { 122 return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath) 123 } 124 if _, err := os.Lstat(fullPath); err != nil { 125 return "", "", convertPathError(err, path) 126 } 127 return 128 }