Skip to content

Commit c4b4c8c

Browse files
Merge pull request #152 from julianmesa-gitkraken/fix-delete-move-watched-folder
Fix delete/move watched folder
2 parents dd18a13 + af2a1cc commit c4b4c8c

File tree

11 files changed

+203
-21
lines changed

11 files changed

+203
-21
lines changed

includes/linux/InotifyTree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class InotifyTree {
3131
bool nodeExists(int wd);
3232
void removeDirectory(int wd);
3333
void renameDirectory(int fromWd, std::string fromName, int toWd, std::string toName);
34+
bool existWatchedPath();
3435

3536
~InotifyTree();
3637
private:
@@ -96,6 +97,7 @@ class InotifyTree {
9697
std::map<int, InotifyNode *> *mInotifyNodeByWatchDescriptor;
9798
std::unordered_set<ino_t> inodes;
9899
InotifyNode *mRoot;
100+
std::string mWatchedPath;
99101

100102
friend class InotifyNode;
101103
};

includes/osx/FSEventsService.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class FSEventsService {
5151
std::string mPath;
5252
RunLoop *mRunLoop;
5353
std::shared_ptr<EventQueue> mQueue;
54+
bool mRootChanged;
5455
};
5556

5657
#endif

includes/win32/Watcher.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Watcher
1818
~Watcher();
1919

2020
bool isRunning() const { return mRunning; }
21-
std::string getError() const;
21+
std::string getError();
2222

2323
private:
2424
void run();
@@ -34,11 +34,15 @@ class Watcher
3434

3535
std::string getUTF8Directory(std::wstring path) ;
3636

37+
std::wstring Watcher::getWatchedPath();
38+
void Watcher::checkWatchedPath();
39+
3740
std::atomic<bool> mRunning;
3841
SingleshotSemaphore mHasStartedSemaphore;
3942
SingleshotSemaphore mIsRunningSemaphore;
4043
mutable std::mutex mErrorMutex;
4144
std::string mError;
45+
std::wstring mWatchedPath;
4246

4347
const std::wstring mPath;
4448
std::shared_ptr<EventQueue> mQueue;

js/spec/index-spec.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,128 @@ describe('Node Sentinel File Watcher', function() {
587587
})
588588
]);
589589
});
590+
591+
it('Report error on rename watched folder', async function() {
592+
const inPath = path.join(workDir, 'test4');
593+
const renamed = path.join(workDir, 'test4_renamed');
594+
let errorOk = false;
595+
let watch = await nsfw(
596+
inPath,
597+
() => { },
598+
{
599+
errorCallback: (error) => {
600+
errorOk = error.message === 'Service shutdown: root path changed (renamed or deleted)';
601+
}
602+
}
603+
);
604+
try {
605+
await watch.start();
606+
await sleep(TIMEOUT_PER_STEP);
607+
await fse.rename(inPath, renamed);
608+
await sleep(TIMEOUT_PER_STEP);
609+
610+
assert.ok(errorOk);
611+
} finally {
612+
try {
613+
await watch.stop();
614+
} catch (err) { /* do nothing */ }
615+
watch = null;
616+
}
617+
});
618+
619+
if (process.platform !== 'win32') {
620+
it('Report error on delete watched folder', async function() {
621+
const inPath = path.join(workDir, 'test5');
622+
let errorMsg = '';
623+
let watch = await nsfw(
624+
inPath,
625+
() => { },
626+
{
627+
errorCallback: (error) => {
628+
errorMsg = error.message;
629+
}
630+
}
631+
);
632+
try {
633+
await sleep(100);
634+
await watch.start();
635+
await sleep(TIMEOUT_PER_STEP);
636+
await fse.remove(inPath);
637+
await sleep(TIMEOUT_PER_STEP);
638+
639+
assert.equal(errorMsg, 'Service shutdown: root path changed (renamed or deleted)');
640+
} finally {
641+
try {
642+
await watch.stop();
643+
} catch (err) { /* do nothing */ }
644+
watch = null;
645+
}
646+
});
647+
648+
it('Report error on rename parent of the watched folder', async function() {
649+
const parentPath = path.join(workDir, 'test4');
650+
const renamed = path.join(workDir, 'test4_renamed');
651+
const inPath = path.join(parentPath, 'test4_watched');
652+
let errorOk = false;
653+
let watch;
654+
try {
655+
await fse.mkdir(inPath);
656+
await sleep(TIMEOUT_PER_STEP);
657+
watch = await nsfw(
658+
inPath,
659+
() => { },
660+
{
661+
errorCallback: (error) => {
662+
errorOk = error.message === 'Service shutdown: root path changed (renamed or deleted)';
663+
}
664+
}
665+
);
666+
await watch.start();
667+
await sleep(TIMEOUT_PER_STEP);
668+
await fse.rename(parentPath, renamed);
669+
await sleep(TIMEOUT_PER_STEP);
670+
671+
assert.ok(errorOk);
672+
} finally {
673+
try {
674+
await watch.stop();
675+
} catch (err) { /* do nothing */ }
676+
watch = null;
677+
}
678+
});
679+
680+
it('Report error on delete parent of the watched folder', async function() {
681+
const parentPath = path.join(workDir, 'test4');
682+
const inPath = path.join(parentPath, 'test4_watched');
683+
let errorOk = false;
684+
let watch;
685+
try {
686+
await fse.mkdir(inPath);
687+
await sleep(TIMEOUT_PER_STEP);
688+
watch = await nsfw(
689+
inPath,
690+
() => { },
691+
{
692+
errorCallback: (error) => {
693+
errorOk = error.message === 'Service shutdown: root path changed (renamed or deleted)';
694+
}
695+
}
696+
);
697+
await watch.start();
698+
await sleep(TIMEOUT_PER_STEP);
699+
await fse.remove(parentPath);
700+
await sleep(TIMEOUT_PER_STEP);
701+
702+
assert.ok(errorOk);
703+
} finally {
704+
try {
705+
await watch.stop();
706+
} catch (err) { /* do nothing */ }
707+
watch = null;
708+
}
709+
});
710+
}
711+
590712
});
591713

592714
describe('Stress', function() {

src/linux/InotifyEventLoop.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ void InotifyEventLoop::work() {
108108
isDirectoryRemoval = event->mask & (uint32_t)(IN_IGNORED | IN_DELETE_SELF);
109109
isDirectoryEvent = event->mask & (uint32_t)(IN_ISDIR);
110110

111-
if (!isDirectoryRemoval && *event->name <= 31) {
112-
continue;
113-
}
114-
115111
if (event->mask & (uint32_t)(IN_ATTRIB | IN_MODIFY)) {
116112
modify();
117113
} else if (event->mask & (uint32_t)IN_CREATE) {
@@ -133,7 +129,6 @@ void InotifyEventLoop::work() {
133129

134130
renameStart();
135131
} else if (event->mask & (uint32_t)IN_MOVE_SELF) {
136-
inotifyService->remove(event->wd, event->name);
137132
inotifyService->removeDirectory(event->wd);
138133
}
139134
} while((position += sizeof(struct inotify_event) + event->len) < bytesRead);

src/linux/InotifyService.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ void InotifyService::dispatchRename(int fromWd, std::string fromName, int toWd,
5858
}
5959

6060
std::string InotifyService::getError() {
61-
if (!isWatching()) {
62-
return "Service shutdown unexpectedly";
61+
if (mTree != NULL && mTree->hasErrored()) {
62+
return mTree->getError();
6363
}
6464

65-
if (mTree->hasErrored()) {
66-
return mTree->getError();
65+
if (!isWatching()) {
66+
return "Service shutdown unexpectedly";
6767
}
6868

6969
return "";

src/linux/InotifyTree.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ InotifyTree::InotifyTree(int inotifyInstance, std::string path):
1919
watchName = path.substr(location + 1);
2020
}
2121

22+
mWatchedPath = directory + "/" + watchName;
23+
2224
struct stat file;
23-
if (stat((directory + "/" + watchName).c_str(), &file) < 0) {
25+
if (stat(mWatchedPath.c_str(), &file) < 0) {
2426
mRoot = NULL;
2527
return;
2628
}
@@ -42,6 +44,11 @@ InotifyTree::InotifyTree(int inotifyInstance, std::string path):
4244
}
4345
}
4446

47+
bool InotifyTree::existWatchedPath() {
48+
struct stat file;
49+
return stat(mWatchedPath.c_str(), &file) >= 0;
50+
}
51+
4552
void InotifyTree::addDirectory(int wd, std::string name, EmitCreatedEvent emitCreatedEvent) {
4653
auto nodeIterator = mInotifyNodeByWatchDescriptor->find(wd);
4754
if (nodeIterator == mInotifyNodeByWatchDescriptor->end()) {
@@ -71,7 +78,10 @@ bool InotifyTree::getPath(std::string &out, int wd) {
7178
}
7279

7380
bool InotifyTree::hasErrored() {
74-
return mError != "";
81+
if (mError.empty() && !existWatchedPath()) {
82+
mError = "Service shutdown: root path changed (renamed or deleted)";
83+
}
84+
return !mError.empty();
7585
}
7686

7787
bool InotifyTree::isRootAlive() {
@@ -94,6 +104,7 @@ void InotifyTree::removeDirectory(int wd) {
94104
if (parent == NULL) {
95105
delete mRoot;
96106
mRoot = NULL;
107+
setError("Service shutdown: root path changed (renamed or deleted)");
97108
return;
98109
}
99110

src/osx/FSEventsService.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <iostream>
33

44
FSEventsService::FSEventsService(std::shared_ptr<EventQueue> queue, std::string path):
5-
mPath(path), mQueue(queue) {
5+
mPath(path), mQueue(queue), mRootChanged(false) {
66
mRunLoop = new RunLoop(this, path);
77

88
if (!mRunLoop->isLooping()) {
@@ -29,7 +29,11 @@ void FSEventsServiceCallback(
2929
FSEventsService *eventsService = (FSEventsService *)clientCallBackInfo;
3030
char **paths = (char **)eventPaths;
3131
std::vector<std::string> *renamedPaths = new std::vector<std::string>;
32-
for (size_t i = 0; i < numEvents; ++i) {
32+
for (size_t i = 0; i < numEvents && !eventsService->mRootChanged; ++i) {
33+
if (eventIds[i] == 0 && (eventFlags[i] & kFSEventStreamEventFlagRootChanged) == kFSEventStreamEventFlagRootChanged) {
34+
eventsService->mRootChanged = true;
35+
break;
36+
}
3337
bool isCreated = (eventFlags[i] & kFSEventStreamEventFlagItemCreated) == kFSEventStreamEventFlagItemCreated;
3438
bool isRemoved = (eventFlags[i] & kFSEventStreamEventFlagItemRemoved) == kFSEventStreamEventFlagItemRemoved;
3539
bool isModified = (eventFlags[i] & kFSEventStreamEventFlagItemModified) == kFSEventStreamEventFlagItemModified ||
@@ -51,7 +55,9 @@ void FSEventsServiceCallback(
5155
eventsService->demangle(paths[i]);
5256
}
5357
}
54-
eventsService->rename(renamedPaths);
58+
if(!eventsService->mRootChanged) {
59+
eventsService->rename(renamedPaths);
60+
}
5561
delete renamedPaths;
5662
}
5763

@@ -82,12 +88,14 @@ void FSEventsService::dispatch(EventType action, std::string path) {
8288
}
8389

8490
std::string FSEventsService::getError() {
91+
if (mRootChanged) {
92+
return "Service shutdown: root path changed (renamed or deleted)";
93+
}
8594
return "Service shutdown unexpectedly";
8695
}
8796

8897
bool FSEventsService::hasErrored() {
89-
struct stat root;
90-
return !isWatching() || stat(mPath.c_str(), &root) < 0;
98+
return !isWatching() || mRootChanged;
9199
}
92100

93101
bool FSEventsService::isWatching() {

src/osx/RunLoop.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void RunLoop::work() {
5656
pathsToWatch,
5757
kFSEventStreamEventIdSinceNow,
5858
latency,
59-
kFSEventStreamCreateFlagFileEvents
59+
kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagWatchRoot
6060
);
6161

6262
FSEventStreamScheduleWithRunLoop(mEventStream, mRunLoop, kCFRunLoopDefaultMode);

src/win32/Controller.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "../includes/win32/Controller.h"
1+
#include "../../includes/win32/Controller.h"
22

33
static bool isNtPath(const std::wstring &path) {
44
return path.rfind(L"\\\\?\\", 0) == 0 || path.rfind(L"\\??\\", 0) == 0;

0 commit comments

Comments
 (0)