basicfs_copy_range_ioctl.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  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. "golang.org/x/sys/unix"
  11. )
  12. func init() {
  13. registerCopyRangeImplementation(CopyRangeMethodIoctl, copyRangeImplementationForBasicFile(copyRangeIoctl))
  14. }
  15. func copyRangeIoctl(src, dst basicFile, srcOffset, dstOffset, size int64) error {
  16. fi, err := src.Stat()
  17. if err != nil {
  18. return err
  19. }
  20. if srcOffset+size > fi.Size() {
  21. return io.ErrUnexpectedEOF
  22. }
  23. // https://www.man7.org/linux/man-pages/man2/ioctl_ficlonerange.2.html
  24. // If src_length is zero, the ioctl reflinks to the end of the source file.
  25. if srcOffset+size == fi.Size() {
  26. size = 0
  27. }
  28. if srcOffset == 0 && dstOffset == 0 && size == 0 {
  29. // Optimization for whole file copies.
  30. _, err := withFileDescriptors(src, dst, func(srcFd, dstFd uintptr) (int, error) {
  31. return 0, unix.IoctlFileClone(int(dstFd), int(srcFd))
  32. })
  33. return err
  34. }
  35. _, err = withFileDescriptors(src, dst, func(srcFd, dstFd uintptr) (int, error) {
  36. params := unix.FileCloneRange{
  37. Src_fd: int64(srcFd),
  38. Src_offset: uint64(srcOffset),
  39. Src_length: uint64(size),
  40. Dest_offset: uint64(dstOffset),
  41. }
  42. return 0, unix.IoctlFileCloneRange(int(dstFd), &params)
  43. })
  44. return err
  45. }