diff options
author | Robert Griebl <robert.griebl@qt.io> | 2020-05-06 01:26:54 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@qt.io> | 2022-01-21 16:57:51 +0000 |
commit | c32c359b9f313f59bab57ccd3748a27b4e44a887 (patch) | |
tree | 142a337dc330e9fddfcd41ff1e03b2b802c26f8b | |
parent | 325e6b0dc6d450eeef1533d841f6690ae39b7df8 (diff) | |
download | qtapplicationmanager-c32c359b9f313f59bab57ccd3748a27b4e44a887.tar.gz |
Better mount-point management
This adds the option to watch individual mount-points for mount and
umount events from the kernel.
(this is a manual cherry-pick of 34a562c2f55d391 from dev)
Change-Id: Icba453ef4b5ecb7499b6a5df028184737f4610cc
Reviewed-by: Dominik Holland <dominik.holland@qt.io>
-rw-r--r-- | src/common-lib/common-lib.pro | 2 | ||||
-rw-r--r-- | src/common-lib/filesystemmountwatcher.cpp | 251 | ||||
-rw-r--r-- | src/common-lib/filesystemmountwatcher.h | 75 | ||||
-rw-r--r-- | src/common-lib/utilities.cpp | 54 | ||||
-rw-r--r-- | src/common-lib/utilities.h | 5 |
5 files changed, 330 insertions, 57 deletions
diff --git a/src/common-lib/common-lib.pro b/src/common-lib/common-lib.pro index ed98d0ad..f240bbef 100644 --- a/src/common-lib/common-lib.pro +++ b/src/common-lib/common-lib.pro @@ -25,6 +25,7 @@ contains(DEFINES, "AM_USE_STACKWALKER"):include($$SOURCE_DIR/3rdparty/stackwalke SOURCES += \ exception.cpp \ + filesystemmountwatcher.cpp \ utilities.cpp \ qtyaml.cpp \ startuptimer.cpp \ @@ -42,6 +43,7 @@ HEADERS += \ global.h \ error.h \ exception.h \ + filesystemmountwatcher.h \ utilities.h \ qtyaml.h \ startuptimer.h \ diff --git a/src/common-lib/filesystemmountwatcher.cpp b/src/common-lib/filesystemmountwatcher.cpp new file mode 100644 index 00000000..9b63de71 --- /dev/null +++ b/src/common-lib/filesystemmountwatcher.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 https://www.qt.io/terms-conditions. +** For further information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include <QSocketNotifier> +#include <QFileSystemWatcher> + +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +# include <unistd.h> +# include <sys/statvfs.h> +# include <sys/mount.h> +#elif defined(Q_OS_LINUX) +# include <mntent.h> +#endif +#include <qplatformdefs.h> + +#include "filesystemmountwatcher.h" + +QT_BEGIN_NAMESPACE_AM + + +class FileSystemMountWatcherPrivate +{ +public: + FileSystemMountWatcherPrivate() + { +#if defined(Q_OS_LINUX) + if (s_mountTabFile.isEmpty()) { + s_mountTabFile = "/proc/self/mounts";; + + m_procMountsFd = QT_OPEN(s_mountTabFile.constData(), O_RDONLY); + if (m_procMountsFd >= 0) { + m_procMountsNotifier = new QSocketNotifier(m_procMountsFd, QSocketNotifier::Exception); + QObject::connect(m_procMountsNotifier, &QSocketNotifier::activated, + [this]() { mountsChanged(); }); + } + } else { + m_autoTestMountTabWatcher = new QFileSystemWatcher({ QString::fromLocal8Bit(s_mountTabFile) }); + QObject::connect(m_autoTestMountTabWatcher, &QFileSystemWatcher::fileChanged, + [this]() { mountsChanged(); }); + } +#endif + } + + ~FileSystemMountWatcherPrivate() + { + delete m_autoTestMountTabWatcher; + delete m_procMountsNotifier; + if (m_procMountsFd >= 0) + QT_CLOSE(m_procMountsFd); + } + + bool add(FileSystemMountWatcher *watcher, const QString &mountPoint) + { + if ((!m_procMountsNotifier && !m_autoTestMountTabWatcher) || !watcher || mountPoint.isEmpty()) + return false; + + m_mountPoints.insert(mountPoint, watcher); + + if (m_mountPoints.size() == 1) + m_mounts = currentMountPoints(); // we need to know from where we started + return true; + } + + bool remove(FileSystemMountWatcher *watcher, const QString &mountPoint) + { + if ((!m_procMountsNotifier && !m_autoTestMountTabWatcher) || !watcher || mountPoint.isEmpty()) + return false; + + m_mountPoints.remove(mountPoint, watcher); + return true; + } + + void attach() + { + m_ref.ref(); + } + + bool detach(FileSystemMountWatcher *watcher) + { + for (auto it = m_mountPoints.begin(); it != m_mountPoints.end(); ) { + if (it.value() == watcher) + it = m_mountPoints.erase(it); + else + ++it; + } + return !m_ref.deref(); + } + + static QMultiMap<QString, QString> currentMountPoints() + { + QMultiMap<QString, QString> result; +#if defined(Q_OS_WIN) + return result; // no mounts on Windows + +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) + struct statfs *sfs = nullptr; + int count = getmntinfo(&sfs, MNT_NOWAIT); + + for (int i = 0; i < count; ++i, ++sfs) { + result.insert(QString::fromLocal8Bit(sfs->f_mntonname), QString::fromLocal8Bit(sfs->f_mntfromname)); + } +#else + FILE *pm = fopen(s_mountTabFile.constData(), "r"); + if (!pm) + return result; + +# if defined(Q_OS_ANDROID) + while (struct mntent *mntPtr = getmntent(pm)) { + result.insert(QString::fromLocal8Bit(mntPtr->mnt_dir), + QString::fromLocal8Bit(mntPtr->mnt_fsname)); + } +# else + static const int pathMax = static_cast<int>(pathconf("/", _PC_PATH_MAX)) * 2 + 1024; // quite big, but better be safe than sorry + QScopedArrayPointer<char> strBuf(new char[pathMax]); + struct mntent mntBuf; + + while (getmntent_r(pm, &mntBuf, strBuf.data(), pathMax - 1)) { + result.insert(QString::fromLocal8Bit(mntBuf.mnt_dir), + QString::fromLocal8Bit(mntBuf.mnt_fsname)); + } +# endif + fclose(pm); +#endif + + return result; + } + + void mountsChanged() + { + if (m_mountPoints.isEmpty()) + return; + + auto newMounts = currentMountPoints(); + + for (auto it = m_mountPoints.cbegin(); it != m_mountPoints.cend(); ++it) { + const QString mp = it.key(); + bool isMounted = newMounts.contains(mp); + bool wasMounted = m_mounts.contains(mp); + + if (isMounted != wasMounted) + emit it.value()->mountChanged(mp, newMounts.value(mp)); + } + + m_mounts = newMounts; + } + +private: + QAtomicInt m_ref; + + static QByteArray s_mountTabFile; + + int m_procMountsFd = -1; + QSocketNotifier *m_procMountsNotifier = nullptr; + QFileSystemWatcher *m_autoTestMountTabWatcher = nullptr; + QMultiMap<QString, FileSystemMountWatcher *> m_mountPoints; + QMultiMap<QString, QString> m_mounts; + + friend class FileSystemMountWatcher; +}; + +QByteArray FileSystemMountWatcherPrivate::s_mountTabFile; + + +FileSystemMountWatcherPrivate *FileSystemMountWatcher::d = nullptr; + +FileSystemMountWatcher::FileSystemMountWatcher(QObject *parent) + : QObject(parent) +{ + if (!d) + d = new FileSystemMountWatcherPrivate(); + d->attach(); +} + +FileSystemMountWatcher::~FileSystemMountWatcher() +{ + if (d->detach(this)) { + delete d; + d = nullptr; + } +} + +QMultiMap<QString, QString> FileSystemMountWatcher::currentMountPoints() +{ + // if we have an active cache, then use it + if (d && d->m_procMountsNotifier) + return d->m_mounts; + return FileSystemMountWatcherPrivate::currentMountPoints(); +} + +void FileSystemMountWatcher::addMountPoint(const QString &directory) +{ + d->add(this, directory); +} + +void FileSystemMountWatcher::removeMountPoint(const QString &directory) +{ + d->remove(this, directory); +} + +bool FileSystemMountWatcher::setMountTabFileForTesting(const QString &mtabFile) +{ + if (d) + return false; // too late +#if defined(Q_OS_LINUX) + FileSystemMountWatcherPrivate::s_mountTabFile = mtabFile.toLocal8Bit(); + return true; +#else + Q_UNUSED(mtabFile) + return false; +#endif +} + +QT_END_NAMESPACE_AM diff --git a/src/common-lib/filesystemmountwatcher.h b/src/common-lib/filesystemmountwatcher.h new file mode 100644 index 00000000..4062f46f --- /dev/null +++ b/src/common-lib/filesystemmountwatcher.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite 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 https://www.qt.io/terms-conditions. +** For further information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QObject> +#include <QMultiMap> +#include <QtAppManCommon/global.h> + +QT_BEGIN_NAMESPACE_AM + +class FileSystemMountWatcherPrivate; + +class FileSystemMountWatcher : public QObject +{ + Q_OBJECT + +public: + FileSystemMountWatcher(QObject *parent = nullptr); + ~FileSystemMountWatcher(); + + static QMultiMap<QString, QString> currentMountPoints(); + + void addMountPoint(const QString &directory); + void removeMountPoint(const QString &directory); + + // Auto-test API, only functional on Linux. Needs to be called before constructor: + static bool setMountTabFileForTesting(const QString &mtabFile); + +Q_SIGNALS: + void mountChanged(const QString &mountPoint, const QString &device); + +private: + static FileSystemMountWatcherPrivate *d; +}; + +QT_END_NAMESPACE_AM diff --git a/src/common-lib/utilities.cpp b/src/common-lib/utilities.cpp index eeee57c3..41e1535a 100644 --- a/src/common-lib/utilities.cpp +++ b/src/common-lib/utilities.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2019 Luxoft Sweden AB ** Copyright (C) 2018 Pelagicore AG ** Contact: https://www.qt.io/licensing/ @@ -63,19 +63,7 @@ # include <tlhelp32.h> #elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) # include <unistd.h> -# include <sys/mount.h> -# include <sys/statvfs.h> # include <sys/sysctl.h> -#else -# include <mntent.h> -# include <sys/stat.h> -# if defined(Q_OS_ANDROID) -# include <sys/vfs.h> -# define statvfs statfs -# else -# include <sys/statvfs.h> -# endif -# include <qplatformdefs.h> #endif QT_BEGIN_NAMESPACE_AM @@ -142,46 +130,6 @@ YamlFormat checkYamlFormat(const QVector<QVariant> &docs, int numberOfDocuments, return actualFormatTypeAndVersion; } -QMultiMap<QString, QString> mountedDirectories() -{ - QMultiMap<QString, QString> result; -#if defined(Q_OS_WIN) - return result; // no mounts on Windows - -#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) - struct statfs *sfs = nullptr; - int count = getmntinfo(&sfs, MNT_NOWAIT); - - for (int i = 0; i < count; ++i, ++sfs) { - result.insert(QString::fromLocal8Bit(sfs->f_mntonname), - QString::fromLocal8Bit(sfs->f_mntfromname)); - } -#else - FILE *pm = fopen("/proc/self/mounts", "r"); - if (!pm) - return result; - -# if defined(Q_OS_ANDROID) - while (struct mntent *mntPtr = getmntent(pm)) { - result.insert(QString::fromLocal8Bit(mntPtr->mnt_dir), - QString::fromLocal8Bit(mntPtr->mnt_fsname)); - } -# else - int pathMax = static_cast<int>(pathconf("/", _PC_PATH_MAX)) * 2 + 1024; // quite big, but better be safe than sorry - QScopedArrayPointer<char> strBuf(new char[pathMax]); - struct mntent mntBuf; - - while (getmntent_r(pm, &mntBuf, strBuf.data(), pathMax - 1)) { - result.insert(QString::fromLocal8Bit(mntBuf.mnt_dir), - QString::fromLocal8Bit(mntBuf.mnt_fsname)); - } -# endif - fclose(pm); -#endif - - return result; -} - bool safeRemove(const QString &path, RecursiveOperationType type) { static const QFileDevice::Permissions fullAccess = diff --git a/src/common-lib/utilities.h b/src/common-lib/utilities.h index 0d51fc6f..fa99f90f 100644 --- a/src/common-lib/utilities.h +++ b/src/common-lib/utilities.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2019 Luxoft Sweden AB ** Copyright (C) 2018 Pelagicore AG ** Contact: https://www.qt.io/licensing/ @@ -46,7 +46,6 @@ #include <QVector> #include <QPair> #include <QByteArray> -#include <QMultiMap> #include <QVariant> #include <QString> #include <QUrl> @@ -106,8 +105,6 @@ inline QString toAbsoluteFilePath(const QString &path, const QString &baseDir = */ void recursiveMergeVariantMap(QVariantMap &into, const QVariantMap &from); -QMultiMap<QString, QString> mountedDirectories(); - enum class RecursiveOperationType { EnterDirectory, |