summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/sendfile.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/sendfile.cc')
-rw-r--r--test/syscalls/linux/sendfile.cc587
1 files changed, 587 insertions, 0 deletions
diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc
new file mode 100644
index 000000000..64123e904
--- /dev/null
+++ b/test/syscalls/linux/sendfile.cc
@@ -0,0 +1,587 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <linux/unistd.h>
+#include <sys/eventfd.h>
+#include <sys/sendfile.h>
+#include <unistd.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "test/util/eventfd_util.h"
+#include "test/util/file_descriptor.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+TEST(SendFileTest, SendZeroBytes) {
+ // Create temp files.
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct value.
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, 0),
+ SyscallSucceedsWithValue(0));
+}
+
+TEST(SendFileTest, InvalidOffset) {
+ // Create temp files.
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct value.
+ off_t offset = -1;
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), &offset, 0),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+int memfd_create(const std::string& name, unsigned int flags) {
+ return syscall(__NR_memfd_create, name.c_str(), flags);
+}
+
+TEST(SendFileTest, Overflow) {
+ // Create input file.
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file.
+ int fd;
+ EXPECT_THAT(fd = memfd_create("overflow", 0), SyscallSucceeds());
+ const FileDescriptor outf(fd);
+
+ // out_offset + kSize overflows INT64_MAX.
+ loff_t out_offset = 0x7ffffffffffffffeull;
+ constexpr int kSize = 3;
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), &out_offset, kSize),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(SendFileTest, SendTrivially) {
+ // Create temp files.
+ constexpr char kData[] = "To be, or not to be, that is the question:";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ FileDescriptor outf;
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct value.
+ int bytes_sent;
+ EXPECT_THAT(bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize),
+ SyscallSucceedsWithValue(kDataSize));
+
+ // Close outf to avoid leak.
+ outf.reset();
+
+ // Open the output file as read only.
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Verify that the output file has the correct data.
+ char actual[kDataSize];
+ ASSERT_THAT(read(outf.get(), &actual, bytes_sent),
+ SyscallSucceedsWithValue(kDataSize));
+ EXPECT_EQ(kData, absl::string_view(actual, bytes_sent));
+}
+
+TEST(SendFileTest, SendTriviallyWithBothFilesReadWrite) {
+ // Create temp files.
+ constexpr char kData[] = "Whether 'tis nobler in the mind to suffer";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as readwrite.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
+
+ // Open the output file as readwrite.
+ FileDescriptor outf;
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR));
+
+ // Send data and verify that sendfile returns the correct value.
+ int bytes_sent;
+ EXPECT_THAT(bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize),
+ SyscallSucceedsWithValue(kDataSize));
+
+ // Close outf to avoid leak.
+ outf.reset();
+
+ // Open the output file as read only.
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Verify that the output file has the correct data.
+ char actual[kDataSize];
+ ASSERT_THAT(read(outf.get(), &actual, bytes_sent),
+ SyscallSucceedsWithValue(kDataSize));
+ EXPECT_EQ(kData, absl::string_view(actual, bytes_sent));
+}
+
+TEST(SendFileTest, SendAndUpdateFileOffset) {
+ // Create temp files.
+ // Test input string length must be > 2 AND even.
+ constexpr char kData[] = "The slings and arrows of outrageous fortune,";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ constexpr int kHalfDataSize = kDataSize / 2;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ FileDescriptor outf;
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct value.
+ int bytes_sent;
+ EXPECT_THAT(
+ bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize),
+ SyscallSucceedsWithValue(kHalfDataSize));
+
+ // Close outf to avoid leak.
+ outf.reset();
+
+ // Open the output file as read only.
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Verify that the output file has the correct data.
+ char actual[kHalfDataSize];
+ ASSERT_THAT(read(outf.get(), &actual, bytes_sent),
+ SyscallSucceedsWithValue(kHalfDataSize));
+ EXPECT_EQ(absl::string_view(kData, kHalfDataSize),
+ absl::string_view(actual, bytes_sent));
+
+ // Verify that the input file offset has been updated
+ ASSERT_THAT(read(inf.get(), &actual, kDataSize - bytes_sent),
+ SyscallSucceedsWithValue(kHalfDataSize));
+ EXPECT_EQ(
+ absl::string_view(kData + kDataSize - bytes_sent, kDataSize - bytes_sent),
+ absl::string_view(actual, kHalfDataSize));
+}
+
+TEST(SendFileTest, SendAndUpdateFileOffsetFromNonzeroStartingPoint) {
+ // Create temp files.
+ // Test input string length must be > 2 AND divisible by 4.
+ constexpr char kData[] = "The slings and arrows of outrageous fortune,";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ constexpr int kHalfDataSize = kDataSize / 2;
+ constexpr int kQuarterDataSize = kHalfDataSize / 2;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ FileDescriptor outf;
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Read a quarter of the data from the infile which should update the file
+ // offset, we don't actually care about the data so it goes into the garbage.
+ char garbage[kQuarterDataSize];
+ ASSERT_THAT(read(inf.get(), &garbage, kQuarterDataSize),
+ SyscallSucceedsWithValue(kQuarterDataSize));
+
+ // Send data and verify that sendfile returns the correct value.
+ int bytes_sent;
+ EXPECT_THAT(
+ bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize),
+ SyscallSucceedsWithValue(kHalfDataSize));
+
+ // Close out_fd to avoid leak.
+ outf.reset();
+
+ // Open the output file as read only.
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Verify that the output file has the correct data.
+ char actual[kHalfDataSize];
+ ASSERT_THAT(read(outf.get(), &actual, bytes_sent),
+ SyscallSucceedsWithValue(kHalfDataSize));
+ EXPECT_EQ(absl::string_view(kData + kQuarterDataSize, kHalfDataSize),
+ absl::string_view(actual, bytes_sent));
+
+ // Verify that the input file offset has been updated
+ ASSERT_THAT(read(inf.get(), &actual, kQuarterDataSize),
+ SyscallSucceedsWithValue(kQuarterDataSize));
+
+ EXPECT_EQ(
+ absl::string_view(kData + kDataSize - kQuarterDataSize, kQuarterDataSize),
+ absl::string_view(actual, kQuarterDataSize));
+}
+
+TEST(SendFileTest, SendAndUpdateGivenOffset) {
+ // Create temp files.
+ // Test input string length must be >= 4 AND divisible by 4.
+ constexpr char kData[] = "Or to take Arms against a Sea of troubles,";
+ constexpr int kDataSize = sizeof(kData) + 1;
+ constexpr int kHalfDataSize = kDataSize / 2;
+ constexpr int kQuarterDataSize = kHalfDataSize / 2;
+ constexpr int kThreeFourthsDataSize = 3 * kDataSize / 4;
+
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ FileDescriptor outf;
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Create offset for sending.
+ off_t offset = kQuarterDataSize;
+
+ // Send data and verify that sendfile returns the correct value.
+ int bytes_sent;
+ EXPECT_THAT(
+ bytes_sent = sendfile(outf.get(), inf.get(), &offset, kHalfDataSize),
+ SyscallSucceedsWithValue(kHalfDataSize));
+
+ // Close out_fd to avoid leak.
+ outf.reset();
+
+ // Open the output file as read only.
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Verify that the output file has the correct data.
+ char actual[kHalfDataSize];
+ ASSERT_THAT(read(outf.get(), &actual, bytes_sent),
+ SyscallSucceedsWithValue(kHalfDataSize));
+ EXPECT_EQ(absl::string_view(kData + kQuarterDataSize, kHalfDataSize),
+ absl::string_view(actual, bytes_sent));
+
+ // Verify that the input file offset has NOT been updated.
+ ASSERT_THAT(read(inf.get(), &actual, kHalfDataSize),
+ SyscallSucceedsWithValue(kHalfDataSize));
+ EXPECT_EQ(absl::string_view(kData, kHalfDataSize),
+ absl::string_view(actual, kHalfDataSize));
+
+ // Verify that the offset pointer has been updated.
+ EXPECT_EQ(offset, kThreeFourthsDataSize);
+}
+
+TEST(SendFileTest, DoNotSendfileIfOutfileIsAppendOnly) {
+ // Create temp files.
+ constexpr char kData[] = "And by opposing end them: to die, to sleep";
+ constexpr int kDataSize = sizeof(kData) - 1;
+
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as append only.
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY | O_APPEND));
+
+ // Send data and verify that sendfile returns the correct errno.
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(SendFileTest, AppendCheckOrdering) {
+ constexpr char kData[] = "And by opposing end them: to die, to sleep";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+
+ const FileDescriptor read =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
+ const FileDescriptor write =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));
+ const FileDescriptor append =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_APPEND));
+
+ // Check that read/write file mode is verified before append.
+ EXPECT_THAT(sendfile(append.get(), read.get(), nullptr, kDataSize),
+ SyscallFailsWithErrno(EBADF));
+ EXPECT_THAT(sendfile(write.get(), write.get(), nullptr, kDataSize),
+ SyscallFailsWithErrno(EBADF));
+}
+
+TEST(SendFileTest, DoNotSendfileIfOutfileIsNotWritable) {
+ // Create temp files.
+ constexpr char kData[] = "No more; and by a sleep, to say we end";
+ constexpr int kDataSize = sizeof(kData) - 1;
+
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as read only.
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Send data and verify that sendfile returns the correct errno.
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize),
+ SyscallFailsWithErrno(EBADF));
+}
+
+TEST(SendFileTest, DoNotSendfileIfInfileIsNotReadable) {
+ // Create temp files.
+ constexpr char kData[] = "the heart-ache, and the thousand natural shocks";
+ constexpr int kDataSize = sizeof(kData) - 1;
+
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as write only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_WRONLY));
+
+ // Open the output file as write only.
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct errno.
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize),
+ SyscallFailsWithErrno(EBADF));
+}
+
+TEST(SendFileTest, DoNotSendANegativeNumberOfBytes) {
+ // Create temp files.
+ constexpr char kData[] = "that Flesh is heir to? 'Tis a consummation";
+
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct errno.
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, -1),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(SendFileTest, SendTheCorrectNumberOfBytesEvenIfWeTryToSendTooManyBytes) {
+ // Create temp files.
+ constexpr char kData[] = "devoutly to be wished. To die, to sleep,";
+ constexpr int kDataSize = sizeof(kData) - 1;
+
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Open the output file as write only.
+ FileDescriptor outf;
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Send data and verify that sendfile returns the correct value.
+ int bytes_sent;
+ EXPECT_THAT(
+ bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize + 100),
+ SyscallSucceedsWithValue(kDataSize));
+
+ // Close outf to avoid leak.
+ outf.reset();
+
+ // Open the output file as read only.
+ outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
+
+ // Verify that the output file has the correct data.
+ char actual[kDataSize];
+ ASSERT_THAT(read(outf.get(), &actual, bytes_sent),
+ SyscallSucceedsWithValue(kDataSize));
+ EXPECT_EQ(kData, absl::string_view(actual, bytes_sent));
+}
+
+TEST(SendFileTest, SendToNotARegularFile) {
+ // Make temp input directory and open as read only.
+ const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY));
+
+ // Make temp output file and open as write only.
+ const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const FileDescriptor outf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
+
+ // Receive an error since a directory is not a regular file.
+ EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, 0),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(SendFileTest, SendPipeWouldBlock) {
+ // Create temp file.
+ constexpr char kData[] =
+ "The fool doth think he is wise, but the wise man knows himself to be a "
+ "fool.";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Setup the output named pipe.
+ int fds[2];
+ ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds());
+ const FileDescriptor rfd(fds[0]);
+ const FileDescriptor wfd(fds[1]);
+
+ // Fill up the pipe's buffer.
+ int pipe_size = -1;
+ ASSERT_THAT(pipe_size = fcntl(wfd.get(), F_GETPIPE_SZ), SyscallSucceeds());
+ std::vector<char> buf(2 * pipe_size);
+ ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(pipe_size));
+
+ EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, kDataSize),
+ SyscallFailsWithErrno(EWOULDBLOCK));
+}
+
+TEST(SendFileTest, SendPipeBlocks) {
+ // Create temp file.
+ constexpr char kData[] =
+ "The fault, dear Brutus, is not in our stars, but in ourselves.";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+
+ // Open the input file as read only.
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Setup the output named pipe.
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ const FileDescriptor rfd(fds[0]);
+ const FileDescriptor wfd(fds[1]);
+
+ // Fill up the pipe's buffer.
+ int pipe_size = -1;
+ ASSERT_THAT(pipe_size = fcntl(wfd.get(), F_GETPIPE_SZ), SyscallSucceeds());
+ std::vector<char> buf(pipe_size);
+ ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(pipe_size));
+
+ ScopedThread t([&]() {
+ absl::SleepFor(absl::Milliseconds(100));
+ ASSERT_THAT(read(rfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(pipe_size));
+ });
+
+ EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, kDataSize),
+ SyscallSucceedsWithValue(kDataSize));
+}
+
+TEST(SendFileTest, SendToSpecialFile) {
+ // Create temp file.
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode));
+
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
+ constexpr int kSize = 0x7ff;
+ ASSERT_THAT(ftruncate(inf.get(), kSize), SyscallSucceeds());
+
+ auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
+
+ // eventfd can accept a number of bytes which is a multiple of 8.
+ EXPECT_THAT(sendfile(eventfd.get(), inf.get(), nullptr, 0xfffff),
+ SyscallSucceedsWithValue(kSize & (~7)));
+}
+
+TEST(SendFileTest, SendFileToPipe) {
+ // Create temp file.
+ constexpr char kData[] = "<insert-quote-here>";
+ constexpr int kDataSize = sizeof(kData) - 1;
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
+ const FileDescriptor inf =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
+
+ // Create a pipe for sending to a pipe.
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ const FileDescriptor rfd(fds[0]);
+ const FileDescriptor wfd(fds[1]);
+
+ // Expect to read up to the given size.
+ std::vector<char> buf(kDataSize);
+ ScopedThread t([&]() {
+ absl::SleepFor(absl::Milliseconds(100));
+ ASSERT_THAT(read(rfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(kDataSize));
+ });
+
+ // Send with twice the size of the file, which should hit EOF.
+ EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, kDataSize * 2),
+ SyscallSucceedsWithValue(kDataSize));
+}
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor