archiver.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package containerfs // import "github.com/docker/docker/pkg/containerfs"
  2. import (
  3. "archive/tar"
  4. "errors"
  5. "io"
  6. "os"
  7. "path/filepath"
  8. "runtime"
  9. "time"
  10. "github.com/docker/docker/pkg/archive"
  11. "github.com/docker/docker/pkg/idtools"
  12. "github.com/docker/docker/pkg/system"
  13. "github.com/sirupsen/logrus"
  14. )
  15. // TarFunc provides a function definition for a custom Tar function
  16. type TarFunc func(string, *archive.TarOptions) (io.ReadCloser, error)
  17. // UntarFunc provides a function definition for a custom Untar function
  18. type UntarFunc func(io.Reader, string, *archive.TarOptions) error
  19. // Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
  20. type Archiver struct {
  21. SrcDriver Driver
  22. DstDriver Driver
  23. Tar TarFunc
  24. Untar UntarFunc
  25. IDMapping idtools.IdentityMapping
  26. }
  27. // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
  28. // If either Tar or Untar fails, TarUntar aborts and returns the error.
  29. func (archiver *Archiver) TarUntar(src, dst string) error {
  30. logrus.Debugf("TarUntar(%s %s)", src, dst)
  31. tarArchive, err := archiver.Tar(src, &archive.TarOptions{Compression: archive.Uncompressed})
  32. if err != nil {
  33. return err
  34. }
  35. defer tarArchive.Close()
  36. options := &archive.TarOptions{
  37. IDMap: archiver.IDMapping,
  38. }
  39. return archiver.Untar(tarArchive, dst, options)
  40. }
  41. // UntarPath untar a file from path to a destination, src is the source tar file path.
  42. func (archiver *Archiver) UntarPath(src, dst string) error {
  43. tarArchive, err := archiver.SrcDriver.Open(src)
  44. if err != nil {
  45. return err
  46. }
  47. defer tarArchive.Close()
  48. options := &archive.TarOptions{
  49. IDMap: archiver.IDMapping,
  50. }
  51. return archiver.Untar(tarArchive, dst, options)
  52. }
  53. // CopyWithTar creates a tar archive of filesystem path `src`, and
  54. // unpacks it at filesystem path `dst`.
  55. // The archive is streamed directly with fixed buffering and no
  56. // intermediary disk IO.
  57. func (archiver *Archiver) CopyWithTar(src, dst string) error {
  58. srcSt, err := archiver.SrcDriver.Stat(src)
  59. if err != nil {
  60. return err
  61. }
  62. if !srcSt.IsDir() {
  63. return archiver.CopyFileWithTar(src, dst)
  64. }
  65. // if this archiver is set up with ID mapping we need to create
  66. // the new destination directory with the remapped root UID/GID pair
  67. // as owner
  68. identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID}
  69. // Create dst, copy src's content into it
  70. if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil {
  71. return err
  72. }
  73. logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
  74. return archiver.TarUntar(src, dst)
  75. }
  76. // CopyFileWithTar emulates the behavior of the 'cp' command-line
  77. // for a single file. It copies a regular file from path `src` to
  78. // path `dst`, and preserves all its metadata.
  79. func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) {
  80. logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst)
  81. srcDriver := archiver.SrcDriver
  82. dstDriver := archiver.DstDriver
  83. srcSt, retErr := srcDriver.Stat(src)
  84. if retErr != nil {
  85. return retErr
  86. }
  87. if srcSt.IsDir() {
  88. return errors.New("cannot copy a directory")
  89. }
  90. // Clean up the trailing slash. This must be done in an operating
  91. // system specific manner.
  92. if dst[len(dst)-1] == dstDriver.Separator() {
  93. dst = dstDriver.Join(dst, srcDriver.Base(src))
  94. }
  95. // The original call was system.MkdirAll, which is just
  96. // os.MkdirAll on not-Windows and changed for Windows.
  97. if runtime.GOOS == "windows" {
  98. // Now we are WCOW
  99. if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil {
  100. return err
  101. }
  102. } else {
  103. // We can just use the driver.MkdirAll function
  104. if err := dstDriver.MkdirAll(dstDriver.Dir(dst), 0700); err != nil {
  105. return err
  106. }
  107. }
  108. r, w := io.Pipe()
  109. errC := make(chan error, 1)
  110. go func() {
  111. defer close(errC)
  112. errC <- func() error {
  113. defer w.Close()
  114. srcF, err := srcDriver.Open(src)
  115. if err != nil {
  116. return err
  117. }
  118. defer srcF.Close()
  119. hdr, err := archive.FileInfoHeaderNoLookups(srcSt, "")
  120. if err != nil {
  121. return err
  122. }
  123. hdr.Format = tar.FormatPAX
  124. hdr.ModTime = hdr.ModTime.Truncate(time.Second)
  125. hdr.AccessTime = time.Time{}
  126. hdr.ChangeTime = time.Time{}
  127. hdr.Name = dstDriver.Base(dst)
  128. if runtime.GOOS == "windows" {
  129. hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
  130. } else {
  131. hdr.Mode = int64(os.FileMode(hdr.Mode))
  132. }
  133. if err := remapIDs(archiver.IDMapping, hdr); err != nil {
  134. return err
  135. }
  136. tw := tar.NewWriter(w)
  137. defer tw.Close()
  138. if err := tw.WriteHeader(hdr); err != nil {
  139. return err
  140. }
  141. if _, err := io.Copy(tw, srcF); err != nil {
  142. return err
  143. }
  144. return nil
  145. }()
  146. }()
  147. defer func() {
  148. if err := <-errC; retErr == nil && err != nil {
  149. retErr = err
  150. }
  151. }()
  152. retErr = archiver.Untar(r, dstDriver.Dir(dst), nil)
  153. if retErr != nil {
  154. r.CloseWithError(retErr)
  155. }
  156. return retErr
  157. }
  158. // IdentityMapping returns the IdentityMapping of the archiver.
  159. func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping {
  160. return archiver.IDMapping
  161. }
  162. func remapIDs(idMapping idtools.IdentityMapping, hdr *tar.Header) error {
  163. ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
  164. hdr.Uid, hdr.Gid = ids.UID, ids.GID
  165. return err
  166. }
  167. // chmodTarEntry is used to adjust the file permissions used in tar header based
  168. // on the platform the archival is done.
  169. func chmodTarEntry(perm os.FileMode) os.FileMode {
  170. // perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
  171. permPart := perm & os.ModePerm
  172. noPermPart := perm &^ os.ModePerm
  173. // Add the x bit: make everything +x from windows
  174. permPart |= 0111
  175. permPart &= 0755
  176. return noPermPart | permPart
  177. }