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