Fix comparison of absolute, unclean paths in QDir
authorShane Kearns <shane.kearns@accenture.com>
Thu, 1 Sep 2011 14:04:33 +0000 (15:04 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 2 Sep 2011 08:01:20 +0000 (10:01 +0200)
QDir::operator== was creating a clean absolute path for comparison
purposes if the original path was relative.
However original absolute paths were trusted, even though they could
be unclean. Now they are checked for cleanliness first.

Task-Number: QTBUG-19995
Task-Number: QTBUG-20495
Change-Id: I047a1a40ae5151e4604085e4ac87f30a4e4979c4
Reviewed-on: http://codereview.qt.nokia.com/4099
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Prasanth Ullattil <prasanth.ullattil@nokia.com>
src/corelib/io/qdir.cpp
src/corelib/io/qfilesystementry.cpp
src/corelib/io/qfilesystementry_p.h
tests/auto/qdir/tst_qdir.cpp
tests/auto/qfilesystementry/tst_qfilesystementry.cpp

index 49d8a7b..d1c9be2 100644 (file)
@@ -190,7 +190,7 @@ inline void QDirPrivate::resolveAbsoluteEntry() const
 
     QString absoluteName;
     if (fileEngine.isNull()) {
-        if (!dirEntry.isRelative()) {
+        if (!dirEntry.isRelative() && dirEntry.isClean()) {
             absoluteDirEntry = dirEntry;
             return;
         }
index bb7cb4e..2f37542 100644 (file)
@@ -380,4 +380,35 @@ void QFileSystemEntry::findFileNameSeparators() const
     }
 }
 
+bool QFileSystemEntry::isClean() const
+{
+    resolveFilePath();
+    int dots = 0;
+    bool dotok = true; // checking for ".." or "." starts to relative paths
+    bool slashok = true;
+    for (QString::const_iterator iter = m_filePath.constBegin(); iter != m_filePath.constEnd(); iter++) {
+        if (*iter == QLatin1Char('/')) {
+            if (dots == 1 || dots == 2)
+                return false; // path contains "./" or "../"
+            if (!slashok)
+                return false; // path contains "//"
+            dots = 0;
+            dotok = true;
+            slashok = false;
+        } else if (dotok) {
+            slashok = true;
+            if (*iter == QLatin1Char('.')) {
+                dots++;
+                if (dots > 2)
+                    dotok = false;
+            } else {
+                //path element contains a character other than '.', it's clean
+                dots = 0;
+                dotok = false;
+            }
+        }
+    }
+    return (dots != 1 && dots != 2); // clean if path doesn't end in . or ..
+}
+
 QT_END_NAMESPACE
index 34b083a..8d524c0 100644 (file)
@@ -91,6 +91,7 @@ public:
     QString completeSuffix() const;
     bool isAbsolute() const;
     bool isRelative() const;
+    bool isClean() const;
 
 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
     bool isDriveRoot() const;
index 8436014..fd1ecd5 100644 (file)
@@ -1109,6 +1109,11 @@ void tst_QDir::absolutePath_data()
     QTest::newRow("4") << "c:/machine/share/dir1" << "c:/machine/share/dir1";
     QTest::newRow("5") << "c:\\machine\\share\\dir1" << "c:/machine/share/dir1";
 #endif
+    //test dirty paths are cleaned (QTBUG-19995)
+    QTest::newRow("/home/qt/.") << QDir::rootPath() + "home/qt/." << QDir::rootPath() + "home/qt";
+    QTest::newRow("/system/data/../config") << QDir::rootPath() + "system/data/../config" << QDir::rootPath() + "system/config";
+    QTest::newRow("//home//qt/") << QDir::rootPath() + "/home//qt/" << QDir::rootPath() + "home/qt";
+    QTest::newRow("foo/../bar") << "foo/../bar" << QDir::currentPath() + "/bar";
     QTest::newRow("resource") << ":/prefix/foo.bar" << ":/prefix/foo.bar";
 }
 
@@ -1872,6 +1877,14 @@ void tst_QDir::equalityOperator_data()
         << "./entrylist" << "*.cpp" << int(QDir::Name) << int(QDir::Files)
         << true;
 
+    QTest::newRow("QTBUG-20495") << QDir::currentPath() + "/entrylist/.." << "*.cpp" << int(QDir::Name) << int(QDir::Files)
+        << "." << "*.cpp" << int(QDir::Name) << int(QDir::Files)
+        << true;
+
+    QTest::newRow("QTBUG-20495-root") << QDir::rootPath() + "tmp/.." << "*.cpp" << int(QDir::Name) << int(QDir::Files)
+        << QDir::rootPath() << "*.cpp" << int(QDir::Name) << int(QDir::Files)
+        << true;
+
     QTest::newRow("diff-filters") << SRCDIR << "*.cpp" << int(QDir::Name) << int(QDir::Files)
         << SRCDIR << "*.cpp" << int(QDir::Name) << int(QDir::Dirs)
         << false;
index 016bcbf..2daabee 100644 (file)
@@ -68,6 +68,8 @@ private slots:
     void absoluteOrRelative_data();
     void absoluteOrRelative();
 #endif
+    void isClean_data();
+    void isClean();
 };
 
 #if defined(WIN_STUFF)
@@ -383,5 +385,35 @@ void tst_QFileSystemEntry::absoluteOrRelative()
 }
 #endif
 
+void tst_QFileSystemEntry::isClean_data()
+{
+    QTest::addColumn<QString>("path");
+    QTest::addColumn<bool>("isClean");
+
+    QTest::newRow("simple") << "foo" << true;
+    QTest::newRow("complex") << "/foo/bar/bz" << true;
+    QTest::newRow(".file") << "/foo/.file" << true;
+    QTest::newRow("..file") << "/foo/..file" << true;
+    QTest::newRow("...") << "/foo/.../bar" << true;
+    QTest::newRow("./") << "./" << false;
+    QTest::newRow("../") << "../" << false;
+    QTest::newRow(".") << "." << false;
+    QTest::newRow("..") << ".." << false;
+    QTest::newRow("/.") << "/." << false;
+    QTest::newRow("/..") << "/.." << false;
+    QTest::newRow("/../") << "foo/../bar" << false;
+    QTest::newRow("/./") << "foo/./bar" << false;
+    QTest::newRow("//") << "foo//bar" << false;
+}
+
+void tst_QFileSystemEntry::isClean()
+{
+    QFETCH(QString, path);
+    QFETCH(bool, isClean);
+
+    QFileSystemEntry fi(path);
+    QCOMPARE(fi.isClean(), isClean);
+}
+
 QTEST_MAIN(tst_QFileSystemEntry)
 #include <tst_qfilesystementry.moc>