]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/fswatchercmn.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / fswatchercmn.cpp
index 28aeffdcb68dc9cb7c4988008b3fee75000cc631..2990795fb6dd3bcd2875e308f7a44741cd0c227c 100644 (file)
@@ -40,6 +40,16 @@ static wxString GetFSWEventChangeTypeName(int type)
         return "MODIFY";
     case wxFSW_EVENT_ACCESS:
         return "ACCESS";
+    case wxFSW_EVENT_ATTRIB: // Currently this is wxGTK-only
+        return "ATTRIBUTE";
+#ifdef wxHAS_INOTIFY
+    case wxFSW_EVENT_UNMOUNT: // Currently this is wxGTK-only
+        return "UNMOUNT";
+#endif
+    case wxFSW_EVENT_WARNING:
+        return "WARNING";
+    case wxFSW_EVENT_ERROR:
+        return "ERROR";
     }
 
     // should never be reached!
@@ -108,17 +118,28 @@ wxFileSystemWatcherBase::AddAny(const wxFileName& path,
     if (canonical.IsEmpty())
         return false;
 
-    wxCHECK_MSG(m_watches.find(canonical) == m_watches.end(), false,
-                wxString::Format("Path '%s' is already watched", canonical));
-
     // adding a path in a platform specific way
     wxFSWatchInfo watch(canonical, events, type, filespec);
     if ( !m_service->Add(watch) )
         return false;
 
-    // on success, add path to our 'watch-list'
-    wxFSWatchInfoMap::value_type val(canonical, watch);
-    return m_watches.insert(val).second;
+    // on success, either add path to our 'watch-list'
+    // or, if already watched, inc the refcount. This may happen if
+    // a dir is Add()ed, then later AddTree() is called on a parent dir
+    wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
+    if ( it == m_watches.end() )
+    {
+        wxFSWatchInfoMap::value_type val(canonical, watch);
+        m_watches.insert(val).second;
+    }
+    else
+    {
+        wxFSWatchInfo& watch = it->second;
+        int count = watch.IncRef();
+        wxLogTrace(wxTRACE_FSWATCHER,
+                   "'%s' is now watched %d times", canonical, count);
+    }
+    return true;
 }
 
 bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
@@ -132,12 +153,17 @@ bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
     wxCHECK_MSG(it != m_watches.end(), false,
                 wxString::Format("Path '%s' is not watched", canonical));
 
-    // remove from watch-list
-    wxFSWatchInfo watch = it->second;
-    m_watches.erase(it);
+    // Decrement the watch's refcount and remove from watch-list if 0
+    bool ret = true;
+    wxFSWatchInfo& watch = it->second;
+    if ( !watch.DecRef() )
+    {
+        // remove in a platform specific way
+        ret = m_service->Remove(watch);
 
-    // remove in a platform specific way
-    return m_service->Remove(watch);
+        m_watches.erase(it);
+    }
+    return ret;
 }
 
 bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
@@ -157,34 +183,20 @@ bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
         {
         }
 
-        // CHECK we choose which files to delegate to Add(), maybe we should pass
-        // all of them to Add() and let it choose? this is useful when adding a
-        // file to a dir that is already watched, then not only should we know
-        // about that, but Add() should also behave well then
-        virtual wxDirTraverseResult OnFile(const wxString& filename)
+        virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
         {
-            if ( m_watcher->AddAny(wxFileName::FileName(filename),
-                                   m_events, wxFSWPath_File) )
-            {
-                wxLogTrace(wxTRACE_FSWATCHER,
-                       "--- AddTree adding file '%s' ---", filename);
-            }
+            // There is no need to watch individual files as we watch the
+            // parent directory which will notify us about any changes in them.
             return wxDIR_CONTINUE;
         }
 
         virtual wxDirTraverseResult OnDir(const wxString& dirname)
         {
-            // We can't currently watch only the files with the given filespec
-            // in the subdirectories so we only watch subdirectories at all if
-            // we want to watch everything.
-            if ( m_filespec.empty() )
+            if ( m_watcher->AddAny(wxFileName::DirName(dirname),
+                                   m_events, wxFSWPath_Tree, m_filespec) )
             {
-                if ( m_watcher->AddAny(wxFileName::DirName(dirname),
-                                       m_events, wxFSWPath_Dir) )
-                {
-                    wxLogTrace(wxTRACE_FSWATCHER,
-                       "--- AddTree adding directory '%s' ---", dirname);
-                }
+                wxLogTrace(wxTRACE_FSWATCHER,
+                   "--- AddTree adding directory '%s' ---", dirname);
             }
             return wxDIR_CONTINUE;
         }
@@ -196,11 +208,17 @@ bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
     };
 
     wxDir dir(path.GetFullPath());
+    // Prevent asserts or infinite loops in trees containing symlinks
+    int flags = wxDIR_DIRS;
+    if ( !path.ShouldFollowLink() )
+    {
+        flags |= wxDIR_NO_FOLLOW;
+    }
     AddTraverser traverser(this, events, filespec);
-    dir.Traverse(traverser, filespec);
+    dir.Traverse(traverser, filespec, flags);
 
     // Add the path itself explicitly as Traverse() doesn't return it.
-    AddAny(path.GetPathWithSep(), events, wxFSWPath_Dir, filespec);
+    AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
 
     return true;
 }
@@ -221,24 +239,16 @@ bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
         {
         }
 
-        virtual wxDirTraverseResult OnFile(const wxString& filename)
+        virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
         {
-            m_watcher->Remove(wxFileName(filename));
+            // We never watch the individual files when watching the tree, so
+            // nothing to do here.
             return wxDIR_CONTINUE;
         }
 
         virtual wxDirTraverseResult OnDir(const wxString& dirname)
         {
-            // Currently the subdirectories would have been added only if there
-            // is no filespec.
-            //
-            // Notice that we still need to recurse into them even if we're
-            // using a filespec because they can contain files matching it.
-            if ( m_filespec.empty() )
-            {
-                m_watcher->Remove(wxFileName::DirName(dirname));
-            }
-
+            m_watcher->Remove(wxFileName::DirName(dirname));
             return wxDIR_CONTINUE;
         }
 
@@ -266,8 +276,17 @@ bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
 #endif // __WINDOWS__
 
     wxDir dir(path.GetFullPath());
+    // AddTree() might have used the wxDIR_NO_FOLLOW to prevent asserts or
+    // infinite loops in trees containing symlinks. We need to do the same
+    // or we'll try to remove unwatched items. Let's hope the caller used
+    // the same ShouldFollowLink() setting as in AddTree()...
+    int flags = wxDIR_DIRS;
+    if ( !path.ShouldFollowLink() )
+    {
+        flags |= wxDIR_NO_FOLLOW;
+    }
     RemoveTraverser traverser(this, filespec);
-    dir.Traverse(traverser, filespec);
+    dir.Traverse(traverser, filespec, flags);
 
     // As in AddTree() above, handle the path itself explicitly.
     Remove(path);