Source file src/internal/poll/copy_file_range_unix.go
1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build freebsd || linux 6 7 package poll 8 9 import "internal/syscall/unix" 10 11 // CopyFileRange copies at most remain bytes of data from src to dst, using 12 // the copy_file_range system call. dst and src must refer to regular files. 13 func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) { 14 if !supportCopyFileRange() { 15 return 0, false, nil 16 } 17 18 for remain > 0 { 19 max := min(remain, maxCopyFileRangeRound) 20 n, e := copyFileRange(dst, src, int(max)) 21 if n > 0 { 22 remain -= n 23 written += n 24 } 25 handled, err = handleCopyFileRangeErr(e, n, written) 26 if n == 0 || !handled || err != nil { 27 return 28 } 29 } 30 31 return written, true, nil 32 } 33 34 // copyFileRange performs one round of copy_file_range(2). 35 func copyFileRange(dst, src *FD, max int) (written int64, err error) { 36 // For Linux, the signature of copy_file_range(2) is: 37 // 38 // ssize_t copy_file_range(int fd_in, loff_t *off_in, 39 // int fd_out, loff_t *off_out, 40 // size_t len, unsigned int flags); 41 // 42 // For FreeBSD, the signature of copy_file_range(2) is: 43 // 44 // ssize_t 45 // copy_file_range(int infd, off_t *inoffp, int outfd, off_t *outoffp, 46 // size_t len, unsigned int flags); 47 // 48 // Note that in the call to unix.CopyFileRange below, we use nil 49 // values for off_in/off_out and inoffp/outoffp, which means "the file 50 // offset for infd(fd_in) or outfd(fd_out) respectively will be used and 51 // updated by the number of bytes copied". 52 // 53 // That is why we must acquire locks for both file descriptors (and why 54 // this whole machinery is in the internal/poll package to begin with). 55 if err := dst.writeLock(); err != nil { 56 return 0, err 57 } 58 defer dst.writeUnlock() 59 if err := src.readLock(); err != nil { 60 return 0, err 61 } 62 defer src.readUnlock() 63 return ignoringEINTR2(func() (int64, error) { 64 n, err := unix.CopyFileRange(src.Sysfd, nil, dst.Sysfd, nil, max, 0) 65 return int64(n), err 66 }) 67 } 68