github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/fuse/linux/fuse_base.h (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 #ifndef GVISOR_TEST_FUSE_FUSE_BASE_H_ 16 #define GVISOR_TEST_FUSE_FUSE_BASE_H_ 17 18 #include <linux/fuse.h> 19 #include <string.h> 20 #include <sys/stat.h> 21 #include <sys/uio.h> 22 23 #include <iostream> 24 #include <unordered_map> 25 #include <vector> 26 27 #include "gtest/gtest.h" 28 #include "test/util/posix_error.h" 29 #include "test/util/temp_path.h" 30 31 namespace gvisor { 32 namespace testing { 33 34 constexpr char kMountOpts[] = "rootmode=755,user_id=0,group_id=0"; 35 36 constexpr struct fuse_init_out kDefaultFUSEInitOutPayload = {.major = 7}; 37 38 // Internal commands used to communicate between testing thread and the FUSE 39 // server. See test/fuse/README.md for further detail. 40 enum class FuseTestCmd { 41 kSetResponse = 0, 42 kSetInodeLookup, 43 kGetRequest, 44 kGetNumUnconsumedRequests, 45 kGetNumUnsentResponses, 46 kGetTotalReceivedBytes, 47 kSkipRequest, 48 }; 49 50 // Holds the information of a memory block in a serial buffer. 51 struct FuseMemBlock { 52 uint32_t opcode; 53 size_t offset; 54 size_t len; 55 }; 56 57 // A wrapper of a simple serial buffer that can be used with read(2) and 58 // write(2). Contains a cursor to indicate accessing. This class is not thread- 59 // safe and can only be used in single-thread version. 60 class FuseMemBuffer { 61 public: 62 FuseMemBuffer() : cursor_(0) { 63 // To read from /dev/fuse, a buffer needs at least FUSE_MIN_READ_BUFFER 64 // bytes to avoid EINVAL. FuseMemBuffer holds memory that can accommodate 65 // a sequence of FUSE request/response, so it is initiated with double 66 // minimal requirement. 67 mem_.resize(FUSE_MIN_READ_BUFFER * 2); 68 } 69 70 // Returns whether there is no memory block. 71 bool Empty() { return blocks_.empty(); } 72 73 // Returns if there is no more remaining memory blocks. 74 bool End() { return cursor_ == blocks_.size(); } 75 76 // Returns how many bytes that have been received. 77 size_t UsedBytes() { 78 return Empty() ? 0 : blocks_.back().offset + blocks_.back().len; 79 } 80 81 // Returns the available bytes remains in the serial buffer. 82 size_t AvailBytes() { return mem_.size() - UsedBytes(); } 83 84 // Appends a memory block information that starts at the tail of the serial 85 // buffer. /dev/fuse requires at least FUSE_MIN_READ_BUFFER bytes to read, or 86 // it will issue EINVAL. If it is not enough, just double the buffer length. 87 void AddMemBlock(uint32_t opcode, void* data, size_t len) { 88 if (AvailBytes() < FUSE_MIN_READ_BUFFER) { 89 mem_.resize(mem_.size() << 1); 90 } 91 size_t offset = UsedBytes(); 92 memcpy(mem_.data() + offset, data, len); 93 blocks_.push_back(FuseMemBlock{opcode, offset, len}); 94 } 95 96 // Returns the memory address at a specific offset. Used with read(2) or 97 // write(2). 98 char* DataAtOffset(size_t offset) { return mem_.data() + offset; } 99 100 // Returns current memory block pointed by the cursor and increase by 1. 101 FuseMemBlock Next() { 102 if (End()) { 103 std::cerr << "Buffer is already exhausted." << std::endl; 104 return FuseMemBlock{}; 105 } 106 return blocks_[cursor_++]; 107 } 108 109 // Returns the number of the blocks that has not been requested. 110 size_t RemainingBlocks() { return blocks_.size() - cursor_; } 111 112 private: 113 size_t cursor_; 114 std::vector<FuseMemBlock> blocks_; 115 std::vector<char> mem_; 116 }; 117 118 // FuseTest base class is useful in FUSE integration test. Inherit this class 119 // to automatically set up a fake FUSE server and use the member functions 120 // to manipulate with it. Refer to test/fuse/README.md for detailed explanation. 121 class FuseTest : public ::testing::Test { 122 public: 123 // nodeid_ is the ID of a fake inode. We starts from 2 since 1 is occupied by 124 // the mount point. 125 FuseTest() : nodeid_(2) {} 126 void SetUp() override; 127 void TearDown() override; 128 129 // Called by the testing thread to set up a fake response for an expected 130 // opcode via socket. This can be used multiple times to define a sequence of 131 // expected FUSE reactions. 132 void SetServerResponse(uint32_t opcode, std::vector<struct iovec>& iovecs); 133 134 // Called by the testing thread to install a fake path under the mount point. 135 // e.g. a file under /mnt/dir/file and moint point is /mnt, then it will look 136 // up "dir/file" in this case. 137 // 138 // It sets a fixed response to the FUSE_LOOKUP requests issued with this 139 // path, pretending there is an inode and avoid ENOENT when testing. If mode 140 // is not given, it creates a regular file with mode 0600. 141 void SetServerInodeLookup(const std::string& path, 142 mode_t mode = S_IFREG | S_IRUSR | S_IWUSR, 143 uint64_t size = 512); 144 145 // Called by the testing thread to ask the FUSE server for its next received 146 // FUSE request. Be sure to use the corresponding struct of iovec to receive 147 // data from server. 148 void GetServerActualRequest(std::vector<struct iovec>& iovecs); 149 150 // Called by the testing thread to query the number of unconsumed requests in 151 // the requests_ serial buffer of the FUSE server. TearDown() ensures all 152 // FUSE requests received by the FUSE server were consumed by the testing 153 // thread. 154 uint32_t GetServerNumUnconsumedRequests(); 155 156 // Called by the testing thread to query the number of unsent responses in 157 // the responses_ serial buffer of the FUSE server. TearDown() ensures all 158 // preset FUSE responses were sent out by the FUSE server. 159 uint32_t GetServerNumUnsentResponses(); 160 161 // Called by the testing thread to ask the FUSE server for its total received 162 // bytes from /dev/fuse. 163 uint32_t GetServerTotalReceivedBytes(); 164 165 // Called by the testing thread to ask the FUSE server to skip stored 166 // request data. 167 void SkipServerActualRequest(); 168 169 protected: 170 TempPath mount_point_; 171 172 // Opens /dev/fuse and inherit the file descriptor for the FUSE server. 173 void MountFuse(const char* mountOpts = kMountOpts); 174 175 // Creates a socketpair for communication and forks FUSE server. 176 void SetUpFuseServer( 177 const struct fuse_init_out* payload = &kDefaultFUSEInitOutPayload); 178 179 // Unmounts the mountpoint of the FUSE server. 180 void UnmountFuse(); 181 182 private: 183 // Sends a FuseTestCmd and gets a uint32_t data from the FUSE server. 184 inline uint32_t GetServerData(uint32_t cmd); 185 186 // Waits for FUSE server to complete its processing. Complains if the FUSE 187 // server responds any failure during tests. 188 void WaitServerComplete(); 189 190 // The FUSE server stays here and waits next command or FUSE request until it 191 // is terminated. 192 void ServerFuseLoop(); 193 194 // Used by the FUSE server to tell testing thread if it is OK to proceed next 195 // command. Will be issued after processing each FuseTestCmd. 196 void ServerCompleteWith(bool success); 197 198 // Consumes the first FUSE request when mounting FUSE. Replies with a 199 // response with empty payload. 200 PosixError ServerConsumeFuseInit(const struct fuse_init_out* payload); 201 202 // A command switch that dispatch different FuseTestCmd to its handler. 203 void ServerHandleCommand(); 204 205 // The FUSE server side's corresponding code of `SetServerResponse()`. 206 // Handles `kSetResponse` command. Saves the fake response into its output 207 // memory queue. 208 void ServerReceiveResponse(); 209 210 // The FUSE server side's corresponding code of `SetServerInodeLookup()`. 211 // Handles `kSetInodeLookup` command. Receives an expected file mode and 212 // file path under the mount point. 213 void ServerReceiveInodeLookup(); 214 215 // The FUSE server side's corresponding code of `GetServerActualRequest()`. 216 // Handles `kGetRequest` command. Sends the next received request pointed by 217 // the cursor. 218 void ServerSendReceivedRequest(); 219 220 // Sends a uint32_t data via socket. 221 inline void ServerSendData(uint32_t data); 222 223 // The FUSE server side's corresponding code of `SkipServerActualRequest()`. 224 // Handles `kSkipRequest` command. Skip the request pointed by current cursor. 225 void ServerSkipReceivedRequest(); 226 227 // Handles FUSE request sent to /dev/fuse by its saved responses. 228 void ServerProcessFuseRequest(); 229 230 // Responds to FUSE request with a saved data. 231 void ServerRespondFuseSuccess(FuseMemBuffer& mem_buf, 232 const FuseMemBlock& block, uint64_t unique); 233 234 // Responds an error header to /dev/fuse when bad thing happens. 235 void ServerRespondFuseError(uint64_t unique); 236 237 int dev_fd_; 238 int sock_[2]; 239 240 uint64_t nodeid_; 241 std::unordered_map<std::string, FuseMemBlock> lookup_map_; 242 243 FuseMemBuffer requests_; 244 FuseMemBuffer responses_; 245 FuseMemBuffer lookups_; 246 }; 247 248 } // namespace testing 249 } // namespace gvisor 250 251 #endif // GVISOR_TEST_FUSE_FUSE_BASE_H_