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_