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