3232# include < dirent.h>
3333# include < sys/stat.h>
3434# include < sys/statvfs.h>
35+ # include < sys/types.h>
3536# include < unistd.h>
3637#endif
3738#include < fcntl.h> /* values for fchmodat */
3839#include < time.h>
3940
41+ // since Linux 4.5 and FreeBSD 13
42+ #if defined(__linux__) || defined(__FreeBSD__)
43+ # define _LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE
44+ #endif
4045#if __has_include(<sys/sendfile.h>)
4146# include < sys/sendfile.h>
4247# define _LIBCPP_FILESYSTEM_USE_SENDFILE
@@ -178,8 +183,36 @@ void __copy(const path& from, const path& to, copy_options options, error_code*
178183namespace detail {
179184namespace {
180185
186+ #if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
187+ bool copy_file_impl_copy_file_range (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
188+ size_t count = read_fd.get_stat ().st_size ;
189+ // a zero-length file is either empty, or not copyable by this syscall
190+ // return early to avoid the syscall cost
191+ if (count == 0 ) {
192+ ec = {EINVAL, generic_category ()};
193+ return false ;
194+ }
195+ // do not modify the fd positions as copy_file_impl_sendfile may be called after a partial copy
196+ off_t off_in = 0 ;
197+ off_t off_out = 0 ;
198+ do {
199+ ssize_t res;
200+
201+ if ((res = ::copy_file_range (read_fd.fd , &off_in, write_fd.fd , &off_out, count, 0 )) == -1 ) {
202+ ec = capture_errno ();
203+ return false ;
204+ }
205+ count -= res;
206+ } while (count > 0 );
207+
208+ ec.clear ();
209+
210+ return true ;
211+ }
212+ #endif
213+
181214#if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
182- bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
215+ bool copy_file_impl_sendfile (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
183216 size_t count = read_fd.get_stat ().st_size ;
184217 do {
185218 ssize_t res;
@@ -194,6 +227,46 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
194227
195228 return true ;
196229}
230+ #endif
231+
232+ #if defined(__linux__) || defined(__FreeBSD__)
233+ bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
234+ # if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
235+ if (copy_file_impl_copy_file_range (read_fd, write_fd, ec)) {
236+ return true ;
237+ }
238+ // EINVAL: src and dst are the same file (this is not cheaply
239+ // detectable from userspace)
240+ // EINVAL: copy_file_range is unsupported for this file type by the
241+ // underlying filesystem
242+ // ENOTSUP: undocumented, can arise with old kernels and NFS
243+ // EOPNOTSUPP: filesystem does not implement copy_file_range
244+ // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
245+ // with normal copying)
246+ // EXDEV: src and dst are on different filesystems that do not support
247+ // cross-fs copy_file_range
248+ // ENOENT: undocumented, can arise with CIFS
249+ // ENOSYS: unsupported by kernel or blocked by seccomp
250+ if (ec.value () != EINVAL && ec.value () != ENOTSUP && ec.value () != EOPNOTSUPP && ec.value () != ETXTBSY &&
251+ ec.value () != EXDEV && ec.value () != ENOENT && ec.value () != ENOSYS) {
252+ return false ;
253+ }
254+ ec.clear ();
255+ # endif
256+
257+ # if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
258+ if (copy_file_impl_sendfile (read_fd, write_fd, ec)) {
259+ return true ;
260+ }
261+ // EINVAL: unsupported file type
262+ if (ec.value () != EINVAL) {
263+ return false ;
264+ }
265+ ec.clear ();
266+ # endif
267+ ec = {EINVAL, generic_category ()};
268+ return false ;
269+ }
197270#elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
198271bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
199272 struct CopyFileState {
0 commit comments