2021-08-23 09:14:53 -04:00
|
|
|
//go:build linux
|
2017-08-30 22:07:02 -04:00
|
|
|
// +build linux
|
|
|
|
|
2018-02-05 16:05:59 -05:00
|
|
|
package copy // import "github.com/docker/docker/daemon/graphdriver/copy"
|
2017-08-30 22:07:02 -04:00
|
|
|
|
|
|
|
import (
|
2017-11-23 19:31:31 -05:00
|
|
|
"fmt"
|
2017-08-30 22:07:02 -04:00
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-11-23 19:31:31 -05:00
|
|
|
"syscall"
|
2017-08-30 22:07:02 -04:00
|
|
|
"testing"
|
2017-11-23 19:31:31 -05:00
|
|
|
"time"
|
2017-11-21 13:11:43 -05:00
|
|
|
|
2017-11-23 19:31:31 -05:00
|
|
|
"github.com/docker/docker/pkg/system"
|
|
|
|
"golang.org/x/sys/unix"
|
2020-02-07 08:39:24 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
2017-08-30 22:07:02 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestCopy(t *testing.T) {
|
|
|
|
copyWithFileRange := true
|
|
|
|
copyWithFileClone := true
|
|
|
|
doCopyTest(t, ©WithFileRange, ©WithFileClone)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCopyWithoutRange(t *testing.T) {
|
|
|
|
copyWithFileRange := false
|
|
|
|
copyWithFileClone := false
|
|
|
|
doCopyTest(t, ©WithFileRange, ©WithFileClone)
|
|
|
|
}
|
|
|
|
|
2017-11-23 19:31:31 -05:00
|
|
|
func TestCopyDir(t *testing.T) {
|
2021-08-24 06:10:50 -04:00
|
|
|
srcDir, err := os.MkdirTemp("", "srcDir")
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2021-05-01 08:39:50 -04:00
|
|
|
defer os.RemoveAll(srcDir)
|
2017-11-23 19:31:31 -05:00
|
|
|
populateSrcDir(t, srcDir, 3)
|
|
|
|
|
2021-08-24 06:10:50 -04:00
|
|
|
dstDir, err := os.MkdirTemp("", "testdst")
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-11-23 19:31:31 -05:00
|
|
|
defer os.RemoveAll(dstDir)
|
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, DirCopy(srcDir, dstDir, Content, false))
|
|
|
|
assert.NilError(t, filepath.Walk(srcDir, func(srcPath string, f os.FileInfo, err error) error {
|
2017-11-23 19:31:31 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebase path
|
|
|
|
relPath, err := filepath.Rel(srcDir, srcPath)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-11-23 19:31:31 -05:00
|
|
|
if relPath == "." {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
dstPath := filepath.Join(dstDir, relPath)
|
|
|
|
|
|
|
|
// If we add non-regular dirs and files to the test
|
|
|
|
// then we need to add more checks here.
|
|
|
|
dstFileInfo, err := os.Lstat(dstPath)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-11-23 19:31:31 -05:00
|
|
|
|
|
|
|
srcFileSys := f.Sys().(*syscall.Stat_t)
|
|
|
|
dstFileSys := dstFileInfo.Sys().(*syscall.Stat_t)
|
|
|
|
|
|
|
|
t.Log(relPath)
|
|
|
|
if srcFileSys.Dev == dstFileSys.Dev {
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, srcFileSys.Ino != dstFileSys.Ino)
|
2017-11-23 19:31:31 -05:00
|
|
|
}
|
2019-11-27 09:43:23 -05:00
|
|
|
// Todo: check size, and ctim is not equal on filesystems that have granular ctimes
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, is.DeepEqual(srcFileSys.Mode, dstFileSys.Mode))
|
|
|
|
assert.Check(t, is.DeepEqual(srcFileSys.Uid, dstFileSys.Uid))
|
|
|
|
assert.Check(t, is.DeepEqual(srcFileSys.Gid, dstFileSys.Gid))
|
|
|
|
assert.Check(t, is.DeepEqual(srcFileSys.Mtim, dstFileSys.Mtim))
|
2017-11-23 19:31:31 -05:00
|
|
|
|
|
|
|
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
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, os.Mkdir(dirName, randomMode(0700)))
|
2017-11-23 19:31:31 -05:00
|
|
|
populateSrcDir(t, dirName, remainingDepth-1)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, system.Chtimes(dirName, aTime, mTime))
|
2017-11-23 19:31:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
fileName := filepath.Join(srcDir, fmt.Sprintf("srcfile-%d", i))
|
|
|
|
// Owner read bit set
|
2021-08-24 06:10:50 -04:00
|
|
|
assert.NilError(t, os.WriteFile(fileName, []byte{}, randomMode(0400)))
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, system.Chtimes(fileName, aTime, mTime))
|
2017-11-23 19:31:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-30 22:07:02 -04:00
|
|
|
func doCopyTest(t *testing.T, copyWithFileRange, copyWithFileClone *bool) {
|
2021-08-24 06:10:50 -04:00
|
|
|
dir, err := os.MkdirTemp("", "docker-copy-check")
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-08-30 22:07:02 -04:00
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
srcFilename := filepath.Join(dir, "srcFilename")
|
2021-05-01 08:39:50 -04:00
|
|
|
dstFilename := filepath.Join(dir, "dstFilename")
|
2017-08-30 22:07:02 -04:00
|
|
|
|
|
|
|
r := rand.New(rand.NewSource(0))
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
_, err = r.Read(buf)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2021-08-24 06:10:50 -04:00
|
|
|
assert.NilError(t, os.WriteFile(srcFilename, buf, 0777))
|
2017-08-30 22:07:02 -04:00
|
|
|
fileinfo, err := os.Stat(srcFilename)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-08-30 22:07:02 -04:00
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, copyRegular(srcFilename, dstFilename, fileinfo, copyWithFileRange, copyWithFileClone))
|
2021-08-24 06:10:50 -04:00
|
|
|
readBuf, err := os.ReadFile(dstFilename)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.DeepEqual(buf, readBuf))
|
2017-08-30 22:07:02 -04:00
|
|
|
}
|
2017-11-21 13:11:43 -05:00
|
|
|
|
|
|
|
func TestCopyHardlink(t *testing.T) {
|
|
|
|
var srcFile1FileInfo, srcFile2FileInfo, dstFile1FileInfo, dstFile2FileInfo unix.Stat_t
|
|
|
|
|
2021-08-24 06:10:50 -04:00
|
|
|
srcDir, err := os.MkdirTemp("", "srcDir")
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-11-21 13:11:43 -05:00
|
|
|
defer os.RemoveAll(srcDir)
|
|
|
|
|
2021-08-24 06:10:50 -04:00
|
|
|
dstDir, err := os.MkdirTemp("", "dstDir")
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-11-21 13:11:43 -05:00
|
|
|
defer os.RemoveAll(dstDir)
|
|
|
|
|
|
|
|
srcFile1 := filepath.Join(srcDir, "file1")
|
|
|
|
srcFile2 := filepath.Join(srcDir, "file2")
|
|
|
|
dstFile1 := filepath.Join(dstDir, "file1")
|
|
|
|
dstFile2 := filepath.Join(dstDir, "file2")
|
2021-08-24 06:10:50 -04:00
|
|
|
assert.NilError(t, os.WriteFile(srcFile1, []byte{}, 0777))
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, os.Link(srcFile1, srcFile2))
|
2017-11-21 13:11:43 -05:00
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.Check(t, DirCopy(srcDir, dstDir, Content, false))
|
2017-11-21 13:11:43 -05:00
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, unix.Stat(srcFile1, &srcFile1FileInfo))
|
|
|
|
assert.NilError(t, unix.Stat(srcFile2, &srcFile2FileInfo))
|
|
|
|
assert.Equal(t, srcFile1FileInfo.Ino, srcFile2FileInfo.Ino)
|
2017-11-21 13:11:43 -05:00
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, unix.Stat(dstFile1, &dstFile1FileInfo))
|
|
|
|
assert.NilError(t, unix.Stat(dstFile2, &dstFile2FileInfo))
|
|
|
|
assert.Check(t, is.Equal(dstFile1FileInfo.Ino, dstFile2FileInfo.Ino))
|
2017-11-21 13:11:43 -05:00
|
|
|
}
|