copy_test.go 4.5 KB

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