last_write_time() now consistently follows symlinks and respects timestamps (#207)

This commit is contained in:
Steffen Schümann 2026-07-12 12:02:18 +02:00
parent 9a98ce0d2b
commit 38f91bd470
3 changed files with 22 additions and 2 deletions

View File

@ -665,6 +665,8 @@ to the expected behavior.
now reports an error for non-regular sources. now reports an error for non-regular sources.
* Fix for [#206](https://github.com/gulrak/filesystem/issues/206), Windows error * Fix for [#206](https://github.com/gulrak/filesystem/issues/206), Windows error
formatting now handles unknown error codes safely. formatting now handles unknown error codes safely.
* Fix for [#207](https://github.com/gulrak/filesystem/issues/207),
`last_write_time()` now consistently follows symlinks.
* Fix for [#185](https://github.com/gulrak/filesystem/issues/185), * Fix for [#185](https://github.com/gulrak/filesystem/issues/185),
`lexically_normal()` now preserves unresolved parent components in relative `lexically_normal()` now preserves unresolved parent components in relative
paths. paths.

View File

@ -4713,9 +4713,9 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
times[1].tv_sec = static_cast<decltype(times[1].tv_sec)>(std::chrono::duration_cast<std::chrono::seconds>(d).count()); times[1].tv_sec = static_cast<decltype(times[1].tv_sec)>(std::chrono::duration_cast<std::chrono::seconds>(d).count());
times[1].tv_nsec = static_cast<decltype(times[1].tv_nsec)>(std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000); times[1].tv_nsec = static_cast<decltype(times[1].tv_nsec)>(std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000);
#if defined(__ANDROID_API__) && __ANDROID_API__ < 12 #if defined(__ANDROID_API__) && __ANDROID_API__ < 12
if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, 0) != 0) {
#else #else
if (::utimensat((int)AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { if (::utimensat((int)AT_FDCWD, p.c_str(), times, 0) != 0) {
#endif #endif
ec = detail::make_system_error(); ec = detail::make_system_error();
} }

View File

@ -2568,6 +2568,24 @@ TEST_CASE("fs.op.last_write_time - last_write_time", "[filesystem][operations][f
CHECK_NOTHROW(fs::last_write_time("foo", nt, ec)); CHECK_NOTHROW(fs::last_write_time("foo", nt, ec));
CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1); CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
CHECK(!ec); CHECK(!ec);
if (is_symlink_creation_supported()) {
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
struct ::stat linkStatBefore;
REQUIRE(::lstat("foo2", &linkStatBefore) == 0);
#endif
nt = timeFromString("2015-10-21T04:28:00");
CHECK_NOTHROW(fs::last_write_time("foo2", nt));
CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
nt = timeFromString("2015-10-21T04:27:00");
CHECK_NOTHROW(fs::last_write_time("foo2", nt, ec));
CHECK(!ec);
CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB)
struct ::stat linkStatAfter;
REQUIRE(::lstat("foo2", &linkStatAfter) == 0);
CHECK(linkStatAfter.st_mtime == linkStatBefore.st_mtime);
#endif
}
CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error); CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error);
CHECK_NOTHROW(fs::last_write_time("bar", nt, ec)); CHECK_NOTHROW(fs::last_write_time("bar", nt, ec));
CHECK(ec); CHECK(ec);