32
32
# include < dirent.h>
33
33
# include < sys/stat.h>
34
34
# include < sys/statvfs.h>
35
+ # include < sys/types.h>
35
36
# include < unistd.h>
36
37
#endif
37
38
#include < fcntl.h> /* values for fchmodat */
@@ -178,8 +179,35 @@ void __copy(const path& from, const path& to, copy_options options, error_code*
178
179
namespace detail {
179
180
namespace {
180
181
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 not modify the fd positions as copy_file_impl_sendfile may be called after a partial copy
192
+ off_t off_in = 0 ;
193
+ off_t off_out = 0 ;
194
+ do {
195
+ ssize_t res;
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
181
209
#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) {
183
211
size_t count = read_fd.get_stat ().st_size ;
184
212
do {
185
213
ssize_t res;
@@ -194,6 +222,34 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
194
222
195
223
return true ;
196
224
}
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
+ # if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
230
+ if (copy_file_impl_copy_file_range (read_fd, write_fd, ec)) {
231
+ return true ;
232
+ }
233
+ // EINVAL: src and dst are the same file (this is not cheaply
234
+ // detectable from userspace)
235
+ // EINVAL: copy_file_range is unsupported for this file type by the
236
+ // underlying filesystem
237
+ // ENOTSUP: undocumented, can arise with old kernels and NFS
238
+ // EOPNOTSUPP: filesystem does not implement copy_file_range
239
+ // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
240
+ // with normal copying)
241
+ // EXDEV: src and dst are on different filesystems that do not support
242
+ // cross-fs copy_file_range
243
+ // ENOENT: undocumented, can arise with CIFS
244
+ // ENOSYS: unsupported by kernel or blocked by seccomp
245
+ if (ec.value () != EINVAL && ec.value () != ENOTSUP && ec.value () != EOPNOTSUPP && ec.value () != ETXTBSY &&
246
+ ec.value () != EXDEV && ec.value () != ENOENT && ec.value () != ENOSYS) {
247
+ return false ;
248
+ }
249
+ ec.clear ();
250
+ # endif
251
+ return copy_file_impl_sendfile (read_fd, write_fd, ec);
252
+ }
197
253
#elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
198
254
bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
199
255
struct CopyFileState {
0 commit comments