github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/truncate.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 <errno.h>
    16  #include <signal.h>
    17  #include <sys/resource.h>
    18  #include <sys/stat.h>
    19  #include <sys/vfs.h>
    20  #include <time.h>
    21  #include <unistd.h>
    22  
    23  #include <iostream>
    24  #include <string>
    25  
    26  #include "gmock/gmock.h"
    27  #include "gtest/gtest.h"
    28  #include "absl/strings/string_view.h"
    29  #include "test/syscalls/linux/file_base.h"
    30  #include "test/util/capability_util.h"
    31  #include "test/util/cleanup.h"
    32  #include "test/util/file_descriptor.h"
    33  #include "test/util/temp_path.h"
    34  #include "test/util/test_util.h"
    35  
    36  namespace gvisor {
    37  namespace testing {
    38  
    39  namespace {
    40  
    41  class FixtureTruncateTest : public FileTest {
    42    void SetUp() override { FileTest::SetUp(); }
    43  };
    44  
    45  TEST_F(FixtureTruncateTest, Truncate) {
    46    // Get the current rlimit and restore after test run.
    47    struct rlimit initial_lim;
    48    ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds());
    49    auto cleanup = Cleanup([&initial_lim] {
    50      EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds());
    51    });
    52  
    53    // Check that it starts at size zero.
    54    struct stat buf;
    55    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
    56    EXPECT_EQ(buf.st_size, 0);
    57  
    58    // Stay at size zero.
    59    EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds());
    60    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
    61    EXPECT_EQ(buf.st_size, 0);
    62  
    63    // Grow to ten bytes.
    64    EXPECT_THAT(truncate(test_file_name_.c_str(), 10), SyscallSucceeds());
    65    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
    66    EXPECT_EQ(buf.st_size, 10);
    67  
    68    // Can't be truncated to a negative number.
    69    EXPECT_THAT(truncate(test_file_name_.c_str(), -1),
    70                SyscallFailsWithErrno(EINVAL));
    71  
    72    // Try growing past the file size limit.
    73    sigset_t new_mask;
    74    sigemptyset(&new_mask);
    75    sigaddset(&new_mask, SIGXFSZ);
    76    sigprocmask(SIG_BLOCK, &new_mask, nullptr);
    77    struct timespec timelimit;
    78    timelimit.tv_sec = 10;
    79    timelimit.tv_nsec = 0;
    80  
    81    struct rlimit setlim;
    82    setlim.rlim_cur = 1024;
    83    setlim.rlim_max = RLIM_INFINITY;
    84    ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds());
    85    EXPECT_THAT(truncate(test_file_name_.c_str(), 1025),
    86                SyscallFailsWithErrno(EFBIG));
    87    EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ);
    88    ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds());
    89  
    90    // Shrink back down to zero.
    91    EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds());
    92    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
    93    EXPECT_EQ(buf.st_size, 0);
    94  }
    95  
    96  TEST_F(FixtureTruncateTest, Ftruncate) {
    97    // Get the current rlimit and restore after test run.
    98    struct rlimit initial_lim;
    99    ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds());
   100    auto cleanup = Cleanup([&initial_lim] {
   101      EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds());
   102    });
   103  
   104    // Check that it starts at size zero.
   105    struct stat buf;
   106    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
   107    EXPECT_EQ(buf.st_size, 0);
   108  
   109    // Stay at size zero.
   110    EXPECT_THAT(ftruncate(test_file_fd_.get(), 0), SyscallSucceeds());
   111    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
   112    EXPECT_EQ(buf.st_size, 0);
   113  
   114    // Grow to ten bytes.
   115    EXPECT_THAT(ftruncate(test_file_fd_.get(), 10), SyscallSucceeds());
   116    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
   117    EXPECT_EQ(buf.st_size, 10);
   118  
   119    // Can't be truncated to a negative number.
   120    EXPECT_THAT(ftruncate(test_file_fd_.get(), -1),
   121                SyscallFailsWithErrno(EINVAL));
   122  
   123    // Try growing past the file size limit.
   124    sigset_t new_mask;
   125    sigemptyset(&new_mask);
   126    sigaddset(&new_mask, SIGXFSZ);
   127    sigprocmask(SIG_BLOCK, &new_mask, nullptr);
   128    struct timespec timelimit;
   129    timelimit.tv_sec = 10;
   130    timelimit.tv_nsec = 0;
   131  
   132    struct rlimit setlim;
   133    setlim.rlim_cur = 1024;
   134    setlim.rlim_max = RLIM_INFINITY;
   135    ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds());
   136    EXPECT_THAT(ftruncate(test_file_fd_.get(), 1025),
   137                SyscallFailsWithErrno(EFBIG));
   138    EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ);
   139    ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds());
   140  
   141    // Shrink back down to zero.
   142    EXPECT_THAT(ftruncate(test_file_fd_.get(), 0), SyscallSucceeds());
   143    ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds());
   144    EXPECT_EQ(buf.st_size, 0);
   145  }
   146  
   147  // Truncating a file down clears that portion of the file.
   148  TEST_F(FixtureTruncateTest, FtruncateShrinkGrow) {
   149    std::vector<char> buf(10, 'a');
   150    EXPECT_THAT(WriteFd(test_file_fd_.get(), buf.data(), buf.size()),
   151                SyscallSucceedsWithValue(buf.size()));
   152  
   153    // Shrink then regrow the file. This should clear the second half of the file.
   154    EXPECT_THAT(ftruncate(test_file_fd_.get(), 5), SyscallSucceeds());
   155    EXPECT_THAT(ftruncate(test_file_fd_.get(), 10), SyscallSucceeds());
   156  
   157    EXPECT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), SyscallSucceeds());
   158  
   159    std::vector<char> buf2(10);
   160    EXPECT_THAT(ReadFd(test_file_fd_.get(), buf2.data(), buf2.size()),
   161                SyscallSucceedsWithValue(buf2.size()));
   162  
   163    std::vector<char> expect = {'a',  'a',  'a',  'a',  'a',
   164                                '\0', '\0', '\0', '\0', '\0'};
   165    EXPECT_EQ(expect, buf2);
   166  }
   167  
   168  TEST(TruncateTest, TruncateDir) {
   169    auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   170    EXPECT_THAT(truncate(temp_dir.path().c_str(), 0),
   171                SyscallFailsWithErrno(EISDIR));
   172  }
   173  
   174  TEST(TruncateTest, FtruncateDir) {
   175    auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   176    const FileDescriptor fd =
   177        ASSERT_NO_ERRNO_AND_VALUE(Open(temp_dir.path(), O_DIRECTORY | O_RDONLY));
   178    EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL));
   179  }
   180  
   181  TEST(TruncateTest, TruncateNonWriteable) {
   182    // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
   183    // always override write permissions.
   184    AutoCapability cap(CAP_DAC_OVERRIDE, false);
   185    auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   186        GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */));
   187    EXPECT_THAT(truncate(temp_file.path().c_str(), 0),
   188                SyscallFailsWithErrno(EACCES));
   189  }
   190  
   191  TEST(TruncateTest, FtruncateNonWriteable) {
   192    auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   193        GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */));
   194    const FileDescriptor fd =
   195        ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY));
   196    EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL));
   197  }
   198  
   199  TEST(TruncateTest, FtruncateWithOpath) {
   200    SKIP_IF(IsRunningWithVFS1());
   201    auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   202        GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */));
   203    const FileDescriptor fd =
   204        ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_PATH));
   205    EXPECT_THAT(ftruncate(fd.get(), 0), AnyOf(SyscallFailsWithErrno(EBADF),
   206                                              SyscallFailsWithErrno(EINVAL)));
   207  }
   208  
   209  // ftruncate(2) should succeed as long as the file descriptor is writeable,
   210  // regardless of whether the file permissions allow writing.
   211  TEST(TruncateTest, FtruncateWithoutWritePermission) {
   212    // Drop capabilities that allow us to override file permissions.
   213    AutoCapability cap(CAP_DAC_OVERRIDE, false);
   214  
   215    // The only time we can open a file with flags forbidden by its permissions
   216    // is when we are creating the file. We cannot re-open with the same flags,
   217    // so we cannot restore an fd obtained from such an operation.
   218    const DisableSave ds;
   219    auto path = NewTempAbsPath();
   220    const FileDescriptor fd =
   221        ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDWR | O_CREAT, 0444));
   222  
   223    // In goferfs, ftruncate may be converted to a remote truncate operation that
   224    // unavoidably requires write permission.
   225    SKIP_IF(IsRunningOnGvisor() && !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(path)));
   226    ASSERT_THAT(ftruncate(fd.get(), 100), SyscallSucceeds());
   227  }
   228  
   229  TEST(TruncateTest, TruncateNonExist) {
   230    EXPECT_THAT(truncate("/foo/bar", 0), SyscallFailsWithErrno(ENOENT));
   231  }
   232  
   233  TEST(TruncateTest, FtruncateVirtualTmp) {
   234    auto temp_file = NewTempAbsPathInDir("/dev/shm");
   235    const DisableSave ds;  // Incompatible permissions.
   236    const FileDescriptor fd =
   237        ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file, O_RDWR | O_CREAT | O_EXCL, 0));
   238    EXPECT_THAT(ftruncate(fd.get(), 100), SyscallSucceeds());
   239  }
   240  
   241  // NOTE: There are additional truncate(2)/ftruncate(2) tests in mknod.cc
   242  // which are there to avoid running the tests on a number of different
   243  // filesystems which may not support mknod.
   244  
   245  }  // namespace
   246  
   247  }  // namespace testing
   248  }  // namespace gvisor