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, &copyWithFileRange, &copyWithFileClone)
    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, &copyWithFileRange, &copyWithFileClone)
    45  }
    46  
    47  func TestCopyWithoutRange(t *testing.T) {
    48  	copyWithFileRange := false
    49  	copyWithFileClone := false
    50  	doCopyTest(t, &copyWithFileRange, &copyWithFileClone)
    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  }