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 */
@@ -178,8 +179,35 @@ void __copy(const path& from, const path& to, copy_options options, error_code*
178179namespace detail {
179180namespace {
180181
182+ #if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
183+ bool copy_file_impl_copy_file_range (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
184+ size_t count = read_fd.get_stat ().st_size ;
185+ // a zero-length file is either empty, or not copyable by this syscall
186+ // return early to avoid the syscall cost
187+ if (count == 0 ) {
188+ ec = {EINVAL, generic_category ()};
189+ return false ;
190+ }
191+ do {
192+ ssize_t res;
193+ // do not modify the fd positions as copy_file_impl_sendfile may be called after a partial copy
194+ off_t off_in = 0 ;
195+ off_t off_out = 0 ;
196+
197+ if ((res = ::copy_file_range (read_fd.fd , &off_in, write_fd.fd , &off_out, count, 0 )) == -1 ) {
198+ ec = capture_errno ();
199+ return false ;
200+ }
201+ count -= res;
202+ } while (count > 0 );
203+
204+ ec.clear ();
205+
206+ return true ;
207+ }
208+ #endif
181209#if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
182- bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
210+ bool copy_file_impl_sendfile (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
183211 size_t count = read_fd.get_stat ().st_size ;
184212 do {
185213 ssize_t res;
@@ -194,6 +222,36 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
194222
195223 return true ;
196224}
225+ #endif
226+
227+ #if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
228+ bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
229+ bool has_copied = false ;
230+ # if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
231+ has_copied = copy_file_impl_copy_file_range (read_fd, write_fd, ec);
232+ if (has_copied) {
233+ return true ;
234+ }
235+ // EINVAL: src and dst are the same file (this is not cheaply
236+ // detectable from userspace)
237+ // EINVAL: copy_file_range is unsupported for this file type by the
238+ // underlying filesystem
239+ // ENOTSUP: undocumented, can arise with old kernels and NFS
240+ // EOPNOTSUPP: filesystem does not implement copy_file_range
241+ // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
242+ // with normal copying)
243+ // EXDEV: src and dst are on different filesystems that do not support
244+ // cross-fs copy_file_range
245+ // ENOENT: undocumented, can arise with CIFS
246+ // ENOSYS: unsupported by kernel or blocked by seccomp
247+ if (ec.value () != EINVAL && ec.value () != ENOTSUP && ec.value () != EOPNOTSUPP && ec.value () != ETXTBSY &&
248+ ec.value () != EXDEV && ec.value () != ENOENT && ec.value () != ENOSYS) {
249+ return false ;
250+ }
251+ ec.clear ();
252+ # endif
253+ return copy_file_impl_sendfile (read_fd, write_fd, ec);
254+ }
197255#elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
198256bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
199257 struct CopyFileState {
0 commit comments