nextcloud-desktop/src/gui/folderwatcher_win.cpp

174 lines
6 KiB
C++
Raw Normal View History

2012-12-04 21:15:37 +04:00
/*
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
2014-01-13 19:16:19 +04:00
* the Free Software Foundation; version 2 of the License.
2012-12-04 21:15:37 +04:00
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QThread>
#include <QDebug>
#include <QDir>
#include "filesystem.h"
#include "folderwatcher.h"
#include "folderwatcher_win.h"
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
2012-12-04 21:15:37 +04:00
2014-11-10 00:34:07 +03:00
namespace OCC {
2012-12-04 21:15:37 +04:00
void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
bool* increaseBufferSize)
{
*increaseBufferSize = false;
QString longPath = FileSystem::longWinPath(_path);
_handle = CreateFileW(
(wchar_t*) longPath.utf16(),
FILE_LIST_DIRECTORY,
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
2012-12-04 21:15:37 +04:00
2012-12-06 19:26:27 +04:00
if (_handle == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
qDebug() << Q_FUNC_INFO << "Failed to create handle for" << _path << ", error:" << errorCode;
2012-12-06 19:26:27 +04:00
_handle = 0;
return;
}
2012-12-04 21:15:37 +04:00
// 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);
// Skip modifications of folders: One of these is triggered for changes
// and new files in a folder, probably because of the folder's mtime
// changing. We don't need them.
bool skip = curEntry->Action == FILE_ACTION_MODIFIED
&& QFileInfo(longfile).isDir();
if (!skip) {
//qDebug() << Q_FUNC_INFO << "Found change in" << longfile
// << "action:" << curEntry->Action;
emit changed(longfile);
}
if (curEntry->NextEntryOffset == 0) {
break;
}
curEntry = (FILE_NOTIFY_INFORMATION*)(
(char*)curEntry + curEntry->NextEntryOffset);
2012-12-06 19:26:27 +04:00
}
} 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);
}
}
2012-12-04 21:15:37 +04:00
}
2012-12-06 19:26:27 +04:00
WatcherThread::~WatcherThread()
{
if (_handle) {
CloseHandle(_handle);
_handle = NULL;
}
2012-12-06 19:26:27 +04:00
}
FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString& path)
: _parent(p)
{
_thread = new WatcherThread(path);
2012-12-06 19:26:27 +04:00
connect(_thread, SIGNAL(changed(const QString&)),
_parent,SLOT(changeDetected(const QString&)));
_thread->start();
}
FolderWatcherPrivate::~FolderWatcherPrivate()
{
2012-12-06 19:26:27 +04:00
_thread->terminate();
_thread->wait();
delete _thread;
}
2014-11-10 00:34:07 +03:00
} // namespace OCC