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(), ×), 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