Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan White <support@dmapps.us>2019-10-14 16:31:23 +0300
committerJonathan White <support@dmapps.us>2019-10-21 01:56:41 +0300
commit744b4abce801440ff4e143fb2bb416b3d7a91e07 (patch)
tree6d0f520feb41501f173536dc32a1b5d7ab0d494a /src/core/FileWatcher.cpp
parent6b746913e4cd96dc6d539486c539de86a39f5d28 (diff)
Move FileWatcher into Database class
* Fix #3506 * Fix #2389 * Fix #2536 * Fix #2230 Every database that has been opened now watch's it's own file. This allows the database class to manage file changes and detect fail conditions during saving. Additionally, all stakeholders of the database can listen for the database file changed notification and respond accordingly. Performed significant cleanup of the autoreload code within DatabaseWidget. Fixed several issues with handling changes due to merging, not merging, and other scenarios while reloading. Prevent database saves to the same file if there are changes on disk that have not been merged with the open database.
Diffstat (limited to 'src/core/FileWatcher.cpp')
-rw-r--r--src/core/FileWatcher.cpp107
1 files changed, 65 insertions, 42 deletions
diff --git a/src/core/FileWatcher.cpp b/src/core/FileWatcher.cpp
index ae7878191..1b39e597d 100644
--- a/src/core/FileWatcher.cpp
+++ b/src/core/FileWatcher.cpp
@@ -19,6 +19,7 @@
#include "FileWatcher.h"
#include "core/Clock.h"
+#include <QCryptographicHash>
#include <QFileInfo>
#ifdef Q_OS_LINUX
@@ -27,36 +28,23 @@
namespace
{
- const int FileChangeDelay = 500;
- const int TimerResolution = 100;
+ const int FileChangeDelay = 200;
} // namespace
-DelayingFileWatcher::DelayingFileWatcher(QObject* parent)
+FileWatcher::FileWatcher(QObject* parent)
: QObject(parent)
, m_ignoreFileChange(false)
{
- connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged()));
- connect(&m_fileUnblockTimer, SIGNAL(timeout()), this, SLOT(observeFileChanges()));
- connect(&m_fileChangeDelayTimer, SIGNAL(timeout()), this, SIGNAL(fileChanged()));
+ connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(onWatchedFileChanged()));
+ connect(&m_fileChangeDelayTimer, SIGNAL(timeout()), SIGNAL(fileChanged()));
+ connect(&m_fileChecksumTimer, SIGNAL(timeout()), SLOT(checkFileChecksum()));
m_fileChangeDelayTimer.setSingleShot(true);
- m_fileUnblockTimer.setSingleShot(true);
+ m_fileIgnoreDelayTimer.setSingleShot(true);
}
-void DelayingFileWatcher::restart()
+void FileWatcher::start(const QString& filePath, int checksumInterval)
{
- m_fileWatcher.addPath(m_filePath);
-}
-
-void DelayingFileWatcher::stop()
-{
- m_fileWatcher.removePath(m_filePath);
-}
-
-void DelayingFileWatcher::start(const QString& filePath)
-{
- if (!m_filePath.isEmpty()) {
- m_fileWatcher.removePath(m_filePath);
- }
+ stop();
#if defined(Q_OS_LINUX)
struct statfs statfsBuf;
@@ -74,45 +62,80 @@ void DelayingFileWatcher::start(const QString& filePath)
#endif
m_fileWatcher.addPath(filePath);
+ m_filePath = filePath;
+ m_fileChecksum = calculateChecksum();
+ m_fileChecksumTimer.start(checksumInterval);
+ m_ignoreFileChange = false;
+}
- if (!filePath.isEmpty()) {
- m_filePath = filePath;
+void FileWatcher::stop()
+{
+ if (!m_filePath.isEmpty()) {
+ m_fileWatcher.removePath(m_filePath);
}
+ m_filePath.clear();
+ m_fileChecksum.clear();
+ m_fileChangeDelayTimer.stop();
}
-void DelayingFileWatcher::ignoreFileChanges()
+void FileWatcher::pause()
{
m_ignoreFileChange = true;
m_fileChangeDelayTimer.stop();
}
-void DelayingFileWatcher::observeFileChanges(bool delayed)
+void FileWatcher::resume()
{
- int timeout = 0;
- if (delayed) {
- timeout = FileChangeDelay;
- } else {
- m_ignoreFileChange = false;
- start(m_filePath);
- }
- if (timeout > 0 && !m_fileUnblockTimer.isActive()) {
- m_fileUnblockTimer.start(timeout);
+ m_ignoreFileChange = false;
+ // Add a short delay to start in the next event loop
+ if (!m_fileIgnoreDelayTimer.isActive()) {
+ m_fileIgnoreDelayTimer.start(0);
}
}
-void DelayingFileWatcher::onWatchedFileChanged()
+void FileWatcher::onWatchedFileChanged()
{
- if (m_ignoreFileChange) {
- // the client forcefully silenced us
+ // Don't notify if we are ignoring events or already started a notification chain
+ if (shouldIgnoreChanges()) {
return;
}
- if (m_fileChangeDelayTimer.isActive()) {
- // we are waiting to fire the delayed fileChanged event, so nothing
- // to do here
+
+ m_fileChecksum = calculateChecksum();
+ m_fileChangeDelayTimer.start(0);
+}
+
+bool FileWatcher::shouldIgnoreChanges()
+{
+ return m_filePath.isEmpty() || m_ignoreFileChange || m_fileIgnoreDelayTimer.isActive()
+ || m_fileChangeDelayTimer.isActive();
+}
+
+bool FileWatcher::hasSameFileChecksum()
+{
+ return calculateChecksum() == m_fileChecksum;
+}
+
+void FileWatcher::checkFileChecksum()
+{
+ if (shouldIgnoreChanges()) {
return;
}
- m_fileChangeDelayTimer.start(FileChangeDelay);
+ if (!hasSameFileChecksum()) {
+ onWatchedFileChanged();
+ }
+}
+
+QByteArray FileWatcher::calculateChecksum()
+{
+ QFile file(m_filePath);
+ if (file.open(QFile::ReadOnly)) {
+ QCryptographicHash hash(QCryptographicHash::Sha256);
+ if (hash.addData(&file)) {
+ return hash.result();
+ }
+ }
+ return {};
}
BulkFileWatcher::BulkFileWatcher(QObject* parent)
@@ -281,7 +304,7 @@ void BulkFileWatcher::observeFileChanges(bool delayed)
{
int timeout = 0;
if (delayed) {
- timeout = TimerResolution;
+ timeout = FileChangeDelay;
} else {
const QDateTime current = Clock::currentDateTimeUtc();
for (const QString& key : m_watchedFilesIgnored.keys()) {