Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
gloomman committed Jun 15, 2024
1 parent 04dc87e commit d027570
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ stress-test
crash.log
test/snapshot.txt
npm
test/fanotify/
test/ll.1.mv.up
34 changes: 28 additions & 6 deletions src/Event.hh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ using namespace Napi;

struct Event {
std::string path;
std::string pathTo;
std::string pathFrom;
bool isCreated;
bool isDeleted;
bool isMoved;
Expand All @@ -29,16 +31,24 @@ struct Event {
EscapableHandleScope scope(env);
Object res = Object::New(env);
res.Set(String::New(env, "path"), String::New(env, path.c_str()));
res.Set(String::New(env, "type"), String::New(env, (isCreated ? "create" : isDeleted ? "delete" : isMoved ? "move" : "update")));
res.Set(String::New(env, "pathFrom"), String::New(env, pathFrom.c_str()));
res.Set(String::New(env, "pathTo"), String::New(env, pathTo.c_str()));
res.Set(String::New(env, "type"), String::New(env, (isMoved ? "move" : isCreated ? "create" : isDeleted ? "delete" : "update")));
return scope.Escape(res);
}
};

class EventList {
public:
void create(const std::string &path) {
void create(const std::string &path, bool isMoved = false) {
std::lock_guard<std::mutex> l(mMutex);
Event *event = internalUpdate(path);
event->isMoved = isMoved;

if (isMoved) {
event->pathTo = path;
}

if (event->isDeleted) {
// Assume update event when rapidly removed and created
// https://github.com/parcel-bundler/watcher/issues/72
Expand All @@ -54,16 +64,28 @@ public:
return internalUpdate(path);
}

void move(const std::string &path) {
void move(const std::string &path, const std::string &pathTo) {
std::lock_guard<std::mutex> l(mMutex);
Event *event = internalUpdate(path);
event->isMoved = true;
Event *eventFrom = internalUpdate(path);
eventFrom->pathTo = pathTo;
eventFrom->pathFrom = path;
eventFrom->isMoved = true;

Event *eventTo = internalUpdate(pathTo);
eventTo->pathTo = pathTo;
eventTo->pathFrom = path;
eventTo->isMoved = true;
}

void remove(const std::string &path) {
void remove(const std::string &path, bool isMoved = false) {
std::lock_guard<std::mutex> l(mMutex);
Event *event = internalUpdate(path);
event->isDeleted = true;
event->isMoved = isMoved;

if (isMoved) {
event->pathFrom = path;
}
}

size_t size() {
Expand Down
73 changes: 60 additions & 13 deletions src/linux/FAnotifyCrawlBackend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
#include "FAnotifyCrawlBackend.hh"

#ifdef FAN_REPORT_DIR_FID
#define FAN_EVENT_INFO_TYPE FAN_EVENT_INFO_TYPE_DFID_NAME
#define FANOTIFY_EVENT_INFO_TYPE_FID FAN_EVENT_INFO_TYPE_DFID_NAME
#define FANOTIFY_EVENT_INFO_TYPE_FID_OLD FAN_EVENT_INFO_TYPE_OLD_DFID_NAME
#else
#define FAN_EVENT_INFO_TYPE FAN_EVENT_INFO_TYPE_FID
#define FANOTIFY_EVENT_INFO_TYPE_FID FAN_EVENT_INFO_TYPE_FID
#define FANOTIFY_EVENT_INFO_TYPE_FID_OLD FAN_EVENT_INFO_TYPE_FID
#endif

#define BUFFER_SIZE 8192
Expand Down Expand Up @@ -148,17 +150,37 @@ void FAnotifyCrawlBackend::handleEvents() {
auto eventLen = event->event_len;
unsigned processedLen = event->metadata_len;

fanotify_event_info_fid *fid = nullptr;
fanotify_event_info_fid *fidTo = nullptr;

while (eventLen > processedLen) {
auto header = (fanotify_event_info_header *)((char *)event + processedLen);

if (FAN_EVENT_INFO_TYPE == header->info_type) {
auto fid = (fanotify_event_info_fid *)header;
handleEvent(event, fid, watchers);
if (FANOTIFY_EVENT_INFO_TYPE_FID == header->info_type || FANOTIFY_EVENT_INFO_TYPE_FID_OLD == header->info_type) {
fid = (fanotify_event_info_fid *)header;

if (FANOTIFY_EVENT_INFO_TYPE_FID == header->info_type) {
break;
}
}

#ifdef FAN_EVENT_INFO_TYPE_OLD_DFID_NAME
if (FAN_EVENT_INFO_TYPE_NEW_DFID_NAME == header->info_type) {
fidTo = (fanotify_event_info_fid *)header;

if (fid != nullptr) {
break;
}
}
#endif

processedLen += header->len;
}

if (fid != nullptr) {
handleEvent(event, fid, watchers, fidTo);
}

event = FAN_EVENT_NEXT(event, n);
}
}
Expand All @@ -168,8 +190,11 @@ void FAnotifyCrawlBackend::handleEvents() {
}
}

void FAnotifyCrawlBackend::handleEvent(
fanotify_event_metadata *metadata, fanotify_event_info_fid *fid, std::unordered_set<WatcherRef> &watchers) {
void FAnotifyCrawlBackend::handleEvent(fanotify_event_metadata *metadata,
fanotify_event_info_fid *fid,
std::unordered_set<WatcherRef> &watchers,
fanotify_event_info_fid *fidTo) {

std::unique_lock<std::mutex> lock(mMutex);

// Find the subscriptions for this watch descriptor
Expand All @@ -179,25 +204,42 @@ void FAnotifyCrawlBackend::handleEvent(
set.insert(it->second);
}

std::string mountPathTo;

if (fidTo != nullptr) {
auto sTo = mSubscriptions.find(toString((fsid_t *)&fidTo->fsid));
mountPathTo.assign(sTo->second->path);
}

for (auto &s : set) {
if (handleSubscription(metadata, fid, s)) {
if (handleSubscription(metadata, fid, s, fidTo, mountPathTo)) {
watchers.insert(s->watcher);
}
}
}

bool FAnotifyCrawlBackend::handleSubscription(
fanotify_event_metadata *metadata, fanotify_event_info_fid *fid, std::shared_ptr<FAnotifySubscription> sub) {
bool FAnotifyCrawlBackend::handleSubscription(fanotify_event_metadata *metadata,
fanotify_event_info_fid *fid,
std::shared_ptr<FAnotifySubscription> sub,
fanotify_event_info_fid *fidTo,
const std::string &mountPathTo) {

// Build full path and check if its in our ignore list.
auto watcher = sub->watcher;
std::string path(sub->path);
std::string pathTo;
bool isDir = (metadata->mask & FAN_ONDIR) == FAN_ONDIR;

#ifdef FAN_EVENT_INFO_TYPE_DFID_NAME
if (FAN_EVENT_INFO_TYPE_DFID_NAME == fid->hdr.info_type) {
if (FAN_EVENT_INFO_TYPE_DFID_NAME == fid->hdr.info_type || FAN_EVENT_INFO_TYPE_OLD_DFID_NAME == fid->hdr.info_type) {
auto handle = (file_handle *)fid->handle;
path.append("/").append((char *)handle + sizeof(file_handle) + handle->handle_bytes);
}

if (fidTo != nullptr) {
auto handle = (file_handle *)fidTo->handle;
pathTo.assign(mountPathTo).append("/").append((char *)handle + sizeof(file_handle) + handle->handle_bytes);
}
#endif

if (watcher->isIgnored(path)) {
Expand All @@ -207,7 +249,7 @@ bool FAnotifyCrawlBackend::handleSubscription(
// If this is a create, check if it's a directory and start watching if it is.
// In any case, keep the directory tree up to date.
if (metadata->mask & (FAN_CREATE | FAN_MOVED_TO)) {
watcher->mEvents.create(path);
watcher->mEvents.create(path, (metadata->mask & FAN_MOVED_TO) == FAN_MOVED_TO);

struct stat st;
// Use lstat to avoid resolving symbolic links that we cannot watch anyway
Expand Down Expand Up @@ -249,9 +291,12 @@ bool FAnotifyCrawlBackend::handleSubscription(
}
}

watcher->mEvents.remove(path);
watcher->mEvents.remove(path, (metadata->mask & FAN_MOVED_FROM) == FAN_MOVED_FROM);
sub->tree->remove(path);
}
else if (metadata->mask & (FAN_RENAME)) {
watcher->mEvents.move(path, pathTo);
}

return true;
}
Expand All @@ -266,6 +311,8 @@ void FAnotifyCrawlBackend::unsubscribe(WatcherRef watcher) {
if (markRc != 0) {
throw WatcherError(std::string("Unable to remove watcher: ") + strerror(errno), watcher);
}

close(it->second->mountFd);
}

it = mSubscriptions.erase(it);
Expand Down
14 changes: 11 additions & 3 deletions src/linux/FAnotifyCrawlBackend.hh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class FAnotifyCrawlBackend : public FAnotifyBackend {
public:
void start() override;
~FAnotifyCrawlBackend();
~FAnotifyCrawlBackend() override;
void subscribe(WatcherRef watcher) override;
void unsubscribe(WatcherRef watcher) override;

Expand All @@ -21,8 +21,16 @@ private:

bool watchDir(WatcherRef watcher, const std::string &path, std::shared_ptr<DirTree> tree);
void handleEvents();
void handleEvent(fanotify_event_metadata *metadata, fanotify_event_info_fid *fid, std::unordered_set<WatcherRef> &watchers);
bool handleSubscription(fanotify_event_metadata *metadata, fanotify_event_info_fid *fid, std::shared_ptr<FAnotifySubscription> sub);
void handleEvent(fanotify_event_metadata *metadata,
fanotify_event_info_fid *fid,
std::unordered_set<WatcherRef> &watchers,
fanotify_event_info_fid *fidTo);

bool handleSubscription(fanotify_event_metadata *metadata,
fanotify_event_info_fid *fid,
std::shared_ptr<FAnotifySubscription> sub,
fanotify_event_info_fid *fidTo,
const std::string &mountPathTo);
};

#endif
2 changes: 1 addition & 1 deletion src/linux/FAnotifyFsBackend.hh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class FAnotifyFsBackend : public BruteForceBackend {
public:
void start() override;
~FAnotifyFsBackend();
~FAnotifyFsBackend() override;
void subscribe(WatcherRef watcher) override;
void unsubscribe(WatcherRef watcher) override;
private:
Expand Down

0 comments on commit d027570

Please sign in to comment.