mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 19:08:07 +08:00
cmSystemTools: Re-implement ToNormalizedPathOnDisk without translation map
Use `cm::PathResolver`'s `LogicalPath` variant to normalize paths while preserving symlinks not followed by `..` components. This avoids needing the KWSys path translation map to preserve symlinks through `realpath` operations. It also works with symlinks on Windows. Fixes: #16228
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
#include "cmDuration.h"
|
#include "cmDuration.h"
|
||||||
#include "cmELF.h"
|
#include "cmELF.h"
|
||||||
#include "cmMessageMetadata.h"
|
#include "cmMessageMetadata.h"
|
||||||
|
#include "cmPathResolver.h"
|
||||||
#include "cmProcessOutput.h"
|
#include "cmProcessOutput.h"
|
||||||
#include "cmRange.h"
|
#include "cmRange.h"
|
||||||
#include "cmStringAlgorithms.h"
|
#include "cmStringAlgorithms.h"
|
||||||
@@ -128,6 +129,97 @@ cmSystemTools::MessageCallback s_MessageCallback;
|
|||||||
cmSystemTools::OutputCallback s_StderrCallback;
|
cmSystemTools::OutputCallback s_StderrCallback;
|
||||||
cmSystemTools::OutputCallback s_StdoutCallback;
|
cmSystemTools::OutputCallback s_StdoutCallback;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string GetDosDriveWorkingDirectory(char letter)
|
||||||
|
{
|
||||||
|
// The Windows command processor tracks a per-drive working
|
||||||
|
// directory for compatibility with MS-DOS by using special
|
||||||
|
// environment variables named "=C:".
|
||||||
|
// https://web.archive.org/web/20100522040616/
|
||||||
|
// https://blogs.msdn.com/oldnewthing/archive/2010/05/06/10008132.aspx
|
||||||
|
return cmSystemTools::GetEnvVar(cmStrCat('=', letter, ':'))
|
||||||
|
.value_or(std::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
cmsys::Status ReadNameOnDisk(std::string const& path, std::string& name)
|
||||||
|
{
|
||||||
|
std::wstring wp = cmsys::Encoding::ToWide(path);
|
||||||
|
HANDLE h = CreateFileW(
|
||||||
|
wp.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||||
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
|
return cmsys::Status::Windows_GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
WCHAR local_fni[((sizeof(FILE_NAME_INFO) - 1) / sizeof(WCHAR)) + 1024];
|
||||||
|
size_t fni_size = sizeof(local_fni);
|
||||||
|
auto* fni = reinterpret_cast<FILE_NAME_INFO*>(local_fni);
|
||||||
|
if (!GetFileInformationByHandleEx(h, FileNameInfo, fni, fni_size)) {
|
||||||
|
DWORD e = GetLastError();
|
||||||
|
if (e != ERROR_MORE_DATA) {
|
||||||
|
CloseHandle(h);
|
||||||
|
return cmsys::Status::Windows(e);
|
||||||
|
}
|
||||||
|
fni_size = fni->FileNameLength;
|
||||||
|
fni = static_cast<FILE_NAME_INFO*>(malloc(fni_size));
|
||||||
|
if (!fni) {
|
||||||
|
e = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
CloseHandle(h);
|
||||||
|
return cmsys::Status::Windows(e);
|
||||||
|
}
|
||||||
|
if (!GetFileInformationByHandleEx(h, FileNameInfo, fni, fni_size)) {
|
||||||
|
e = GetLastError();
|
||||||
|
free(fni);
|
||||||
|
CloseHandle(h);
|
||||||
|
return cmsys::Status::Windows(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring wn{ fni->FileName, fni->FileNameLength / sizeof(WCHAR) };
|
||||||
|
std::string nn = cmsys::Encoding::ToNarrow(wn);
|
||||||
|
std::string::size_type last_slash = nn.find_last_of("/\\");
|
||||||
|
if (last_slash != std::string::npos) {
|
||||||
|
name = nn.substr(last_slash + 1);
|
||||||
|
}
|
||||||
|
if (fni != reinterpret_cast<FILE_NAME_INFO*>(local_fni)) {
|
||||||
|
free(fni);
|
||||||
|
}
|
||||||
|
CloseHandle(h);
|
||||||
|
return cmsys::Status::Success();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class RealSystem : public cm::PathResolver::System
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~RealSystem() override = default;
|
||||||
|
cmsys::Status ReadSymlink(std::string const& path,
|
||||||
|
std::string& link) override
|
||||||
|
{
|
||||||
|
return cmSystemTools::ReadSymlink(path, link);
|
||||||
|
}
|
||||||
|
bool PathExists(std::string const& path) override
|
||||||
|
{
|
||||||
|
return cmSystemTools::PathExists(path);
|
||||||
|
}
|
||||||
|
std::string GetWorkingDirectory() override
|
||||||
|
{
|
||||||
|
return cmSystemTools::GetLogicalWorkingDirectory();
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string GetWorkingDirectoryOnDrive(char letter) override
|
||||||
|
{
|
||||||
|
return GetDosDriveWorkingDirectory(letter);
|
||||||
|
}
|
||||||
|
cmsys::Status ReadName(std::string const& path, std::string& name) override
|
||||||
|
{
|
||||||
|
return ReadNameOnDisk(path, name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
RealSystem RealOS;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
|
#if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
|
||||||
@@ -1662,11 +1754,10 @@ std::vector<std::string> cmSystemTools::SplitEnvPathNormalized(
|
|||||||
|
|
||||||
std::string cmSystemTools::ToNormalizedPathOnDisk(std::string p)
|
std::string cmSystemTools::ToNormalizedPathOnDisk(std::string p)
|
||||||
{
|
{
|
||||||
p = cmSystemTools::CollapseFullPath(p);
|
using namespace cm::PathResolver;
|
||||||
cmSystemTools::ConvertToUnixSlashes(p);
|
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::LogicalPath
|
||||||
#ifdef _WIN32
|
static const Resolver<Policies::LogicalPath> resolver(RealOS);
|
||||||
p = cmSystemTools::GetActualCaseForPathCached(p);
|
resolver.Resolve(std::move(p), p);
|
||||||
#endif
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -404,6 +404,12 @@ public:
|
|||||||
static std::vector<std::string> SplitEnvPath(cm::string_view in);
|
static std::vector<std::string> SplitEnvPath(cm::string_view in);
|
||||||
static std::vector<std::string> SplitEnvPathNormalized(cm::string_view in);
|
static std::vector<std::string> SplitEnvPathNormalized(cm::string_view in);
|
||||||
|
|
||||||
|
/** Convert an input path to an absolute path with no '/..' components.
|
||||||
|
Backslashes in the input path are converted to forward slashes.
|
||||||
|
Relative paths are interpreted w.r.t. GetLogicalWorkingDirectory.
|
||||||
|
On Windows, the on-disk capitalization is loaded for existing paths.
|
||||||
|
This is similar to 'realpath', but preserves symlinks that are
|
||||||
|
not erased by '../' components. */
|
||||||
static std::string ToNormalizedPathOnDisk(std::string p);
|
static std::string ToNormalizedPathOnDisk(std::string p);
|
||||||
|
|
||||||
#ifndef CMAKE_BOOTSTRAP
|
#ifndef CMAKE_BOOTSTRAP
|
||||||
|
Reference in New Issue
Block a user