summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2021-05-31 16:25:21 +0200
committerKarsten Heimrich <karsten.heimrich@qt.io>2021-06-05 01:16:06 +0200
commitded82d1b073adb769afd28104515d240e8e1dd3f (patch)
tree933acea7f79e2e21af71e9e38c37868974a87d7e
parent0564ebdb3641d7325f73dbbf2cbb04e6dca92d83 (diff)
downloadqtbase-ded82d1b073adb769afd28104515d240e8e1dd3f.tar.gz
Implement QFileInfo::junctionTarget(), adjust auto-test
The change in 004e3e0dc2cab4a4534d2ed3ace41aad6bfbe45d introduces Windows junction awareness, though users were still unable to resolve the junction target. This change adds the ability to solve this. Fixes: QTBUG-93869 Change-Id: I9f4d4ed87b92e757f7b6d8739e2a61b58c096f63 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/corelib/io/qabstractfileengine.cpp2
-rw-r--r--src/corelib/io/qabstractfileengine_p.h3
-rw-r--r--src/corelib/io/qfileinfo.cpp32
-rw-r--r--src/corelib/io/qfileinfo.h5
-rw-r--r--src/corelib/io/qfilesystemengine.cpp13
-rw-r--r--src/corelib/io/qfilesystemengine_p.h3
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp20
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp2
-rw-r--r--src/corelib/io/qfsfileengine_win.cpp2
-rw-r--r--tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp28
-rw-r--r--tests/benchmarks/corelib/io/qfileinfo/main.cpp10
11 files changed, 110 insertions, 10 deletions
diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp
index cc603dd63f..44d5d1b76f 100644
--- a/src/corelib/io/qabstractfileengine.cpp
+++ b/src/corelib/io/qabstractfileengine.cpp
@@ -267,6 +267,8 @@ QAbstractFileEngine *QAbstractFileEngine::create(const QString &fileName)
\value CanonicalName Often very similar to LinkName. Will return the true path to the file.
\value CanonicalPathName Same as CanonicalName, excluding the base name.
\value BundleName Returns the name of the bundle implies BundleType is set.
+ \value JunctionName The full name of the directory that this NTFS junction
+ is linked to. (This will be empty if this file is not an NTFS junction.)
\omitvalue NFileNames
diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h
index c88b66c7ce..bed4aa56d9 100644
--- a/src/corelib/io/qabstractfileengine_p.h
+++ b/src/corelib/io/qabstractfileengine_p.h
@@ -106,7 +106,8 @@ public:
CanonicalName,
CanonicalPathName,
BundleName,
- NFileNames = 9
+ JunctionName,
+ NFileNames // Must be last.
};
enum FileOwner {
OwnerUser,
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 25091b0d3f..7ca7468317 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -70,6 +70,9 @@ QString QFileInfoPrivate::getFileName(QAbstractFileEngine::FileName name) const
case QAbstractFileEngine::LinkName:
ret = QFileSystemEngine::getLinkTarget(fileEntry, metaData).filePath();
break;
+ case QAbstractFileEngine::JunctionName:
+ ret = QFileSystemEngine::getJunctionTarget(fileEntry, metaData).filePath();
+ break;
case QAbstractFileEngine::BundleName:
ret = QFileSystemEngine::bundleName(fileEntry);
break;
@@ -1225,6 +1228,28 @@ QString QFileInfo::symLinkTarget() const
}
/*!
+ \since 6.2
+
+ Resolves an NTFS junction to the path it references.
+
+ Returns the absolute path to the directory an NTFS junction points to, or
+ an empty string if the object is not an NTFS junction.
+
+ There is no guarantee that the directory named by the NTFS junction actually
+ exists.
+
+ \sa isJunction(), isFile(), isDir(), isSymLink(), isSymbolicLink(),
+ isShortcut()
+*/
+QString QFileInfo::junctionTarget() const
+{
+ Q_D(const QFileInfo);
+ if (d->isDefaultConstructed)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::JunctionName);
+}
+
+/*!
Returns the owner of the file. On systems where files
do not have owners, or if an error occurs, an empty string is
returned.
@@ -1631,6 +1656,13 @@ QDebug operator<<(QDebug dbg, const QFileInfo &fi)
\sa symLinkTarget()
*/
/*!
+ \fn std::filesystem::path QFileInfo::filesystemJunctionTarget() const
+ \since 6.2
+
+ Returns junctionTarget() as a \c{std::filesystem::path}.
+ \sa junctionTarget()
+*/
+/*!
\macro QT_IMPLICIT_QFILEINFO_CONSTRUCTION
\since 6.0
\relates QFileInfo
diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h
index 90be1c2540..4bc5db9e2b 100644
--- a/src/corelib/io/qfileinfo.h
+++ b/src/corelib/io/qfileinfo.h
@@ -161,9 +161,14 @@ public:
bool isBundle() const;
QString symLinkTarget() const;
+ QString junctionTarget() const;
+
#if QT_CONFIG(cxx17_filesystem) || defined(Q_CLANG_QDOC)
std::filesystem::path filesystemSymLinkTarget() const
{ return QtPrivate::toFilesystemPath(symLinkTarget()); }
+
+ std::filesystem::path filesystemJunctionTarget() const
+ { return QtPrivate::toFilesystemPath(junctionTarget()); }
#endif // QT_CONFIG(cxx17_filesystem)
QString owner() const;
diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp
index 1a9a01ac92..f00aba78c3 100644
--- a/src/corelib/io/qfilesystemengine.cpp
+++ b/src/corelib/io/qfilesystemengine.cpp
@@ -232,4 +232,17 @@ QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFile
#endif
}
+//static
+QFileSystemEntry QFileSystemEngine::getJunctionTarget(const QFileSystemEntry &link,
+ QFileSystemMetaData &data)
+{
+#if defined(Q_OS_WIN)
+ return junctionTarget(link, data);
+#else
+ Q_UNUSED(link);
+ Q_UNUSED(data);
+ return {};
+#endif
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfilesystemengine_p.h b/src/corelib/io/qfilesystemengine_p.h
index dffd304282..b5daa3b88e 100644
--- a/src/corelib/io/qfilesystemengine_p.h
+++ b/src/corelib/io/qfilesystemengine_p.h
@@ -101,6 +101,7 @@ public:
}
static QFileSystemEntry getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data);
+ static QFileSystemEntry getJunctionTarget(const QFileSystemEntry &link, QFileSystemMetaData &data);
static QFileSystemEntry canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data);
static QFileSystemEntry absoluteName(const QFileSystemEntry &entry);
static QByteArray id(const QFileSystemEntry &entry);
@@ -130,7 +131,7 @@ public:
QFileSystemMetaData *data = nullptr);
#endif
#if defined(Q_OS_WIN)
-
+ static QFileSystemEntry junctionTarget(const QFileSystemEntry &link, QFileSystemMetaData &data);
static bool uncListSharesOnServer(const QString &server, QStringList *list); //Used also by QFSFileEngineIterator::hasNext()
static bool fillMetaData(int fd, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what);
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index d587e2bfd0..b2e1870b3d 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -536,6 +536,26 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
}
//static
+QFileSystemEntry QFileSystemEngine::junctionTarget(const QFileSystemEntry &link,
+ QFileSystemMetaData &data)
+{
+ Q_CHECK_FILE_NAME(link, link);
+
+ if (data.missingFlags(QFileSystemMetaData::JunctionType))
+ QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType);
+
+ QString target;
+ if (data.isJunction())
+ target = readSymLink(link);
+ QFileSystemEntry ret(target);
+ if (!target.isEmpty() && ret.isRelative()) {
+ target.prepend(absoluteName(link).path() + QLatin1Char('/'));
+ ret = QFileSystemEntry(QDir::cleanPath(target));
+ }
+ return ret;
+}
+
+//static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
Q_CHECK_FILE_NAME(entry, entry);
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
index 9980359881..e85f81f267 100644
--- a/src/corelib/io/qfsfileengine_unix.cpp
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -476,6 +476,8 @@ QString QFSFileEngine::fileName(FileName file) const
return entry.filePath();
}
return QString();
+ case JunctionName:
+ return QString();
case DefaultName:
case NFileNames:
break;
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
index af7da61f9a..7ac1368ab3 100644
--- a/src/corelib/io/qfsfileengine_win.cpp
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -647,6 +647,8 @@ QString QFSFileEngine::fileName(FileName file) const
return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath();
} else if (file == BundleName) {
return QString();
+ } else if (file == JunctionName) {
+ return QFileSystemEngine::getJunctionTarget(d->fileEntry, d->metaData).filePath();
}
return d->fileEntry.filePath();
}
diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
index 916b084e24..4d74fe4d98 100644
--- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
+++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
@@ -1782,14 +1782,36 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks()
}
}
});
- const QString actualSymLinkTarget = isSymLink ? fi.symLinkTarget() : QString();
- const QString actualCanonicalFilePath = isSymLink ? fi.canonicalFilePath() : QString();
+ const QString actualCanonicalFilePath = fi.canonicalFilePath();
QCOMPARE(fi.isJunction(), isJunction);
QCOMPARE(fi.isSymbolicLink(), isSymLink);
if (isSymLink) {
- QCOMPARE(actualSymLinkTarget, linkTarget);
+ QCOMPARE(fi.symLinkTarget(), linkTarget);
QCOMPARE(actualCanonicalFilePath, canonicalFilePath);
}
+
+ if (isJunction) {
+ if (creationResult.target.startsWith(uR"(\??\)"))
+ creationResult.target = creationResult.target.sliced(4);
+
+ // resolve volume to drive letter
+ static const QRegularExpression matchVolumeRe(uR"(^Volume\{([a-z]|[0-9]|-)+\}\\)"_qs,
+ QRegularExpression::CaseInsensitiveOption);
+ auto matchVolume = matchVolumeRe.match(creationResult.target);
+ if (matchVolume.hasMatch()) {
+ Q_ASSERT(matchVolume.capturedStart() == 0);
+ DWORD len;
+ wchar_t buffer[MAX_PATH];
+ const QString volumeName = uR"(\\?\)"_qs + matchVolume.captured();
+ if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()),
+ buffer, MAX_PATH, &len) != 0) {
+ creationResult.target.replace(0, matchVolume.capturedLength(),
+ QString::fromWCharArray(buffer));
+ }
+ }
+ QCOMPARE(fi.junctionTarget(), QDir::fromNativeSeparators(creationResult.target));
+ QCOMPARE(actualCanonicalFilePath, QFileInfo(creationResult.link).canonicalFilePath());
+ }
}
void tst_QFileInfo::brokenShortcut()
diff --git a/tests/benchmarks/corelib/io/qfileinfo/main.cpp b/tests/benchmarks/corelib/io/qfileinfo/main.cpp
index 65b712898b..2cd06eef27 100644
--- a/tests/benchmarks/corelib/io/qfileinfo/main.cpp
+++ b/tests/benchmarks/corelib/io/qfileinfo/main.cpp
@@ -43,7 +43,7 @@ private slots:
void existsStatic();
#if defined(Q_OS_WIN)
void symLinkTargetPerformanceLNK();
- void symLinkTargetPerformanceMounpoint();
+ void junctionTargetPerformanceMountpoint();
#endif
void initTestCase();
void cleanupTestCase();
@@ -86,7 +86,7 @@ void qfileinfo::symLinkTargetPerformanceLNK()
QVERIFY(QFile::remove("link.lnk"));
}
-void qfileinfo::symLinkTargetPerformanceMounpoint()
+void qfileinfo::junctionTargetPerformanceMountpoint()
{
wchar_t buffer[MAX_PATH];
QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
@@ -99,11 +99,11 @@ void qfileinfo::symLinkTargetPerformanceMounpoint()
QFileInfo info(mountpoint);
info.setCaching(false);
- QVERIFY(info.isSymLink());
- QString linkTarget;
+ QVERIFY(info.isJunction());
+ QString junctionTarget;
QBENCHMARK {
for(int i=0; i<100; i++)
- linkTarget = info.symLinkTarget();
+ junctionTarget = info.junctionTarget();
}
QVERIFY(QDir().rmdir(mountpoint));
}