]> git.saurik.com Git - wxWidgets.git/blame - src/common/fswatchercmn.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / fswatchercmn.cpp
CommitLineData
6b8ef0b3 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/common/fswatchercmn.cpp
6b8ef0b3
VZ
3// Purpose: wxMswFileSystemWatcher
4// Author: Bartosz Bekier
5// Created: 2009-05-26
6// RCS-ID: $Id$
7// Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif
17
18#if wxUSE_FSWATCHER
19
20#include "wx/fswatcher.h"
21#include "wx/private/fswatcher.h"
22
23// ============================================================================
24// helpers
25// ============================================================================
26
27wxDEFINE_EVENT(wxEVT_FSWATCHER, wxFileSystemWatcherEvent);
28
29static wxString GetFSWEventChangeTypeName(int type)
30{
31 switch (type)
32 {
33 case wxFSW_EVENT_CREATE:
34 return "CREATE";
35 case wxFSW_EVENT_DELETE:
36 return "DELETE";
37 case wxFSW_EVENT_RENAME:
38 return "RENAME";
39 case wxFSW_EVENT_MODIFY:
40 return "MODIFY";
41 case wxFSW_EVENT_ACCESS:
42 return "ACCESS";
f31f9900
VZ
43 case wxFSW_EVENT_ATTRIB: // Currently this is wxGTK-only
44 return "ATTRIBUTE";
092e08a8
VZ
45#ifdef wxHAS_INOTIFY
46 case wxFSW_EVENT_UNMOUNT: // Currently this is wxGTK-only
47 return "UNMOUNT";
48#endif
1ec4e9c2
VZ
49 case wxFSW_EVENT_WARNING:
50 return "WARNING";
51 case wxFSW_EVENT_ERROR:
52 return "ERROR";
6b8ef0b3
VZ
53 }
54
55 // should never be reached!
56 wxFAIL_MSG("Unknown change type");
57 return "INVALID_TYPE";
58}
59
60
61// ============================================================================
62// wxFileSystemWatcherEvent implementation
63// ============================================================================
64
65wxString wxFileSystemWatcherEvent::ToString() const
66{
67 return wxString::Format("FSW_EVT type=%d (%s) path='%s'", m_changeType,
68 GetFSWEventChangeTypeName(m_changeType), GetPath().GetFullPath());
69}
70
71
72// ============================================================================
73// wxFileSystemWatcherEvent implementation
74// ============================================================================
75
76wxFileSystemWatcherBase::wxFileSystemWatcherBase() :
77 m_service(0), m_owner(this)
78{
79}
80
81wxFileSystemWatcherBase::~wxFileSystemWatcherBase()
82{
83 RemoveAll();
84 if (m_service)
85 {
86 delete m_service;
87 }
88}
89
90bool wxFileSystemWatcherBase::Add(const wxFileName& path, int events)
91{
f8d37148
VZ
92 wxFSWPathType type = wxFSWPath_None;
93 if ( path.FileExists() )
94 {
95 type = wxFSWPath_File;
96 }
97 else if ( path.DirExists() )
98 {
99 type = wxFSWPath_Dir;
100 }
101 else
102 {
103 wxLogError(_("Can't monitor non-existent path \"%s\" for changes."),
104 path.GetFullPath());
6b8ef0b3 105 return false;
f8d37148 106 }
6b8ef0b3 107
3a2b3701 108 return AddAny(path, events, type);
f8d37148
VZ
109}
110
111bool
3a2b3701
VZ
112wxFileSystemWatcherBase::AddAny(const wxFileName& path,
113 int events,
227dee95
VZ
114 wxFSWPathType type,
115 const wxString& filespec)
f8d37148 116{
6b8ef0b3
VZ
117 wxString canonical = GetCanonicalPath(path);
118 if (canonical.IsEmpty())
119 return false;
120
6b8ef0b3 121 // adding a path in a platform specific way
227dee95 122 wxFSWatchInfo watch(canonical, events, type, filespec);
6b8ef0b3
VZ
123 if ( !m_service->Add(watch) )
124 return false;
125
76cfd1bf
VZ
126 // on success, either add path to our 'watch-list'
127 // or, if already watched, inc the refcount. This may happen if
128 // a dir is Add()ed, then later AddTree() is called on a parent dir
129 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
130 if ( it == m_watches.end() )
131 {
132 wxFSWatchInfoMap::value_type val(canonical, watch);
133 m_watches.insert(val).second;
134 }
135 else
136 {
137 wxFSWatchInfo& watch = it->second;
138 int count = watch.IncRef();
139 wxLogTrace(wxTRACE_FSWATCHER,
140 "'%s' is now watched %d times", canonical, count);
141 }
142 return true;
6b8ef0b3
VZ
143}
144
145bool wxFileSystemWatcherBase::Remove(const wxFileName& path)
146{
147 // args validation & consistency checks
148 wxString canonical = GetCanonicalPath(path);
149 if (canonical.IsEmpty())
150 return false;
151
152 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
153 wxCHECK_MSG(it != m_watches.end(), false,
154 wxString::Format("Path '%s' is not watched", canonical));
155
76cfd1bf
VZ
156 // Decrement the watch's refcount and remove from watch-list if 0
157 bool ret = true;
158 wxFSWatchInfo& watch = it->second;
159 if ( !watch.DecRef() )
160 {
161 // remove in a platform specific way
162 ret = m_service->Remove(watch);
6b8ef0b3 163
76cfd1bf
VZ
164 m_watches.erase(it);
165 }
166 return ret;
6b8ef0b3
VZ
167}
168
169bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
227dee95 170 const wxString& filespec)
6b8ef0b3
VZ
171{
172 if (!path.DirExists())
173 return false;
174
175 // OPT could be optimised if we stored information about relationships
176 // between paths
177 class AddTraverser : public wxDirTraverser
178 {
179 public:
227dee95
VZ
180 AddTraverser(wxFileSystemWatcherBase* watcher, int events,
181 const wxString& filespec) :
182 m_watcher(watcher), m_events(events), m_filespec(filespec)
6b8ef0b3
VZ
183 {
184 }
185
6eef5763 186 virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
6b8ef0b3 187 {
6eef5763
VZ
188 // There is no need to watch individual files as we watch the
189 // parent directory which will notify us about any changes in them.
6b8ef0b3
VZ
190 return wxDIR_CONTINUE;
191 }
192
193 virtual wxDirTraverseResult OnDir(const wxString& dirname)
194 {
6eef5763
VZ
195 if ( m_watcher->AddAny(wxFileName::DirName(dirname),
196 m_events, wxFSWPath_Tree, m_filespec) )
227dee95 197 {
6eef5763
VZ
198 wxLogTrace(wxTRACE_FSWATCHER,
199 "--- AddTree adding directory '%s' ---", dirname);
227dee95 200 }
6b8ef0b3
VZ
201 return wxDIR_CONTINUE;
202 }
203
204 private:
205 wxFileSystemWatcherBase* m_watcher;
206 int m_events;
227dee95 207 wxString m_filespec;
6b8ef0b3
VZ
208 };
209
210 wxDir dir(path.GetFullPath());
0fccda2c 211 // Prevent asserts or infinite loops in trees containing symlinks
fe6afcb3 212 int flags = wxDIR_DIRS;
0fccda2c
VZ
213 if ( !path.ShouldFollowLink() )
214 {
215 flags |= wxDIR_NO_FOLLOW;
216 }
227dee95 217 AddTraverser traverser(this, events, filespec);
0fccda2c 218 dir.Traverse(traverser, filespec, flags);
6b8ef0b3 219
b5b1a8e4 220 // Add the path itself explicitly as Traverse() doesn't return it.
6eef5763 221 AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
b5b1a8e4 222
6b8ef0b3
VZ
223 return true;
224}
225
226bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
227{
228 if (!path.DirExists())
229 return false;
230
231 // OPT could be optimised if we stored information about relationships
232 // between paths
233 class RemoveTraverser : public wxDirTraverser
234 {
235 public:
227dee95
VZ
236 RemoveTraverser(wxFileSystemWatcherBase* watcher,
237 const wxString& filespec) :
238 m_watcher(watcher), m_filespec(filespec)
6b8ef0b3
VZ
239 {
240 }
241
6eef5763 242 virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename))
6b8ef0b3 243 {
6eef5763
VZ
244 // We never watch the individual files when watching the tree, so
245 // nothing to do here.
6b8ef0b3
VZ
246 return wxDIR_CONTINUE;
247 }
248
249 virtual wxDirTraverseResult OnDir(const wxString& dirname)
250 {
6eef5763 251 m_watcher->Remove(wxFileName::DirName(dirname));
6b8ef0b3
VZ
252 return wxDIR_CONTINUE;
253 }
254
255 private:
256 wxFileSystemWatcherBase* m_watcher;
227dee95 257 wxString m_filespec;
6b8ef0b3
VZ
258 };
259
227dee95
VZ
260 // If AddTree() used a filespec, we must use the same one
261 wxString canonical = GetCanonicalPath(path);
262 wxFSWatchInfoMap::iterator it = m_watches.find(canonical);
263 wxCHECK_MSG( it != m_watches.end(), false,
264 wxString::Format("Path '%s' is not watched", canonical) );
265 wxFSWatchInfo watch = it->second;
266 const wxString filespec = watch.GetFilespec();
267
268#if defined(__WINDOWS__)
269 // When there's no filespec, the wxMSW AddTree() would have set a watch
270 // on only the passed 'path'. We must therefore remove only this
271 if (filespec.empty())
272 {
273 return Remove(path);
274 }
275 // Otherwise fall through to the generic implementation
276#endif // __WINDOWS__
277
6b8ef0b3 278 wxDir dir(path.GetFullPath());
0fccda2c
VZ
279 // AddTree() might have used the wxDIR_NO_FOLLOW to prevent asserts or
280 // infinite loops in trees containing symlinks. We need to do the same
281 // or we'll try to remove unwatched items. Let's hope the caller used
282 // the same ShouldFollowLink() setting as in AddTree()...
fe6afcb3 283 int flags = wxDIR_DIRS;
0fccda2c
VZ
284 if ( !path.ShouldFollowLink() )
285 {
286 flags |= wxDIR_NO_FOLLOW;
287 }
227dee95 288 RemoveTraverser traverser(this, filespec);
0fccda2c 289 dir.Traverse(traverser, filespec, flags);
6b8ef0b3 290
b5b1a8e4
VZ
291 // As in AddTree() above, handle the path itself explicitly.
292 Remove(path);
293
6b8ef0b3
VZ
294 return true;
295}
296
297bool wxFileSystemWatcherBase::RemoveAll()
298{
299 m_service->RemoveAll();
300 m_watches.clear();
301 return true;
302}
303
304int wxFileSystemWatcherBase::GetWatchedPathsCount() const
305{
306 return m_watches.size();
307}
308
309int wxFileSystemWatcherBase::GetWatchedPaths(wxArrayString* paths) const
310{
311 wxCHECK_MSG( paths != NULL, -1, "Null array passed to retrieve paths");
312
313 wxFSWatchInfoMap::const_iterator it = m_watches.begin();
314 for ( ; it != m_watches.end(); ++it)
315 {
316 paths->push_back(it->first);
317 }
318
319 return m_watches.size();
320}
321
322#endif // wxUSE_FSWATCHER