basicfs_copy_range_ioctl.go 1.4 KB

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