diff.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. package archive
  2. import (
  3. "log"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. )
  8. // ApplyLayer parses a diff in the standard layer format from `layer`, and
  9. // applies it to the directory `dest`.
  10. func ApplyLayer(dest string, layer Archive) error {
  11. // Poor man's diff applyer in 2 steps:
  12. // Step 1: untar everything in place
  13. if err := Untar(layer, dest); err != nil {
  14. return err
  15. }
  16. // Step 2: walk for whiteouts and apply them, removing them in the process
  17. err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error {
  18. if err != nil {
  19. if os.IsNotExist(err) {
  20. // This happens in the case of whiteouts in parent dir removing a directory
  21. // We just ignore it
  22. return filepath.SkipDir
  23. }
  24. return err
  25. }
  26. // Rebase path
  27. path, err := filepath.Rel(dest, fullPath)
  28. if err != nil {
  29. return err
  30. }
  31. path = filepath.Join("/", path)
  32. // Skip AUFS metadata
  33. if matched, err := filepath.Match("/.wh..wh.*", path); err != nil {
  34. return err
  35. } else if matched {
  36. log.Printf("Removing aufs metadata %s", fullPath)
  37. _ = os.RemoveAll(fullPath)
  38. }
  39. filename := filepath.Base(path)
  40. if strings.HasPrefix(filename, ".wh.") {
  41. rmTargetName := filename[len(".wh."):]
  42. rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName)
  43. // Remove the file targeted by the whiteout
  44. log.Printf("Removing whiteout target %s", rmTargetPath)
  45. _ = os.Remove(rmTargetPath)
  46. // Remove the whiteout itself
  47. log.Printf("Removing whiteout %s", fullPath)
  48. _ = os.RemoveAll(fullPath)
  49. }
  50. return nil
  51. })
  52. if err != nil {
  53. return err
  54. }
  55. return nil
  56. }