github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/fuse/linux/write_test.cc (about) 1 // Copyright 2020 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 <errno.h> 16 #include <fcntl.h> 17 #include <linux/fuse.h> 18 #include <sys/stat.h> 19 #include <sys/statfs.h> 20 #include <sys/types.h> 21 #include <unistd.h> 22 23 #include <string> 24 #include <vector> 25 26 #include "gtest/gtest.h" 27 #include "test/fuse/linux/fuse_base.h" 28 #include "test/util/fuse_util.h" 29 #include "test/util/test_util.h" 30 31 namespace gvisor { 32 namespace testing { 33 34 namespace { 35 36 class WriteTest : public FuseTest { 37 void SetUp() override { 38 FuseTest::SetUp(); 39 test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_); 40 } 41 42 // TearDown overrides the parent's function 43 // to skip checking the unconsumed release request at the end. 44 void TearDown() override { UnmountFuse(); } 45 46 protected: 47 const std::string test_file_ = "test_file"; 48 const mode_t test_file_mode_ = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; 49 const uint64_t test_fh_ = 1; 50 const uint32_t open_flag_ = O_RDWR; 51 52 std::string test_file_path_; 53 54 PosixErrorOr<FileDescriptor> OpenTestFile(const std::string &path, 55 uint64_t size = 512) { 56 SetServerInodeLookup(test_file_, test_file_mode_, size); 57 58 struct fuse_out_header out_header_open = { 59 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), 60 }; 61 struct fuse_open_out out_payload_open = { 62 .fh = test_fh_, 63 .open_flags = open_flag_, 64 }; 65 auto iov_out_open = FuseGenerateIovecs(out_header_open, out_payload_open); 66 SetServerResponse(FUSE_OPEN, iov_out_open); 67 68 auto res = Open(path.c_str(), open_flag_); 69 if (res.ok()) { 70 SkipServerActualRequest(); 71 } 72 return res; 73 } 74 }; 75 76 class WriteTestSmallMaxWrite : public WriteTest { 77 void SetUp() override { 78 MountFuse(); 79 SetUpFuseServer(&fuse_init_payload); 80 test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_); 81 } 82 83 protected: 84 const static uint32_t max_write_ = 4096; 85 constexpr static struct fuse_init_out fuse_init_payload = { 86 .major = 7, 87 .max_write = max_write_, 88 }; 89 90 const uint32_t size_fragment = max_write_; 91 }; 92 93 TEST_F(WriteTest, WriteNormal) { 94 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); 95 96 // Prepare for the write. 97 const int n_write = 10; 98 struct fuse_out_header out_header_write = { 99 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), 100 }; 101 struct fuse_write_out out_payload_write = { 102 .size = n_write, 103 }; 104 auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); 105 SetServerResponse(FUSE_WRITE, iov_out_write); 106 107 // Issue the write. 108 std::vector<char> buf(n_write); 109 RandomizeBuffer(buf.data(), buf.size()); 110 EXPECT_THAT(write(fd.get(), buf.data(), n_write), 111 SyscallSucceedsWithValue(n_write)); 112 113 // Check the write request. 114 struct fuse_in_header in_header_write; 115 struct fuse_write_in in_payload_write; 116 std::vector<char> payload_buf(n_write); 117 auto iov_in_write = 118 FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); 119 GetServerActualRequest(iov_in_write); 120 121 EXPECT_EQ(in_payload_write.fh, test_fh_); 122 EXPECT_EQ(in_header_write.len, 123 sizeof(in_header_write) + sizeof(in_payload_write)); 124 EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); 125 EXPECT_EQ(in_payload_write.offset, 0); 126 EXPECT_EQ(in_payload_write.size, n_write); 127 EXPECT_EQ(buf, payload_buf); 128 } 129 130 TEST_F(WriteTest, WriteShort) { 131 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); 132 133 // Prepare for the write. 134 const int n_write = 10, n_written = 5; 135 struct fuse_out_header out_header_write = { 136 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), 137 }; 138 struct fuse_write_out out_payload_write = { 139 .size = n_written, 140 }; 141 auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); 142 SetServerResponse(FUSE_WRITE, iov_out_write); 143 144 // Issue the write. 145 std::vector<char> buf(n_write); 146 RandomizeBuffer(buf.data(), buf.size()); 147 EXPECT_THAT(write(fd.get(), buf.data(), n_write), 148 SyscallSucceedsWithValue(n_written)); 149 150 // Check the write request. 151 struct fuse_in_header in_header_write; 152 struct fuse_write_in in_payload_write; 153 std::vector<char> payload_buf(n_write); 154 auto iov_in_write = 155 FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); 156 GetServerActualRequest(iov_in_write); 157 158 EXPECT_EQ(in_payload_write.fh, test_fh_); 159 EXPECT_EQ(in_header_write.len, 160 sizeof(in_header_write) + sizeof(in_payload_write)); 161 EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); 162 EXPECT_EQ(in_payload_write.offset, 0); 163 EXPECT_EQ(in_payload_write.size, n_write); 164 EXPECT_EQ(buf, payload_buf); 165 } 166 167 TEST_F(WriteTest, WriteShortZero) { 168 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); 169 170 // Prepare for the write. 171 const int n_write = 10; 172 struct fuse_out_header out_header_write = { 173 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), 174 }; 175 struct fuse_write_out out_payload_write = { 176 .size = 0, 177 }; 178 auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); 179 SetServerResponse(FUSE_WRITE, iov_out_write); 180 181 // Issue the write. 182 std::vector<char> buf(n_write); 183 RandomizeBuffer(buf.data(), buf.size()); 184 EXPECT_THAT(write(fd.get(), buf.data(), n_write), SyscallFailsWithErrno(EIO)); 185 186 // Check the write request. 187 struct fuse_in_header in_header_write; 188 struct fuse_write_in in_payload_write; 189 std::vector<char> payload_buf(n_write); 190 auto iov_in_write = 191 FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); 192 GetServerActualRequest(iov_in_write); 193 194 EXPECT_EQ(in_payload_write.fh, test_fh_); 195 EXPECT_EQ(in_header_write.len, 196 sizeof(in_header_write) + sizeof(in_payload_write)); 197 EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); 198 EXPECT_EQ(in_payload_write.offset, 0); 199 EXPECT_EQ(in_payload_write.size, n_write); 200 EXPECT_EQ(buf, payload_buf); 201 } 202 203 TEST_F(WriteTest, WriteZero) { 204 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); 205 206 // Issue the write. 207 std::vector<char> buf(0); 208 EXPECT_THAT(write(fd.get(), buf.data(), 0), SyscallSucceedsWithValue(0)); 209 } 210 211 TEST_F(WriteTest, PWrite) { 212 const int file_size = 512; 213 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, file_size)); 214 215 // Prepare for the write. 216 const int n_write = 10; 217 struct fuse_out_header out_header_write = { 218 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), 219 }; 220 struct fuse_write_out out_payload_write = { 221 .size = n_write, 222 }; 223 auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); 224 SetServerResponse(FUSE_WRITE, iov_out_write); 225 226 // Issue the write. 227 std::vector<char> buf(n_write); 228 RandomizeBuffer(buf.data(), buf.size()); 229 const int offset_write = file_size >> 1; 230 EXPECT_THAT(pwrite(fd.get(), buf.data(), n_write, offset_write), 231 SyscallSucceedsWithValue(n_write)); 232 233 // Check the write request. 234 struct fuse_in_header in_header_write; 235 struct fuse_write_in in_payload_write; 236 std::vector<char> payload_buf(n_write); 237 auto iov_in_write = 238 FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); 239 GetServerActualRequest(iov_in_write); 240 241 EXPECT_EQ(in_payload_write.fh, test_fh_); 242 EXPECT_EQ(in_header_write.len, 243 sizeof(in_header_write) + sizeof(in_payload_write)); 244 EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); 245 EXPECT_EQ(in_payload_write.offset, offset_write); 246 EXPECT_EQ(in_payload_write.size, n_write); 247 EXPECT_EQ(buf, payload_buf); 248 } 249 250 TEST_F(WriteTestSmallMaxWrite, WriteSmallMaxWrie) { 251 const int n_fragment = 10; 252 const int n_write = size_fragment * n_fragment; 253 254 auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, n_write)); 255 256 // Prepare for the write. 257 struct fuse_out_header out_header_write = { 258 .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), 259 }; 260 struct fuse_write_out out_payload_write = { 261 .size = size_fragment, 262 }; 263 auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); 264 265 for (int i = 0; i < n_fragment; ++i) { 266 SetServerResponse(FUSE_WRITE, iov_out_write); 267 } 268 269 // Issue the write. 270 std::vector<char> buf(n_write); 271 RandomizeBuffer(buf.data(), buf.size()); 272 EXPECT_THAT(write(fd.get(), buf.data(), n_write), 273 SyscallSucceedsWithValue(n_write)); 274 275 ASSERT_EQ(GetServerNumUnsentResponses(), 0); 276 ASSERT_EQ(GetServerNumUnconsumedRequests(), n_fragment); 277 278 // Check the write request. 279 struct fuse_in_header in_header_write; 280 struct fuse_write_in in_payload_write; 281 std::vector<char> payload_buf(size_fragment); 282 auto iov_in_write = 283 FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); 284 285 for (int i = 0; i < n_fragment; ++i) { 286 GetServerActualRequest(iov_in_write); 287 288 EXPECT_EQ(in_payload_write.fh, test_fh_); 289 EXPECT_EQ(in_header_write.len, 290 sizeof(in_header_write) + sizeof(in_payload_write)); 291 EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); 292 EXPECT_EQ(in_payload_write.offset, i * size_fragment); 293 EXPECT_EQ(in_payload_write.size, size_fragment); 294 295 auto it = buf.begin() + i * size_fragment; 296 EXPECT_EQ(std::vector<char>(it, it + size_fragment), payload_buf); 297 } 298 } 299 300 } // namespace 301 302 } // namespace testing 303 } // namespace gvisor