github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/fuse/linux/read_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 ReadTest : 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 ReadTestSmallMaxRead : public ReadTest {
    77    void SetUp() override {
    78      MountFuse(mountOpts);
    79      SetUpFuseServer();
    80      test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_);
    81    }
    82  
    83   protected:
    84    constexpr static char mountOpts[] =
    85        "rootmode=755,user_id=0,group_id=0,max_read=4096";
    86    // 4096 is hard-coded as the max_read in mount options.
    87    const int size_fragment = 4096;
    88  };
    89  
    90  TEST_F(ReadTest, ReadWhole) {
    91    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_));
    92  
    93    // Prepare for the read.
    94    const int n_read = 5;
    95    std::vector<char> data(n_read);
    96    RandomizeBuffer(data.data(), data.size());
    97    struct fuse_out_header out_header_read = {
    98        .len =
    99            static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()),
   100    };
   101    auto iov_out_read = FuseGenerateIovecs(out_header_read, data);
   102    SetServerResponse(FUSE_READ, iov_out_read);
   103  
   104    // Read the whole "file".
   105    std::vector<char> buf(n_read);
   106    EXPECT_THAT(read(fd.get(), buf.data(), n_read),
   107                SyscallSucceedsWithValue(n_read));
   108  
   109    // Check the read request.
   110    struct fuse_in_header in_header_read;
   111    struct fuse_read_in in_payload_read;
   112    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   113    GetServerActualRequest(iov_in);
   114  
   115    EXPECT_EQ(in_payload_read.fh, test_fh_);
   116    EXPECT_EQ(in_header_read.len,
   117              sizeof(in_header_read) + sizeof(in_payload_read));
   118    EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   119    EXPECT_EQ(in_payload_read.offset, 0);
   120    EXPECT_EQ(buf, data);
   121  }
   122  
   123  TEST_F(ReadTest, ReadPartial) {
   124    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_));
   125  
   126    // Prepare for the read.
   127    const int n_data = 10;
   128    std::vector<char> data(n_data);
   129    RandomizeBuffer(data.data(), data.size());
   130    // Note: due to read ahead, current read implementation will treat any
   131    // response that is longer than requested as correct (i.e. not reach the EOF).
   132    // Therefore, the test below should make sure the size to read does not exceed
   133    // n_data.
   134    struct fuse_out_header out_header_read = {
   135        .len =
   136            static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()),
   137    };
   138    auto iov_out_read = FuseGenerateIovecs(out_header_read, data);
   139    struct fuse_in_header in_header_read;
   140    struct fuse_read_in in_payload_read;
   141    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   142  
   143    std::vector<char> buf(n_data);
   144  
   145    // Read 1 bytes.
   146    SetServerResponse(FUSE_READ, iov_out_read);
   147    EXPECT_THAT(read(fd.get(), buf.data(), 1), SyscallSucceedsWithValue(1));
   148  
   149    // Check the 1-byte read request.
   150    GetServerActualRequest(iov_in);
   151    EXPECT_EQ(in_payload_read.fh, test_fh_);
   152    EXPECT_EQ(in_header_read.len,
   153              sizeof(in_header_read) + sizeof(in_payload_read));
   154    EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   155    EXPECT_EQ(in_payload_read.offset, 0);
   156  
   157    // Read 3 bytes.
   158    SetServerResponse(FUSE_READ, iov_out_read);
   159    EXPECT_THAT(read(fd.get(), buf.data(), 3), SyscallSucceedsWithValue(3));
   160  
   161    // Check the 3-byte read request.
   162    GetServerActualRequest(iov_in);
   163    EXPECT_EQ(in_payload_read.fh, test_fh_);
   164    EXPECT_EQ(in_payload_read.offset, 1);
   165  
   166    // Read 5 bytes.
   167    SetServerResponse(FUSE_READ, iov_out_read);
   168    EXPECT_THAT(read(fd.get(), buf.data(), 5), SyscallSucceedsWithValue(5));
   169  
   170    // Check the 5-byte read request.
   171    GetServerActualRequest(iov_in);
   172    EXPECT_EQ(in_payload_read.fh, test_fh_);
   173    EXPECT_EQ(in_payload_read.offset, 4);
   174  }
   175  
   176  TEST_F(ReadTest, PRead) {
   177    const int file_size = 512;
   178    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, file_size));
   179  
   180    // Prepare for the read.
   181    const int n_read = 5;
   182    std::vector<char> data(n_read);
   183    RandomizeBuffer(data.data(), data.size());
   184    struct fuse_out_header out_header_read = {
   185        .len =
   186            static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()),
   187    };
   188    auto iov_out_read = FuseGenerateIovecs(out_header_read, data);
   189    SetServerResponse(FUSE_READ, iov_out_read);
   190  
   191    // Read some bytes.
   192    std::vector<char> buf(n_read);
   193    const int offset_read = file_size >> 1;
   194    EXPECT_THAT(pread(fd.get(), buf.data(), n_read, offset_read),
   195                SyscallSucceedsWithValue(n_read));
   196  
   197    // Check the read request.
   198    struct fuse_in_header in_header_read;
   199    struct fuse_read_in in_payload_read;
   200    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   201    GetServerActualRequest(iov_in);
   202  
   203    EXPECT_EQ(in_payload_read.fh, test_fh_);
   204    EXPECT_EQ(in_header_read.len,
   205              sizeof(in_header_read) + sizeof(in_payload_read));
   206    EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   207    EXPECT_EQ(in_payload_read.offset, offset_read);
   208    EXPECT_EQ(buf, data);
   209  }
   210  
   211  TEST_F(ReadTest, ReadZero) {
   212    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_));
   213  
   214    // Issue the read.
   215    std::vector<char> buf;
   216    EXPECT_THAT(read(fd.get(), buf.data(), 0), SyscallSucceedsWithValue(0));
   217  }
   218  
   219  TEST_F(ReadTest, ReadShort) {
   220    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_));
   221  
   222    // Prepare for the short read.
   223    const int n_read = 5;
   224    std::vector<char> data(n_read >> 1);
   225    RandomizeBuffer(data.data(), data.size());
   226    struct fuse_out_header out_header_read = {
   227        .len =
   228            static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()),
   229    };
   230    auto iov_out_read = FuseGenerateIovecs(out_header_read, data);
   231    SetServerResponse(FUSE_READ, iov_out_read);
   232  
   233    // Read the whole "file".
   234    std::vector<char> buf(n_read);
   235    EXPECT_THAT(read(fd.get(), buf.data(), n_read),
   236                SyscallSucceedsWithValue(data.size()));
   237  
   238    // Check the read request.
   239    struct fuse_in_header in_header_read;
   240    struct fuse_read_in in_payload_read;
   241    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   242    GetServerActualRequest(iov_in);
   243  
   244    EXPECT_EQ(in_payload_read.fh, test_fh_);
   245    EXPECT_EQ(in_header_read.len,
   246              sizeof(in_header_read) + sizeof(in_payload_read));
   247    EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   248    EXPECT_EQ(in_payload_read.offset, 0);
   249    std::vector<char> short_buf(buf.begin(), buf.begin() + data.size());
   250    EXPECT_EQ(short_buf, data);
   251  }
   252  
   253  TEST_F(ReadTest, ReadShortEOF) {
   254    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_));
   255  
   256    // Prepare for the short read.
   257    struct fuse_out_header out_header_read = {
   258        .len = static_cast<uint32_t>(sizeof(struct fuse_out_header)),
   259    };
   260    auto iov_out_read = FuseGenerateIovecs(out_header_read);
   261    SetServerResponse(FUSE_READ, iov_out_read);
   262  
   263    // Read the whole "file".
   264    const int n_read = 10;
   265    std::vector<char> buf(n_read);
   266    EXPECT_THAT(read(fd.get(), buf.data(), n_read), SyscallSucceedsWithValue(0));
   267  
   268    // Check the read request.
   269    struct fuse_in_header in_header_read;
   270    struct fuse_read_in in_payload_read;
   271    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   272    GetServerActualRequest(iov_in);
   273  
   274    EXPECT_EQ(in_payload_read.fh, test_fh_);
   275    EXPECT_EQ(in_header_read.len,
   276              sizeof(in_header_read) + sizeof(in_payload_read));
   277    EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   278    EXPECT_EQ(in_payload_read.offset, 0);
   279  }
   280  
   281  TEST_F(ReadTestSmallMaxRead, ReadSmallMaxRead) {
   282    const int n_fragment = 10;
   283    const int n_read = size_fragment * n_fragment;
   284  
   285    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, n_read));
   286  
   287    // Prepare for the read.
   288    std::vector<char> data(size_fragment);
   289    RandomizeBuffer(data.data(), data.size());
   290    struct fuse_out_header out_header_read = {
   291        .len =
   292            static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()),
   293    };
   294    auto iov_out_read = FuseGenerateIovecs(out_header_read, data);
   295  
   296    for (int i = 0; i < n_fragment; ++i) {
   297      SetServerResponse(FUSE_READ, iov_out_read);
   298    }
   299  
   300    // Read the whole "file".
   301    std::vector<char> buf(n_read);
   302    EXPECT_THAT(read(fd.get(), buf.data(), n_read),
   303                SyscallSucceedsWithValue(n_read));
   304  
   305    ASSERT_EQ(GetServerNumUnsentResponses(), 0);
   306    ASSERT_EQ(GetServerNumUnconsumedRequests(), n_fragment);
   307  
   308    // Check each read segment.
   309    struct fuse_in_header in_header_read;
   310    struct fuse_read_in in_payload_read;
   311    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   312  
   313    for (int i = 0; i < n_fragment; ++i) {
   314      GetServerActualRequest(iov_in);
   315      EXPECT_EQ(in_payload_read.fh, test_fh_);
   316      EXPECT_EQ(in_header_read.len,
   317                sizeof(in_header_read) + sizeof(in_payload_read));
   318      EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   319      EXPECT_EQ(in_payload_read.offset, i * size_fragment);
   320      EXPECT_EQ(in_payload_read.size, size_fragment);
   321  
   322      auto it = buf.begin() + i * size_fragment;
   323      EXPECT_EQ(std::vector<char>(it, it + size_fragment), data);
   324    }
   325  }
   326  
   327  TEST_F(ReadTestSmallMaxRead, ReadSmallMaxReadShort) {
   328    const int n_fragment = 10;
   329    const int n_read = size_fragment * n_fragment;
   330  
   331    auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, n_read));
   332  
   333    // Prepare for the read.
   334    std::vector<char> data(size_fragment);
   335    RandomizeBuffer(data.data(), data.size());
   336    struct fuse_out_header out_header_read = {
   337        .len =
   338            static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()),
   339    };
   340    auto iov_out_read = FuseGenerateIovecs(out_header_read, data);
   341  
   342    for (int i = 0; i < n_fragment - 1; ++i) {
   343      SetServerResponse(FUSE_READ, iov_out_read);
   344    }
   345  
   346    // The last fragment is a short read.
   347    std::vector<char> half_data(data.begin(), data.begin() + (data.size() >> 1));
   348    struct fuse_out_header out_header_read_short = {
   349        .len = static_cast<uint32_t>(sizeof(struct fuse_out_header) +
   350                                     half_data.size()),
   351    };
   352    auto iov_out_read_short =
   353        FuseGenerateIovecs(out_header_read_short, half_data);
   354    SetServerResponse(FUSE_READ, iov_out_read_short);
   355  
   356    // Read the whole "file".
   357    std::vector<char> buf(n_read);
   358    EXPECT_THAT(read(fd.get(), buf.data(), n_read),
   359                SyscallSucceedsWithValue(n_read - (data.size() >> 1)));
   360  
   361    ASSERT_EQ(GetServerNumUnsentResponses(), 0);
   362    ASSERT_EQ(GetServerNumUnconsumedRequests(), n_fragment);
   363  
   364    // Check each read segment.
   365    struct fuse_in_header in_header_read;
   366    struct fuse_read_in in_payload_read;
   367    auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read);
   368  
   369    for (int i = 0; i < n_fragment; ++i) {
   370      GetServerActualRequest(iov_in);
   371      EXPECT_EQ(in_payload_read.fh, test_fh_);
   372      EXPECT_EQ(in_header_read.len,
   373                sizeof(in_header_read) + sizeof(in_payload_read));
   374      EXPECT_EQ(in_header_read.opcode, FUSE_READ);
   375      EXPECT_EQ(in_payload_read.offset, i * size_fragment);
   376      EXPECT_EQ(in_payload_read.size, size_fragment);
   377  
   378      auto it = buf.begin() + i * size_fragment;
   379      if (i != n_fragment - 1) {
   380        EXPECT_EQ(std::vector<char>(it, it + data.size()), data);
   381      } else {
   382        EXPECT_EQ(std::vector<char>(it, it + half_data.size()), half_data);
   383      }
   384    }
   385  }
   386  
   387  }  // namespace
   388  
   389  }  // namespace testing
   390  }  // namespace gvisor