From b168c6c8248662da31dbaaf2afb8e771a9ecdc85 Mon Sep 17 00:00:00 2001
From: Thiago Macieira <thiago.macieira@intel.com>
Date: Tue, 17 Nov 2015 22:21:53 -0800
Subject: [PATCH] QStorageInfo: fix matching of mountpoints to sibling
 directories

The path "/usrfoo" starts with "/usr", so if you tried to get
QStorageInfo("/usrfoo") when "/usr" is a mount point, you'd get the
wrong filesystem.

[ChangeLog][QtCore][QStorageInfo] Fixed a bug that caused QStorageInfo
to report information for the wrong filesystem if there is a mounted
filesystem at a path that is a prefix of the requested path (e.g., it
would report "/usr" filesystem for "/usrfoo").

Task-number: QTBUG-49498
Change-Id: I3e15a26e0e424169ac2bffff1417b7a27cd0132d
Reviewed-by: Jake Petroules <jake.petroules@qt.io>
---
 src/corelib/io/qstorageinfo_unix.cpp          | 58 +++++++------
 .../io/qstorageinfo/tst_qstorageinfo.cpp      |  2 +
 tests/manual/manual.pro                       |  1 +
 tests/manual/qstorageinfo/main.cpp            | 81 +++++++++++++++++++
 tests/manual/qstorageinfo/qstorageinfo.pro    |  4 +
 5 files changed, 122 insertions(+), 24 deletions(-)
 create mode 100644 tests/manual/qstorageinfo/main.cpp
 create mode 100644 tests/manual/qstorageinfo/qstorageinfo.pro

diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp
index e365d8d7e6f..bbcc29c50b0 100644
--- a/src/corelib/io/qstorageinfo_unix.cpp
+++ b/src/corelib/io/qstorageinfo_unix.cpp
@@ -102,25 +102,6 @@
 
 QT_BEGIN_NAMESPACE
 
-static bool isPseudoFs(const QString &mountDir, const QByteArray &type)
-{
-    if (mountDir.startsWith(QLatin1String("/dev"))
-        || mountDir.startsWith(QLatin1String("/proc"))
-        || mountDir.startsWith(QLatin1String("/sys"))
-        || mountDir.startsWith(QLatin1String("/var/run"))
-        || mountDir.startsWith(QLatin1String("/var/lock"))) {
-        return true;
-    }
-    if (type == "tmpfs")
-        return true;
-#if defined(Q_OS_LINUX)
-    if (type == "rootfs" || type == "rpc_pipefs")
-        return true;
-#endif
-
-    return false;
-}
-
 class QStorageIterator
 {
 public:
@@ -158,6 +139,36 @@ private:
 #endif
 };
 
+template <typename String>
+static bool isParentOf(const String &parent, const QString &dirName)
+{
+    return dirName.startsWith(parent) &&
+            (dirName.size() == parent.size() || dirName.at(parent.size()) == QLatin1Char('/') ||
+             parent.size() == 1);
+}
+
+static bool isPseudoFs(const QStorageIterator &it)
+{
+    QString mountDir = it.rootPath();
+    if (isParentOf(QLatin1String("/dev"), mountDir)
+        || isParentOf(QLatin1String("/proc"), mountDir)
+        || isParentOf(QLatin1String("/sys"), mountDir)
+        || isParentOf(QLatin1String("/var/run"), mountDir)
+        || isParentOf(QLatin1String("/var/lock"), mountDir)) {
+        return true;
+    }
+
+    QByteArray type = it.fileSystemType();
+    if (type == "tmpfs")
+        return true;
+#if defined(Q_OS_LINUX)
+    if (type == "rootfs" || type == "rpc_pipefs")
+        return true;
+#endif
+
+    return false;
+}
+
 #if defined(Q_OS_BSD4)
 
 inline QStorageIterator::QStorageIterator()
@@ -444,10 +455,10 @@ void QStorageInfoPrivate::initRootPath()
     while (it.next()) {
         const QString mountDir = it.rootPath();
         const QByteArray fsName = it.fileSystemType();
-        if (isPseudoFs(mountDir, fsName))
+        if (isPseudoFs(it))
             continue;
         // we try to find most suitable entry
-        if (oldRootPath.startsWith(mountDir) && maxLength < mountDir.length()) {
+        if (isParentOf(mountDir, oldRootPath) && maxLength < mountDir.length()) {
             maxLength = mountDir.length();
             rootPath = mountDir;
             device = it.device();
@@ -536,11 +547,10 @@ QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
     QList<QStorageInfo> volumes;
 
     while (it.next()) {
-        const QString mountDir = it.rootPath();
-        const QByteArray fsName = it.fileSystemType();
-        if (isPseudoFs(mountDir, fsName))
+        if (isPseudoFs(it))
             continue;
 
+        const QString mountDir = it.rootPath();
         volumes.append(QStorageInfo(mountDir));
     }
 
diff --git a/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp b/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp
index efbcdc78e0b..1afcf051356 100644
--- a/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp
+++ b/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp
@@ -162,6 +162,7 @@ void tst_QStorageInfo::tempFile()
 #endif
 
     qint64 free = storage1.bytesFree();
+    QVERIFY(free != -1);
 
     file.write(QByteArray(1024*1024, '1'));
     file.flush();
@@ -185,6 +186,7 @@ void tst_QStorageInfo::caching()
     qint64 free = storage1.bytesFree();
     QStorageInfo storage2(storage1);
     QVERIFY(free == storage2.bytesFree());
+    QVERIFY(free != -1);
 
     file.write(QByteArray(1024*1024, '\0'));
     file.flush();
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index bec098d4626..8e77a321dd7 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -24,6 +24,7 @@ qnetworkaccessmanager/qget \
 qnetworkconfigurationmanager \
 qnetworkconfiguration \
 qnetworkreply \
+qstorageinfo \
 qscreen \
 qssloptions \
 qsslsocket \
diff --git a/tests/manual/qstorageinfo/main.cpp b/tests/manual/qstorageinfo/main.cpp
new file mode 100644
index 00000000000..884e5559ce4
--- /dev/null
+++ b/tests/manual/qstorageinfo/main.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Intel Corporation
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStorageInfo>
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication a(argc, argv);
+
+    QList<QStorageInfo> volumes;
+    QStringList args = a.arguments();
+    args.takeFirst();   // skip application name
+
+    foreach (const QString &path, args) {
+        QStorageInfo info(path);
+        if (!info.isValid()) {
+            // no error string...
+            fprintf(stderr, "Could not get info on %s\n", qPrintable(path));
+            return 1;
+        }
+        volumes << info;
+    }
+
+    if (volumes.isEmpty())
+        volumes = QStorageInfo::mountedVolumes();
+
+    // Sample output:
+    //  Filesystem (Type)            Size  Available BSize  Label            Mounted on
+    //  /dev/sda2 (ext4)    RO     388480     171218  1024                   /boot
+    //  /dev/mapper/system-root (btrfs) RW
+    //                          214958080   39088272  4096                   /
+    //  /dev/disk1s2 (hfs)  RW  488050672  419909696  4096  Macintosh HD2    /Volumes/Macintosh HD2
+
+    printf("Filesystem (Type)            Size  Available BSize  Label            Mounted on\n");
+    foreach (const QStorageInfo &info, volumes) {
+        const QString fsAndType = info.device() + QLatin1String(" (") +
+                                  info.fileSystemType() + QLatin1Char(')');
+
+        printf("%-19s R%c ", qPrintable(fsAndType), info.isReadOnly() ? 'O' : 'W');
+        if (fsAndType.size() > 19)
+            printf("\n%23s", "");
+
+        printf("%10llu %10llu %5u  ", info.bytesTotal() / 1024, info.bytesFree() / 1024, info.blockSize());
+        printf("%-16s %s\n", qPrintable(info.name()), qPrintable(info.rootPath()));
+    }
+
+    return 0;
+}
diff --git a/tests/manual/qstorageinfo/qstorageinfo.pro b/tests/manual/qstorageinfo/qstorageinfo.pro
new file mode 100644
index 00000000000..25acd24c806
--- /dev/null
+++ b/tests/manual/qstorageinfo/qstorageinfo.pro
@@ -0,0 +1,4 @@
+QT = core
+CONFIG += console
+CONFIG -= app_bundle
+SOURCES += main.cpp
-- 
GitLab