diff --git a/fs/copy.go b/fs/copy.go index a1fe735..8c44c7c 100644 --- a/fs/copy.go +++ b/fs/copy.go @@ -4,10 +4,19 @@ import ( "io/ioutil" "os" "path/filepath" + "sync" "github.com/pkg/errors" ) +var ( + bufferPool = &sync.Pool{ + New: func() interface{} { + return make([]byte, 32*1024) + }, + } +) + // CopyDirectory copies the directory from src to dst. // Most efficient copy of files is attempted. func CopyDirectory(dst, src string) error { diff --git a/fs/copy_linux.go b/fs/copy_linux.go index 293bec9..a6c193c 100644 --- a/fs/copy_linux.go +++ b/fs/copy_linux.go @@ -3,49 +3,12 @@ package fs import ( "io" "os" - "strconv" "syscall" "github.com/pkg/errors" "github.com/stevvooe/continuity/sysx" ) -var ( - kernel, major int -) - -func init() { - uts := &syscall.Utsname{} - - err := syscall.Uname(uts) - if err != nil { - panic(err) - } - - p := [2][]byte{{}, {}} - - release := uts.Release - i := 0 - for pi := 0; pi < len(p); pi++ { - for release[i] != 0 { - c := byte(release[i]) - i++ - if c == '.' || c == '-' { - break - } - p[pi] = append(p[pi], c) - } - } - kernel, err = strconv.Atoi(string(p[0])) - if err != nil { - panic(err) - } - major, err = strconv.Atoi(string(p[1])) - if err != nil { - panic(err) - } -} - func copyFileInfo(fi os.FileInfo, name string) error { st := fi.Sys().(*syscall.Stat_t) if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil { @@ -66,29 +29,28 @@ func copyFileInfo(fi os.FileInfo, name string) error { } func copyFileContent(dst, src *os.File) error { - if checkKernel(4, 5) { - // Use copy_file_range to do in kernel copying - // See https://lwn.net/Articles/659523/ - // 326 on x86_64 - st, err := src.Stat() - if err != nil { - return errors.Wrap(err, "unable to stat source") - } - n, _, e1 := syscall.Syscall6(326, src.Fd(), 0, dst.Fd(), 0, uintptr(st.Size()), 0) - if e1 != 0 { - return errors.Wrap(err, "copy_file_range failed") - } - if int64(n) != st.Size() { - return errors.Wrapf(err, "short copy: %d of %d", int64(n), st.Size()) - } - return nil + st, err := src.Stat() + if err != nil { + return errors.Wrap(err, "unable to stat source") } - _, err := io.Copy(dst, src) - return err -} -func checkKernel(k, m int) bool { - return (kernel == k && major >= m) || kernel > k + n, err := sysx.CopyFileRange(src.Fd(), nil, dst.Fd(), nil, int(st.Size()), 0) + if err != nil { + if err != syscall.ENOSYS && err != syscall.EXDEV { + return errors.Wrap(err, "copy file range failed") + } + + buf := bufferPool.Get().([]byte) + _, err = io.CopyBuffer(dst, src, buf) + bufferPool.Put(buf) + return err + } + + if int64(n) != st.Size() { + return errors.Wrapf(err, "short copy: %d of %d", int64(n), st.Size()) + } + + return nil } func copyXAttrs(dst, src string) error { diff --git a/fs/copy_windows.go b/fs/copy_windows.go index e17c912..fb4933c 100644 --- a/fs/copy_windows.go +++ b/fs/copy_windows.go @@ -18,7 +18,9 @@ func copyFileInfo(fi os.FileInfo, name string) error { } func copyFileContent(dst, src *os.File) error { - _, err := io.Copy(dst, src) + buf := bufferPool.Get().([]byte) + _, err := io.CopyBuffer(dst, src, buf) + bufferPool.Put(buf) return err }