copy_linux.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package fs
  2. import (
  3. "io"
  4. "os"
  5. "syscall"
  6. "github.com/containerd/continuity/sysx"
  7. "github.com/pkg/errors"
  8. "golang.org/x/sys/unix"
  9. )
  10. func copyFileInfo(fi os.FileInfo, name string) error {
  11. st := fi.Sys().(*syscall.Stat_t)
  12. if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
  13. if os.IsPermission(err) {
  14. // Normally if uid/gid are the same this would be a no-op, but some
  15. // filesystems may still return EPERM... for instance NFS does this.
  16. // In such a case, this is not an error.
  17. if dstStat, err2 := os.Lstat(name); err2 == nil {
  18. st2 := dstStat.Sys().(*syscall.Stat_t)
  19. if st.Uid == st2.Uid && st.Gid == st2.Gid {
  20. err = nil
  21. }
  22. }
  23. }
  24. if err != nil {
  25. return errors.Wrapf(err, "failed to chown %s", name)
  26. }
  27. }
  28. if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
  29. if err := os.Chmod(name, fi.Mode()); err != nil {
  30. return errors.Wrapf(err, "failed to chmod %s", name)
  31. }
  32. }
  33. timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
  34. if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
  35. return errors.Wrapf(err, "failed to utime %s", name)
  36. }
  37. return nil
  38. }
  39. func copyFileContent(dst, src *os.File) error {
  40. st, err := src.Stat()
  41. if err != nil {
  42. return errors.Wrap(err, "unable to stat source")
  43. }
  44. n, err := unix.CopyFileRange(int(src.Fd()), nil, int(dst.Fd()), nil, int(st.Size()), 0)
  45. if err != nil {
  46. if err != unix.ENOSYS && err != unix.EXDEV {
  47. return errors.Wrap(err, "copy file range failed")
  48. }
  49. buf := bufferPool.Get().(*[]byte)
  50. _, err = io.CopyBuffer(dst, src, *buf)
  51. bufferPool.Put(buf)
  52. return err
  53. }
  54. if int64(n) != st.Size() {
  55. return errors.Wrapf(err, "short copy: %d of %d", int64(n), st.Size())
  56. }
  57. return nil
  58. }
  59. func copyXAttrs(dst, src string) error {
  60. xattrKeys, err := sysx.LListxattr(src)
  61. if err != nil {
  62. return errors.Wrapf(err, "failed to list xattrs on %s", src)
  63. }
  64. for _, xattr := range xattrKeys {
  65. data, err := sysx.LGetxattr(src, xattr)
  66. if err != nil {
  67. return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
  68. }
  69. if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
  70. return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
  71. }
  72. }
  73. return nil
  74. }
  75. func copyDevice(dst string, fi os.FileInfo) error {
  76. st, ok := fi.Sys().(*syscall.Stat_t)
  77. if !ok {
  78. return errors.New("unsupported stat type")
  79. }
  80. return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
  81. }