copy_test.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. //go:build linux
  2. package copy // import "github.com/docker/docker/daemon/graphdriver/copy"
  3. import (
  4. "fmt"
  5. "math/rand"
  6. "os"
  7. "path/filepath"
  8. "syscall"
  9. "testing"
  10. "time"
  11. "github.com/docker/docker/pkg/system"
  12. "golang.org/x/sys/unix"
  13. "gotest.tools/v3/assert"
  14. is "gotest.tools/v3/assert/cmp"
  15. )
  16. func TestCopy(t *testing.T) {
  17. copyWithFileRange := true
  18. copyWithFileClone := true
  19. doCopyTest(t, &copyWithFileRange, &copyWithFileClone)
  20. }
  21. func TestCopyWithoutRange(t *testing.T) {
  22. copyWithFileRange := false
  23. copyWithFileClone := false
  24. doCopyTest(t, &copyWithFileRange, &copyWithFileClone)
  25. }
  26. func TestCopyDir(t *testing.T) {
  27. srcDir, err := os.MkdirTemp("", "srcDir")
  28. assert.NilError(t, err)
  29. defer os.RemoveAll(srcDir)
  30. populateSrcDir(t, srcDir, 3)
  31. dstDir, err := os.MkdirTemp("", "testdst")
  32. assert.NilError(t, err)
  33. defer os.RemoveAll(dstDir)
  34. assert.Check(t, DirCopy(srcDir, dstDir, Content, false))
  35. assert.NilError(t, filepath.Walk(srcDir, func(srcPath string, f os.FileInfo, err error) error {
  36. if err != nil {
  37. return err
  38. }
  39. // Rebase path
  40. relPath, err := filepath.Rel(srcDir, srcPath)
  41. assert.NilError(t, err)
  42. if relPath == "." {
  43. return nil
  44. }
  45. dstPath := filepath.Join(dstDir, relPath)
  46. // If we add non-regular dirs and files to the test
  47. // then we need to add more checks here.
  48. dstFileInfo, err := os.Lstat(dstPath)
  49. assert.NilError(t, err)
  50. srcFileSys := f.Sys().(*syscall.Stat_t)
  51. dstFileSys := dstFileInfo.Sys().(*syscall.Stat_t)
  52. t.Log(relPath)
  53. if srcFileSys.Dev == dstFileSys.Dev {
  54. assert.Check(t, srcFileSys.Ino != dstFileSys.Ino)
  55. }
  56. // Todo: check size, and ctim is not equal on filesystems that have granular ctimes
  57. assert.Check(t, is.DeepEqual(srcFileSys.Mode, dstFileSys.Mode))
  58. assert.Check(t, is.DeepEqual(srcFileSys.Uid, dstFileSys.Uid))
  59. assert.Check(t, is.DeepEqual(srcFileSys.Gid, dstFileSys.Gid))
  60. assert.Check(t, is.DeepEqual(srcFileSys.Mtim, dstFileSys.Mtim))
  61. return nil
  62. }))
  63. }
  64. func randomMode(baseMode int) os.FileMode {
  65. for i := 0; i < 7; i++ {
  66. baseMode = baseMode | (1&rand.Intn(2))<<uint(i)
  67. }
  68. return os.FileMode(baseMode)
  69. }
  70. func populateSrcDir(t *testing.T, srcDir string, remainingDepth int) {
  71. if remainingDepth == 0 {
  72. return
  73. }
  74. aTime := time.Unix(rand.Int63(), 0)
  75. mTime := time.Unix(rand.Int63(), 0)
  76. for i := 0; i < 10; i++ {
  77. dirName := filepath.Join(srcDir, fmt.Sprintf("srcdir-%d", i))
  78. // Owner all bits set
  79. assert.NilError(t, os.Mkdir(dirName, randomMode(0o700)))
  80. populateSrcDir(t, dirName, remainingDepth-1)
  81. assert.NilError(t, system.Chtimes(dirName, aTime, mTime))
  82. }
  83. for i := 0; i < 10; i++ {
  84. fileName := filepath.Join(srcDir, fmt.Sprintf("srcfile-%d", i))
  85. // Owner read bit set
  86. assert.NilError(t, os.WriteFile(fileName, []byte{}, randomMode(0o400)))
  87. assert.NilError(t, system.Chtimes(fileName, aTime, mTime))
  88. }
  89. }
  90. func doCopyTest(t *testing.T, copyWithFileRange, copyWithFileClone *bool) {
  91. dir, err := os.MkdirTemp("", "docker-copy-check")
  92. assert.NilError(t, err)
  93. defer os.RemoveAll(dir)
  94. srcFilename := filepath.Join(dir, "srcFilename")
  95. dstFilename := filepath.Join(dir, "dstFilename")
  96. r := rand.New(rand.NewSource(0))
  97. buf := make([]byte, 1024)
  98. _, err = r.Read(buf)
  99. assert.NilError(t, err)
  100. assert.NilError(t, os.WriteFile(srcFilename, buf, 0o777))
  101. fileinfo, err := os.Stat(srcFilename)
  102. assert.NilError(t, err)
  103. assert.NilError(t, copyRegular(srcFilename, dstFilename, fileinfo, copyWithFileRange, copyWithFileClone))
  104. readBuf, err := os.ReadFile(dstFilename)
  105. assert.NilError(t, err)
  106. assert.Check(t, is.DeepEqual(buf, readBuf))
  107. }
  108. func TestCopyHardlink(t *testing.T) {
  109. var srcFile1FileInfo, srcFile2FileInfo, dstFile1FileInfo, dstFile2FileInfo unix.Stat_t
  110. srcDir, err := os.MkdirTemp("", "srcDir")
  111. assert.NilError(t, err)
  112. defer os.RemoveAll(srcDir)
  113. dstDir, err := os.MkdirTemp("", "dstDir")
  114. assert.NilError(t, err)
  115. defer os.RemoveAll(dstDir)
  116. srcFile1 := filepath.Join(srcDir, "file1")
  117. srcFile2 := filepath.Join(srcDir, "file2")
  118. dstFile1 := filepath.Join(dstDir, "file1")
  119. dstFile2 := filepath.Join(dstDir, "file2")
  120. assert.NilError(t, os.WriteFile(srcFile1, []byte{}, 0o777))
  121. assert.NilError(t, os.Link(srcFile1, srcFile2))
  122. assert.Check(t, DirCopy(srcDir, dstDir, Content, false))
  123. assert.NilError(t, unix.Stat(srcFile1, &srcFile1FileInfo))
  124. assert.NilError(t, unix.Stat(srcFile2, &srcFile2FileInfo))
  125. assert.Equal(t, srcFile1FileInfo.Ino, srcFile2FileInfo.Ino)
  126. assert.NilError(t, unix.Stat(dstFile1, &dstFile1FileInfo))
  127. assert.NilError(t, unix.Stat(dstFile2, &dstFile2FileInfo))
  128. assert.Check(t, is.Equal(dstFile1FileInfo.Ino, dstFile2FileInfo.Ino))
  129. }