gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/sendfile_socket.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <arpa/inet.h>
    16  #include <netinet/in.h>
    17  #include <sys/sendfile.h>
    18  #include <sys/socket.h>
    19  #include <unistd.h>
    20  
    21  #include <iostream>
    22  #include <vector>
    23  
    24  #include "gtest/gtest.h"
    25  #include "absl/strings/string_view.h"
    26  #include "test/syscalls/linux/ip_socket_test_util.h"
    27  #include "test/util/file_descriptor.h"
    28  #include "test/util/socket_util.h"
    29  #include "test/util/temp_path.h"
    30  #include "test/util/test_util.h"
    31  #include "test/util/thread_util.h"
    32  
    33  namespace gvisor {
    34  namespace testing {
    35  namespace {
    36  
    37  class SendFileTest : public ::testing::TestWithParam<int> {
    38   protected:
    39    PosixErrorOr<std::unique_ptr<SocketPair>> Sockets(int type) {
    40      // Bind a server socket.
    41      int family = GetParam();
    42      switch (family) {
    43        case AF_INET: {
    44          if (type == SOCK_STREAM) {
    45            return SocketPairKind{
    46                "TCP", AF_INET, type, 0,
    47                TCPAcceptBindSocketPairCreator(AF_INET, type, 0, false)}
    48                .Create();
    49          } else {
    50            return SocketPairKind{
    51                "UDP", AF_INET, type, 0,
    52                UDPBidirectionalBindSocketPairCreator(AF_INET, type, 0, false)}
    53                .Create();
    54          }
    55        }
    56        case AF_UNIX: {
    57          if (type == SOCK_STREAM) {
    58            return SocketPairKind{
    59                "UNIX", AF_UNIX, type, 0,
    60                FilesystemAcceptBindSocketPairCreator(AF_UNIX, type, 0)}
    61                .Create();
    62          } else {
    63            return SocketPairKind{
    64                "UNIX", AF_UNIX, type, 0,
    65                FilesystemBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)}
    66                .Create();
    67          }
    68        }
    69        default:
    70          return PosixError(EINVAL);
    71      }
    72    }
    73  };
    74  
    75  // Sends large file to exercise the path that read and writes data multiple
    76  // times, esp. when more data is read than can be written.
    77  TEST_P(SendFileTest, SendMultiple) {
    78    std::vector<char> data(5 * 1024 * 1024);
    79    RandomizeBuffer(data.data(), data.size());
    80  
    81    // Create temp files.
    82    const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
    83        GetAbsoluteTestTmpdir(), absl::string_view(data.data(), data.size()),
    84        TempPath::kDefaultFileMode));
    85    const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    86  
    87    // Create sockets.
    88    auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_STREAM));
    89  
    90    // Thread that reads data from socket and dumps to a file.
    91    ScopedThread th([&] {
    92      FileDescriptor outf =
    93          ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY));
    94  
    95      // Read until socket is closed.
    96      char buf[10240];
    97      for (int cnt = 0;; cnt++) {
    98        int r = RetryEINTR(read)(socks->first_fd(), buf, sizeof(buf));
    99        // We cannot afford to save on every read() call.
   100        if (cnt % 1000 == 0) {
   101          ASSERT_THAT(r, SyscallSucceeds());
   102        } else {
   103          const DisableSave ds;
   104          ASSERT_THAT(r, SyscallSucceeds());
   105        }
   106        if (r == 0) {
   107          // EOF
   108          break;
   109        }
   110        int w = RetryEINTR(write)(outf.get(), buf, r);
   111        // We cannot afford to save on every write() call.
   112        if (cnt % 1010 == 0) {
   113          ASSERT_THAT(w, SyscallSucceedsWithValue(r));
   114        } else {
   115          const DisableSave ds;
   116          ASSERT_THAT(w, SyscallSucceedsWithValue(r));
   117        }
   118      }
   119    });
   120  
   121    // Open the input file as read only.
   122    const FileDescriptor inf =
   123        ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
   124  
   125    int cnt = 0;
   126    for (size_t sent = 0; sent < data.size(); cnt++) {
   127      const size_t remain = data.size() - sent;
   128      std::cout << "sendfile, size=" << data.size() << ", sent=" << sent
   129                << ", remain=" << remain << std::endl;
   130  
   131      // Send data and verify that sendfile returns the correct value.
   132      int res = sendfile(socks->second_fd(), inf.get(), nullptr, remain);
   133      // We cannot afford to save on every sendfile() call.
   134      if (cnt % 120 == 0) {
   135        MaybeSave();
   136      }
   137      if (res == 0) {
   138        // EOF
   139        break;
   140      }
   141      if (res > 0) {
   142        sent += res;
   143      } else {
   144        ASSERT_TRUE(errno == EINTR || errno == EAGAIN) << "errno=" << errno;
   145      }
   146    }
   147  
   148    // Close socket to stop thread.
   149    close(socks->release_second_fd());
   150    th.Join();
   151  
   152    // Verify that the output file has the correct data.
   153    const FileDescriptor outf =
   154        ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY));
   155    std::vector<char> actual(data.size(), '\0');
   156    ASSERT_THAT(RetryEINTR(read)(outf.get(), actual.data(), actual.size()),
   157                SyscallSucceedsWithValue(actual.size()));
   158    ASSERT_EQ(memcmp(data.data(), actual.data(), data.size()), 0);
   159  }
   160  
   161  TEST_P(SendFileTest, Shutdown) {
   162    // Create a socket.
   163    auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_STREAM));
   164  
   165    // If this is a TCP socket, then turn off linger.
   166    if (GetParam() == AF_INET) {
   167      struct linger sl;
   168      sl.l_onoff = 1;
   169      sl.l_linger = 0;
   170      ASSERT_THAT(
   171          setsockopt(socks->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
   172          SyscallSucceeds());
   173    }
   174  
   175    // Create a 1m file with random data.
   176    std::vector<char> data(1024 * 1024);
   177    RandomizeBuffer(data.data(), data.size());
   178    const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   179        GetAbsoluteTestTmpdir(), absl::string_view(data.data(), data.size()),
   180        TempPath::kDefaultFileMode));
   181    const FileDescriptor inf =
   182        ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
   183  
   184    // Read some data, then shutdown the socket. We don't actually care about
   185    // checking the contents (other tests do that), so we just re-use the same
   186    // buffer as above.
   187    ScopedThread t([&]() {
   188      size_t done = 0;
   189      while (done < data.size()) {
   190        int n = RetryEINTR(read)(socks->first_fd(), data.data(), data.size());
   191        ASSERT_THAT(n, SyscallSucceeds());
   192        done += n;
   193      }
   194      // Close the server side socket.
   195      close(socks->release_first_fd());
   196    });
   197  
   198    // Continuously stream from the file to the socket. Note we do not assert
   199    // that a specific amount of data has been written at any time, just that some
   200    // data is written. Eventually, we should get a connection reset error.
   201    while (1) {
   202      off_t offset = 0;  // Always read from the start.
   203      int n = sendfile(socks->second_fd(), inf.get(), &offset, data.size());
   204      EXPECT_THAT(n, AnyOf(SyscallFailsWithErrno(ECONNRESET),
   205                           SyscallFailsWithErrno(EPIPE), SyscallSucceeds()));
   206      if (n <= 0) {
   207        break;
   208      }
   209    }
   210  }
   211  
   212  TEST_P(SendFileTest, SendpageFromEmptyFileToUDP) {
   213    auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_DGRAM));
   214  
   215    TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   216    const FileDescriptor fd =
   217        ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));
   218  
   219    // The value to the count argument has to be so that it is impossible to
   220    // allocate a buffer of this size. In Linux, sendfile transfer at most
   221    // 0x7ffff000 (MAX_RW_COUNT) bytes.
   222    EXPECT_THAT(sendfile(socks->first_fd(), fd.get(), 0x0, 0x8000000000004),
   223                SyscallSucceedsWithValue(0));
   224  }
   225  
   226  INSTANTIATE_TEST_SUITE_P(AddressFamily, SendFileTest,
   227                           ::testing::Values(AF_UNIX, AF_INET));
   228  
   229  }  // namespace
   230  }  // namespace testing
   231  }  // namespace gvisor