diff.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package archive
  2. import (
  3. "code.google.com/p/go/src/pkg/archive/tar"
  4. "io"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "syscall"
  9. "time"
  10. )
  11. // Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
  12. // They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major,
  13. // then the top 12 bits of the minor
  14. func mkdev(major int64, minor int64) uint32 {
  15. return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
  16. }
  17. func timeToTimespec(time time.Time) (ts syscall.Timespec) {
  18. if time.IsZero() {
  19. // Return UTIME_OMIT special value
  20. ts.Sec = 0
  21. ts.Nsec = ((1 << 30) - 2)
  22. return
  23. }
  24. return syscall.NsecToTimespec(time.UnixNano())
  25. }
  26. // ApplyLayer parses a diff in the standard layer format from `layer`, and
  27. // applies it to the directory `dest`.
  28. func ApplyLayer(dest string, layer ArchiveReader) error {
  29. // We need to be able to set any perms
  30. oldmask := syscall.Umask(0)
  31. defer syscall.Umask(oldmask)
  32. layer, err := DecompressStream(layer)
  33. if err != nil {
  34. return err
  35. }
  36. tr := tar.NewReader(layer)
  37. var dirs []*tar.Header
  38. // Iterate through the files in the archive.
  39. for {
  40. hdr, err := tr.Next()
  41. if err == io.EOF {
  42. // end of tar archive
  43. break
  44. }
  45. if err != nil {
  46. return err
  47. }
  48. // Normalize name, for safety and for a simple is-root check
  49. hdr.Name = filepath.Clean(hdr.Name)
  50. if !strings.HasSuffix(hdr.Name, "/") {
  51. // Not the root directory, ensure that the parent directory exists.
  52. // This happened in some tests where an image had a tarfile without any
  53. // parent directories.
  54. parent := filepath.Dir(hdr.Name)
  55. parentPath := filepath.Join(dest, parent)
  56. if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
  57. err = os.MkdirAll(parentPath, 600)
  58. if err != nil {
  59. return err
  60. }
  61. }
  62. }
  63. // Skip AUFS metadata dirs
  64. if strings.HasPrefix(hdr.Name, ".wh..wh.") {
  65. continue
  66. }
  67. path := filepath.Join(dest, hdr.Name)
  68. base := filepath.Base(path)
  69. if strings.HasPrefix(base, ".wh.") {
  70. originalBase := base[len(".wh."):]
  71. originalPath := filepath.Join(filepath.Dir(path), originalBase)
  72. if err := os.RemoveAll(originalPath); err != nil {
  73. return err
  74. }
  75. } else {
  76. // If path exits we almost always just want to remove and replace it.
  77. // The only exception is when it is a directory *and* the file from
  78. // the layer is also a directory. Then we want to merge them (i.e.
  79. // just apply the metadata from the layer).
  80. if fi, err := os.Lstat(path); err == nil {
  81. if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
  82. if err := os.RemoveAll(path); err != nil {
  83. return err
  84. }
  85. }
  86. }
  87. if err := createTarFile(path, dest, hdr, tr); err != nil {
  88. return err
  89. }
  90. // Directory mtimes must be handled at the end to avoid further
  91. // file creation in them to modify the directory mtime
  92. if hdr.Typeflag == tar.TypeDir {
  93. dirs = append(dirs, hdr)
  94. }
  95. }
  96. }
  97. for _, hdr := range dirs {
  98. path := filepath.Join(dest, hdr.Name)
  99. ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
  100. if err := syscall.UtimesNano(path, ts); err != nil {
  101. return err
  102. }
  103. }
  104. return nil
  105. }