archive.go 3.3 KB

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