summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/splice.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/splice.cc')
-rw-r--r--test/syscalls/linux/splice.cc833
1 files changed, 0 insertions, 833 deletions
diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc
deleted file mode 100644
index c2369db54..000000000
--- a/test/syscalls/linux/splice.cc
+++ /dev/null
@@ -1,833 +0,0 @@
-// Copyright 2019 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/resource.h>
-#include <sys/sendfile.h>
-#include <sys/time.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/file_descriptor.h"
-#include "test/util/signal_util.h"
-#include "test/util/temp_path.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-#include "test/util/timer_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-TEST(SpliceTest, TwoRegularFiles) {
- // 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 in_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
-
- // Open the output file as write only.
- const FileDescriptor out_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
-
- // Verify that it is rejected as expected; regardless of offsets.
- loff_t in_offset = 0;
- loff_t out_offset = 0;
- EXPECT_THAT(splice(in_fd.get(), &in_offset, out_fd.get(), &out_offset, 1, 0),
- SyscallFailsWithErrno(EINVAL));
- EXPECT_THAT(splice(in_fd.get(), nullptr, out_fd.get(), &out_offset, 1, 0),
- SyscallFailsWithErrno(EINVAL));
- EXPECT_THAT(splice(in_fd.get(), &in_offset, out_fd.get(), nullptr, 1, 0),
- SyscallFailsWithErrno(EINVAL));
- EXPECT_THAT(splice(in_fd.get(), nullptr, out_fd.get(), nullptr, 1, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-int memfd_create(const std::string& name, unsigned int flags) {
- return syscall(__NR_memfd_create, name.c_str(), flags);
-}
-
-TEST(SpliceTest, NegativeOffset) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill the pipe.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Open the output file as write only.
- int fd;
- EXPECT_THAT(fd = memfd_create("negative", 0), SyscallSucceeds());
- const FileDescriptor out_fd(fd);
-
- loff_t out_offset = 0xffffffffffffffffull;
- constexpr int kSize = 2;
- EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-// Write offset + size overflows int64.
-//
-// This is a regression test for b/148041624.
-TEST(SpliceTest, WriteOverflow) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill the pipe.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Open the output file.
- int fd;
- EXPECT_THAT(fd = memfd_create("overflow", 0), SyscallSucceeds());
- const FileDescriptor out_fd(fd);
-
- // out_offset + kSize overflows INT64_MAX.
- loff_t out_offset = 0x7ffffffffffffffeull;
- constexpr int kSize = 3;
- EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(SpliceTest, SamePipe) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill the pipe.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Attempt to splice to itself.
- EXPECT_THAT(splice(rfd.get(), nullptr, wfd.get(), nullptr, kPageSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(TeeTest, SamePipe) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill the pipe.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Attempt to tee to itself.
- EXPECT_THAT(tee(rfd.get(), wfd.get(), kPageSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(TeeTest, RegularFile) {
- // Open some file.
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor in_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Attempt to tee from the file.
- EXPECT_THAT(tee(in_fd.get(), wfd.get(), kPageSize, 0),
- SyscallFailsWithErrno(EINVAL));
- EXPECT_THAT(tee(rfd.get(), in_fd.get(), kPageSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(SpliceTest, PipeOffsets) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // All pipe offsets should be rejected.
- loff_t in_offset = 0;
- loff_t out_offset = 0;
- EXPECT_THAT(splice(rfd1.get(), &in_offset, wfd2.get(), &out_offset, 1, 0),
- SyscallFailsWithErrno(ESPIPE));
- EXPECT_THAT(splice(rfd1.get(), nullptr, wfd2.get(), &out_offset, 1, 0),
- SyscallFailsWithErrno(ESPIPE));
- EXPECT_THAT(splice(rfd1.get(), &in_offset, wfd2.get(), nullptr, 1, 0),
- SyscallFailsWithErrno(ESPIPE));
-}
-
-// Event FDs may be used with splice without an offset.
-TEST(SpliceTest, FromEventFD) {
- // Open the input eventfd with an initial value so that it is readable.
- constexpr uint64_t kEventFDValue = 1;
- int efd;
- ASSERT_THAT(efd = eventfd(kEventFDValue, 0), SyscallSucceeds());
- const FileDescriptor in_fd(efd);
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Splice 8-byte eventfd value to pipe.
- constexpr int kEventFDSize = 8;
- EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, kEventFDSize, 0),
- SyscallSucceedsWithValue(kEventFDSize));
-
- // Contents should be equal.
- std::vector<char> rbuf(kEventFDSize);
- ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kEventFDSize));
- EXPECT_EQ(memcmp(rbuf.data(), &kEventFDValue, rbuf.size()), 0);
-}
-
-// Event FDs may not be used with splice with an offset.
-TEST(SpliceTest, FromEventFDOffset) {
- int efd;
- ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds());
- const FileDescriptor in_fd(efd);
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Attempt to splice 8-byte eventfd value to pipe with offset.
- //
- // This is not allowed because eventfd doesn't support pread.
- constexpr int kEventFDSize = 8;
- loff_t in_off = 0;
- EXPECT_THAT(splice(in_fd.get(), &in_off, wfd.get(), nullptr, kEventFDSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-// Event FDs may not be used with splice with an offset.
-TEST(SpliceTest, ToEventFDOffset) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill with a value.
- constexpr int kEventFDSize = 8;
- std::vector<char> buf(kEventFDSize);
- buf[0] = 1;
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kEventFDSize));
-
- int efd;
- ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds());
- const FileDescriptor out_fd(efd);
-
- // Attempt to splice 8-byte eventfd value to pipe with offset.
- //
- // This is not allowed because eventfd doesn't support pwrite.
- loff_t out_off = 0;
- EXPECT_THAT(
- splice(rfd.get(), nullptr, out_fd.get(), &out_off, kEventFDSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(SpliceTest, ToPipe) {
- // Open the input file.
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor in_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(in_fd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
- ASSERT_THAT(lseek(in_fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Splice to the pipe.
- EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, kPageSize, 0),
- SyscallSucceedsWithValue(kPageSize));
-
- // Contents should be equal.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
-}
-
-TEST(SpliceTest, ToPipeEOF) {
- // Create and open an empty input file.
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor in_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Splice from the empty file to the pipe.
- EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, 123, 0),
- SyscallSucceedsWithValue(0));
-}
-
-TEST(SpliceTest, ToPipeOffset) {
- // Open the input file.
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor in_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(in_fd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Splice to the pipe.
- loff_t in_offset = kPageSize / 2;
- EXPECT_THAT(
- splice(in_fd.get(), &in_offset, wfd.get(), nullptr, kPageSize / 2, 0),
- SyscallSucceedsWithValue(kPageSize / 2));
-
- // Contents should be equal to only the second part.
- std::vector<char> rbuf(kPageSize / 2);
- ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize / 2));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data() + (kPageSize / 2), rbuf.size()), 0);
-}
-
-TEST(SpliceTest, FromPipe) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Open the output file.
- const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor out_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR));
-
- // Splice to the output file.
- EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, kPageSize, 0),
- SyscallSucceedsWithValue(kPageSize));
-
- // The offset of the output should be equal to kPageSize. We assert that and
- // reset to zero so that we can read the contents and ensure they match.
- EXPECT_THAT(lseek(out_fd.get(), 0, SEEK_CUR),
- SyscallSucceedsWithValue(kPageSize));
- ASSERT_THAT(lseek(out_fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));
-
- // Contents should be equal.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(out_fd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
-}
-
-TEST(SpliceTest, FromPipeMultiple) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- std::string buf = "abcABC123";
- ASSERT_THAT(write(wfd.get(), buf.c_str(), buf.size()),
- SyscallSucceedsWithValue(buf.size()));
-
- // Open the output file.
- const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor out_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR));
-
- // Splice from the pipe to the output file over several calls.
- EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3, 0),
- SyscallSucceedsWithValue(3));
- EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3, 0),
- SyscallSucceedsWithValue(3));
- EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3, 0),
- SyscallSucceedsWithValue(3));
-
- // Reset cursor to zero so that we can check the contents.
- ASSERT_THAT(lseek(out_fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));
-
- // Contents should be equal.
- std::vector<char> rbuf(buf.size());
- ASSERT_THAT(read(out_fd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(rbuf.size()));
- EXPECT_EQ(memcmp(rbuf.data(), buf.c_str(), buf.size()), 0);
-}
-
-TEST(SpliceTest, FromPipeOffset) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Open the input file.
- const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor out_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR));
-
- // Splice to the output file.
- loff_t out_offset = kPageSize / 2;
- EXPECT_THAT(
- splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kPageSize, 0),
- SyscallSucceedsWithValue(kPageSize));
-
- // Content should reflect the splice. We write to a specific offset in the
- // file, so the internals should now be allocated sparsely.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(out_fd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- std::vector<char> zbuf(kPageSize / 2);
- memset(zbuf.data(), 0, zbuf.size());
- EXPECT_EQ(memcmp(rbuf.data(), zbuf.data(), zbuf.size()), 0);
- EXPECT_EQ(memcmp(rbuf.data() + kPageSize / 2, buf.data(), kPageSize / 2), 0);
-}
-
-TEST(SpliceTest, TwoPipes) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd1.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Splice to the second pipe, using two operations.
- EXPECT_THAT(
- splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize / 2, 0),
- SyscallSucceedsWithValue(kPageSize / 2));
- EXPECT_THAT(
- splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize / 2, 0),
- SyscallSucceedsWithValue(kPageSize / 2));
-
- // Content should reflect the splice.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0);
-}
-
-TEST(SpliceTest, TwoPipesCircular) {
- // This test deadlocks the sentry on VFS1 because VFS1 splice ordering is
- // based on fs.File.UniqueID, which does not prevent circular ordering between
- // e.g. inode-level locks taken by fs.FileOperations.
- SKIP_IF(IsRunningWithVFS1());
-
- // Create two pipes.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor first_rfd(fds[0]);
- const FileDescriptor first_wfd(fds[1]);
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor second_rfd(fds[0]);
- const FileDescriptor second_wfd(fds[1]);
-
- // On Linux, each pipe is normally limited to
- // include/linux/pipe_fs_i.h:PIPE_DEF_BUFFERS buffers worth of data.
- constexpr size_t PIPE_DEF_BUFFERS = 16;
-
- // Write some data to each pipe. Below we splice 1 byte at a time between
- // pipes, which very quickly causes each byte to be stored in a separate
- // buffer, so we must ensure that the total amount of data in the system is <=
- // PIPE_DEF_BUFFERS bytes.
- std::vector<char> buf(PIPE_DEF_BUFFERS / 2);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(first_wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(buf.size()));
- ASSERT_THAT(write(second_wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(buf.size()));
-
- // Have another thread splice from the second pipe to the first, while we
- // splice from the first to the second. The test passes if this does not
- // deadlock.
- const int kIterations = 1000;
- DisableSave ds;
- ScopedThread t([&]() {
- for (int i = 0; i < kIterations; i++) {
- ASSERT_THAT(
- splice(second_rfd.get(), nullptr, first_wfd.get(), nullptr, 1, 0),
- SyscallSucceedsWithValue(1));
- }
- });
- for (int i = 0; i < kIterations; i++) {
- ASSERT_THAT(
- splice(first_rfd.get(), nullptr, second_wfd.get(), nullptr, 1, 0),
- SyscallSucceedsWithValue(1));
- }
-}
-
-TEST(SpliceTest, Blocking) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // This thread writes to the main pipe.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ScopedThread t([&]() {
- ASSERT_THAT(write(wfd1.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
- });
-
- // Attempt a splice immediately; it should block.
- EXPECT_THAT(splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize, 0),
- SyscallSucceedsWithValue(kPageSize));
-
- // Thread should be joinable.
- t.Join();
-
- // Content should reflect the splice.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0);
-}
-
-TEST(TeeTest, Blocking) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // This thread writes to the main pipe.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ScopedThread t([&]() {
- ASSERT_THAT(write(wfd1.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
- });
-
- // Attempt a tee immediately; it should block.
- EXPECT_THAT(tee(rfd1.get(), wfd2.get(), kPageSize, 0),
- SyscallSucceedsWithValue(kPageSize));
-
- // Thread should be joinable.
- t.Join();
-
- // Content should reflect the splice, in both pipes.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0);
- ASSERT_THAT(read(rfd1.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0);
-}
-
-TEST(TeeTest, BlockingWrite) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // Make some data available to be read.
- std::vector<char> buf1(kPageSize);
- RandomizeBuffer(buf1.data(), buf1.size());
- ASSERT_THAT(write(wfd1.get(), buf1.data(), buf1.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Fill up the write pipe's buffer.
- int pipe_size = -1;
- ASSERT_THAT(pipe_size = fcntl(wfd2.get(), F_GETPIPE_SZ), SyscallSucceeds());
- std::vector<char> buf2(pipe_size);
- ASSERT_THAT(write(wfd2.get(), buf2.data(), buf2.size()),
- SyscallSucceedsWithValue(pipe_size));
-
- ScopedThread t([&]() {
- absl::SleepFor(absl::Milliseconds(100));
- ASSERT_THAT(read(rfd2.get(), buf2.data(), buf2.size()),
- SyscallSucceedsWithValue(pipe_size));
- });
-
- // Attempt a tee immediately; it should block.
- EXPECT_THAT(tee(rfd1.get(), wfd2.get(), kPageSize, 0),
- SyscallSucceedsWithValue(kPageSize));
-
- // Thread should be joinable.
- t.Join();
-
- // Content should reflect the tee.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf1.data(), kPageSize), 0);
-}
-
-TEST(SpliceTest, NonBlocking) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // Splice with no data to back it.
- EXPECT_THAT(splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize,
- SPLICE_F_NONBLOCK),
- SyscallFailsWithErrno(EAGAIN));
-}
-
-TEST(TeeTest, NonBlocking) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // Splice with no data to back it.
- EXPECT_THAT(tee(rfd1.get(), wfd2.get(), kPageSize, SPLICE_F_NONBLOCK),
- SyscallFailsWithErrno(EAGAIN));
-}
-
-TEST(TeeTest, MultiPage) {
- // Create two new pipes.
- int first[2], second[2];
- ASSERT_THAT(pipe(first), SyscallSucceeds());
- const FileDescriptor rfd1(first[0]);
- const FileDescriptor wfd1(first[1]);
- ASSERT_THAT(pipe(second), SyscallSucceeds());
- const FileDescriptor rfd2(second[0]);
- const FileDescriptor wfd2(second[1]);
-
- // Make some data available to be read.
- std::vector<char> wbuf(8 * kPageSize);
- RandomizeBuffer(wbuf.data(), wbuf.size());
- ASSERT_THAT(write(wfd1.get(), wbuf.data(), wbuf.size()),
- SyscallSucceedsWithValue(wbuf.size()));
-
- // Attempt a tee immediately; it should complete.
- EXPECT_THAT(tee(rfd1.get(), wfd2.get(), wbuf.size(), 0),
- SyscallSucceedsWithValue(wbuf.size()));
-
- // Content should reflect the tee.
- std::vector<char> rbuf(wbuf.size());
- ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(rbuf.size()));
- EXPECT_EQ(memcmp(rbuf.data(), wbuf.data(), rbuf.size()), 0);
- ASSERT_THAT(read(rfd1.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(rbuf.size()));
- EXPECT_EQ(memcmp(rbuf.data(), wbuf.data(), rbuf.size()), 0);
-}
-
-TEST(SpliceTest, FromPipeMaxFileSize) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- // Open the input file.
- const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const FileDescriptor out_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR));
-
- EXPECT_THAT(ftruncate(out_fd.get(), 13 << 20), SyscallSucceeds());
- EXPECT_THAT(lseek(out_fd.get(), 0, SEEK_END),
- SyscallSucceedsWithValue(13 << 20));
-
- // Set our file size limit.
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, SIGXFSZ);
- TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0);
- rlimit rlim = {};
- rlim.rlim_cur = rlim.rlim_max = (13 << 20);
- EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &rlim), SyscallSucceeds());
-
- // Splice to the output file.
- EXPECT_THAT(
- splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3 * kPageSize, 0),
- SyscallFailsWithErrno(EFBIG));
-
- // Contents should be equal.
- std::vector<char> rbuf(kPageSize);
- ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
-}
-
-TEST(SpliceTest, FromPipeToDevZero) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- FileDescriptor wfd(fds[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- const FileDescriptor zero =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY));
-
- // Close the write end to prevent blocking below.
- wfd.reset();
-
- // Splice to /dev/zero. The first call should empty the pipe, and the return
- // value should not exceed the number of bytes available for reading.
- EXPECT_THAT(
- splice(rfd.get(), nullptr, zero.get(), nullptr, kPageSize + 123, 0),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_THAT(splice(rfd.get(), nullptr, zero.get(), nullptr, 1, 0),
- SyscallSucceedsWithValue(0));
-}
-
-static volatile int signaled = 0;
-void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; }
-
-TEST(SpliceTest, ToPipeWithSmallCapacityDoesNotSpin_NoRandomSave) {
- // Writes to a pipe that are less than PIPE_BUF must be atomic. This test
- // creates a pipe with only 128 bytes of capacity (< PIPE_BUF) and checks that
- // splicing to the pipe does not spin. See b/170743336.
-
- // Create a file with one page of data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- GetAbsoluteTestTmpdir(), absl::string_view(buf.data(), buf.size()),
- TempPath::kDefaultFileMode));
- auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
-
- // Create a pipe with size 4096, and fill all but 128 bytes of it.
- int p[2];
- ASSERT_THAT(pipe(p), SyscallSucceeds());
- ASSERT_THAT(fcntl(p[1], F_SETPIPE_SZ, kPageSize), SyscallSucceeds());
- const int kWriteSize = kPageSize - 128;
- std::vector<char> writeBuf(kWriteSize);
- RandomizeBuffer(writeBuf.data(), writeBuf.size());
- ASSERT_THAT(write(p[1], writeBuf.data(), writeBuf.size()),
- SyscallSucceedsWithValue(kWriteSize));
-
- // Set up signal handler.
- struct sigaction sa = {};
- sa.sa_sigaction = SigUsr1Handler;
- sa.sa_flags = SA_SIGINFO;
- const auto cleanup_sigact =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa));
-
- // Send SIGUSR1 to this thread in 1 second.
- struct sigevent sev = {};
- sev.sigev_notify = SIGEV_THREAD_ID;
- sev.sigev_signo = SIGUSR1;
- sev.sigev_notify_thread_id = gettid();
- auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev));
- struct itimerspec its = {};
- its.it_value = absl::ToTimespec(absl::Seconds(1));
- DisableSave ds; // Asserting an EINTR.
- ASSERT_NO_ERRNO(timer.Set(0, its));
-
- // Now splice the file to the pipe. This should block, but not spin, and
- // should return EINTR because it is interrupted by the signal.
- EXPECT_THAT(splice(fd.get(), nullptr, p[1], nullptr, kPageSize, 0),
- SyscallFailsWithErrno(EINTR));
-
- // Alarm should have been handled.
- EXPECT_EQ(signaled, 1);
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor