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

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Kamm <kamm@incasoftware.de>2014-11-06 02:36:04 +0300
committerChristian Kamm <kamm@incasoftware.de>2014-11-20 14:36:17 +0300
commitd4e0941c2732508b2d944f19115c2c76c7eb593b (patch)
tree9277dda39773485e72ee9c6a2808d6395e722bc8 /src
parent9dc57359b9e74ed6d8f74d17a72e00c73a67ce49 (diff)
Windows filewatcher: switch to ReadDirectoryChangesW.
Based on danimo's #2454 fix for #2455 and related to #2297.
Diffstat (limited to 'src')
-rw-r--r--src/mirall/folderwatcher_win.cpp132
-rw-r--r--src/mirall/folderwatcher_win.h2
2 files changed, 104 insertions, 30 deletions
diff --git a/src/mirall/folderwatcher_win.cpp b/src/mirall/folderwatcher_win.cpp
index c6c8242fa..ced1c2d23 100644
--- a/src/mirall/folderwatcher_win.cpp
+++ b/src/mirall/folderwatcher_win.cpp
@@ -13,6 +13,7 @@
#include <QThread>
#include <QDebug>
+#include <QDir>
#include "mirall/folderwatcher.h"
#include "mirall/folderwatcher_win.h"
@@ -23,52 +24,123 @@
namespace Mirall {
-void WatcherThread::run()
+void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
+ bool* increaseBufferSize)
{
- _handle = FindFirstChangeNotification((wchar_t*)_path.utf16(),
- true, // recursive watch
- FILE_NOTIFY_CHANGE_FILE_NAME |
- FILE_NOTIFY_CHANGE_DIR_NAME |
- FILE_NOTIFY_CHANGE_LAST_WRITE);
+ *increaseBufferSize = false;
+
+ _handle = CreateFileW(
+ (wchar_t*)_path.utf16(),
+ FILE_LIST_DIRECTORY,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL
+ );
if (_handle == INVALID_HANDLE_VALUE)
{
- qDebug() << Q_FUNC_INFO << "FindFirstChangeNotification function failed, stopping watcher!";
- FindCloseChangeNotification(_handle);
+ DWORD errorCode = GetLastError();
+ qDebug() << Q_FUNC_INFO << "Failed to create handle for" << _path << ", error:" << errorCode;
_handle = 0;
return;
}
- if (_handle == NULL)
- {
- qDebug() << Q_FUNC_INFO << "FindFirstChangeNotification returned null, stopping watcher!";
- FindCloseChangeNotification(_handle);
- _handle = 0;
- return;
- }
+ // QVarLengthArray ensures the stack-buffer is aligned like double and qint64.
+ QVarLengthArray<char, 4096*10> fileNotifyBuffer;
+ fileNotifyBuffer.resize(fileNotifyBufferSize);
+
+ const size_t fileNameBufferSize = 4096;
+ TCHAR fileNameBuffer[fileNameBufferSize];
+
+ forever {
+ FILE_NOTIFY_INFORMATION *pFileNotifyBuffer =
+ (FILE_NOTIFY_INFORMATION*)fileNotifyBuffer.data();
+ DWORD dwBytesReturned = 0;
+ SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize);
+ if(ReadDirectoryChangesW( _handle, (LPVOID)pFileNotifyBuffer,
+ fileNotifyBufferSize, true,
+ FILE_NOTIFY_CHANGE_FILE_NAME |
+ FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_LAST_WRITE,
+ &dwBytesReturned, NULL, NULL))
+ {
+ FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer;
+ forever {
+ size_t len = curEntry->FileNameLength / 2;
+ QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, len);
+
+ // Unless the file was removed or renamed, get its full long name
+ // TODO: We could still try expanding the path in the tricky cases...
+ QString longfile = file;
+ if (curEntry->Action != FILE_ACTION_REMOVED
+ && curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) {
+ size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize);
+ if (longNameSize > 0) {
+ longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), longNameSize);
+ } else {
+ qDebug() << Q_FUNC_INFO << "Error converting file name to full length, keeping original name.";
+ }
+ }
+ longfile = QDir::cleanPath(longfile);
+
+ qDebug() << Q_FUNC_INFO << "Found change in" << longfile << "action:" << curEntry->Action;
+ emit changed(longfile);
- while(true) {
- switch(WaitForSingleObject(_handle, /*wait*/ INFINITE)) {
- case WAIT_OBJECT_0:
- if (FindNextChangeNotification(_handle) == false) {
- qDebug() << Q_FUNC_INFO << "FindFirstChangeNotification returned FALSE, stopping watcher!";
- FindCloseChangeNotification(_handle);
- _handle = 0;
- return;
+ if (curEntry->NextEntryOffset == 0) {
+ break;
+ }
+ curEntry = (FILE_NOTIFY_INFORMATION*)(
+ (char*)curEntry + curEntry->NextEntryOffset);
}
- // qDebug() << Q_FUNC_INFO << "Change detected in" << _path << "from" << QThread::currentThread ();
- emit changed(_path);
- break;
- default:
- qDebug() << Q_FUNC_INFO << "Error while watching";
+ } else {
+ DWORD errorCode = GetLastError();
+ switch(errorCode) {
+ case ERROR_NOTIFY_ENUM_DIR:
+ qDebug() << Q_FUNC_INFO << "The buffer for changes overflowed! Triggering a generic change and resizing";
+ emit changed(_path);
+ *increaseBufferSize = true;
+ break;
+ default:
+ qDebug() << Q_FUNC_INFO << "General error" << errorCode << "while watching. Exiting.";
+ break;
+ }
+ CloseHandle(_handle);
+ _handle = NULL;
+ return;
+ }
+ }
+}
+
+void WatcherThread::run()
+{
+ // If this buffer fills up before we've extracted its data we will lose
+ // change information. Therefore start big.
+ size_t bufferSize = 4096*10;
+ size_t maxBuffer = 64*1024;
+
+ forever {
+ bool increaseBufferSize = false;
+ watchChanges(bufferSize, &increaseBufferSize);
+
+ if (increaseBufferSize) {
+ bufferSize = qMin(bufferSize*2, maxBuffer);
+ } else {
+ // Other errors shouldn't actually happen,
+ // so sleep a bit to avoid running into the same error case in a
+ // tight loop.
+ sleep(2);
}
}
}
WatcherThread::~WatcherThread()
{
- if (_handle)
- FindCloseChangeNotification(_handle);
+ if (_handle) {
+ CloseHandle(_handle);
+ _handle = NULL;
+ }
}
FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString& path)
diff --git a/src/mirall/folderwatcher_win.h b/src/mirall/folderwatcher_win.h
index 56ddfccd0..465477fd9 100644
--- a/src/mirall/folderwatcher_win.h
+++ b/src/mirall/folderwatcher_win.h
@@ -33,6 +33,8 @@ public:
protected:
void run();
+ void watchChanges(size_t fileNotifyBufferSize,
+ bool* increaseBufferSize);
signals:
void changed(const QString &path);