[libcxx-commits] [libcxx] [libc++] use copy_file_range for fs::copy (PR #109211)

Jannik Glückert via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 2 00:44:49 PDT 2024


================
@@ -194,6 +271,46 @@ bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_cod
 
   return true;
 }
+#endif
+
+#if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE) || defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
+bool copy_file_impl(FileDescriptor& read_fd, FileDescriptor& write_fd, error_code& ec) {
+#  if defined(_LIBCPP_FILESYSTEM_USE_COPY_FILE_RANGE)
+  if (copy_file_impl_copy_file_range(read_fd, write_fd, ec)) {
+    return true;
+  }
+  // EINVAL: src and dst are the same file (this is not cheaply
+  // detectable from userspace)
+  // EINVAL: copy_file_range is unsupported for this file type by the
+  // underlying filesystem
+  // ENOTSUP: undocumented, can arise with old kernels and NFS
+  // EOPNOTSUPP: filesystem does not implement copy_file_range
+  // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
+  // with normal copying)
+  // EXDEV: src and dst are on different filesystems that do not support
+  // cross-fs copy_file_range
+  // ENOENT: undocumented, can arise with CIFS
+  // ENOSYS: unsupported by kernel or blocked by seccomp
+  if (ec.value() != EINVAL && ec.value() != ENOTSUP && ec.value() != EOPNOTSUPP && ec.value() != ETXTBSY &&
+      ec.value() != EXDEV && ec.value() != ENOENT && ec.value() != ENOSYS) {
+    return false;
+  }
+  ec.clear();
+#  endif
+
+#  if defined(_LIBCPP_FILESYSTEM_USE_SENDFILE)
+  if (copy_file_impl_sendfile(read_fd, write_fd, ec)) {
+    return true;
+  }
+  // EINVAL: unsupported file type
+  if (ec.value() != EINVAL) {
+    return false;
+  }
+  ec.clear();
+#  endif
+
+  return copy_file_impl_fstream(read_fd, write_fd, ec);
+}
----------------
Jannik2099 wrote:

That doesn't work, `copy_file_range` will (usually) fail on cross-fs copy, and we'd want to fall back to `sendfile` in that case, and not abandon ship and do a userspace copy.

Since only Linux and FreeBSD support `copy_file_range`, and only linux supports `sendfile` on files, perhaps something like:

```
#ifdef __linux__ || __FreeBSD__
copy_file_impl {
    #ifdef HAS_COPY_FILE_RANGE
    if(copy_file_range) { return; }
    #endif
    #ifdef HAS_SENDFILE
    if(sendfile) { return; }
    #endif
    fstream
}
#endif
```

Of course, this will get procedually more messy when another Unix platform implements any combination of `copy_file_range` and `sendfile`.

https://github.com/llvm/llvm-project/pull/109211


More information about the libcxx-commits mailing list