diff options
author | Karsten Heimrich <karsten.heimrich@qt.io> | 2021-06-08 11:59:59 +0200 |
---|---|---|
committer | Karsten Heimrich <karsten.heimrich@qt.io> | 2021-06-09 04:37:16 +0200 |
commit | 915dd3166ad38884dd57c1e439611201d924dc4c (patch) | |
tree | fed658e8e21e8997c42e6e5578417f55440006a5 | |
parent | e99a9ff495d90e3d97615c8f283cc6d71514035c (diff) |
Unify behavior for long path or UNC prefix removal
Split the code out of QDir::fromNativeSeparator into a separate
reusable function to remove the above-mentioned prefixes. Fixes
and unifies behavior if the prefix was given with slashes instead
of backslashes. Add a couple more test cases.
Fixes: QTBUG-93868
Change-Id: Ibd94ae283e2fb113f9c2db97475fbc7d89522bbf
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
(cherry picked from commit 0564ebdb3641d7325f73dbbf2cbb04e6dca92d83)
-rw-r--r-- | src/corelib/io/qdir.cpp | 31 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemengine_win.cpp | 14 | ||||
-rw-r--r-- | src/corelib/io/qfilesystementry.cpp | 28 | ||||
-rw-r--r-- | src/corelib/io/qfilesystementry_p.h | 1 | ||||
-rw-r--r-- | tests/auto/corelib/io/qdir/tst_qdir.cpp | 13 | ||||
-rw-r--r-- | tests/auto/corelib/io/qtemporarydir/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp | 23 | ||||
-rw-r--r-- | tests/auto/corelib/io/qtemporaryfile/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp | 27 |
9 files changed, 101 insertions, 46 deletions
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 7f58569a28..8bff48e8e7 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -953,35 +953,10 @@ QString QDir::toNativeSeparators(const QString &pathName) QString QDir::fromNativeSeparators(const QString &pathName) { #if defined(Q_OS_WIN) - const QChar nativeSeparator = u'\\'; - int i = pathName.indexOf(nativeSeparator); - if (i != -1) { - QString n(pathName); - const QStringView uncPrefix(uR"(\\?\UNC\)"); - const QStringView extendedLengthPathPrefix(uR"(\\?\)"); - if (n.startsWith(uncPrefix)) { - // Keep the initial double-slash, chop out the rest of the prefix. - n = n.remove(2, uncPrefix.size() - 2); - if ((i = n.indexOf(nativeSeparator)) == -1) - return n; - } else if (n.startsWith(extendedLengthPathPrefix)) { - n = n.sliced(extendedLengthPathPrefix.size()); - if ((i = n.indexOf(nativeSeparator)) == -1) - return n; - } - - QChar * const data = n.data(); - data[i++] = u'/'; - - for (; i < n.length(); ++i) { - if (data[i] == nativeSeparator) - data[i] = u'/'; - } - - return n; - } -#endif + return QFileSystemEntry::removeUncOrLongPathPrefix(pathName).replace(u'\\', u'/'); +#else return pathName; +#endif } static QString qt_cleanPath(const QString &path, bool *ok = nullptr); diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index f27040b07c..590d7c04a3 100644 --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -297,18 +297,8 @@ static QString readSymLink(const QFileSystemEntry &link) const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset]; result = QString::fromWCharArray(PathBuffer, length); } - // cut-off "\\?\" and "\??\" - if (result.size() > 4 - && result.at(0) == QLatin1Char('\\') - && result.at(2) == QLatin1Char('?') - && result.at(3) == QLatin1Char('\\')) { - result = result.mid(4); - // cut off UNC in addition when the link points at a UNC share - // in which case we need to prepend another backslash to get \\server\share - if (QStringView{result}.left(3) == QLatin1String("UNC")) { - result.replace(0, 3, QLatin1Char('\\')); - } - } + // remove "\\?\", "\??\" or "\\?\UNC\" + result = QFileSystemEntry::removeUncOrLongPathPrefix(result); } free(rdb); CloseHandle(handle); diff --git a/src/corelib/io/qfilesystementry.cpp b/src/corelib/io/qfilesystementry.cpp index 83f8a86439..50fb192a16 100644 --- a/src/corelib/io/qfilesystementry.cpp +++ b/src/corelib/io/qfilesystementry.cpp @@ -290,6 +290,34 @@ bool QFileSystemEntry::isDriveRootPath(const QString &path) && path.at(0).isLetter() && path.at(1) == QLatin1Char(':') && path.at(2) == QLatin1Char('/')); } + +QString QFileSystemEntry::removeUncOrLongPathPrefix(QString path) +{ + constexpr qsizetype minPrefixSize = 4; + if (path.size() < minPrefixSize) + return path; + + auto data = path.data(); + const auto slash = path[0]; + if (slash != u'\\' && slash != u'/') + return path; + + // check for "//?/" or "/??/" + if (data[2] == u'?' && data[3] == slash && (data[1] == slash || data[1] == u'?')) { + path = path.sliced(minPrefixSize); + + // check for a possible "UNC/" prefix left-over + if (path.size() >= 4) { + data = path.data(); + if (data[0] == u'U' && data[1] == u'N' && data[2] == u'C' && data[3] == slash) { + data[2] = slash; + return path.sliced(2); + } + } + } + + return path; +} #endif // Q_OS_WIN bool QFileSystemEntry::isRootPath(const QString &path) diff --git a/src/corelib/io/qfilesystementry_p.h b/src/corelib/io/qfilesystementry_p.h index 251eca553a..129b5ac486 100644 --- a/src/corelib/io/qfilesystementry_p.h +++ b/src/corelib/io/qfilesystementry_p.h @@ -95,6 +95,7 @@ public: #if defined(Q_OS_WIN) bool isDriveRoot() const; static bool isDriveRootPath(const QString &path); + static QString removeUncOrLongPathPrefix(QString path); #endif bool isRoot() const; diff --git a/tests/auto/corelib/io/qdir/tst_qdir.cpp b/tests/auto/corelib/io/qdir/tst_qdir.cpp index cec01eb752..d43e45e12f 100644 --- a/tests/auto/corelib/io/qdir/tst_qdir.cpp +++ b/tests/auto/corelib/io/qdir/tst_qdir.cpp @@ -1268,9 +1268,20 @@ tst_QDir::cleanPath_data() QTest::newRow("drive-above-root") << "A:/.." << "A:/.."; QTest::newRow("unc-server-up") << "//server/path/.." << "//server"; QTest::newRow("unc-server-above-root") << "//server/.." << "//server/.."; + QTest::newRow("longpath") << "\\\\?\\d:\\" << "d:/"; + QTest::newRow("longpath-slash") << "//?/d:/" << "d:/"; + QTest::newRow("longpath-mixed-slashes") << "//?/d:\\" << "d:/"; + QTest::newRow("longpath-mixed-slashes-2") << "\\\\?\\d:/" << "d:/"; + QTest::newRow("unc-network-share") << "\\\\?\\UNC\\localhost\\c$\\tmp.txt" << "//localhost/c$/tmp.txt"; + QTest::newRow("unc-network-share-slash") << "//?/UNC/localhost/c$/tmp.txt" + << "//localhost/c$/tmp.txt"; + QTest::newRow("unc-network-share-mixed-slashes") << "//?/UNC/localhost\\c$\\tmp.txt" + << "//localhost/c$/tmp.txt"; + QTest::newRow("unc-network-share-mixed-slashes-2") << "\\\\?\\UNC\\localhost/c$/tmp.txt" + << "//localhost/c$/tmp.txt"; #else QTest::newRow("data15") << "//c:/foo" << "/c:/foo"; #endif // non-windows @@ -1748,6 +1759,8 @@ void tst_QDir::nativeSeparators() QCOMPARE(QDir::fromNativeSeparators(QLatin1String("\\\\?\\C:\\")), QString("C:/")); QCOMPARE(QDir::fromNativeSeparators(QLatin1String(R"(\\?\UNC\localhost\c$\tmp.txt)")), QString("//localhost/c$/tmp.txt")); + QCOMPARE(QDir::fromNativeSeparators(QLatin1String(R"(//?/UNC/localhost\c$\tmp.txt)")), + QString("//localhost/c$/tmp.txt")); #else QCOMPARE(QDir::toNativeSeparators(QLatin1String("/")), QString("/")); QCOMPARE(QDir::toNativeSeparators(QLatin1String("\\")), QString("\\")); diff --git a/tests/auto/corelib/io/qtemporarydir/CMakeLists.txt b/tests/auto/corelib/io/qtemporarydir/CMakeLists.txt index fa8efa114b..fcfd409d5b 100644 --- a/tests/auto/corelib/io/qtemporarydir/CMakeLists.txt +++ b/tests/auto/corelib/io/qtemporarydir/CMakeLists.txt @@ -10,3 +10,8 @@ qt_internal_add_test(tst_qtemporarydir PUBLIC_LIBRARIES Qt::TestPrivate ) + +qt_internal_extend_target(tst_qtemporarydir CONDITION WIN32 + LIBRARIES + shlwapi +) diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp index 14f3a10593..94fcc006bb 100644 --- a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp +++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp @@ -37,6 +37,7 @@ #include <qset.h> #include <QtTest/private/qtesthelpers_p.h> #ifdef Q_OS_WIN +# include <shlwapi.h> # include <windows.h> #endif #ifdef Q_OS_UNIX // for geteuid() @@ -159,9 +160,25 @@ void tst_QTemporaryDir::fileTemplate_data() } #ifdef Q_OS_WIN - const auto tmp = QDir::toNativeSeparators(QDir::tempPath()).sliced(QDir::rootPath().size()); - QTest::newRow("UNC") << "\\\\localhost\\C$\\" + tmp + "\\UNC.XXXXXX.tmpDir" - << "UNC." << ".tmpDir"; + auto tmp = QDir::toNativeSeparators(QDir::tempPath()); + if (PathGetDriveNumber((const wchar_t *) tmp.utf16()) < 0) + return; // skip if we have no drive letter + + tmp.data()[1] = u'$'; + const auto tmpPath = tmp + QLatin1String(R"(\UNC.XXXXXX.tmpDir)"); + + QTest::newRow("UNC-backslash") + << "\\\\localhost\\" + tmpPath << "UNC." + << ".tmpDir"; + QTest::newRow("UNC-prefix") + << "\\\\?\\UNC\\localhost\\" + tmpPath << "UNC." + << ".tmpDir"; + QTest::newRow("UNC-slash") + << "//localhost/" + QDir::fromNativeSeparators(tmpPath) << "UNC." + << ".tmpDir"; + QTest::newRow("UNC-prefix-slash") + << "//?/UNC/localhost/" + QDir::fromNativeSeparators(tmpPath) << "UNC." + << ".tmpDir"; #endif } diff --git a/tests/auto/corelib/io/qtemporaryfile/CMakeLists.txt b/tests/auto/corelib/io/qtemporaryfile/CMakeLists.txt index 11d4b991ce..f8419cee87 100644 --- a/tests/auto/corelib/io/qtemporaryfile/CMakeLists.txt +++ b/tests/auto/corelib/io/qtemporaryfile/CMakeLists.txt @@ -45,3 +45,8 @@ if(ANDROID AND NOT ANDROID_EMBEDDED) ${android_testdata_resource_files} ) endif() + +qt_internal_extend_target(tst_qtemporaryfile CONDITION WIN32 + LIBRARIES + shlwapi +) diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp index e23df1da2b..7071f2142e 100644 --- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp +++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp @@ -40,6 +40,7 @@ #include <QtTest/private/qtesthelpers_p.h> #if defined(Q_OS_WIN) +# include <shlwapi.h> # include <windows.h> #endif #if defined(Q_OS_UNIX) @@ -208,9 +209,29 @@ void tst_QTemporaryFile::fileTemplate_data() } #ifdef Q_OS_WIN - const auto tmp = QDir::toNativeSeparators(QDir::tempPath()).sliced(QDir::rootPath().size()); - QTest::newRow("UNC") << "\\\\localhost\\C$\\" + tmp + "\\QTBUG-74291.XXXXXX.tmpFile" - << "QTBUG-74291." << ".tmpFile" << ""; + auto tmp = QDir::toNativeSeparators(QDir::tempPath()); + if (PathGetDriveNumber((const wchar_t *) tmp.utf16()) < 0) + return; // skip if we have no drive letter + + tmp.data()[1] = u'$'; + const auto tmpPath = tmp + QLatin1String(R"(\QTBUG-74291.XXXXXX.tmpFile)"); + + QTest::newRow("UNC-backslash") + << "\\\\localhost\\" + tmpPath << "QTBUG-74291." + << ".tmpFile" + << ""; + QTest::newRow("UNC-prefix") + << "\\\\?\\UNC\\localhost\\" + tmpPath << "QTBUG-74291." + << ".tmpFile" + << ""; + QTest::newRow("UNC-slash") + << "//localhost/" + QDir::fromNativeSeparators(tmpPath) << "QTBUG-74291." + << ".tmpFile" + << ""; + QTest::newRow("UNC-prefix-slash") + << "//?/UNC/localhost/" + QDir::fromNativeSeparators(tmpPath) << "QTBUG-74291." + << ".tmpFile" + << ""; #endif } |