X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/20ffcd779a70518a2e06bc1bf53ca12465de0a6d..06a32e049c1bad9249079ad2e91659303424a774:/src/unix/fswatcher_inotify.cpp diff --git a/src/unix/fswatcher_inotify.cpp b/src/unix/fswatcher_inotify.cpp index 5bcc475698..39b3a85aaa 100644 --- a/src/unix/fswatcher_inotify.cpp +++ b/src/unix/fswatcher_inotify.cpp @@ -236,34 +236,82 @@ protected: // get watch entry for this event wxFSWatchEntryDescriptors::iterator it = m_watchMap.find(inevt.wd); - if (it == m_watchMap.end()) + + // wd will be -1 for IN_Q_OVERFLOW, which would trigger the wxFAIL_MSG + if (inevt.wd != -1) { - // It's not in the map; check if was recently removed from it. - if (m_staleDescriptors.Index(inevt.wd) != wxNOT_FOUND) - { - wxLogTrace(wxTRACE_FSWATCHER, - "Got an event for stale wd %i", inevt.wd); - } - else + if (it == m_watchMap.end()) { - wxFAIL_MSG("Event for unknown watch descriptor."); - } + // It's not in the map; check if was recently removed from it. + if (m_staleDescriptors.Index(inevt.wd) != wxNOT_FOUND) + { + wxLogTrace(wxTRACE_FSWATCHER, + "Got an event for stale wd %i", inevt.wd); + } + else + { + // In theory we shouldn't reach here. In practice, some + // events, e.g. IN_MODIFY, arrive just after the IN_IGNORED + // so their wd has already been discarded. Warn about them. + wxFileSystemWatcherEvent + event + ( + wxFSW_EVENT_WARNING, + wxString::Format + ( + _("Unexpected event for \"%s\": no " + "matching watch descriptor."), + inevt.len ? inevt.name : "" + ) + ); + SendEvent(event); - // In any case, don't process this event: it's either for an - // already removed entry, or for a completely unknown one. - return; + } + + // In any case, don't process this event: it's either for an + // already removed entry, or for an unknown one. + return; + } } - wxFSWatchEntry& watch = *(it->second); int nativeFlags = inevt.mask; int flags = Native2WatcherFlags(nativeFlags); // check out for error/warning condition if (flags & wxFSW_EVENT_WARNING || flags & wxFSW_EVENT_ERROR) { - wxString errMsg = GetErrorDescription(Watcher2NativeFlags(flags)); + wxString errMsg = GetErrorDescription(nativeFlags); wxFileSystemWatcherEvent event(flags, errMsg); SendEvent(event); + return; + } + + if (inevt.wd == -1) + { + // Although this is not supposed to happen, we seem to be getting + // occasional IN_ACCESS/IN_MODIFY events without valid watch value. + wxFileSystemWatcherEvent + event + ( + wxFSW_EVENT_WARNING, + wxString::Format + ( + _("Invalid inotify event for \"%s\""), + inevt.len ? inevt.name : "" + ) + ); + SendEvent(event); + return; + } + + wxFSWatchEntry& watch = *(it->second); + + // Now IN_UNMOUNT. We must do so here, as it's not in the watch flags + if (nativeFlags & IN_UNMOUNT) + { + wxFileName path = GetEventPath(watch, inevt); + wxFileSystemWatcherEvent event(wxFSW_EVENT_UNMOUNT, path, path); + SendEvent(event); } // filter out ignored events and those not asked for. // we never filter out warnings or exceptions @@ -344,6 +392,17 @@ protected: // renames else if (nativeFlags & IN_MOVE) { + // IN_MOVE events are produced in the following circumstances: + // * A move within a dir (what the user sees as a rename) gives an + // IN_MOVED_FROM and IN_MOVED_TO pair, each with the same path. + // * A move within watched dir foo/ e.g. from foo/bar1/ to foo/bar2/ + // will also produce such a pair, but with different paths. + // * A move to baz/ will give only an IN_MOVED_FROM for foo/bar1; + // if baz/ is inside a different watch, that gets the IN_MOVED_TO. + + // The first event to arrive, usually the IN_MOVED_FROM, is + // cached to await a matching IN_MOVED_TO; should none arrive, the + // unpaired event will be processed later in ProcessRenames(). wxInotifyCookies::iterator it2 = m_cookies.find(inevt.cookie); if ( it2 == m_cookies.end() ) { @@ -362,15 +421,35 @@ protected: // If there's a filespec, assume he's not if ( watch.GetFilespec().empty() ) { + // The the only way to know the path for the first event, + // normally the IN_MOVED_FROM, is to retrieve the watch + // corresponding to oldinevt. This is needed for a move + // within a watch. + wxFSWatchEntry* oldwatch; + wxFSWatchEntryDescriptors::iterator oldwatch_it = + m_watchMap.find(oldinevt.wd); + if (oldwatch_it != m_watchMap.end()) + { + oldwatch = oldwatch_it->second; + } + else + { + wxLogTrace(wxTRACE_FSWATCHER, + "oldinevt's watch descriptor not in the watch map"); + // For want of a better alternative, use 'watch'. That + // will work fine for renames, though not for moves + oldwatch = &watch; + } + wxFileSystemWatcherEvent event(flags); if ( inevt.mask & IN_MOVED_FROM ) { event.SetPath(GetEventPath(watch, inevt)); - event.SetNewPath(GetEventPath(watch, oldinevt)); + event.SetNewPath(GetEventPath(*oldwatch, oldinevt)); } else { - event.SetPath(GetEventPath(watch, oldinevt)); + event.SetPath(GetEventPath(*oldwatch, oldinevt)); event.SetNewPath(GetEventPath(watch, inevt)); } SendEvent(event); @@ -395,6 +474,8 @@ protected: void ProcessRenames() { + // After all of a batch of events has been processed, this deals with + // any still-unpaired IN_MOVED_FROM or IN_MOVED_TO events. wxInotifyCookies::iterator it = m_cookies.begin(); while ( it != m_cookies.end() ) { @@ -405,19 +486,24 @@ protected: // get watch entry for this event wxFSWatchEntryDescriptors::iterator wit = m_watchMap.find(inevt.wd); - wxCHECK_RET(wit != m_watchMap.end(), - "Watch descriptor not present in the watch map!"); - - // Tell the owner, in case it's interested - // If there's a filespec, assume he's not - wxFSWatchEntry& watch = *(wit->second); - if ( watch.GetFilespec().empty() ) + if (wit == m_watchMap.end()) { - int flags = Native2WatcherFlags(inevt.mask); - wxFileName path = GetEventPath(watch, inevt); + wxLogTrace(wxTRACE_FSWATCHER, + "Watch descriptor not present in the watch map!"); + } + else + { + // Tell the owner, in case it's interested + // If there's a filespec, assume he's not + wxFSWatchEntry& watch = *(wit->second); + if ( watch.GetFilespec().empty() ) { - wxFileSystemWatcherEvent event(flags, path, path); - SendEvent(event); + int flags = Native2WatcherFlags(inevt.mask); + wxFileName path = GetEventPath(watch, inevt); + { + wxFileSystemWatcherEvent event(flags, path, path); + SendEvent(event); + } } } @@ -479,10 +565,33 @@ protected: return path; } - static int Watcher2NativeFlags(int WXUNUSED(flags)) + static int Watcher2NativeFlags(int flags) { - // TODO: it would be nice to subscribe only to the events we really need - return IN_ALL_EVENTS; + // Start with the standard case of wanting all events + if (flags == wxFSW_EVENT_ALL) + { + return IN_ALL_EVENTS; + } + + static const int flag_mapping[][2] = { + { wxFSW_EVENT_ACCESS, IN_ACCESS }, + { wxFSW_EVENT_MODIFY, IN_MODIFY }, + { wxFSW_EVENT_ATTRIB, IN_ATTRIB }, + { wxFSW_EVENT_RENAME, IN_MOVE }, + { wxFSW_EVENT_CREATE, IN_CREATE }, + { wxFSW_EVENT_DELETE, IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF }, + { wxFSW_EVENT_UNMOUNT, IN_UNMOUNT } + // wxFSW_EVENT_ERROR/WARNING make no sense here + }; + + int native_flags = 0; + for ( unsigned int i=0; i < WXSIZEOF(flag_mapping); ++i) + { + if (flags & flag_mapping[i][0]) + native_flags |= flag_mapping[i][1]; + } + + return native_flags; } static int Native2WatcherFlags(int flags) @@ -490,7 +599,7 @@ protected: static const int flag_mapping[][2] = { { IN_ACCESS, wxFSW_EVENT_ACCESS }, // generated during read! { IN_MODIFY, wxFSW_EVENT_MODIFY }, - { IN_ATTRIB, 0 }, + { IN_ATTRIB, wxFSW_EVENT_ATTRIB }, { IN_CLOSE_WRITE, 0 }, { IN_CLOSE_NOWRITE, 0 }, { IN_OPEN, 0 }, @@ -501,10 +610,10 @@ protected: { IN_DELETE_SELF, wxFSW_EVENT_DELETE }, { IN_MOVE_SELF, wxFSW_EVENT_DELETE }, - { IN_UNMOUNT, wxFSW_EVENT_ERROR }, + { IN_UNMOUNT, wxFSW_EVENT_UNMOUNT}, { IN_Q_OVERFLOW, wxFSW_EVENT_WARNING}, - // ignored, because this is genereted mainly by watcher::Remove() + // ignored, because this is generated mainly by watcher::Remove() { IN_IGNORED, 0 } }; @@ -527,8 +636,6 @@ protected: { switch ( flag ) { - case IN_UNMOUNT: - return _("File system containing watched object was unmounted"); case IN_Q_OVERFLOW: return _("Event queue overflowed"); }