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 */
38
39
#include < time.h>
39
40
41
+ // since Linux 4.5 and FreeBSD 13
42
+ #if defined(__linux__) || defined(__FreeBSD__)
43
+ # define _LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE
44
+ #endif
40
45
#if __has_include(<sys/sendfile.h>)
41
46
# include < sys/sendfile.h>
42
47
# define _LIBCPP_FILESYSTEM_USE_SENDFILE
@@ -178,8 +183,36 @@ void __copy(const path& from, const path& to, copy_options options, error_code*
178
183
namespace detail {
179
184
namespace {
180
185
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
+
181
214
#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) {
183
216
size_t count = read_fd.get_stat ().st_size ;
184
217
do {
185
218
ssize_t res;
@@ -194,6 +227,46 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
194
227
195
228
return true ;
196
229
}
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
+ }
197
270
#elif defined(_LIBCPP_FILESYSTEM_USE_COPYFILE)
198
271
bool copy_file_impl (FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
199
272
struct CopyFileState {
0 commit comments