Skip to content

Commit e9fff81

Browse files
authored
Merge pull request #10500 from nextcloud/bugfix/10464/isFileLocked
fix(discovery): move file lock state off the GUI thread.
2 parents e5272e2 + c163e46 commit e9fff81

4 files changed

Lines changed: 51 additions & 1 deletion

File tree

src/libsync/discovery.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, const Entries &ent
353353
}
354354
}
355355

356-
if (excluded == CSYNC_NOT_EXCLUDED && OCC::FileSystem::isFileLocked(_discoveryData->_localDir + path, OCC::FileSystem::LockMode::SharedRead) &&
356+
// Lock state was accessed off the GUI thread during discovery, so this read cannot block
357+
if (excluded == CSYNC_NOT_EXCLUDED && entries.localEntry.isLocked &&
357358
(!entries.dbEntry.isValid() || !entries.serverEntry.isValid() || entries.serverEntry.etag == entries.dbEntry._etag)) {
358359
qCInfo(lcDisco) << _discoveryData->_localDir + path << "is locked" << "exluding it from sync";
359360
excluded = CSYNC_FILE_LOCKED_SILENTLY_EXCLUDED;

src/libsync/discoveryphase.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "common/asserts.h"
1919
#include "common/checksums.h"
20+
#include "common/filesystembase.h"
2021

2122
#include <csync_exclude.h>
2223
#include "vio/csync_vio_local.h"
@@ -403,6 +404,14 @@ void DiscoverySingleLocalDirectoryJob::run() {
403404
i.isMetadataMissing = dirent->is_metadata_missing;
404405
i.isPermissionsInvalid = dirent->isPermissionsInvalid;
405406
i.type = dirent->type;
407+
408+
// Access lock state on the worker thread so a blocking open cannot freeze the GUI #10464
409+
if (!i.isSymLink && !i.isVirtualFile && !i.isDirectory) {
410+
const auto absoluteLocalPath = localPath + QLatin1Char('/') + i.name;
411+
i.isLocked = FileSystem::isFileLocked(absoluteLocalPath, FileSystem::LockMode::SharedRead);
412+
qCDebug(lcDiscovery) << "File" << absoluteLocalPath << "isLocked" << i.isLocked;
413+
}
414+
406415
results.push_back(i);
407416
}
408417
if (errno != 0) {

src/libsync/discoveryphase.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct LocalInfo
6464
bool isSymLink = false;
6565
bool isMetadataMissing = false;
6666
bool isPermissionsInvalid = false;
67+
bool isLocked = false;
6768
[[nodiscard]] bool isValid() const { return !name.isNull(); }
6869
};
6970

test/testlockedfiles.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "lockwatcher.h"
1515
#include <syncengine.h>
1616
#include <localdiscoverytracker.h>
17+
#include "discoveryphase.h"
18+
#include <QThreadPool>
1719

1820
using namespace OCC;
1921

@@ -130,6 +132,43 @@ private slots:
130132
}
131133

132134
#ifdef Q_OS_WIN
135+
void testLockDetectionUsesRealFileSystemCheck()
136+
{
137+
// Regression guard for #10464: exercise the real FileSystem::isFileLocked path
138+
QTemporaryDir tmp;
139+
QVERIFY(tmp.isValid());
140+
for (const auto &name : { QStringLiteral("locked.bin"), QStringLiteral("test.txt") }) {
141+
QFile tmpFile(tmp.filePath(name));
142+
QVERIFY(tmpFile.open(QIODevice::WriteOnly));
143+
tmpFile.write("x");
144+
}
145+
QVERIFY(QDir(tmp.path()).mkdir(QStringLiteral("subdir")));
146+
147+
auto handle = makeHandle(tmp.filePath(QStringLiteral("locked.bin")), 0);
148+
QVERIFY(handle != INVALID_HANDLE_VALUE);
149+
150+
const auto job = new DiscoverySingleLocalDirectoryJob({}, tmp.path(), nullptr, false);
151+
QSignalSpy finishedSpy(job, &DiscoverySingleLocalDirectoryJob::finished);
152+
QThreadPool::globalInstance()->start(job);
153+
QTRY_COMPARE_WITH_TIMEOUT(finishedSpy.count(), 1, 5000);
154+
155+
CloseHandle(handle);
156+
157+
const auto results = finishedSpy.takeFirst().at(0).value<QVector<OCC::LocalInfo>>();
158+
QCOMPARE(results.size(), 3);
159+
for (const auto &info : results) {
160+
if (info.name == QStringLiteral("locked.bin")) {
161+
QVERIFY(info.isLocked);
162+
continue;
163+
}
164+
165+
QVERIFY(!info.isLocked);
166+
if (info.name == QStringLiteral("subdir")) {
167+
QVERIFY(info.isDirectory);
168+
}
169+
}
170+
}
171+
133172
void testDirectoryLockChecks()
134173
{
135174
QTemporaryDir tmp;

0 commit comments

Comments
 (0)