#include "INotify.h" #include "Log.h" #include "ZString.h" #include namespace core { INotify::INotify(EPoll &ePoll) : Socket(ePoll, "INotify") { setDescriptor(inotify_init()); // See isKnownINotify()'s own doc comment - registers this object // as safe for Thread::run() to dispatch epoll events to directly. std::lock_guard guard(knownINotifiesMutex()); knownINotifies().insert(this); } INotify::~INotify() { shutdown(); std::lock_guard guard(knownINotifiesMutex()); knownINotifies().erase(this); } int INotify::addWatch(coreutils::ZString &watch) { return inotify_add_watch(getDescriptor(), watch.c_str(), IN_ALL_EVENTS); } void INotify::removeWatch(int wd) { inotify_rm_watch(getDescriptor(), wd); } void INotify::onDataReceived(coreutils::ZString &buffer) { const struct inotify_event *event; char *ptr; for (ptr = buffer.getData(); ptr < buffer.getData() + buffer.getLength(); ptr += sizeof(struct inotify_event) + event->len) { event = (const struct inotify_event *) ptr; // event->name only exists - and is only NUL-terminated - when // event->len > 0 (inotify(7): "This field is present only when // the event relates to a file inside a watched directory ... // otherwise ... this field is absent."). For events with // len == 0 (e.g. IN_UNMOUNT, IN_Q_OVERFLOW, IN_IGNORED, or // self-watch events), event->name is not a valid C-string at // all - calling strlen() on it (as the old ZString(const char*) // one-arg constructor did unconditionally here) reads past // whatever garbage follows in the socket's 4096-byte receive // buffer looking for a NUL, a real heap-buffer-overflow read // confirmed via AddressSanitizer under concurrent load (the // eventual heap corruption this caused was previously // misdiagnosed as a MySQL client thread-safety issue, since it // surfaces on whichever EPoll worker thread happens to service // this watcher's socket and corrupts unrelated heap state). // strnlen() bounded by event->len (rather than the one-arg // ZString ctor's unbounded strlen) matches the kernel's actual // guarantee: name is NUL-padded to exactly event->len bytes. coreutils::ZString name = event->len > 0 ? coreutils::ZString(event->name, strnlen(event->name, event->len)) : coreutils::ZString(""); if(event->mask & IN_ACCESS) inAccess(name); if(event->mask & IN_ATTRIB) inAttrib(name.str()); if(event->mask & IN_CLOSE_WRITE) inCloseWrite(name.str()); if(event->mask & IN_CLOSE_NOWRITE) inCloseNoWrite(name.str()); if(event->mask & IN_CREATE) inCreate(name); if(event->mask & IN_DELETE) inDelete(name.str()); if(event->mask & IN_DELETE_SELF) inDeleteSelf(name.str()); if(event->mask & IN_MODIFY) inModify(name.str()); if(event->mask & IN_MOVE_SELF) inMoveSelf(name.str()); if(event->mask & IN_MOVED_FROM) inMovedFrom(name.str()); if(event->mask & IN_MOVED_TO) inMovedTo(name.str()); if(event->mask & IN_OPEN) inOpen(name.str()); } } }