github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+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/containerfs"
    12  	"github.com/docker/docker/pkg/ioutils"
    13  	"github.com/docker/docker/pkg/tarsum"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  type archiveContext struct {
    18  	root containerfs.ContainerFS
    19  	sums tarsum.FileInfoSums
    20  }
    21  
    22  func (c *archiveContext) Close() error {
    23  	return c.root.RemoveAll(c.root.Path())
    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  // FromArchive returns a build source 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 FromArchive(tarStream io.Reader) (builder.Source, error) {
    50  	root, err := ioutils.TempDir("", "docker-builder")
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	// Assume local file system. Since it's coming from a tar file.
    56  	tsc := &archiveContext{root: containerfs.NewLocalContainerFS(root)}
    57  
    58  	// Make sure we clean-up upon error.  In the happy case the caller
    59  	// is expected to manage the clean-up
    60  	defer func() {
    61  		if err != nil {
    62  			tsc.Close()
    63  		}
    64  	}()
    65  
    66  	decompressedStream, err := archive.DecompressStream(tarStream)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	sum, err := tarsum.NewTarSum(decompressedStream, true, tarsum.Version1)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	err = chrootarchive.Untar(sum, root, nil)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	tsc.sums = sum.GetSums()
    82  	return tsc, nil
    83  }
    84  
    85  func (c *archiveContext) Root() containerfs.ContainerFS {
    86  	return c.root
    87  }
    88  
    89  func (c *archiveContext) Remove(path string) error {
    90  	_, fullpath, err := normalize(path, c.root)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	return c.root.RemoveAll(fullpath)
    95  }
    96  
    97  func (c *archiveContext) 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 := c.root.Rel(c.root.Path(), 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 string, root containerfs.ContainerFS) (cleanPath, fullPath string, err error) {
   119  	cleanPath = root.Clean(string(root.Separator()) + path)[1:]
   120  	fullPath, err = root.ResolveScopedPath(path, true)
   121  	if err != nil {
   122  		return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath)
   123  	}
   124  	return
   125  }