From b3c02798a33a3eee1b32a88156861593da37d8ee Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 14 Jan 2015 12:35:28 +0100 Subject: [PATCH] Propagator: Use FILE_SHARE_DELETE on Windows. #2070 #2597 --- src/libsync/filesystem.cpp | 57 +++++++++++++++++++++++++++++++++ src/libsync/filesystem.h | 10 ++++++ src/libsync/propagateupload.cpp | 6 ++-- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 9721bef42..555c32997 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -25,6 +25,7 @@ #ifdef Q_OS_WIN #include #include +#include #endif // We use some internals of csync: @@ -149,6 +150,62 @@ bool FileSystem::renameReplace(const QString& originFileName, const QString& des return true; } +bool FileSystem::openFileSharedRead(QFile* file, QString* error) +{ + bool ok = false; + if (error) { + error->clear(); + } +#ifdef Q_OS_WIN + // + // The following code is adapted from Qt's QFSFileEnginePrivate::nativeOpen() + // by including the FILE_SHARE_DELETE share mode. + // + + // Enable full sharing. + DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + + int accessRights = GENERIC_READ; + DWORD creationDisp = OPEN_EXISTING; + + // Create the file handle. + SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; + HANDLE fileHandle = CreateFileW( + (const wchar_t*)file->fileName().utf16(), + accessRights, + shareMode, + &securityAtts, + creationDisp, + FILE_ATTRIBUTE_NORMAL, + NULL); + + // Bail out on error. + if (fileHandle == INVALID_HANDLE_VALUE) { + if (error) { + *error = qt_error_string(); + } + return false; + } + + // Convert the HANDLE to an fd and pass it to QFile's foreign-open + // function. The fd owns the handle, so when QFile later closes + // the fd the handle will be closed too. + int fd = _open_osfhandle((intptr_t)fileHandle, _O_RDONLY); + if (fd == -1) { + if (error) { + *error = "could not make fd from handle"; + } + return false; + } + ok = file->open(fd, QIODevice::ReadOnly, QFile::AutoCloseHandle); +#else + ok = file->open(QFile::ReadOnly); +#endif + if (! ok && error) { + *error = file->errorString(); + } + return ok; +} } diff --git a/src/libsync/filesystem.h b/src/libsync/filesystem.h index c7592a871..ac5bc1e72 100644 --- a/src/libsync/filesystem.h +++ b/src/libsync/filesystem.h @@ -14,6 +14,7 @@ #pragma once #include +#include #include #include @@ -49,4 +50,13 @@ bool setModTime(const QString &filename, time_t modTime); bool renameReplace(const QString &originFileName, const QString &destinationFileName, QString *errorString); +/** + * Replacement for QFile::open(ReadOnly) that sets a more permissive sharing mode + * on Windows. + * + * Warning: The resuting file may have an empty fileName and be unsuitable for use + * with QFileInfo! + */ +bool openFileSharedRead(QFile* file, QString* error); + }} diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 51b87b797..f302b7a74 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -348,9 +348,11 @@ void PropagateUploadFileQNAM::startNextChunk() QString path = _item._file; QFile file(_propagator->getFilePath(_item._file), this); - if (!file.open(QIODevice::ReadOnly)) { + QString openError; + // Warning: this clears file->fileName() and makes it unsuitable for QFileInfo! + if (!FileSystem::openFileSharedRead(&file, &openError)) { // Soft error because this is likely caused by the user modifying his files while syncing - abortWithError(SyncFileItem::SoftError, file.errorString()); + abortWithError(SyncFileItem::SoftError, openError); return; }