gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/pwritev2.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 <fcntl.h> 16 #include <sys/syscall.h> 17 #include <sys/types.h> 18 #include <sys/uio.h> 19 20 #include <string> 21 #include <vector> 22 23 #include "gtest/gtest.h" 24 #include "test/syscalls/linux/file_base.h" 25 #include "test/util/file_descriptor.h" 26 #include "test/util/temp_path.h" 27 #include "test/util/test_util.h" 28 29 namespace gvisor { 30 namespace testing { 31 32 namespace { 33 34 #ifndef SYS_pwritev2 35 #if defined(__x86_64__) 36 #define SYS_pwritev2 328 37 #elif defined(__aarch64__) 38 #define SYS_pwritev2 287 39 #else 40 #error "Unknown architecture" 41 #endif 42 #endif // SYS_pwrite2 43 44 #ifndef RWF_HIPRI 45 #define RWF_HIPRI 0x1 46 #endif // RWF_HIPRI 47 48 #ifndef RWF_DSYNC 49 #define RWF_DSYNC 0x2 50 #endif // RWF_DSYNC 51 52 #ifndef RWF_SYNC 53 #define RWF_SYNC 0x4 54 #endif // RWF_SYNC 55 56 constexpr int kBufSize = 1024; 57 58 void SetContent(std::vector<char>& content) { 59 for (uint i = 0; i < content.size(); i++) { 60 content[i] = static_cast<char>((i % 10) + '0'); 61 } 62 } 63 64 ssize_t pwritev2(unsigned long fd, const struct iovec* iov, 65 unsigned long iovcnt, off_t offset, unsigned long flags) { 66 // syscall on pwritev2 does some weird things (see man syscall and search 67 // pwritev2), so we insert a 0 to word align the flags argument on native. 68 return syscall(SYS_pwritev2, fd, iov, iovcnt, offset, 0, flags); 69 } 70 71 // This test is the base case where we call pwritev (no offset, no flags). 72 TEST(Writev2Test, BaseCall) { 73 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 74 75 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 76 GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); 77 const FileDescriptor fd = 78 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 79 80 std::vector<char> content(kBufSize); 81 SetContent(content); 82 struct iovec iov[2]; 83 iov[0].iov_base = content.data(); 84 iov[0].iov_len = content.size() / 2; 85 iov[1].iov_base = static_cast<char*>(iov[0].iov_base) + (content.size() / 2); 86 iov[1].iov_len = content.size() / 2; 87 88 ASSERT_THAT(pwritev2(fd.get(), iov, /*iovcnt=*/2, 89 /*offset=*/0, /*flags=*/0), 90 SyscallSucceedsWithValue(kBufSize)); 91 92 std::vector<char> buf(kBufSize); 93 EXPECT_THAT(read(fd.get(), buf.data(), kBufSize), 94 SyscallSucceedsWithValue(kBufSize)); 95 96 EXPECT_EQ(content, buf); 97 } 98 99 // This test is where we call pwritev2 with a positive offset and no flags. 100 TEST(Pwritev2Test, ValidPositiveOffset) { 101 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 102 103 std::string prefix(kBufSize, '0'); 104 105 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 106 GetAbsoluteTestTmpdir(), prefix, TempPath::kDefaultFileMode)); 107 const FileDescriptor fd = 108 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 109 110 std::vector<char> content(kBufSize); 111 SetContent(content); 112 struct iovec iov; 113 iov.iov_base = content.data(); 114 iov.iov_len = content.size(); 115 116 ASSERT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, 117 /*offset=*/prefix.size(), /*flags=*/0), 118 SyscallSucceedsWithValue(content.size())); 119 120 std::vector<char> buf(prefix.size() + content.size()); 121 EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), 122 SyscallSucceedsWithValue(buf.size())); 123 124 std::vector<char> want(prefix.begin(), prefix.end()); 125 want.insert(want.end(), content.begin(), content.end()); 126 EXPECT_EQ(want, buf); 127 } 128 129 // This test is the base case where we call writev by using -1 as the offset. 130 // The write should use the file offset, so the test increments the file offset 131 // prior to call pwritev2. 132 TEST(Pwritev2Test, NegativeOneOffset) { 133 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 134 135 const std::string prefix = "00"; 136 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 137 GetAbsoluteTestTmpdir(), prefix.data(), TempPath::kDefaultFileMode)); 138 const FileDescriptor fd = 139 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 140 ASSERT_THAT(lseek(fd.get(), prefix.size(), SEEK_SET), 141 SyscallSucceedsWithValue(prefix.size())); 142 143 std::vector<char> content(kBufSize); 144 SetContent(content); 145 struct iovec iov; 146 iov.iov_base = content.data(); 147 iov.iov_len = content.size(); 148 149 ASSERT_THAT(pwritev2(fd.get(), &iov, /*iovcnt*/ 1, 150 /*offset=*/static_cast<off_t>(-1), /*flags=*/0), 151 SyscallSucceedsWithValue(content.size())); 152 153 ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), 154 SyscallSucceedsWithValue(prefix.size() + content.size())); 155 156 std::vector<char> buf(prefix.size() + content.size()); 157 EXPECT_THAT(pread(fd.get(), buf.data(), buf.size(), /*offset=*/0), 158 SyscallSucceedsWithValue(buf.size())); 159 160 std::vector<char> want(prefix.begin(), prefix.end()); 161 want.insert(want.end(), content.begin(), content.end()); 162 EXPECT_EQ(want, buf); 163 } 164 165 // pwritev2 requires if the RWF_HIPRI flag is passed, the fd must be opened with 166 // O_DIRECT. This test implements a correct call with the RWF_HIPRI flag. 167 TEST(Pwritev2Test, CallWithRWF_HIPRI) { 168 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 169 170 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 171 GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); 172 const FileDescriptor fd = 173 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 174 175 std::vector<char> content(kBufSize); 176 SetContent(content); 177 struct iovec iov; 178 iov.iov_base = content.data(); 179 iov.iov_len = content.size(); 180 181 EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, 182 /*offset=*/0, /*flags=*/RWF_HIPRI), 183 SyscallSucceedsWithValue(kBufSize)); 184 185 std::vector<char> buf(content.size()); 186 EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), 187 SyscallSucceedsWithValue(buf.size())); 188 189 EXPECT_EQ(buf, content); 190 } 191 192 // This test calls pwritev2 with a bad file descriptor. 193 TEST(Writev2Test, BadFile) { 194 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 195 ASSERT_THAT(pwritev2(/*fd=*/-1, /*iov=*/nullptr, /*iovcnt=*/0, 196 /*offset=*/0, /*flags=*/0), 197 SyscallFailsWithErrno(EBADF)); 198 } 199 200 // This test calls pwrite2 with an invalid offset. 201 TEST(Pwritev2Test, InvalidOffset) { 202 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 203 204 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 205 GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); 206 const FileDescriptor fd = 207 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 208 209 char buf[16]; 210 struct iovec iov; 211 iov.iov_base = buf; 212 iov.iov_len = sizeof(buf); 213 214 EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, 215 /*offset=*/static_cast<off_t>(-8), /*flags=*/0), 216 SyscallFailsWithErrno(EINVAL)); 217 } 218 219 TEST(Pwritev2Test, UnseekableFileValid) { 220 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 221 222 int pipe_fds[2]; 223 224 ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); 225 226 std::vector<char> content(32, '0'); 227 SetContent(content); 228 struct iovec iov; 229 iov.iov_base = content.data(); 230 iov.iov_len = content.size(); 231 232 EXPECT_THAT(pwritev2(pipe_fds[1], &iov, /*iovcnt=*/1, 233 /*offset=*/static_cast<off_t>(-1), /*flags=*/0), 234 SyscallSucceedsWithValue(content.size())); 235 236 std::vector<char> buf(content.size()); 237 EXPECT_THAT(read(pipe_fds[0], buf.data(), buf.size()), 238 SyscallSucceedsWithValue(buf.size())); 239 240 EXPECT_EQ(content, buf); 241 242 EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); 243 EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); 244 } 245 246 // Calling pwritev2 with a non-negative offset calls pwritev. Calling pwritev 247 // with an unseekable file is not allowed. A pipe is used for an unseekable 248 // file. 249 TEST(Pwritev2Test, UnseekableFileInvalid) { 250 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 251 252 int pipe_fds[2]; 253 char buf[16]; 254 struct iovec iov; 255 iov.iov_base = buf; 256 iov.iov_len = sizeof(buf); 257 258 ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); 259 260 EXPECT_THAT(pwritev2(pipe_fds[1], &iov, /*iovcnt=*/1, 261 /*offset=*/2, /*flags=*/0), 262 SyscallFailsWithErrno(ESPIPE)); 263 264 EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); 265 EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); 266 } 267 268 TEST(Pwritev2Test, ReadOnlyFile) { 269 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 270 271 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 272 GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); 273 const FileDescriptor fd = 274 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); 275 276 char buf[16]; 277 struct iovec iov; 278 iov.iov_base = buf; 279 iov.iov_len = sizeof(buf); 280 281 EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, 282 /*offset=*/0, /*flags=*/0), 283 SyscallFailsWithErrno(EBADF)); 284 } 285 286 TEST(Pwritev2Test, Pwritev2WithOpath) { 287 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 288 289 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 290 const FileDescriptor fd = 291 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); 292 293 char buf[16]; 294 struct iovec iov; 295 iov.iov_base = buf; 296 iov.iov_len = sizeof(buf); 297 298 EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, /*offset=*/0, /*flags=*/0), 299 SyscallFailsWithErrno(EBADF)); 300 } 301 302 // This test calls pwritev2 with an invalid flag. 303 TEST(Pwritev2Test, InvalidFlag) { 304 SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); 305 306 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 307 GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); 308 const FileDescriptor fd = 309 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR | O_DIRECT)); 310 311 char buf[16]; 312 struct iovec iov; 313 iov.iov_base = buf; 314 iov.iov_len = sizeof(buf); 315 316 EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, 317 /*offset=*/0, /*flags=*/0xF0), 318 SyscallFailsWithErrno(EOPNOTSUPP)); 319 } 320 321 } // namespace 322 } // namespace testing 323 } // namespace gvisor