github.com/yogeshlonkar/moby@v1.13.2-0.20201203103638-c0b64beaea94/builder/tarsum.go (about) 1 package builder 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 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 ) 15 16 type tarSumContext struct { 17 root string 18 sums tarsum.FileInfoSums 19 } 20 21 func (c *tarSumContext) Close() error { 22 return os.RemoveAll(c.root) 23 } 24 25 func convertPathError(err error, cleanpath string) error { 26 if err, ok := err.(*os.PathError); ok { 27 err.Path = cleanpath 28 return err 29 } 30 return err 31 } 32 33 func (c *tarSumContext) Open(path string) (io.ReadCloser, error) { 34 cleanpath, fullpath, err := c.normalize(path) 35 if err != nil { 36 return nil, err 37 } 38 r, err := os.Open(fullpath) 39 if err != nil { 40 return nil, convertPathError(err, cleanpath) 41 } 42 return r, nil 43 } 44 45 func (c *tarSumContext) Stat(path string) (string, FileInfo, error) { 46 cleanpath, fullpath, err := c.normalize(path) 47 if err != nil { 48 return "", nil, err 49 } 50 51 st, err := os.Lstat(fullpath) 52 if err != nil { 53 return "", nil, convertPathError(err, cleanpath) 54 } 55 56 rel, err := filepath.Rel(c.root, fullpath) 57 if err != nil { 58 return "", nil, convertPathError(err, cleanpath) 59 } 60 61 // We set sum to path by default for the case where GetFile returns nil. 62 // The usual case is if relative path is empty. 63 sum := path 64 // Use the checksum of the followed path(not the possible symlink) because 65 // this is the file that is actually copied. 66 if tsInfo := c.sums.GetFile(filepath.ToSlash(rel)); tsInfo != nil { 67 sum = tsInfo.Sum() 68 } 69 fi := &HashedFileInfo{PathFileInfo{st, fullpath, filepath.Base(cleanpath)}, sum} 70 return rel, fi, nil 71 } 72 73 // MakeTarSumContext returns a build Context from a tar stream. 74 // 75 // It extracts the tar stream to a temporary folder that is deleted as soon as 76 // the Context is closed. 77 // As the extraction happens, a tarsum is calculated for every file, and the set of 78 // all those sums then becomes the source of truth for all operations on this Context. 79 // 80 // Closing tarStream has to be done by the caller. 81 func MakeTarSumContext(tarStream io.Reader) (ModifiableContext, error) { 82 root, err := ioutils.TempDir("", "docker-builder") 83 if err != nil { 84 return nil, err 85 } 86 87 tsc := &tarSumContext{root: root} 88 89 // Make sure we clean-up upon error. In the happy case the caller 90 // is expected to manage the clean-up 91 defer func() { 92 if err != nil { 93 tsc.Close() 94 } 95 }() 96 97 decompressedStream, err := archive.DecompressStream(tarStream) 98 if err != nil { 99 return nil, err 100 } 101 102 sum, err := tarsum.NewTarSum(decompressedStream, true, tarsum.Version1) 103 if err != nil { 104 return nil, err 105 } 106 107 err = chrootarchive.Untar(sum, root, nil) 108 if err != nil { 109 return nil, err 110 } 111 112 tsc.sums = sum.GetSums() 113 114 return tsc, nil 115 } 116 117 func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err error) { 118 cleanpath = filepath.Clean(string(os.PathSeparator) + path)[1:] 119 fullpath, err = symlink.FollowSymlinkInScope(filepath.Join(c.root, path), c.root) 120 if err != nil { 121 return "", "", fmt.Errorf("Forbidden path outside the build context: %s (%s)", path, fullpath) 122 } 123 _, err = os.Lstat(fullpath) 124 if err != nil { 125 return "", "", convertPathError(err, path) 126 } 127 return 128 } 129 130 func (c *tarSumContext) Walk(root string, walkFn WalkFunc) error { 131 root = filepath.Join(c.root, filepath.Join(string(filepath.Separator), root)) 132 return filepath.Walk(root, func(fullpath string, info os.FileInfo, err error) error { 133 rel, err := filepath.Rel(c.root, fullpath) 134 if err != nil { 135 return err 136 } 137 if rel == "." { 138 return nil 139 } 140 141 sum := rel 142 if tsInfo := c.sums.GetFile(filepath.ToSlash(rel)); tsInfo != nil { 143 sum = tsInfo.Sum() 144 } 145 fi := &HashedFileInfo{PathFileInfo{FileInfo: info, FilePath: fullpath}, sum} 146 if err := walkFn(rel, fi, nil); err != nil { 147 return err 148 } 149 return nil 150 }) 151 } 152 153 func (c *tarSumContext) Remove(path string) error { 154 _, fullpath, err := c.normalize(path) 155 if err != nil { 156 return err 157 } 158 return os.RemoveAll(fullpath) 159 }