basicfs_copy_range_ioctl.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright (C) 2019 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // +build linux
  7. package fs
  8. import (
  9. "io"
  10. "syscall"
  11. "unsafe"
  12. )
  13. func init() {
  14. registerCopyRangeImplementation(CopyRangeMethodIoctl, copyRangeImplementationForBasicFile(copyRangeIoctl))
  15. }
  16. const FICLONE = 0x40049409
  17. const FICLONERANGE = 0x4020940d
  18. /*
  19. http://man7.org/linux/man-pages/man2/ioctl_ficlonerange.2.html
  20. struct file_clone_range {
  21. __s64 src_fd;
  22. __u64 src_offset;
  23. __u64 src_length;
  24. __u64 dest_offset;
  25. };
  26. */
  27. type fileCloneRange struct {
  28. srcFd int64
  29. srcOffset uint64
  30. srcLength uint64
  31. dstOffset uint64
  32. }
  33. func copyRangeIoctl(src, dst basicFile, srcOffset, dstOffset, size int64) error {
  34. fi, err := src.Stat()
  35. if err != nil {
  36. return err
  37. }
  38. if srcOffset+size > fi.Size() {
  39. return io.ErrUnexpectedEOF
  40. }
  41. // https://www.man7.org/linux/man-pages/man2/ioctl_ficlonerange.2.html
  42. // If src_length is zero, the ioctl reflinks to the end of the source file.
  43. if srcOffset+size == fi.Size() {
  44. size = 0
  45. }
  46. if srcOffset == 0 && dstOffset == 0 && size == 0 {
  47. // Optimization for whole file copies.
  48. var errNo syscall.Errno
  49. _, err := withFileDescriptors(src, dst, func(srcFd, dstFd uintptr) (int, error) {
  50. _, _, errNo = syscall.Syscall(syscall.SYS_IOCTL, dstFd, FICLONE, srcFd)
  51. return 0, nil
  52. })
  53. // Failure in withFileDescriptors
  54. if err != nil {
  55. return err
  56. }
  57. if errNo != 0 {
  58. return errNo
  59. }
  60. return nil
  61. }
  62. var errNo syscall.Errno
  63. _, err = withFileDescriptors(src, dst, func(srcFd, dstFd uintptr) (int, error) {
  64. params := fileCloneRange{
  65. srcFd: int64(srcFd),
  66. srcOffset: uint64(srcOffset),
  67. srcLength: uint64(size),
  68. dstOffset: uint64(dstOffset),
  69. }
  70. _, _, errNo = syscall.Syscall(syscall.SYS_IOCTL, dstFd, FICLONERANGE, uintptr(unsafe.Pointer(&params)))
  71. return 0, nil
  72. })
  73. // Failure in withFileDescriptors
  74. if err != nil {
  75. return err
  76. }
  77. if errNo != 0 {
  78. return errNo
  79. }
  80. return nil
  81. }