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