|
@@ -3,17 +3,20 @@
|
|
|
package copy
|
|
|
|
|
|
import (
|
|
|
+ "fmt"
|
|
|
"io/ioutil"
|
|
|
"math/rand"
|
|
|
"os"
|
|
|
"path/filepath"
|
|
|
+ "syscall"
|
|
|
"testing"
|
|
|
-
|
|
|
- "golang.org/x/sys/unix"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/docker/docker/pkg/parsers/kernel"
|
|
|
+ "github.com/docker/docker/pkg/system"
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
"github.com/stretchr/testify/require"
|
|
|
+ "golang.org/x/sys/unix"
|
|
|
)
|
|
|
|
|
|
func TestIsCopyFileRangeSyscallAvailable(t *testing.T) {
|
|
@@ -47,6 +50,84 @@ func TestCopyWithoutRange(t *testing.T) {
|
|
|
doCopyTest(t, ©WithFileRange, ©WithFileClone)
|
|
|
}
|
|
|
|
|
|
+func TestCopyDir(t *testing.T) {
|
|
|
+ srcDir, err := ioutil.TempDir("", "srcDir")
|
|
|
+ require.NoError(t, err)
|
|
|
+ populateSrcDir(t, srcDir, 3)
|
|
|
+
|
|
|
+ dstDir, err := ioutil.TempDir("", "testdst")
|
|
|
+ require.NoError(t, err)
|
|
|
+ defer os.RemoveAll(dstDir)
|
|
|
+
|
|
|
+ assert.NoError(t, DirCopy(srcDir, dstDir, Content, false))
|
|
|
+ require.NoError(t, filepath.Walk(srcDir, func(srcPath string, f os.FileInfo, err error) error {
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // Rebase path
|
|
|
+ relPath, err := filepath.Rel(srcDir, srcPath)
|
|
|
+ require.NoError(t, err)
|
|
|
+ if relPath == "." {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ dstPath := filepath.Join(dstDir, relPath)
|
|
|
+ require.NoError(t, err)
|
|
|
+
|
|
|
+ // If we add non-regular dirs and files to the test
|
|
|
+ // then we need to add more checks here.
|
|
|
+ dstFileInfo, err := os.Lstat(dstPath)
|
|
|
+ require.NoError(t, err)
|
|
|
+
|
|
|
+ srcFileSys := f.Sys().(*syscall.Stat_t)
|
|
|
+ dstFileSys := dstFileInfo.Sys().(*syscall.Stat_t)
|
|
|
+
|
|
|
+ t.Log(relPath)
|
|
|
+ if srcFileSys.Dev == dstFileSys.Dev {
|
|
|
+ assert.NotEqual(t, srcFileSys.Ino, dstFileSys.Ino)
|
|
|
+ }
|
|
|
+ // Todo: check size, and ctim is not equal
|
|
|
+ /// on filesystems that have granular ctimes
|
|
|
+ assert.Equal(t, srcFileSys.Mode, dstFileSys.Mode)
|
|
|
+ assert.Equal(t, srcFileSys.Uid, dstFileSys.Uid)
|
|
|
+ assert.Equal(t, srcFileSys.Gid, dstFileSys.Gid)
|
|
|
+ assert.Equal(t, srcFileSys.Mtim, dstFileSys.Mtim)
|
|
|
+
|
|
|
+ return nil
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+func randomMode(baseMode int) os.FileMode {
|
|
|
+ for i := 0; i < 7; i++ {
|
|
|
+ baseMode = baseMode | (1&rand.Intn(2))<<uint(i)
|
|
|
+ }
|
|
|
+ return os.FileMode(baseMode)
|
|
|
+}
|
|
|
+
|
|
|
+func populateSrcDir(t *testing.T, srcDir string, remainingDepth int) {
|
|
|
+ if remainingDepth == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ aTime := time.Unix(rand.Int63(), 0)
|
|
|
+ mTime := time.Unix(rand.Int63(), 0)
|
|
|
+
|
|
|
+ for i := 0; i < 10; i++ {
|
|
|
+ dirName := filepath.Join(srcDir, fmt.Sprintf("srcdir-%d", i))
|
|
|
+ // Owner all bits set
|
|
|
+ require.NoError(t, os.Mkdir(dirName, randomMode(0700)))
|
|
|
+ populateSrcDir(t, dirName, remainingDepth-1)
|
|
|
+ require.NoError(t, system.Chtimes(dirName, aTime, mTime))
|
|
|
+ }
|
|
|
+
|
|
|
+ for i := 0; i < 10; i++ {
|
|
|
+ fileName := filepath.Join(srcDir, fmt.Sprintf("srcfile-%d", i))
|
|
|
+ // Owner read bit set
|
|
|
+ require.NoError(t, ioutil.WriteFile(fileName, []byte{}, randomMode(0400)))
|
|
|
+ require.NoError(t, system.Chtimes(fileName, aTime, mTime))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func doCopyTest(t *testing.T, copyWithFileRange, copyWithFileClone *bool) {
|
|
|
dir, err := ioutil.TempDir("", "docker-copy-check")
|
|
|
require.NoError(t, err)
|