github.com/rish1988/moby@v25.0.2+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  }