summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/io/qfsfileengine.cpp2
-rw-r--r--src/corelib/io/qfsfileengine_win.cpp18
-rw-r--r--tests/auto/qfileinfo/tst_qfileinfo.cpp113
-rw-r--r--tests/benchmarks/corelib/io/qfileinfo/main.cpp44
-rw-r--r--tests/shared/filesystem.h58
5 files changed, 231 insertions, 4 deletions
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
index a1ffb81..3cf08a7 100644
--- a/src/corelib/io/qfsfileengine.cpp
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -208,6 +208,8 @@ QString QFSFileEnginePrivate::canonicalized(const QString &path)
fi.setFile(prefix);
if (fi.isSymLink()) {
QString target = fi.symLinkTarget();
+ if(QFileInfo(target).isRelative())
+ target = fi.absolutePath() + slash + target;
if (separatorPos != -1) {
if (fi.isDir() && !target.endsWith(slash))
target.append(slash);
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
index eeca07e..668379f 100644
--- a/src/corelib/io/qfsfileengine_win.cpp
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -1275,7 +1275,12 @@ static QString readSymLink(const QString &link)
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)qMalloc(bufsize);
DWORD retsize = 0;
if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) {
- if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+ if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+ int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
+ int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
+ const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset];
+ result = QString::fromWCharArray(PathBuffer, length);
+ } else if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset];
@@ -1287,6 +1292,15 @@ static QString readSymLink(const QString &link)
}
qFree(rdb);
CloseHandle(handle);
+
+ QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive);
+ if(matchVolName.indexIn(result) == 0) {
+ DWORD len;
+ wchar_t buffer[MAX_PATH];
+ QString volumeName = result.mid(0, matchVolName.matchedLength()).prepend(QLatin1String("\\\\?\\"));
+ if(GetVolumePathNamesForVolumeNameW(volumeName.utf16(), buffer, MAX_PATH, &len) != 0)
+ result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer));
+ }
}
#else
Q_UNUSED(link);
@@ -1538,7 +1552,7 @@ bool QFSFileEnginePrivate::isSymlink() const
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
- && findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
+ && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK || findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)) {
is_link = true;
}
}
diff --git a/tests/auto/qfileinfo/tst_qfileinfo.cpp b/tests/auto/qfileinfo/tst_qfileinfo.cpp
index 403c5f9..d482cbc 100644
--- a/tests/auto/qfileinfo/tst_qfileinfo.cpp
+++ b/tests/auto/qfileinfo/tst_qfileinfo.cpp
@@ -56,6 +56,7 @@
#endif
#ifdef Q_OS_WIN
#include <qt_windows.h>
+#include <qlibrary.h>
#endif
#include <qplatformdefs.h>
#include <qdebug.h>
@@ -65,6 +66,7 @@
#endif
#include "../network-settings.h"
#include <private/qfileinfo_p.h>
+#include "../../shared/filesystem.h"
#if defined(Q_OS_SYMBIAN)
# define SRCDIR ""
@@ -161,6 +163,8 @@ private slots:
void refresh();
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ void ntfsJunctionPointsAndSymlinks_data();
+ void ntfsJunctionPointsAndSymlinks();
void brokenShortcut();
#endif
@@ -194,8 +198,15 @@ tst_QFileInfo::~tst_QFileInfo()
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
QDir().rmdir("./.hidden-directory");
#endif
-#ifdef Q_OS_WIN
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
QDir().rmdir("./hidden-directory");
+ QDir().rmdir("abs_symlink");
+ QDir().rmdir("rel_symlink");
+ QDir().rmdir("junction_pwd");
+ QDir().rmdir("junction_root");
+ QDir().rmdir("mountpoint");
+ QFile::remove("abs_symlink.cpp");
+ QFile::remove("rel_symlink.cpp");
#endif
}
@@ -1236,6 +1247,106 @@ void tst_QFileInfo::refresh()
}
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("isSymLink");
+ QTest::addColumn<QString>("linkTarget");
+ QTest::addColumn<QString>("canonicalFilePath");
+
+ QDir pwd;
+ pwd.mkdir("target");
+
+ QLibrary kernel32("kernel32");
+ typedef BOOLEAN (WINAPI *PtrCreateSymbolicLink)(LPCWSTR, LPCWSTR, DWORD);
+ PtrCreateSymbolicLink createSymbolicLinkW = 0;
+ createSymbolicLinkW = (PtrCreateSymbolicLink) kernel32.resolve("CreateSymbolicLinkW");
+ if (!createSymbolicLinkW)
+ QSKIP("symbolic links not supported by operating system",SkipSingle);
+
+ {
+ //Directory symlinks
+ QDir target("target");
+ QVERIFY(target.exists());
+
+ QString absTarget = QDir::toNativeSeparators(target.absolutePath());
+ QString absSymlink = QDir::toNativeSeparators(pwd.absolutePath()).append("\\abs_symlink");
+ QString relTarget = "target";
+ QString relSymlink = "rel_symlink";
+ QString fileInTarget(absTarget);
+ fileInTarget.append("\\file");
+ QString fileInSymlink(absSymlink);
+ fileInSymlink.append("\\file");
+ QFile file(fileInTarget);
+ file.open(QIODevice::ReadWrite);
+ file.close();
+
+ QVERIFY(pwd.exists("abs_symlink") || createSymbolicLinkW(absSymlink.utf16(),absTarget.utf16(),0x1));
+ QVERIFY(pwd.exists(relSymlink) || createSymbolicLinkW(relSymlink.utf16(),relTarget.utf16(),0x1));
+ QVERIFY(file.exists());
+
+ QTest::newRow("absolute dir symlink") << absSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalPath();
+ QTest::newRow("relative dir symlink") << relSymlink << true << QDir::fromNativeSeparators(relTarget) << target.canonicalPath();
+ QTest::newRow("file in symlink dir") << fileInSymlink << false << "" << target.canonicalPath().append("/file");
+ }
+ {
+ //File symlinks
+ QFileInfo target(SRCDIR "tst_qfileinfo.cpp");
+ QString absTarget = QDir::toNativeSeparators(target.absoluteFilePath());
+ QString absSymlink = QDir::toNativeSeparators(pwd.absolutePath()).append("\\abs_symlink.cpp");
+ QString relTarget = QDir::toNativeSeparators(pwd.relativeFilePath(target.absoluteFilePath()));
+ QString relSymlink = "rel_symlink.cpp";
+ QVERIFY(pwd.exists("abs_symlink.cpp") || createSymbolicLinkW(absSymlink.utf16(),absTarget.utf16(),0x0));
+ QVERIFY(pwd.exists(relSymlink) || createSymbolicLinkW(relSymlink.utf16(),relTarget.utf16(),0x0));
+
+ QTest::newRow("absolute file symlink") << absSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath();
+ QTest::newRow("relative file symlink") << relSymlink << true << QDir::fromNativeSeparators(relTarget) << target.canonicalFilePath();
+ }
+
+ //Junctions
+ QString target = "target";
+ QString junction = "junction_pwd";
+ FileSystem::createNtfsJunction(target, junction);
+ QFileInfo targetInfo(target);
+ QTest::newRow("junction_pwd") << junction << true << targetInfo.absoluteFilePath() << targetInfo.canonicalFilePath();
+
+ QFileInfo fileInJunction(targetInfo.absoluteFilePath().append("/file"));
+ QFile file(fileInJunction.absoluteFilePath());
+ file.open(QIODevice::ReadWrite);
+ file.close();
+ QVERIFY(file.exists());
+ QTest::newRow("file in junction") << fileInJunction.absoluteFilePath() << false << "" << fileInJunction.canonicalFilePath();
+
+ target = QDir::rootPath();
+ junction = "junction_root";
+ FileSystem::createNtfsJunction(target, junction);
+ targetInfo.setFile(target);
+ QTest::newRow("junction_root") << junction << true << targetInfo.absoluteFilePath() << targetInfo.canonicalFilePath();
+
+ //Mountpoint
+ wchar_t buffer[MAX_PATH];
+ QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
+ QVERIFY(GetVolumeNameForVolumeMountPointW(rootPath.utf16(), buffer, MAX_PATH));
+ QString rootVolume = QString::fromWCharArray(buffer);
+ junction = "mountpoint";
+ rootVolume.replace("\\\\?\\","\\??\\");
+ FileSystem::createNtfsJunction(rootVolume, junction);
+ QTest::newRow("mountpoint") << junction << true << QDir::fromNativeSeparators(rootPath) << QDir::rootPath();
+}
+
+void tst_QFileInfo::ntfsJunctionPointsAndSymlinks()
+{
+ QFETCH(QString, path);
+ QFETCH(bool, isSymLink);
+ QFETCH(QString, linkTarget);
+ QFETCH(QString, canonicalFilePath);
+
+ QFileInfo fi(path);
+ QCOMPARE(fi.isSymLink(), isSymLink);
+ QCOMPARE(fi.symLinkTarget(), linkTarget);
+ QCOMPARE(fi.canonicalFilePath(), canonicalFilePath);
+}
+
void tst_QFileInfo::brokenShortcut()
{
QString linkName("borkenlink.lnk");
diff --git a/tests/benchmarks/corelib/io/qfileinfo/main.cpp b/tests/benchmarks/corelib/io/qfileinfo/main.cpp
index 025787f..b272bda 100644
--- a/tests/benchmarks/corelib/io/qfileinfo/main.cpp
+++ b/tests/benchmarks/corelib/io/qfileinfo/main.cpp
@@ -43,15 +43,20 @@
#include <QtTest/QtTest>
#include <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
+#include <QtCore/QFile>
#include "private/qfsfileengine_p.h"
+#include "../../../../shared/filesystem.h"
class qfileinfo : public QObject
{
Q_OBJECT
private slots:
void canonicalFileNamePerformance();
-
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ void symLinkTargetPerformanceLNK();
+ void symLinkTargetPerformanceMounpoint();
+#endif
void initTestCase();
void cleanupTestCase();
public:
@@ -78,6 +83,43 @@ void qfileinfo::canonicalFileNamePerformance()
}
}
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+void qfileinfo::symLinkTargetPerformanceLNK()
+{
+ QVERIFY(QFile::link("file","link.lnk"));
+ QFileInfo info("link.lnk");
+ info.setCaching(false);
+ QVERIFY(info.isSymLink());
+ QString linkTarget;
+ QBENCHMARK {
+ for(int i=0; i<100; i++)
+ linkTarget = info.readLink();
+ }
+ QVERIFY(QFile::remove("link.lnk"));
+}
+
+void qfileinfo::symLinkTargetPerformanceMounpoint()
+{
+ wchar_t buffer[MAX_PATH];
+ QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
+ QVERIFY(GetVolumeNameForVolumeMountPointW(rootPath.utf16(), buffer, MAX_PATH));
+ QString rootVolume = QString::fromWCharArray(buffer);
+ QString mountpoint = "mountpoint";
+ rootVolume.replace("\\\\?\\","\\??\\");
+ FileSystem::createNtfsJunction(rootVolume, mountpoint);
+
+ QFileInfo info(mountpoint);
+ info.setCaching(false);
+ QVERIFY(info.isSymLink());
+ QString linkTarget;
+ QBENCHMARK {
+ for(int i=0; i<100; i++)
+ linkTarget = info.readLink();
+ }
+ QVERIFY(QDir().rmdir(mountpoint));
+}
+#endif
+
QTEST_MAIN(qfileinfo)
#include "main.moc"
diff --git a/tests/shared/filesystem.h b/tests/shared/filesystem.h
index 2d46c0d..cc1781e 100644
--- a/tests/shared/filesystem.h
+++ b/tests/shared/filesystem.h
@@ -48,6 +48,11 @@
#include <QDir>
#include <QFile>
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+# define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
+# define REPARSE_MOUNTPOINT_HEADER_SIZE 8
+#endif
+
struct FileSystem
{
~FileSystem()
@@ -86,6 +91,59 @@ struct FileSystem
}
return false;
}
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ static void createNtfsJunction(QString target, QString linkName)
+ {
+ typedef struct {
+ DWORD ReparseTag;
+ DWORD ReparseDataLength;
+ WORD Reserved;
+ WORD ReparseTargetLength;
+ WORD ReparseTargetMaximumLength;
+ WORD Reserved1;
+ WCHAR ReparseTarget[1];
+ } REPARSE_MOUNTPOINT_DATA_BUFFER, *PREPARSE_MOUNTPOINT_DATA_BUFFER;
+
+ char reparseBuffer[MAX_PATH*3];
+ HANDLE hFile;
+ DWORD returnedLength;
+ wchar_t fileSystem[MAX_PATH] = L"";
+ PREPARSE_MOUNTPOINT_DATA_BUFFER reparseInfo = (PREPARSE_MOUNTPOINT_DATA_BUFFER) reparseBuffer;
+
+ QFileInfo junctionInfo(linkName);
+ linkName = QDir::toNativeSeparators(junctionInfo.absoluteFilePath());
+
+ GetVolumeInformationW( linkName.left(3).utf16(), NULL, 0, NULL, NULL, NULL,
+ fileSystem, sizeof(fileSystem)/sizeof(WCHAR));
+ if(QString().fromWCharArray(fileSystem) != "NTFS")
+ QSKIP("This seems not to be an NTFS volume. Junctions are not allowed.",SkipSingle);
+
+ if (!target.startsWith("\\??\\") && !target.startsWith("\\\\?\\")) {
+ QFileInfo targetInfo(target);
+ target = QDir::toNativeSeparators(targetInfo.absoluteFilePath());
+ target.prepend("\\??\\");
+ if(target.endsWith('\\') && target.at(target.length()-2) != ':')
+ target.chop(1);
+ }
+ QDir().mkdir(linkName);
+ hFile = CreateFileW( linkName.utf16(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL );
+ QVERIFY(hFile != INVALID_HANDLE_VALUE );
+
+ memset( reparseInfo, 0, sizeof( *reparseInfo ));
+ reparseInfo->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ reparseInfo->ReparseTargetLength = target.size() * sizeof(wchar_t);
+ reparseInfo->ReparseTargetMaximumLength = reparseInfo->ReparseTargetLength + sizeof(wchar_t);
+ target.toWCharArray(reparseInfo->ReparseTarget);
+ reparseInfo->ReparseDataLength = reparseInfo->ReparseTargetLength + 12;
+
+ bool ioc = DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, reparseInfo,
+ reparseInfo->ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE,
+ NULL, 0, &returnedLength, NULL);
+ CloseHandle( hFile );
+ QVERIFY(ioc);
+ }
+#endif
private:
QDir currentDir;