From 54190595efe55bdbb647c69fa7b0a43d3f84dd68 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 28 Jul 2017 13:25:50 +0200 Subject: Make sure QDir::absoluteFilePath("/dir") includes a drive on MS QDir::isAbsolutePath(name) thinks any path starting with a slash is absolute; however, to return a valid absolute path, we need to put a drive prefix onto such a name. So use QFileSystemEntry::isAbsolute() for that check (it believes in the need for a drive, or UNC prefix) and handle the absolute-but-for-drive case when it arises. Add a regression test and make related changes to existing tests. Task-number: QTBUG-50839 Change-Id: Id5d2b2586bb1423fa2d9375a298a4bb5241cffe0 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/corelib/io/qdir.cpp | 36 ++++++++++++++++++++++++++++++++- tests/auto/corelib/io/qdir/tst_qdir.cpp | 18 +++++++++++------ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 520f98b18f..e4d384e4ca 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -750,13 +750,47 @@ QString QDir::filePath(const QString &fileName) const QString QDir::absoluteFilePath(const QString &fileName) const { const QDirPrivate* d = d_ptr.constData(); - if (isAbsolutePath(fileName)) + // Don't trust our own isAbsolutePath(); Q_OS_WIN needs a drive. + if (QFileSystemEntry(fileName).isAbsolute()) return fileName; d->resolveAbsoluteEntry(); const QString absoluteDirPath = d->absoluteDirEntry.filePath(); if (fileName.isEmpty()) return absoluteDirPath; +#ifdef Q_OS_WIN + // Handle the "absolute except for drive" case (i.e. \blah not c:\blah): + int size = absoluteDirPath.length(); + if ((fileName.startsWith(QLatin1Char('/')) + || fileName.startsWith(QLatin1Char('\\'))) + && size > 1) { + // Combine absoluteDirPath's drive with fileName + int drive = 2; // length of drive prefix + if (Q_UNLIKELY(absoluteDirPath.at(1).unicode() != ':')) { + // Presumably, absoluteDirPath is an UNC path; use its //server/share + // part as "drive" - it's as sane a thing as we can do. + for (int i = 2; i-- > 0; ) { // Scan two "path fragments": + while (drive < size && absoluteDirPath.at(drive).unicode() == '/') + drive++; + if (drive >= size) { + qWarning("Base directory starts with neither a drive nor a UNC share: %s", + qPrintable(QDir::toNativeSeparators(absoluteDirPath))); + return QString(); + } + while (drive < size && absoluteDirPath.at(drive).unicode() != '/') + drive++; + } + // We'll append fileName, which starts with a slash; so omit trailing slash: + if (absoluteDirPath.at(drive).unicode() == '/') + drive--; + } else if (!absoluteDirPath.at(0).isLetter()) { + qWarning("Base directory's drive is not a letter: %s", + qPrintable(QDir::toNativeSeparators(absoluteDirPath))); + return QString(); + } + return absoluteDirPath.leftRef(drive) % fileName; + } +#endif // Q_OS_WIN if (!absoluteDirPath.endsWith(QLatin1Char('/'))) return absoluteDirPath % QLatin1Char('/') % fileName; return absoluteDirPath % fileName; diff --git a/tests/auto/corelib/io/qdir/tst_qdir.cpp b/tests/auto/corelib/io/qdir/tst_qdir.cpp index 9d47bb2884..734b26cd65 100644 --- a/tests/auto/corelib/io/qdir/tst_qdir.cpp +++ b/tests/auto/corelib/io/qdir/tst_qdir.cpp @@ -1384,16 +1384,22 @@ void tst_QDir::absoluteFilePath_data() QTest::addColumn("fileName"); QTest::addColumn("expectedFilePath"); - QTest::newRow("0") << "/etc" << "/passwd" << "/passwd"; - QTest::newRow("1") << "/etc" << "passwd" << "/etc/passwd"; - QTest::newRow("2") << "/" << "passwd" << "/passwd"; - QTest::newRow("3") << "relative" << "path" << QDir::currentPath() + "/relative/path"; - QTest::newRow("4") << "" << "" << QDir::currentPath(); #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - QTest::newRow("5") << "//machine" << "share" << "//machine/share"; + QTest::newRow("UNC") << "//machine" << "share" << "//machine/share"; + QTest::newRow("Drive") << "c:/side/town" << "/my/way/home" << "c:/my/way/home"; +#define DRIVE "Q:" +#else +#define DRIVE #endif + QTest::newRow("0") << DRIVE "/etc" << "/passwd" << DRIVE "/passwd"; + QTest::newRow("1") << DRIVE "/etc" << "passwd" << DRIVE "/etc/passwd"; + QTest::newRow("2") << DRIVE "/" << "passwd" << DRIVE "/passwd"; + QTest::newRow("3") << "relative" << "path" << QDir::currentPath() + "/relative/path"; + QTest::newRow("4") << "" << "" << QDir::currentPath(); + QTest::newRow("resource") << ":/prefix" << "foo.bar" << ":/prefix/foo.bar"; +#undef DRIVE } void tst_QDir::absoluteFilePath() -- cgit v1.2.3