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