Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 54 additions & 16 deletions src/common/filesystembase.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2018 ownCloud GmbH
Expand Down Expand Up @@ -671,32 +671,70 @@
return true;
}

bool FileSystem::isFileLocked(const QString &fileName)
namespace {

/**
* This function creates a file handle with the desired LockMode
*/
#if defined Q_OS_WIN
Utility::Handle lockFile(const QString &fileName, FileSystem::LockMode mode)
{
#ifdef Q_OS_WIN
const QString fName = FileSystem::longWinPath(fileName);
int shareMode = 0;
int accessMode = GENERIC_READ | GENERIC_WRITE;
switch (mode) {
case FileSystem::LockMode::Exclusive:
shareMode = 0;
break;
case FileSystem::LockMode::Shared:
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
break;
case FileSystem::LockMode::SharedRead:
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
accessMode = GENERIC_READ;
break;
}
// Check if file exists
const QString fName = longWinPath(fileName);
DWORD attr = GetFileAttributesW(reinterpret_cast<const wchar_t *>(fName.utf16()));
if (attr != INVALID_FILE_ATTRIBUTES) {
// Try to open the file with as much access as possible..
HANDLE win_h = CreateFileW(
reinterpret_cast<const wchar_t *>(fName.utf16()),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
nullptr);

if (win_h == INVALID_HANDLE_VALUE) {
/* could not be opened, so locked? */
/* 32 == ERROR_SHARING_VIOLATION */
auto out = Utility::Handle{CreateFileW(reinterpret_cast<const wchar_t *>(fName.utf16()), accessMode, shareMode, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr)};

if (out) {
LARGE_INTEGER start;
start.QuadPart = 0;
LARGE_INTEGER end;
end.QuadPart = -1;
if (LockFile(out.handle(), start.LowPart, start.HighPart, end.LowPart, end.HighPart)) {
return out;
} else {
return {};
}
}
return out;
}
return {};
}
#endif

}

bool FileSystem::isFileLocked(const QString &fileName, LockMode mode)
{
#ifdef Q_OS_WIN
const auto handle = lockFile(fileName, mode);
if (!handle) {
const auto error = GetLastError();
if (error == ERROR_SHARING_VIOLATION || error == ERROR_LOCK_VIOLATION) {
return true;
} else {
CloseHandle(win_h);
} else if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) {
qCWarning(lcFileSystem()) << Q_FUNC_INFO << Utility::formatWinError(error);
}
}
#else
Q_UNUSED(fileName);
Q_UNUSED(mode);
#endif
return false;
}
Expand Down
11 changes: 10 additions & 1 deletion src/common/filesystembase.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#pragma once

#include "config.h"

Check failure on line 9 in src/common/filesystembase.h

View workflow job for this annotation

GitHub Actions / build

src/common/filesystembase.h:9:10 [clang-diagnostic-error]

'config.h' file not found

#include "csync/ocsynclib.h"

Expand Down Expand Up @@ -35,6 +35,8 @@
* @brief This file contains file system helper
*/
namespace FileSystem {
OCSYNC_EXPORT Q_NAMESPACE;

enum class FolderPermissions {
ReadOnly,
ReadWrite,
Expand Down Expand Up @@ -178,10 +180,17 @@
bool OCSYNC_EXPORT setAclPermission(const QString &path, FileSystem::FolderPermissions permissions);
#endif

enum class LockMode {
Shared,
Exclusive,
SharedRead,
};
Q_ENUM_NS(LockMode);

/**
* Returns true when a file is locked. (Windows only)
*/
bool OCSYNC_EXPORT isFileLocked(const QString &fileName);
bool OCSYNC_EXPORT isFileLocked(const QString &fileName, LockMode mode);

/**
* Returns whether the file is a shortcut file (ends with .lnk)
Expand Down
45 changes: 45 additions & 0 deletions src/common/utility.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
Expand All @@ -8,7 +8,7 @@
#define UTILITY_H


#include "csync/ocsynclib.h"

Check failure on line 11 in src/common/utility.h

View workflow job for this annotation

GitHub Actions / build

src/common/utility.h:11:10 [clang-diagnostic-error]

'csync/ocsynclib.h' file not found
#include <QString>
#include <QByteArray>
#include <QDateTime>
Expand Down Expand Up @@ -49,6 +49,51 @@
*/
OCSYNC_EXPORT QVector<ProcessInfosForOpenFile> queryProcessInfosKeepingFileOpen(const QString &filePath);

#ifdef Q_OS_WIN
class OCSYNC_EXPORT Handle
{
public:
/**
* A RAAI for Windows Handles
*/
Handle() = default;
explicit Handle(HANDLE h);
explicit Handle(HANDLE h, std::function<void(HANDLE)> &&close);

Handle(const Handle &) = delete;
Handle &operator=(const Handle &) = delete;

Handle(Handle &&other)
{
std::swap(_handle, other._handle);
std::swap(_close, other._close);
}

Handle &operator=(Handle &&other)
{
if (this != &other) {
std::swap(_handle, other._handle);
std::swap(_close, other._close);
}
return *this;
}

~Handle();

HANDLE &handle() { return _handle; }

void close();

explicit operator bool() const { return _handle != INVALID_HANDLE_VALUE; }

operator HANDLE() const { return _handle; }

private:
HANDLE _handle = INVALID_HANDLE_VALUE;
std::function<void(HANDLE)> _close;
};
#endif

OCSYNC_EXPORT int rand();
OCSYNC_EXPORT void sleep(int sec);
OCSYNC_EXPORT void usleep(int usec);
Expand Down
25 changes: 25 additions & 0 deletions src/common/utility_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,4 +609,29 @@ void Utility::LocalFreeDeleter::operator()(void *p) const
::LocalFree(reinterpret_cast<HLOCAL>(p));
}

Utility::Handle::Handle(HANDLE h, std::function<void(HANDLE)> &&close)
: _handle(h)
, _close(std::move(close))
{
}

Utility::Handle::Handle(HANDLE h)
: _handle(h)
, _close(&CloseHandle)
{
}

Utility::Handle::~Handle()
{
close();
}

void Utility::Handle::close()
{
if (_handle != INVALID_HANDLE_VALUE) {
_close(_handle);
_handle = INVALID_HANDLE_VALUE;
}
}

} // namespace OCC
2 changes: 1 addition & 1 deletion src/gui/lockwatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void LockWatcher::checkFiles()
QSet<QString> unlocked;

for (const auto &path : std::as_const(_watchedPaths)) {
if (!FileSystem::isFileLocked(path)) {
if (!FileSystem::isFileLocked(path, FileSystem::LockMode::SharedRead)) {
qCInfo(lcLockWatcher) << "Lock of" << path << "was released";
emit fileUnlocked(path);
unlocked.insert(path);
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/bulkpropagatorjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ void BulkPropagatorJob::triggerUpload()

// If the file is currently locked, we want to retry the sync
// when it becomes available again.
if (FileSystem::isFileLocked(singleFile._localPath)) {
if (FileSystem::isFileLocked(singleFile._localPath, FileSystem::LockMode::SharedRead)) {
emit propagator()->seenLockedFile(singleFile._localPath);
}

Expand Down
4 changes: 2 additions & 2 deletions src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ bool OwncloudPropagator::createConflict(const SyncFileItemPtr &item,

// If the file is locked, we want to retry this sync when it
// becomes available again.
if (FileSystem::isFileLocked(fn)) {
if (FileSystem::isFileLocked(fn, FileSystem::LockMode::SharedRead)) {
emit seenLockedFile(fn);
}

Expand Down Expand Up @@ -1055,7 +1055,7 @@ OCC::Optional<QString> OwncloudPropagator::createCaseClashConflict(const SyncFil

// If the file is locked, we want to retry this sync when it
// becomes available again.
if (FileSystem::isFileLocked(filename)) {
if (FileSystem::isFileLocked(filename, FileSystem::LockMode::SharedRead)) {
emit seenLockedFile(filename);
}

Expand Down
2 changes: 1 addition & 1 deletion src/libsync/propagatedownload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "config.h"

Check failure on line 7 in src/libsync/propagatedownload.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/propagatedownload.cpp:7:10 [clang-diagnostic-error]

'config.h' file not found
#include "libsync/creds/abstractcredentials.h"
#include "owncloudpropagator_p.h"
#include "propagatedownload.h"
Expand Down Expand Up @@ -1194,7 +1194,7 @@
}
}

void PropagateDownloadFile::downloadFinished()

Check warning on line 1197 in src/libsync/propagatedownload.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/propagatedownload.cpp:1197:29 [readability-function-cognitive-complexity]

function 'downloadFinished' has cognitive complexity of 54 (threshold 25)
{
ASSERT(!_tmpFile.isOpen());
const auto filename = propagator()->fullLocalPath(_item->_file);
Expand Down Expand Up @@ -1329,7 +1329,7 @@
qCWarning(lcPropagateDownload) << QStringLiteral("Rename failed: %1 => %2").arg(_tmpFile.fileName()).arg(filename);
// If the file is locked, we want to retry this sync when it
// becomes available again, otherwise try again directly
if (FileSystem::isFileLocked(filename)) {
if (FileSystem::isFileLocked(filename, FileSystem::LockMode::SharedRead)) {
emit propagator()->seenLockedFile(filename);
} else {
propagator()->_anotherSyncNeeded = true;
Expand Down
2 changes: 2 additions & 0 deletions src/libsync/propagateupload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "config.h"

Check failure on line 7 in src/libsync/propagateupload.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/propagateupload.cpp:7:10 [clang-diagnostic-error]

'config.h' file not found
#include "propagateupload.h"
#include "propagateuploadencrypted.h"
#include "owncloudpropagator_p.h"
Expand Down Expand Up @@ -36,6 +36,7 @@
namespace OCC {

Q_LOGGING_CATEGORY(lcPutJob, "nextcloud.sync.networkjob.put", QtInfoMsg)
Q_LOGGING_CATEGORY(lcUploadDevice, "nextcloud.sync.uploaddevice", QtInfoMsg)
Q_LOGGING_CATEGORY(lcPollJob, "nextcloud.sync.networkjob.poll", QtInfoMsg)
Q_LOGGING_CATEGORY(lcPropagateUpload, "nextcloud.sync.propagator.upload", QtInfoMsg)
Q_LOGGING_CATEGORY(lcPropagateUploadV1, "nextcloud.sync.propagator.upload.v1", QtInfoMsg)
Expand Down Expand Up @@ -549,6 +550,7 @@
setErrorString({});
return c;
} else if (c < 0) {
qCWarning(lcUploadDevice) << _file.errorString() << c;
setErrorString(_file.errorString());
return -1;
}
Expand Down
4 changes: 2 additions & 2 deletions src/libsync/propagateuploadng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "config.h"

Check failure on line 7 in src/libsync/propagateuploadng.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/propagateuploadng.cpp:7:10 [clang-diagnostic-error]

'config.h' file not found
#include "propagateupload.h"
#include "owncloudpropagator_p.h"
#include "networkjobs.h"
Expand Down Expand Up @@ -352,12 +352,12 @@

const auto fileName = _fileToUpload._path;
auto device = std::make_unique<UploadDevice>(fileName, _sent, _currentChunkSize, &propagator()->_bandwidthManager);
if (!device->open(QIODevice::ReadOnly)) {
if (auto isLocked = FileSystem::isFileLocked(fileName, FileSystem::LockMode::SharedRead); isLocked || !device->open(QIODevice::ReadOnly)) {
qCWarning(lcPropagateUploadNG) << "Could not prepare upload device: " << device->errorString();

// If the file is currently locked, we want to retry the sync
// when it becomes available again.
if (FileSystem::isFileLocked(fileName)) {
if (isLocked) {
emit propagator()->seenLockedFile(fileName);
}
// Soft error because this is likely caused by the user modifying his files while syncing
Expand Down
4 changes: 2 additions & 2 deletions src/libsync/propagateuploadv1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "config.h"

Check failure on line 7 in src/libsync/propagateuploadv1.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/propagateuploadv1.cpp:7:10 [clang-diagnostic-error]

'config.h' file not found
#include "propagateupload.h"
#include "owncloudpropagator_p.h"
#include "networkjobs.h"
Expand Down Expand Up @@ -74,7 +74,7 @@
startNextChunk();
}

void PropagateUploadFileV1::startNextChunk()

Check warning on line 77 in src/libsync/propagateuploadv1.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/propagateuploadv1.cpp:77:29 [readability-function-cognitive-complexity]

function 'startNextChunk' has cognitive complexity of 27 (threshold 25)
{
if (propagator()->_abortRequested)
return;
Expand Down Expand Up @@ -134,12 +134,12 @@
const QString fileName = _fileToUpload._path;
auto device = std::make_unique<UploadDevice>(
fileName, chunkStart, currentChunkSize, &propagator()->_bandwidthManager);
if (!device->open(QIODevice::ReadOnly)) {
if (auto isLocked = FileSystem::isFileLocked(fileName, FileSystem::LockMode::SharedRead); isLocked || !device->open(QIODevice::ReadOnly)) {
qCWarning(lcPropagateUploadV1) << "Could not prepare upload device: " << device->errorString();

// If the file is currently locked, we want to retry the sync
// when it becomes available again.
if (FileSystem::isFileLocked(fileName)) {
if (isLocked) {
emit propagator()->seenLockedFile(fileName);
}
// Soft error because this is likely caused by the user modifying his files while syncing
Expand Down
6 changes: 3 additions & 3 deletions test/testlockedfiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* any purpose.
*/

#include <QtTest>

Check failure on line 11 in test/testlockedfiles.cpp

View workflow job for this annotation

GitHub Actions / build

test/testlockedfiles.cpp:11:10 [clang-diagnostic-error]

'QtTest' file not found
#include "syncenginetestutils.h"
#include "lockwatcher.h"
#include <syncengine.h>
Expand Down Expand Up @@ -72,7 +72,7 @@
}
QVERIFY(QFile::exists(tmpFile));

QVERIFY(!FileSystem::isFileLocked(tmpFile));
QVERIFY(!FileSystem::isFileLocked(tmpFile, FileSystem::LockMode::SharedRead));
watcher.addFile(tmpFile);
QVERIFY(watcher.contains(tmpFile));

Expand All @@ -86,7 +86,7 @@

#ifdef Q_OS_WIN
auto h = makeHandle(tmpFile, 0);
QVERIFY(FileSystem::isFileLocked(tmpFile));
QVERIFY(FileSystem::isFileLocked(tmpFile, FileSystem::LockMode::SharedRead));
watcher.addFile(tmpFile);

count = 0;
Expand All @@ -99,7 +99,7 @@
QVERIFY(watcher.contains(tmpFile));

CloseHandle(h);
QVERIFY(!FileSystem::isFileLocked(tmpFile));
QVERIFY(!FileSystem::isFileLocked(tmpFile, FileSystem::LockMode::SharedRead));

QThread::msleep(120);
qApp->processEvents();
Expand Down
Loading