copy_linux.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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. size := st.Size()
  45. first := true
  46. srcFd := int(src.Fd())
  47. dstFd := int(dst.Fd())
  48. for size > 0 {
  49. n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, int(size), 0)
  50. if err != nil {
  51. if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
  52. return errors.Wrap(err, "copy file range failed")
  53. }
  54. buf := bufferPool.Get().(*[]byte)
  55. _, err = io.CopyBuffer(dst, src, *buf)
  56. bufferPool.Put(buf)
  57. return errors.Wrap(err, "userspace copy failed")
  58. }
  59. first = false
  60. size -= int64(n)
  61. }
  62. return nil
  63. }
  64. func copyXAttrs(dst, src string) error {
  65. xattrKeys, err := sysx.LListxattr(src)
  66. if err != nil {
  67. return errors.Wrapf(err, "failed to list xattrs on %s", src)
  68. }
  69. for _, xattr := range xattrKeys {
  70. data, err := sysx.LGetxattr(src, xattr)
  71. if err != nil {
  72. return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
  73. }
  74. if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
  75. return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
  76. }
  77. }
  78. return nil
  79. }
  80. func copyDevice(dst string, fi os.FileInfo) error {
  81. st, ok := fi.Sys().(*syscall.Stat_t)
  82. if !ok {
  83. return errors.New("unsupported stat type")
  84. }
  85. return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
  86. }