diff.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package archive
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. "syscall"
  7. "time"
  8. )
  9. // ApplyLayer parses a diff in the standard layer format from `layer`, and
  10. // applies it to the directory `dest`.
  11. func ApplyLayer(dest string, layer Archive) error {
  12. // Poor man's diff applyer in 2 steps:
  13. // Step 1: untar everything in place
  14. if err := Untar(layer, dest, nil); err != nil {
  15. return err
  16. }
  17. modifiedDirs := make(map[string]*syscall.Stat_t)
  18. addDir := func(file string) {
  19. d := filepath.Dir(file)
  20. if _, exists := modifiedDirs[d]; !exists {
  21. if s, err := os.Lstat(d); err == nil {
  22. if sys := s.Sys(); sys != nil {
  23. if stat, ok := sys.(*syscall.Stat_t); ok {
  24. modifiedDirs[d] = stat
  25. }
  26. }
  27. }
  28. }
  29. }
  30. // Step 2: walk for whiteouts and apply them, removing them in the process
  31. err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error {
  32. if err != nil {
  33. if os.IsNotExist(err) {
  34. // This happens in the case of whiteouts in parent dir removing a directory
  35. // We just ignore it
  36. return filepath.SkipDir
  37. }
  38. return err
  39. }
  40. // Rebase path
  41. path, err := filepath.Rel(dest, fullPath)
  42. if err != nil {
  43. return err
  44. }
  45. path = filepath.Join("/", path)
  46. // Skip AUFS metadata
  47. if matched, err := filepath.Match("/.wh..wh.*", path); err != nil {
  48. return err
  49. } else if matched {
  50. addDir(fullPath)
  51. if err := os.RemoveAll(fullPath); err != nil {
  52. return err
  53. }
  54. }
  55. filename := filepath.Base(path)
  56. if strings.HasPrefix(filename, ".wh.") {
  57. rmTargetName := filename[len(".wh."):]
  58. rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName)
  59. // Remove the file targeted by the whiteout
  60. addDir(rmTargetPath)
  61. if err := os.RemoveAll(rmTargetPath); err != nil {
  62. return err
  63. }
  64. // Remove the whiteout itself
  65. addDir(fullPath)
  66. if err := os.RemoveAll(fullPath); err != nil {
  67. return err
  68. }
  69. }
  70. return nil
  71. })
  72. if err != nil {
  73. return err
  74. }
  75. for k, v := range modifiedDirs {
  76. lastAccess := getLastAccess(v)
  77. lastModification := getLastModification(v)
  78. aTime := time.Unix(lastAccess.Unix())
  79. mTime := time.Unix(lastModification.Unix())
  80. if err := os.Chtimes(k, aTime, mTime); err != nil {
  81. return err
  82. }
  83. }
  84. return nil
  85. }