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  }