# include <windows.h>
#endif
+#ifdef HAVE_SYS_SENDFILE_H
+# include <sys/sendfile.h>
+#endif
+
namespace fs = util::filesystem;
using pstr = util::PathString;
FMT("Failed to open {} for writing: {}", dest, strerror(errno)));
}
}
- TRY(read_fd(*src_fd, [&](nonstd::span<const uint8_t> data) {
- write_fd(*dest_fd, data.data(), data.size());
- }));
+
+#if defined(HAVE_SYS_SENDFILE_H)
+ DirEntry dir_entry(src, *src_fd);
+ if (!dir_entry) {
+ return tl::unexpected(FMT("Failed to stat {}: {}", src, strerror(errno)));
+ }
+ ssize_t bytes_left = dir_entry.size();
+ bool fallback_to_rw = false;
+ while (bytes_left > 0) {
+ ssize_t n = sendfile(*dest_fd, *src_fd, nullptr, bytes_left);
+ if (n < 0) {
+ fallback_to_rw = (errno == EINVAL || errno == ENOSYS);
+ if (!fallback_to_rw) {
+ return tl::unexpected(FMT("Failed to copy: {} to {}", src, dest));
+ }
+ break;
+ }
+ bytes_left -= n;
+ }
+
+ if (fallback_to_rw) {
+#endif
+ TRY(read_fd(*src_fd, [&](nonstd::span<const uint8_t> data) {
+ write_fd(*dest_fd, data.data(), data.size());
+ }));
+#if defined(HAVE_SYS_SENDFILE_H)
+ }
+#endif
dest_fd.close();
src_fd.close();
CHECK(util::likely_size_on_disk(4097) == 8192);
}
-TEST_CASE("util::read_file and util::write_file, text data")
+TEST_CASE("util::read_file util::write_file and util::copy_file, text data")
{
TestContext test_context;
REQUIRE(util::write_file("test", "foo\nbar\n"));
- auto data = util::read_file<std::string>("test");
+ CHECK(util::copy_file("test", "test2"));
+ auto data = util::read_file<std::string>("test2");
REQUIRE(data);
CHECK(*data == "foo\nbar\n");
REQUIRE(util::write_file("test", "foo\r\nbar\r\n"));
- data = util::read_file<std::string>("test");
+ CHECK(util::copy_file("test", "test2", util::ViaTmpFile::yes));
+ data = util::read_file<std::string>("test2");
REQUIRE(data);
CHECK(*data == "foo\r\nbar\r\n");
// Newline handling
REQUIRE(util::write_file("test", "foo\r\nbar\n"));
- auto bin_data = util::read_file<std::vector<uint8_t>>("test");
+ CHECK(util::copy_file("test", "test2"));
+ auto bin_data = util::read_file<std::vector<uint8_t>>("test2");
REQUIRE(bin_data);
#ifdef _WIN32
const std::string expected_bin_data = "foo\r\r\nbar\r\n";
CHECK(result.error() == "No such file or directory");
}
-TEST_CASE("util::read_file and util::write_file, binary data")
+TEST_CASE("util::read_file, util::write_file and util::copy_file, binary data")
{
TestContext test_context;
}
CHECK(util::write_file("test", expected));
- auto actual = util::read_file<std::vector<uint8_t>>("test");
+ CHECK(util::copy_file("test", "test2", util::ViaTmpFile::yes));
+ auto actual = util::read_file<std::vector<uint8_t>>("test2");
REQUIRE(actual);
CHECK(*actual == expected);