copy_test.go 5.1 KB

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