1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 19:08:07 +08:00

cmSystemTools: Fix GetRealPath implementation on Windows

In commit 823e1df54c (cmSystemTools: Implement GetRealPath on Windows,
2024-11-04, v4.0.0-rc1~521^2~1) we implemented the POSIX behavior that
resolves symlinks followed by '..' components.  However, Windows just
removes them lexically.  Also, we were not handling all junction types.

Instead, use `GetFinalPathNameByHandleW` via `uv_fs_realpath`.

Note that we previously attempted this in commit 640709e7db
(cmSystemTools: Implement GetRealPath on Windows, 2017-10-02,
v3.11.0-rc1~445^2~1) but reverted it in commit 83630d4918
(cmSystemTools: Revert GetRealPath implementation on Windows,
2018-05-29, v3.11.3~3^2) due to resolving `subst` drives.  This time,
add code to re-`subst`itute the drive in the resolved path.

Fixes: #26750
Issue: #17206
This commit is contained in:
Brad King
2025-03-10 14:07:46 -04:00
parent 5910bf0b40
commit 0a5efe8489

View File

@@ -1340,19 +1340,42 @@ std::string cmSystemTools::GetRealPath(std::string const& path,
std::string* errorMessage)
{
#ifdef _WIN32
std::string resolved_path;
using namespace cm::PathResolver;
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath
static Resolver<Policies::RealPath> const resolver(RealOS);
cmsys::Status status = resolver.Resolve(path, resolved_path);
if (!status) {
if (errorMessage) {
*errorMessage = status.GetString();
resolved_path.clear();
} else {
resolved_path = path;
std::string resolved_path =
cmSystemTools::GetRealPathResolvingWindowsSubst(path, errorMessage);
// If the original path used a subst drive and the real path starts
// with the substitution, restore the subst drive prefix. This may
// incorrectly restore a subst drive if the underlying drive was
// encountered via an absolute symlink, but this is an acceptable
// limitation to otherwise preserve susbt drives.
if (resolved_path.size() >= 2 && resolved_path[1] == ':' &&
path.size() >= 2 && path[1] == ':' &&
toupper(resolved_path[0]) != toupper(path[0])) {
// FIXME: Add thread_local or mutex if we use threads.
static std::map<char, std::string> substMap;
char const drive = static_cast<char>(toupper(path[0]));
std::string maybe_subst = cmStrCat(drive, ":/");
auto smi = substMap.find(drive);
if (smi == substMap.end()) {
smi = substMap
.emplace(
drive,
cmSystemTools::GetRealPathResolvingWindowsSubst(maybe_subst))
.first;
}
std::string const& resolved_subst = smi->second;
std::string::size_type const ns = resolved_subst.size();
if (ns > 0) {
std::string::size_type const np = resolved_path.size();
if (ns == np && resolved_path == resolved_subst) {
resolved_path = maybe_subst;
} else if (ns > 0 && ns < np && resolved_path[ns] == '/' &&
resolved_path.compare(0, ns, resolved_subst) == 0) {
resolved_path.replace(0, ns + 1, maybe_subst);
}
}
}
return resolved_path;
#else
return cmsys::SystemTools::GetRealPath(path, errorMessage);