github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/utimes.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/stat.h>
    17  #include <sys/syscall.h>
    18  #include <sys/time.h>
    19  #include <sys/types.h>
    20  #include <time.h>
    21  #include <unistd.h>
    22  #include <utime.h>
    23  
    24  #include <string>
    25  
    26  #include "absl/time/time.h"
    27  #include "test/util/file_descriptor.h"
    28  #include "test/util/fs_util.h"
    29  #include "test/util/temp_path.h"
    30  #include "test/util/test_util.h"
    31  
    32  namespace gvisor {
    33  namespace testing {
    34  
    35  namespace {
    36  
    37  // TimeBoxed runs fn, setting before and after to (coarse realtime) times
    38  // guaranteed* to come before and after fn started and completed, respectively.
    39  //
    40  // fn may be called more than once if the clock is adjusted.
    41  void TimeBoxed(absl::Time* before, absl::Time* after,
    42                 std::function<void()> const& fn) {
    43    do {
    44      // N.B. utimes and friends use CLOCK_REALTIME_COARSE for setting time (i.e.,
    45      // current_kernel_time()). See fs/attr.c:notify_change.
    46      //
    47      // notify_change truncates the time to a multiple of s_time_gran, but most
    48      // filesystems set it to 1, so we don't do any truncation.
    49      struct timespec ts;
    50      EXPECT_THAT(clock_gettime(CLOCK_REALTIME_COARSE, &ts), SyscallSucceeds());
    51      // FIXME(b/132819225): gVisor filesystem timestamps inconsistently use the
    52      // internal or host clock, which may diverge slightly. Allow some slack on
    53      // times to account for the difference.
    54      *before = absl::TimeFromTimespec(ts) - absl::Seconds(1);
    55  
    56      fn();
    57  
    58      EXPECT_THAT(clock_gettime(CLOCK_REALTIME_COARSE, &ts), SyscallSucceeds());
    59      *after = absl::TimeFromTimespec(ts) + absl::Seconds(1);
    60  
    61      if (*after < *before) {
    62        // Clock jumped backwards; retry.
    63        //
    64        // Technically this misses jumps small enough to keep after > before,
    65        // which could lead to test failures, but that is very unlikely to happen.
    66        continue;
    67      }
    68    } while (*after < *before);
    69  }
    70  
    71  void TestUtimesOnPath(std::string const& path) {
    72    struct stat statbuf;
    73  
    74    struct timeval times[2] = {{10, 0}, {20, 0}};
    75    EXPECT_THAT(utimes(path.c_str(), times), SyscallSucceeds());
    76    EXPECT_THAT(stat(path.c_str(), &statbuf), SyscallSucceeds());
    77    EXPECT_EQ(10, statbuf.st_atime);
    78    EXPECT_EQ(20, statbuf.st_mtime);
    79  
    80    absl::Time before;
    81    absl::Time after;
    82    TimeBoxed(&before, &after, [&] {
    83      EXPECT_THAT(utimes(path.c_str(), nullptr), SyscallSucceeds());
    84    });
    85  
    86    EXPECT_THAT(stat(path.c_str(), &statbuf), SyscallSucceeds());
    87  
    88    absl::Time atime = absl::TimeFromTimespec(statbuf.st_atim);
    89    EXPECT_GE(atime, before);
    90    EXPECT_LE(atime, after);
    91  
    92    absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim);
    93    EXPECT_GE(mtime, before);
    94    EXPECT_LE(mtime, after);
    95  }
    96  
    97  TEST(UtimesTest, OnFile) {
    98    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    99    TestUtimesOnPath(f.path());
   100  }
   101  
   102  TEST(UtimesTest, OnDir) {
   103    auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   104    TestUtimesOnPath(dir.path());
   105  }
   106  
   107  TEST(UtimesTest, MissingPath) {
   108    auto path = NewTempAbsPath();
   109    struct timeval times[2] = {{10, 0}, {20, 0}};
   110    EXPECT_THAT(utimes(path.c_str(), times), SyscallFailsWithErrno(ENOENT));
   111  }
   112  
   113  void TestFutimesat(int dirFd, std::string const& path) {
   114    struct stat statbuf;
   115  
   116    struct timeval times[2] = {{10, 0}, {20, 0}};
   117    EXPECT_THAT(futimesat(dirFd, path.c_str(), times), SyscallSucceeds());
   118    EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds());
   119    EXPECT_EQ(10, statbuf.st_atime);
   120    EXPECT_EQ(20, statbuf.st_mtime);
   121  
   122    absl::Time before;
   123    absl::Time after;
   124    TimeBoxed(&before, &after, [&] {
   125      EXPECT_THAT(futimesat(dirFd, path.c_str(), nullptr), SyscallSucceeds());
   126    });
   127  
   128    EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds());
   129  
   130    absl::Time atime = absl::TimeFromTimespec(statbuf.st_atim);
   131    EXPECT_GE(atime, before);
   132    EXPECT_LE(atime, after);
   133  
   134    absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim);
   135    EXPECT_GE(mtime, before);
   136    EXPECT_LE(mtime, after);
   137  }
   138  
   139  TEST(FutimesatTest, OnAbsPath) {
   140    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   141    TestFutimesat(0, f.path());
   142  }
   143  
   144  TEST(FutimesatTest, OnRelPath) {
   145    auto d = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   146    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(d.path()));
   147    auto basename = std::string(Basename(f.path()));
   148    const FileDescriptor dirFd =
   149        ASSERT_NO_ERRNO_AND_VALUE(Open(d.path(), O_RDONLY | O_DIRECTORY));
   150    TestFutimesat(dirFd.get(), basename);
   151  }
   152  
   153  TEST(FutimesatTest, InvalidNsec) {
   154    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   155    struct timeval times[4][2] = {{
   156                                      {0, 1},                         // Valid
   157                                      {1, static_cast<int64_t>(1e7)}  // Invalid
   158                                  },
   159                                  {
   160                                      {1, static_cast<int64_t>(1e7)},  // Invalid
   161                                      {0, 1}                           // Valid
   162                                  },
   163                                  {
   164                                      {0, 1},  // Valid
   165                                      {1, -1}  // Invalid
   166                                  },
   167                                  {
   168                                      {1, -1},  // Invalid
   169                                      {0, 1}    // Valid
   170                                  }};
   171  
   172    for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) {
   173      std::cout << "test:" << i << "\n";
   174      EXPECT_THAT(futimesat(0, f.path().c_str(), times[i]),
   175                  SyscallFailsWithErrno(EINVAL));
   176    }
   177  }
   178  
   179  void TestUtimensat(int dirFd, std::string const& path) {
   180    struct stat statbuf;
   181    const struct timespec times[2] = {{10, 0}, {20, 0}};
   182    EXPECT_THAT(utimensat(dirFd, path.c_str(), times, 0), SyscallSucceeds());
   183    EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds());
   184    EXPECT_EQ(10, statbuf.st_atime);
   185    EXPECT_EQ(20, statbuf.st_mtime);
   186  
   187    // Test setting with UTIME_NOW and UTIME_OMIT.
   188    struct stat statbuf2;
   189    const struct timespec times2[2] = {
   190        {0, UTIME_NOW},  // Should set atime to now.
   191        {0, UTIME_OMIT}  // Should not change mtime.
   192    };
   193  
   194    absl::Time before;
   195    absl::Time after;
   196    TimeBoxed(&before, &after, [&] {
   197      EXPECT_THAT(utimensat(dirFd, path.c_str(), times2, 0), SyscallSucceeds());
   198    });
   199  
   200    EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf2, 0), SyscallSucceeds());
   201  
   202    absl::Time atime2 = absl::TimeFromTimespec(statbuf2.st_atim);
   203    EXPECT_GE(atime2, before);
   204    EXPECT_LE(atime2, after);
   205  
   206    absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim);
   207    absl::Time mtime2 = absl::TimeFromTimespec(statbuf2.st_mtim);
   208    // mtime should not be changed.
   209    EXPECT_EQ(mtime, mtime2);
   210  
   211    // Test setting with times = NULL. Should set both atime and mtime to the
   212    // current system time.
   213    struct stat statbuf3;
   214    TimeBoxed(&before, &after, [&] {
   215      EXPECT_THAT(utimensat(dirFd, path.c_str(), nullptr, 0), SyscallSucceeds());
   216    });
   217  
   218    EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf3, 0), SyscallSucceeds());
   219  
   220    absl::Time atime3 = absl::TimeFromTimespec(statbuf3.st_atim);
   221    EXPECT_GE(atime3, before);
   222    EXPECT_LE(atime3, after);
   223  
   224    absl::Time mtime3 = absl::TimeFromTimespec(statbuf3.st_mtim);
   225    EXPECT_GE(mtime3, before);
   226    EXPECT_LE(mtime3, after);
   227  
   228    // TODO(b/187074006): atime/mtime may differ with local_gofer_uncached.
   229    // EXPECT_EQ(atime3, mtime3);
   230  }
   231  
   232  TEST(UtimensatTest, OnAbsPath) {
   233    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   234    TestUtimensat(0, f.path());
   235  }
   236  
   237  TEST(UtimensatTest, OnRelPath) {
   238    auto d = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   239    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(d.path()));
   240    auto basename = std::string(Basename(f.path()));
   241    const FileDescriptor dirFd =
   242        ASSERT_NO_ERRNO_AND_VALUE(Open(d.path(), O_RDONLY | O_DIRECTORY));
   243    TestUtimensat(dirFd.get(), basename);
   244  }
   245  
   246  TEST(UtimensatTest, OmitNoop) {
   247    // Setting both timespecs to UTIME_OMIT on a nonexistant path should succeed.
   248    auto path = NewTempAbsPath();
   249    const struct timespec times[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}};
   250    EXPECT_THAT(utimensat(0, path.c_str(), times, 0), SyscallSucceeds());
   251  }
   252  
   253  // Verify that we can actually set atime and mtime to 0.
   254  TEST(UtimeTest, ZeroAtimeandMtime) {
   255    const auto tmp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   256    const auto tmp_file =
   257        ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmp_dir.path()));
   258  
   259    // Stat the file before and after updating atime and mtime.
   260    struct stat stat_before = {};
   261    EXPECT_THAT(stat(tmp_file.path().c_str(), &stat_before), SyscallSucceeds());
   262  
   263    ASSERT_NE(stat_before.st_atime, 0);
   264    ASSERT_NE(stat_before.st_mtime, 0);
   265  
   266    const struct utimbuf times = {};  // Zero for both atime and mtime.
   267    EXPECT_THAT(utime(tmp_file.path().c_str(), &times), SyscallSucceeds());
   268  
   269    struct stat stat_after = {};
   270    EXPECT_THAT(stat(tmp_file.path().c_str(), &stat_after), SyscallSucceeds());
   271  
   272    // We should see the atime and mtime changed when we set them to 0.
   273    ASSERT_EQ(stat_after.st_atime, 0);
   274    ASSERT_EQ(stat_after.st_mtime, 0);
   275  }
   276  
   277  TEST(UtimensatTest, InvalidNsec) {
   278    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   279    struct timespec times[2][2] = {
   280        {
   281            {0, UTIME_OMIT},                 // Valid
   282            {2, static_cast<int64_t>(1e10)}  // Invalid
   283        },
   284        {
   285            {2, static_cast<int64_t>(1e10)},  // Invalid
   286            {0, UTIME_OMIT}                   // Valid
   287        }};
   288  
   289    for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) {
   290      std::cout << "test:" << i << "\n";
   291      EXPECT_THAT(utimensat(0, f.path().c_str(), times[i], 0),
   292                  SyscallFailsWithErrno(EINVAL));
   293    }
   294  }
   295  
   296  TEST(Utimensat, NullPath) {
   297    // From man utimensat(2):
   298    // "the Linux utimensat() system call implements a nonstandard feature: if
   299    // pathname is NULL, then the call modifies the timestamps of the file
   300    // referred to by the file descriptor dirfd (which may refer to any type of
   301    // file).
   302    // Note, however, that the glibc wrapper for utimensat() disallows
   303    // passing NULL as the value for file: the wrapper function returns the error
   304    // EINVAL in this case."
   305    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   306    const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR));
   307    struct stat statbuf;
   308    const struct timespec times[2] = {{10, 0}, {20, 0}};
   309    // Call syscall directly.
   310    EXPECT_THAT(syscall(SYS_utimensat, fd.get(), NULL, times, 0),
   311                SyscallSucceeds());
   312    EXPECT_THAT(fstatat(0, f.path().c_str(), &statbuf, 0), SyscallSucceeds());
   313    EXPECT_EQ(10, statbuf.st_atime);
   314    EXPECT_EQ(20, statbuf.st_mtime);
   315  }
   316  
   317  }  // namespace
   318  
   319  }  // namespace testing
   320  }  // namespace gvisor