fsdiff.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package graphdriver // import "github.com/docker/docker/daemon/graphdriver"
  2. import (
  3. "io"
  4. "time"
  5. "github.com/docker/docker/pkg/archive"
  6. "github.com/docker/docker/pkg/chrootarchive"
  7. "github.com/docker/docker/pkg/idtools"
  8. "github.com/docker/docker/pkg/ioutils"
  9. "github.com/sirupsen/logrus"
  10. )
  11. var (
  12. // ApplyUncompressedLayer defines the unpack method used by the graph
  13. // driver.
  14. ApplyUncompressedLayer = chrootarchive.ApplyUncompressedLayer
  15. )
  16. // NaiveDiffDriver takes a ProtoDriver and adds the
  17. // capability of the Diffing methods on the local file system,
  18. // which it may or may not support on its own. See the comment
  19. // on the exported NewNaiveDiffDriver function below.
  20. type NaiveDiffDriver struct {
  21. ProtoDriver
  22. idMap idtools.IdentityMapping
  23. }
  24. // NewNaiveDiffDriver returns a fully functional driver that wraps the
  25. // given ProtoDriver and adds the capability of the following methods which
  26. // it may or may not support on its own:
  27. //
  28. // Diff(id, parent string) (archive.Archive, error)
  29. // Changes(id, parent string) ([]archive.Change, error)
  30. // ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error)
  31. // DiffSize(id, parent string) (size int64, err error)
  32. func NewNaiveDiffDriver(driver ProtoDriver, idMap idtools.IdentityMapping) Driver {
  33. return &NaiveDiffDriver{ProtoDriver: driver,
  34. idMap: idMap}
  35. }
  36. // Diff produces an archive of the changes between the specified
  37. // layer and its parent layer which may be "".
  38. func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, err error) {
  39. startTime := time.Now()
  40. driver := gdw.ProtoDriver
  41. layerRootFs, err := driver.Get(id, "")
  42. if err != nil {
  43. return nil, err
  44. }
  45. layerFs := layerRootFs
  46. defer func() {
  47. if err != nil {
  48. driver.Put(id)
  49. }
  50. }()
  51. if parent == "" {
  52. archive, err := archive.Tar(layerFs, archive.Uncompressed)
  53. if err != nil {
  54. return nil, err
  55. }
  56. return ioutils.NewReadCloserWrapper(archive, func() error {
  57. err := archive.Close()
  58. driver.Put(id)
  59. return err
  60. }), nil
  61. }
  62. parentFs, err := driver.Get(parent, "")
  63. if err != nil {
  64. return nil, err
  65. }
  66. defer driver.Put(parent)
  67. changes, err := archive.ChangesDirs(layerFs, parentFs)
  68. if err != nil {
  69. return nil, err
  70. }
  71. archive, err := archive.ExportChanges(layerFs, changes, gdw.idMap)
  72. if err != nil {
  73. return nil, err
  74. }
  75. return ioutils.NewReadCloserWrapper(archive, func() error {
  76. err := archive.Close()
  77. driver.Put(id)
  78. // NaiveDiffDriver compares file metadata with parent layers. Parent layers
  79. // are extracted from tar's with full second precision on modified time.
  80. // We need this hack here to make sure calls within same second receive
  81. // correct result.
  82. time.Sleep(time.Until(startTime.Truncate(time.Second).Add(time.Second)))
  83. return err
  84. }), nil
  85. }
  86. // Changes produces a list of changes between the specified layer
  87. // and its parent layer. If parent is "", then all changes will be ADD changes.
  88. func (gdw *NaiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) {
  89. driver := gdw.ProtoDriver
  90. layerFs, err := driver.Get(id, "")
  91. if err != nil {
  92. return nil, err
  93. }
  94. defer driver.Put(id)
  95. parentFs := ""
  96. if parent != "" {
  97. parentFs, err = driver.Get(parent, "")
  98. if err != nil {
  99. return nil, err
  100. }
  101. defer driver.Put(parent)
  102. }
  103. return archive.ChangesDirs(layerFs, parentFs)
  104. }
  105. // ApplyDiff extracts the changeset from the given diff into the
  106. // layer with the specified id and parent, returning the size of the
  107. // new layer in bytes.
  108. func (gdw *NaiveDiffDriver) ApplyDiff(id, parent string, diff io.Reader) (size int64, err error) {
  109. driver := gdw.ProtoDriver
  110. // Mount the root filesystem so we can apply the diff/layer.
  111. layerRootFs, err := driver.Get(id, "")
  112. if err != nil {
  113. return
  114. }
  115. defer driver.Put(id)
  116. layerFs := layerRootFs
  117. options := &archive.TarOptions{IDMap: gdw.idMap}
  118. start := time.Now().UTC()
  119. logrus.WithField("id", id).Debug("Start untar layer")
  120. if size, err = ApplyUncompressedLayer(layerFs, diff, options); err != nil {
  121. return
  122. }
  123. logrus.WithField("id", id).Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
  124. return
  125. }
  126. // DiffSize calculates the changes between the specified layer
  127. // and its parent and returns the size in bytes of the changes
  128. // relative to its base filesystem directory.
  129. func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) {
  130. driver := gdw.ProtoDriver
  131. changes, err := gdw.Changes(id, parent)
  132. if err != nil {
  133. return
  134. }
  135. layerFs, err := driver.Get(id, "")
  136. if err != nil {
  137. return
  138. }
  139. defer driver.Put(id)
  140. return archive.ChangesSize(layerFs, changes), nil
  141. }