143
154
self._watch_handle = None
144
155
# remember the subdirs we have so that when we have a delete we can
145
156
# check if it was a remove
157
self._subdirs = set()
147
158
# ensure that we work with an abspath and that we can deal with
148
159
# long paths over 260 chars.
149
160
if not path.endswith(os.path.sep):
157
168
self._watch_stopped_deferred = defer.Deferred()
159
170
@is_valid_windows_path(path_indexes=[1])
171
def _update_subdirs(self, path, event):
172
"""Adds the path to the internal subdirs.
174
The given path is considered to be a path and therefore this
177
if WINDOWS_ACTIONS[event] == IN_CREATE:
178
self._subdirs.add(path)
179
elif WINDOWS_ACTIONS[event] == IN_DELETE and\
180
path in self._subdirs:
181
self._subdirs.remove(path)
183
@is_valid_windows_path(path_indexes=[1])
160
184
def _path_is_dir(self, path):
161
185
"""Check if the path is a dir and update the local subdir list."""
162
self.log.debug('Testing if path %r is a dir', path)
187
# The logic of this function is the following:
188
# 1. ReadDirectoryChangesW changes does not send if a path
189
# is a new dir or not.
190
# 2. We keep track of subdirs that in self._subdir.
191
# 3. We check if a path is a dir by:
192
# * Asking the os if the path exists.
193
# * Finding the path in self._subdirs
164
196
if os.path.exists(path):
165
197
is_dir = os.path.isdir(path)
167
self.log.debug('Path "%s" was deleted subdirs are %s.',
169
# we removed the path, we look in the internal list
170
if path in self._subdirs:
172
self._subdirs.remove(path)
174
self.log.debug('Adding %s to subdirs %s', path, self._subdirs)
175
self._subdirs.append(path)
199
# path does not exists, was it in the internal list?
200
is_dir = path in self._subdirs
201
self.log.debug('Is path %r a dir? %s', path, is_dir)
178
204
def _process_events(self, events):
191
217
# makes the multiplatform better, linux was first :P
192
218
syncdaemon_path = get_syncdaemon_valid_path(
193
219
os.path.join(self._path, file_name))
194
is_dir = self._path_is_dir(os.path.join(self._path, file_name))
220
full_dir_path = os.path.join(self._path, file_name)
221
is_dir = self._path_is_dir(full_dir_path)
196
self._subdirs.append(file_name)
223
# we need to update the list of subdirs that we have
224
self._update_subdirs(full_dir_path, action)
197
225
mask = WINDOWS_ACTIONS[action]
198
226
head, tail = os.path.split(file_name)
221
249
# add the event only if we do not have an exclude filter or
222
250
# the exclude filter returns False, that is, the event will not
224
self.log.debug('Event is %s.', event)
252
self.log.debug('Pushing event %r to processor.', event)
225
253
self._processor(event)
227
255
def _call_deferred(self, f, *args):
292
320
data = GetOverlappedResult(handle, self._overlapped, True)
293
321
# lets ead the data and store it in the results
294
322
events = FILE_NOTIFY_INFORMATION(buf, data)
295
self.log.debug('Events from ReadDirectoryChangesW are %s', events)
323
self.log.debug('Got from ReadDirectoryChangesW %r.',
324
[(WINDOWS_ACTIONS_NAMES[action], path) for action, path in
296
326
reactor.callFromThread(self._process_events, events)
298
328
@is_valid_windows_path(path_indexes=[1])
319
349
for current_child in os.listdir(self._path):
320
350
full_child_path = os.path.join(self._path, current_child)
321
351
if os.path.isdir(full_child_path):
322
self._subdirs.append(full_child_path)
352
self._subdirs.add(full_child_path)
323
353
# start to diff threads, one to watch the path, the other to
324
354
# process the events.
325
355
self.log.debug('Start watching path.')
332
362
self.log.info('Stop watching %s', self._path)
333
363
SetEvent(self._wait_stop)
334
364
self._watching = False
365
self._subdirs = set()
336
366
return self.stopped
338
368
def update(self, mask, auto_add=False):
419
449
mask, auto_add, self._processor)
420
450
d = self._wdm[self._wd_count].start_watching()
421
451
self._wd_count += 1
422
self.log.debug('Watch count increased to %s', self._wd_count)
425
454
@is_valid_windows_path(path_indexes=[1])
497
526
def __init__(self, monitor, ignore_config=None):
498
527
# XXX: avoid circular imports.
499
528
from ubuntuone.syncdaemon.filesystem_notifications import (
500
GeneralINotifyProcessor
529
GeneralINotifyProcessor)
502
530
self.general_processor = GeneralINotifyProcessor(monitor,
503
531
self.handle_dir_delete, NAME_TRANSLATIONS,
504
532
self.platform_is_ignored, IN_IGNORED, ignore_config=ignore_config)