FileSystem: Use PathCchCanonicalizeEx() for GetWin32Path()
This commit is contained in:
parent
ab445ec69d
commit
979b7bd36f
|
@ -11,10 +11,18 @@ TEST(FileSystem, GetWin32Path)
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("test.txt"), L"test.txt");
|
ASSERT_EQ(FileSystem::GetWin32Path("test.txt"), L"test.txt");
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("D:\\test.txt"), L"\\\\?\\D:\\test.txt");
|
ASSERT_EQ(FileSystem::GetWin32Path("D:\\test.txt"), L"\\\\?\\D:\\test.txt");
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("C:\\foo"), L"\\\\?\\C:\\foo");
|
ASSERT_EQ(FileSystem::GetWin32Path("C:\\foo"), L"\\\\?\\C:\\foo");
|
||||||
|
ASSERT_EQ(FileSystem::GetWin32Path("C:\\foo\\bar\\..\\baz"), L"\\\\?\\C:\\foo\\baz");
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("\\\\foo\\bar\\baz"), L"\\\\?\\UNC\\foo\\bar\\baz");
|
ASSERT_EQ(FileSystem::GetWin32Path("\\\\foo\\bar\\baz"), L"\\\\?\\UNC\\foo\\bar\\baz");
|
||||||
|
ASSERT_EQ(FileSystem::GetWin32Path("\\\\foo\\bar\\baz\\sub\\.."), L"\\\\?\\UNC\\foo\\bar\\baz");
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"), L"ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
|
ASSERT_EQ(FileSystem::GetWin32Path("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"), L"ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("C:\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"), L"\\\\?\\C:\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
|
ASSERT_EQ(FileSystem::GetWin32Path("C:\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"),
|
||||||
ASSERT_EQ(FileSystem::GetWin32Path("\\\\foo\\bar\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"), L"\\\\?\\UNC\\foo\\bar\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
|
L"\\\\?\\C:\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
|
||||||
|
ASSERT_EQ(FileSystem::GetWin32Path("\\\\foo\\bar\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"),
|
||||||
|
L"\\\\?\\UNC\\foo\\bar\\ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
|
||||||
|
ASSERT_EQ(FileSystem::GetWin32Path("C:\\ŻąłóРстуぬね\\のはen🍪\\⟑η∏☉ⴤ\\..\\ℹ︎∩₲ ₱⟑♰⫳🐱"),
|
||||||
|
L"\\\\?\\C:\\ŻąłóРстуぬね\\のはen🍪\\ℹ︎∩₲ ₱⟑♰⫳🐱");
|
||||||
|
ASSERT_EQ(FileSystem::GetWin32Path("\\\\foo\\bar\\ŻąłóРстуぬねのはen🍪\\⟑η∏☉ⴤ\\..\\ℹ︎∩₲ ₱⟑♰⫳🐱"),
|
||||||
|
L"\\\\?\\UNC\\foo\\bar\\ŻąłóРстуぬねのはen🍪\\ℹ︎∩₲ ₱⟑♰⫳🐱");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "windows_headers.h"
|
#include "windows_headers.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <pathcch.h>
|
||||||
#include <share.h>
|
#include <share.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <winioctl.h>
|
#include <winioctl.h>
|
||||||
|
@ -225,33 +227,51 @@ void Path::RemoveLengthLimits(std::string* path)
|
||||||
|
|
||||||
bool FileSystem::GetWin32Path(std::wstring* dest, std::string_view str)
|
bool FileSystem::GetWin32Path(std::wstring* dest, std::string_view str)
|
||||||
{
|
{
|
||||||
const bool absolute = Path::IsAbsolute(str);
|
// Just convert to wide if it's a relative path, MAX_PATH still applies.
|
||||||
const bool unc = IsUNCPath(str);
|
if (!Path::IsAbsolute(str))
|
||||||
const size_t skip = unc ? 2 : 0;
|
return StringUtil::UTF8StringToWideString(*dest, str);
|
||||||
|
|
||||||
dest->clear();
|
// PathCchCanonicalizeEx() thankfully takes care of everything.
|
||||||
if (str.empty())
|
// But need to widen the string first, avoid the stack allocation.
|
||||||
return true;
|
int wlen = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.length()), nullptr, 0);
|
||||||
|
if (wlen <= 0) [[unlikely]]
|
||||||
int wlen = MultiByteToWideChar(CP_UTF8, 0, str.data() + skip, static_cast<int>(str.length() - skip), nullptr, 0);
|
|
||||||
if (wlen <= 0)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Can't fix up non-absolute paths. Hopefully they don't go past MAX_PATH.
|
// So copy it to a temp wide buffer first.
|
||||||
if (absolute)
|
wchar_t* wstr_buf = static_cast<wchar_t*>(_malloca(sizeof(wchar_t) * (static_cast<size_t>(wlen) + 1)));
|
||||||
dest->append(unc ? L"\\\\?\\UNC\\" : L"\\\\?\\");
|
wlen = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.length()), wstr_buf, wlen);
|
||||||
|
if (wlen <= 0) [[unlikely]]
|
||||||
const size_t start = dest->size();
|
{
|
||||||
dest->resize(start + static_cast<u32>(wlen));
|
_freea(wstr_buf);
|
||||||
|
|
||||||
wlen = MultiByteToWideChar(CP_UTF8, 0, str.data() + skip, static_cast<int>(str.length() - skip), dest->data() + start,
|
|
||||||
wlen);
|
|
||||||
if (wlen <= 0)
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dest->resize(start + static_cast<u32>(wlen));
|
// And use PathCchCanonicalizeEx() to fix up any non-direct elements.
|
||||||
|
wstr_buf[wlen] = '\0';
|
||||||
|
dest->resize(std::max<size_t>(static_cast<size_t>(wlen) + (IsUNCPath(str) ? 9 : 5), 16));
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const HRESULT hr =
|
||||||
|
PathCchCanonicalizeEx(dest->data(), dest->size(), wstr_buf, PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
dest->resize(std::wcslen(dest->data()));
|
||||||
|
_freea(wstr_buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
|
||||||
|
{
|
||||||
|
dest->resize(dest->size() * 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else [[unlikely]]
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("PathCchCanonicalizeEx() returned {:08X}", static_cast<unsigned>(hr));
|
||||||
|
_freea(wstr_buf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring FileSystem::GetWin32Path(std::string_view str)
|
std::wstring FileSystem::GetWin32Path(std::string_view str)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue