From b60dc48bb48bec63b21eb9c2ef705af64d9b9069 Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Fri, 23 Sep 2022 17:59:59 +0300 Subject: [PATCH] tdf#151117: Process non-existent long paths correctly Regression after commit 92e835dbf00590c9c29509d2995cc7918a9bbb90 Author Kunal Pawar Date Fri Feb 18 19:15:04 2022 +0530 tdf#98705 Replace GetCaseCorrectPathName with GetLongPathNameW The fix tries to keep the performance improvement, and when the path exists, it will only call GetLongPathNameW once. Anyway, for unclear reason, this normalization only happens on long paths. Change-Id: I1cf9a47dfc35046ec1b5eebbbcaca09edb1c471a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140516 Tested-by: Jenkins Reviewed-by: Mike Kaganski --- sal/osl/w32/file_url.cxx | 101 +++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 20 deletions(-) diff --git a/sal/osl/w32/file_url.cxx b/sal/osl/w32/file_url.cxx index 57d5fee5b320..8c525042ddef 100644 --- a/sal/osl/w32/file_url.cxx +++ b/sal/osl/w32/file_url.cxx @@ -41,14 +41,14 @@ #include "path_helper.hxx" -#define WSTR_SYSTEM_ROOT_PATH u"\\\\.\\" -#define WSTR_LONG_PATH_PREFIX u"\\\\?\\" -#define WSTR_LONG_PATH_PREFIX_UNC u"\\\\?\\UNC\\" - // FileURL functions namespace { +constexpr std::u16string_view WSTR_SYSTEM_ROOT_PATH = u"\\\\.\\"; +constexpr std::u16string_view WSTR_LONG_PATH_PREFIX = u"\\\\?\\"; +constexpr std::u16string_view WSTR_LONG_PATH_PREFIX_UNC = u"\\\\?\\UNC\\"; + // Internal functions that expect only backslashes as path separators bool startsWithDriveColon(std::u16string_view s) @@ -308,13 +308,13 @@ DWORD IsValidFilePath(const OUString& path, DWORD dwFlags, OUString* corrected) if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX_UNC)) { /* This is long path in UNC notation */ - oComponent = path.subView(SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1); + oComponent = path.subView(WSTR_LONG_PATH_PREFIX_UNC.size()); dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH; } else if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX)) { /* This is long path */ - oComponent = path.subView(SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1); + oComponent = path.subView(WSTR_LONG_PATH_PREFIX.size()); if (startsWithDriveColon(*oComponent)) { @@ -567,6 +567,71 @@ static OUString osl_encodeURL_(std::u16string_view sURL) return sEncodedURL.makeStringAndClear(); } +// A helper that makes sure that for existing part of the path, the case is correct. +// Unlike GetLongPathNameW that it wraps, this function does not require the path to exist. +static OUString GetCaseCorrectPathName(std::u16string_view sysPath) +{ + // Prepare a null-terminated string first. + // Neither OUString, nor u16string_view are guaranteed to be null-terminated + osl::LongPathBuffer szPath(sysPath.size() + WSTR_LONG_PATH_PREFIX_UNC.size() + 1); + wchar_t* const pPath = szPath; + wchar_t* pEnd = pPath; + size_t sysPathOffset = 0; + if (sysPath.size() >= MAX_PATH && isAbsolute(sysPath) + && !o3tl::starts_with(sysPath, WSTR_LONG_PATH_PREFIX)) + { + // Allow GetLongPathNameW consume long paths + std::u16string_view prefix = WSTR_LONG_PATH_PREFIX; + if (startsWithSlashSlash(sysPath)) + { + sysPathOffset = 2; // skip leading "\\" + prefix = WSTR_LONG_PATH_PREFIX_UNC; + } + pEnd = std::copy(prefix.begin(), prefix.end(), pEnd); + } + wchar_t* const pStart = pEnd; + pEnd = std::copy(sysPath.begin() + sysPathOffset, sysPath.end(), pStart); + *pEnd = 0; + osl::LongPathBuffer aBuf(MAX_LONG_PATH); + while (pEnd > pStart) + { + std::u16string_view curPath(o3tl::toU(pPath), pEnd - pPath); + if (curPath == u"\\\\" || curPath == WSTR_SYSTEM_ROOT_PATH + || curPath == WSTR_LONG_PATH_PREFIX + || o3tl::equalsIgnoreAsciiCase(curPath, WSTR_LONG_PATH_PREFIX_UNC)) + break; // Do not check if the special path prefix exists itself + + DWORD nNewLen = GetLongPathNameW(pPath, aBuf, aBuf.getBufSizeInSymbols()); + if (nNewLen == 0) + { + // Error? + const DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + { + // Check the base path; skip possible trailing separator + size_t sepPos = curPath.substr(0, curPath.size() - 1).rfind(u'\\'); + if (sepPos != std::u16string_view::npos) + { + pEnd = pPath + sepPos; + *pEnd = 0; + continue; + } + } + else + { + SAL_WARN("sal.osl", "GetLongPathNameW: Windows error code " + << err << " processing path " << OUString(curPath)); + } + break; // All other errors, or no separators left + } + assert(nNewLen < aBuf.getBufSizeInSymbols()); + // Combine the case-correct leading part with the non-existing trailing part + return OUString::Concat(std::u16string_view(o3tl::toU(aBuf), nNewLen)) + + sysPath.substr(pEnd - pStart + sysPathOffset); + }; + return OUString(sysPath); // We found no existing parts - just assume it's OK +} + oslFileError osl_getSystemPathFromFileURL_(const OUString& strURL, rtl_uString **pustrPath, bool bAllowRelative) { OUString sTempPath; @@ -622,25 +687,21 @@ oslFileError osl_getSystemPathFromFileURL_(const OUString& strURL, rtl_uString * } else { - ::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH ); - sal_uInt32 nNewLen = GetLongPathNameW( o3tl::toW(sDecodedURL->getStr()) + nSkip, - o3tl::toW(aBuf), - aBuf.getBufSizeInSymbols() ); - - if ( nNewLen <= MAX_PATH - 12 - || sDecodedURL->matchIgnoreAsciiCase(WSTR_SYSTEM_ROOT_PATH, nSkip) - || sDecodedURL->matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX, nSkip) ) + sDecodedURL = GetCaseCorrectPathName(sDecodedURL->subView(nSkip)); + if (sDecodedURL->getLength() <= MAX_PATH - 12 + || sDecodedURL->startsWith(WSTR_SYSTEM_ROOT_PATH) + || sDecodedURL->startsWith(WSTR_LONG_PATH_PREFIX)) { - sTempPath = std::u16string_view(aBuf, nNewLen); + sTempPath = *sDecodedURL; } - else if ( sDecodedURL->match("\\\\", nSkip) ) + else if (sDecodedURL->startsWith("\\\\")) { /* it should be an UNC path, use the according prefix */ - sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC) + std::u16string_view(aBuf + 2, nNewLen - 2); + sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC) + sDecodedURL->subView(2); } else { - sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX) + std::u16string_view(aBuf, nNewLen); + sTempPath = WSTR_LONG_PATH_PREFIX + *sDecodedURL; } } } @@ -691,7 +752,7 @@ oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** p switch ( dwPathType & PATHTYPE_MASK_TYPE ) { case PATHTYPE_ABSOLUTE_UNC: - static_assert(SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1 == 8, + static_assert(WSTR_LONG_PATH_PREFIX_UNC.size() == 8, "Unexpected long path UNC prefix!"); /* generate the normal UNC path */ @@ -699,7 +760,7 @@ oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** p break; case PATHTYPE_ABSOLUTE_LOCAL: - static_assert(SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1 == 4, + static_assert(WSTR_LONG_PATH_PREFIX.size() == 4, "Unexpected long path prefix!"); /* generate the normal path */