Fix stale Windows directory_entry symlink status cache

This commit is contained in:
fhgffy 2026-10-04 08:02:49 +08:00
parent 3812f8a6f3
commit d07b9153dd
2 changed files with 90 additions and 1 deletions

View File

@ -2384,6 +2384,10 @@ GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status
{
ec.clear();
#ifdef GHC_OS_WINDOWS
// 2026-10-03:查询新路径前使旧链接缓存失效,避免错误沿用先前条目的类型。
if (sls) {
*sls = file_status();
}
if (recurse_count > 16) {
ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/);
return file_status(file_type::unknown);
@ -2415,7 +2419,12 @@ GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status
if (nhl) {
*nhl = 0;
}
return detail::status_from_INFO(p, &attr, ec, sz, lwt);
// 2026-10-03:非符号链接的两个状态相同,刷新缓存供目录条目复用。
file_status result = detail::status_from_INFO(p, &attr, ec, sz, lwt);
if (sls && !ec) {
*sls = result;
}
return result;
#else
(void)recurse_count;
struct ::stat st;

View File

@ -1445,6 +1445,86 @@ TEST_CASE("fs.dir.entry - class directory_entry", "[filesystem][directory_entry]
}
}
// 2026-10-03:目录迭代器缓存的条目在刷新或改路径后必须返回当前文件类型。
TEST_CASE("fs.dir.entry - update cached symlink status", "[filesystem][directory_entry][fs.dir.entry]")
{
TemporaryDirectory t;
std::error_code ec;
fs::create_directory(t.path() / "directory");
auto entry = *fs::directory_iterator(t.path());
REQUIRE(entry.symlink_status().type() == fs::file_type::directory);
generateFile(t.path() / "file");
SECTION("refresh with error code")
{
fs::remove(entry.path());
generateFile(entry.path());
entry.refresh(ec);
}
SECTION("refresh with exceptions")
{
fs::remove(entry.path());
generateFile(entry.path());
entry.refresh();
}
SECTION("assign")
{
entry.assign(t.path() / "file", ec);
}
SECTION("replace filename")
{
entry.replace_filename("file", ec);
}
REQUIRE(!ec);
CHECK(entry.status().type() == fs::file_type::regular);
CHECK(entry.symlink_status().type() == fs::file_type::regular);
CHECK(entry.symlink_status(ec).type() == fs::file_type::regular);
CHECK(!ec);
CHECK(entry.symlink_status().permissions() == fs::symlink_status(entry.path()).permissions());
}
// 2026-10-03:复用符号链接条目时保留链接分类,并让后续不存在的路径正常报错。
TEST_CASE("fs.dir.entry - update cached symlink and errors", "[filesystem][directory_entry][fs.dir.entry]")
{
if (is_symlink_creation_supported()) {
TemporaryDirectory t;
std::error_code ec;
generateFile(t.path() / "file");
fs::create_symlink(t.path() / "file", t.path() / "link");
fs::directory_entry entry(t.path() / "link");
REQUIRE(entry.is_symlink());
CHECK(entry.status().type() == fs::file_type::regular);
CHECK_NOTHROW(entry.refresh());
CHECK(entry.symlink_status().type() == fs::file_type::symlink);
SECTION("assign a non-symlink and refresh a removed link")
{
entry.assign(t.path() / "file", ec);
REQUIRE(!ec);
CHECK(entry.symlink_status().type() == fs::file_type::regular);
CHECK(!entry.is_symlink());
entry.assign(t.path() / "link");
REQUIRE(entry.is_symlink());
#if defined(GHC_OS_WINDOWS) && !defined(USE_STD_FS)
fs::remove(t.path() / "link");
CHECK_THROWS_AS(entry.refresh(), fs::filesystem_error);
entry.refresh(ec);
CHECK(ec);
CHECK(entry.symlink_status(ec).type() == fs::file_type::not_found);
CHECK(ec);
#endif
}
SECTION("refresh a dangling link")
{
fs::remove(t.path() / "file");
CHECK_NOTHROW(entry.refresh());
CHECK(entry.symlink_status().type() == fs::file_type::symlink);
CHECK(entry.symlink_status(ec).type() == fs::file_type::symlink);
CHECK(!ec);
CHECK(entry.is_symlink());
}
}
}
TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]")
{
{