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 := remain 20 if max > maxCopyFileRangeRound { 21 max = maxCopyFileRangeRound 22 } 23 n, e := copyFileRange(dst, src, int(max)) 24 if n > 0 { 25 remain -= n 26 written += n 27 } 28 handled, err = handleCopyFileRangeErr(e, n, written) 29 if n == 0 || !handled || err != nil { 30 return 31 } 32 } 33 34 return written, true, nil 35 } 36 37 // copyFileRange performs one round of copy_file_range(2). 38 func copyFileRange(dst, src *FD, max int) (written int64, err error) { 39 // For Linux, the signature of copy_file_range(2) is: 40 // 41 // ssize_t copy_file_range(int fd_in, loff_t *off_in, 42 // int fd_out, loff_t *off_out, 43 // size_t len, unsigned int flags); 44 // 45 // For FreeBSD, the signature of copy_file_range(2) is: 46 // 47 // ssize_t 48 // copy_file_range(int infd, off_t *inoffp, int outfd, off_t *outoffp, 49 // size_t len, unsigned int flags); 50 // 51 // Note that in the call to unix.CopyFileRange below, we use nil 52 // values for off_in/off_out and inoffp/outoffp, which means "the file 53 // offset for infd(fd_in) or outfd(fd_out) respectively will be used and 54 // updated by the number of bytes copied". 55 // 56 // That is why we must acquire locks for both file descriptors (and why 57 // this whole machinery is in the internal/poll package to begin with). 58 if err := dst.writeLock(); err != nil { 59 return 0, err 60 } 61 defer dst.writeUnlock() 62 if err := src.readLock(); err != nil { 63 return 0, err 64 } 65 defer src.readUnlock() 66 return ignoringEINTR2(func() (int64, error) { 67 n, err := unix.CopyFileRange(src.Sysfd, nil, dst.Sysfd, nil, max, 0) 68 return int64(n), err 69 }) 70 } 71