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