Skip to content

Commit c07d340

Browse files
committed
fix(folderwatcher): ignore stale inotify descriptors
Assisted-by: Codex:GPT-5 Signed-off-by: thstyl2000 <18458458+thstyl2000@users.noreply.github.com>
1 parent 2e4fabb commit c07d340

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

src/gui/folderwatcher_linux.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,13 @@ void FolderWatcherPrivate::slotReceivedNotification(int fd)
168168
|| fileName.startsWith(".sync_")) {
169169
continue;
170170
}
171-
const QString p = _watchToPath[event->wd] + '/' + fileName;
171+
const auto watchPathIt = _watchToPath.constFind(event->wd);
172+
if (watchPathIt == _watchToPath.cend()) {
173+
qCDebug(lcFolderWatcher) << "Ignoring event for unknown watch descriptor" << event->wd << fileName;
174+
continue;
175+
}
176+
177+
const QString p = *watchPathIt + '/' + fileName;
172178
_parent->changeDetected(p);
173179

174180
if ((event->mask & (IN_MOVED_TO | IN_CREATE))

test/testinotifywatcher.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,19 @@
1010

1111
#include <QtTest>
1212

13+
#include <QSignalSpy>
14+
#include <QTemporaryDir>
15+
16+
#include <array>
17+
#include <climits>
18+
#include <sys/inotify.h>
19+
#include <unistd.h>
20+
21+
#define private public
22+
#define protected public
1323
#include "folderwatcher_linux.h"
24+
#undef protected
25+
#undef private
1426
#include "common/utility.h"
1527

1628
using namespace OCC;
@@ -64,6 +76,41 @@ private slots:
6476
QVERIFY2(ok, "findFoldersBelow failed.");
6577
}
6678

79+
void testStaleWatchDescriptorIsIgnored()
80+
{
81+
QTemporaryDir root;
82+
QVERIFY2(root.isValid(), "failed to create temp dir");
83+
QVERIFY(QDir(root.path()).mkpath("sentinel"));
84+
85+
FolderWatcher watcher;
86+
watcher.init(root.path());
87+
const auto initialWatchCount = watcher.testLinuxWatchCount();
88+
QSignalSpy pathChangedSpy(&watcher, &FolderWatcher::pathChanged);
89+
pathChangedSpy.clear();
90+
QVERIFY(!watcher._d->_watchToPath.contains(INT_MAX));
91+
92+
std::array<int, 2> pipeFds {};
93+
QVERIFY(::pipe(pipeFds.data()) == 0);
94+
95+
QByteArray payload(sizeof(inotify_event) + sizeof("lib"), '\0');
96+
auto *event = reinterpret_cast<inotify_event *>(payload.data());
97+
event->wd = INT_MAX;
98+
event->mask = IN_CREATE;
99+
event->cookie = 0;
100+
event->len = sizeof("lib");
101+
memcpy(event->name, "lib", sizeof("lib"));
102+
103+
QCOMPARE(::write(pipeFds[1], payload.constData(), payload.size()), payload.size());
104+
watcher._d->slotReceivedNotification(pipeFds[0]);
105+
106+
::close(pipeFds[0]);
107+
::close(pipeFds[1]);
108+
109+
QCOMPARE(watcher.testLinuxWatchCount(), initialWatchCount);
110+
QCOMPARE(pathChangedSpy.count(), 0);
111+
QVERIFY(!watcher._d->_watchToPath.contains(INT_MAX));
112+
}
113+
67114
void cleanupTestCase() {
68115
if( _root.startsWith(QDir::tempPath() )) {
69116
system( QStringLiteral("rm -rf %1").arg(_root).toLocal8Bit() );

0 commit comments

Comments
 (0)