diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2019-02-04 17:10:22 +0100 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2019-04-10 04:53:52 +0000 |
commit | 3f17029aa12eafd3f8ddd5e0b246a6bf22eabfe3 (patch) | |
tree | d16b5efa9c2c70ff5eb3a4b396fc8274c701654e | |
parent | bafa676d080509fcef3e1189ddc8fe06730dcac9 (diff) |
Fix resolving NTFS symbolic links that point to UNC shares
Without this change, the target of a symbolic link that points to a UNC
share would include UNC in the target path, and not be correctly made
absolute.
Add a relevant test case, and use the opportunity to factor out the
helper code that creates NTFS symlinks into a function that takes
care of error handling.
The file created with the new test case only gets cleaned up correctly
when passing the file path into QDir::rmdir, which is either way the
right thing to do.
[ChangeLog][QtCore][QFileInfo] Fixed resolving of symbolic links to UNC
shares on NTFS file systems.
Change-Id: I9ba75d627aedf7c4cc289f0cb71088d795d30d8a
Fixes: QTBUG-73688
Task-number: QTBUG-63970
Task-number: QTBUG-30401
Task-number: QTBUG-20791
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/io/qfilesystemengine_win.cpp | 13 | ||||
-rw-r--r-- | tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp | 68 |
2 files changed, 56 insertions, 25 deletions
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index 2020e34f93..279918b812 100644 --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -310,9 +310,18 @@ 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('\\')) + // 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 (result.leftRef(3) == QLatin1String("UNC")) { + result.replace(0, 3, QLatin1Char('\\')); + } + } } free(rdb); CloseHandle(handle); diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index 017eebe153..baf78f6a04 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -95,6 +95,33 @@ inline bool qIsLikelyToBeNfs(const QString &path) #endif } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +enum NtfsTargetType { + NtfsTargetFile = 0x0, + NtfsTargetDir = 0x1 +}; + +static bool createNtfsSymLinkHelper(const QString &path, const QString &target, NtfsTargetType targetType) +{ + DWORD dwFlags = targetType; + DWORD err = ERROR_SUCCESS; + + SetLastError(0); + const bool result = CreateSymbolicLink(reinterpret_cast<const wchar_t*>(path.utf16()), + reinterpret_cast<const wchar_t*>(target.utf16()), dwFlags); + err = GetLastError(); + + // CreateSymbolicLink can return TRUE & still fail to create the link, + // the error code in that case might be ERROR_PRIVILEGE_NOT_HELD (1314) + if (!result || err != ERROR_SUCCESS) { + qWarning() << "Error creating NTFS-symlink from:" << path << "to" << target << ":" << qt_error_string(err); + + return false; + } + return true; +} +#endif + static QString seedAndTemplate() { QString base; @@ -704,18 +731,12 @@ void tst_QFileInfo::canonicalFilePath() #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) { - // CreateSymbolicLink can return TRUE & still fail to create the link, - // the error code in that case is ERROR_PRIVILEGE_NOT_HELD (1314) - SetLastError(0); const QString linkTarget = QStringLiteral("res"); - BOOL ret = CreateSymbolicLink((wchar_t*)linkTarget.utf16(), (wchar_t*)m_resourcesDir.utf16(), 1); - DWORD dwErr = GetLastError(); + BOOL ret = createNtfsSymLinkHelper(linkTarget, m_resourcesDir, NtfsTargetDir); if (!ret) QSKIP("Symbolic links aren't supported by FS"); QString currentPath = QDir::currentPath(); bool is_res_Current = QDir::setCurrent(linkTarget); - if (!is_res_Current && dwErr == 1314) - QSKIP("Not enough privilages to create Symbolic links"); QCOMPARE(is_res_Current, true); const QString actualCanonicalPath = QFileInfo("file1").canonicalFilePath(); QVERIFY(QDir::setCurrent(currentPath)); @@ -1482,19 +1503,12 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() file.open(QIODevice::ReadWrite); file.close(); - DWORD err = ERROR_SUCCESS ; + bool result = true; if (!pwd.exists("abs_symlink")) - if (!CreateSymbolicLink((wchar_t*)absSymlink.utf16(),(wchar_t*)absTarget.utf16(),0x1)) - err = GetLastError(); - if (err == ERROR_SUCCESS && !pwd.exists(relSymlink)) - if (!CreateSymbolicLink((wchar_t*)relSymlink.utf16(),(wchar_t*)relTarget.utf16(),0x1)) - err = GetLastError(); - if (err != ERROR_SUCCESS) { - wchar_t errstr[0x100]; - DWORD count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, - 0, err, 0, errstr, 0x100, 0); - QString error(QString::fromWCharArray(errstr, count)); - qWarning() << error; + result = createNtfsSymLinkHelper(absSymlink, absTarget, NtfsTargetDir); + if (result && !pwd.exists(relSymlink)) + result = createNtfsSymLinkHelper(relSymlink, relTarget, NtfsTargetDir); + if (!result) { //we need at least one data set for the test not to assert fail when skipping _data function QDir target("target"); QTest::newRow("dummy") << target.path() << false << "" << target.canonicalPath(); @@ -1517,13 +1531,21 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data() QString relSymlink = "rel_symlink.cpp"; QString relToRelTarget = QDir::toNativeSeparators(relativeDir.relativeFilePath(target.absoluteFilePath())); QString relToRelSymlink = "relative/rel_symlink"; - QVERIFY(pwd.exists("abs_symlink.cpp") || CreateSymbolicLink((wchar_t*)absSymlink.utf16(),(wchar_t*)absTarget.utf16(),0x0)); - QVERIFY(pwd.exists(relSymlink) || CreateSymbolicLink((wchar_t*)relSymlink.utf16(),(wchar_t*)relTarget.utf16(),0x0)); - QVERIFY(pwd.exists(relToRelSymlink) || CreateSymbolicLink((wchar_t*)relToRelSymlink.utf16(), (wchar_t*)relToRelTarget.utf16(),0x0)); + QVERIFY(pwd.exists("abs_symlink.cpp") || createNtfsSymLinkHelper(absSymlink, absTarget, NtfsTargetFile)); + QVERIFY(pwd.exists(relSymlink) || createNtfsSymLinkHelper(relSymlink, relTarget, NtfsTargetFile)); + QVERIFY(pwd.exists(relToRelSymlink) || createNtfsSymLinkHelper(relToRelSymlink, relToRelTarget, NtfsTargetFile)); QTest::newRow("absolute file symlink") << absSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); QTest::newRow("relative file symlink") << relSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); QTest::newRow("relative to relative file symlink") << relToRelSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath(); } + { + // Symlink to UNC share + pwd.mkdir("unc"); + QString uncTarget = QStringLiteral("//") + QtNetworkSettings::winServerName() + "/testshare"; + QString uncSymlink = QDir::toNativeSeparators(pwd.absolutePath().append("\\unc\\link_to_unc")); + QVERIFY(pwd.exists("link_to_unc") || createNtfsSymLinkHelper(uncSymlink, uncTarget, NtfsTargetDir)); + QTest::newRow("UNC symlink") << uncSymlink << true << uncTarget << uncTarget; + } //Junctions QString target = "target"; @@ -1570,7 +1592,7 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks() // Ensure that junctions, mountpoints are removed. If this fails, do not remove // temporary directory to prevent it from trashing the system. if (fi.isDir()) { - if (!QDir().rmdir(fi.fileName())) { + if (!QDir().rmdir(fi.filePath())) { qWarning("Unable to remove NTFS junction '%s'', keeping '%s'.", qPrintable(fi.fileName()), qPrintable(QDir::toNativeSeparators(m_dir.path()))); m_dir.setAutoRemove(false); |