github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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(IsRunningWithVFS1());
   288    SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS);
   289  
   290    const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   291    const FileDescriptor fd =
   292        ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH));
   293  
   294    char buf[16];
   295    struct iovec iov;
   296    iov.iov_base = buf;
   297    iov.iov_len = sizeof(buf);
   298  
   299    EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, /*offset=*/0, /*flags=*/0),
   300                SyscallFailsWithErrno(EBADF));
   301  }
   302  
   303  // This test calls pwritev2 with an invalid flag.
   304  TEST(Pwritev2Test, InvalidFlag) {
   305    SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS);
   306  
   307    const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   308        GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode));
   309    const FileDescriptor fd =
   310        ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR | O_DIRECT));
   311  
   312    char buf[16];
   313    struct iovec iov;
   314    iov.iov_base = buf;
   315    iov.iov_len = sizeof(buf);
   316  
   317    EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1,
   318                         /*offset=*/0, /*flags=*/0xF0),
   319                SyscallFailsWithErrno(EOPNOTSUPP));
   320  }
   321  
   322  }  // namespace
   323  }  // namespace testing
   324  }  // namespace gvisor