dsoprea / PyInotify

An efficient and elegant inotify (Linux filesystem activity monitor) library for Python. Python 2 and 3 compatible.
GNU General Public License v2.0
245 stars 73 forks source link

inotify.adapters.InotifyTree doesn't update watch on rename folder #19

Closed vaniatoperich closed 6 years ago

vaniatoperich commented 8 years ago

If you watch a folder and you rename the folder within that folder, event will not update the watch, if will keep on using old watch_path and filenames changed in that watch path will use old watch path:

Here is Renaming of the folder: DEBUG 2016-08-24 16:35:13,717 (_INOTIFY_EVENT(wd=1, mask=1073741888, cookie=174440, len=16), ['IN_MOVED_FROM', 'IN_ISDIR'], b'/shared/media/content/Movies', b'VBH Demo') DEBUG 2016-08-24 16:35:13,717 (_INOTIFY_EVENT(wd=1, mask=1073741952, cookie=174440, len=32), ['IN_MOVED_TO', 'IN_ISDIR'], b'/shared/media/content/Movies', b'VBH Demo Renamed') DEBUG 2016-08-24 16:35:13,717 (_INOTIFY_EVENT(wd=2, mask=2048, cookie=0, len=0), ['IN_MOVE_SELF'], b'/shared/media/content/Movies/VBH Demo', b'')

And here is toucing the file in that new folder: DEBUG 2016-08-24 16:36:55,914 (_INOTIFY_EVENT(wd=2, mask=256, cookie=0, len=16), ['IN_CREATE'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE') DEBUG 2016-08-24 16:36:55,915 (_INOTIFY_EVENT(wd=2, mask=32, cookie=0, len=16), ['IN_OPEN'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE') DEBUG 2016-08-24 16:36:55,915 (_INOTIFY_EVENT(wd=2, mask=4, cookie=0, len=16), ['IN_ATTRIB'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE') DEBUG 2016-08-24 16:36:55,915 (_INOTIFY_EVENT(wd=2, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE')

As you can see, watch path is not changed to VBH Demo Renamed, it stayed in VBH Demo.

dsoprea commented 8 years ago

Sure. We never accounted for that. Can you make the change or do you want to wait on me?

Dustin

On Aug 24, 2016 12:37 PM, "vaniatoperich" notifications@github.com wrote:

If you watch a folder and you rename the folder within that folder, event will not update the watch, if will keep on using old watch_path and filenames changed in that watch path will use old watch path:

Here is Renaming of the folder: DEBUG 2016-08-24 16:35:13,717 (_INOTIFY_EVENT(wd=1, mask=1073741888, cookie=174440, len=16), ['IN_MOVED_FROM', 'IN_ISDIR'], b'/shared/media/content/Movies', b'VBH Demo') DEBUG 2016-08-24 16:35:13,717 (_INOTIFY_EVENT(wd=1, mask=1073741952, cookie=174440, len=32), ['IN_MOVED_TO', 'IN_ISDIR'], b'/shared/media/content/Movies', b'VBH Demo Renamed') DEBUG 2016-08-24 16:35:13,717 (_INOTIFY_EVENT(wd=2, mask=2048, cookie=0, len=0), ['IN_MOVE_SELF'], b'/shared/media/content/Movies/VBH Demo', b'')

And here is toucing the file in that new folder: DEBUG 2016-08-24 16:36:55,914 (_INOTIFY_EVENT(wd=2, mask=256, cookie=0, len=16), ['IN_CREATE'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE') DEBUG 2016-08-24 16:36:55,915 (_INOTIFY_EVENT(wd=2, mask=32, cookie=0, len=16), ['IN_OPEN'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE') DEBUG 2016-08-24 16:36:55,915 (_INOTIFY_EVENT(wd=2, mask=4, cookie=0, len=16), ['IN_ATTRIB'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE') DEBUG 2016-08-24 16:36:55,915 (_INOTIFY_EVENT(wd=2, mask=8, cookie=0, len=16), ['IN_CLOSE_WRITE'], b'/shared/media/content/Movies/VBH Demo', b'TESTFILE')

As you can see, watch path is not changed to VBH Demo Renamed, it stayed in VBH Demo.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dsoprea/PyInotify/issues/19, or mute the thread https://github.com/notifications/unsubscribe-auth/AArrajufSycWohzEES3o7Hq2f14_rhUNks5qjHNlgaJpZM4JsNXv .

vaniatoperich commented 8 years ago

I guess like this?:

                elif header.mask & inotify.constants.IN_MOVED_FROM:

                    self.__i.remove_watch(full_path, superficial=True)
                elif header.mask & inotify.constants.IN_MOVED_TO:

                    self.__i.add_watch(full_path, self.__mask)
dsoprea commented 8 years ago

This needs to stay open, @vaniatoperich. It's a bug and affects more than just you. Though I'd appreciate it if you took a look at it (sorry, I didn't have a chance to respond yesterday), it'll have to be fixed by someone.

vaniatoperich commented 8 years ago

Ah. Sorry. Missunderstanding. I did look into it and I posted the code that works for me (At least that seems so, but I am not sure if you agree with the approach? Please tell me what I can do to help?

vaniatoperich commented 8 years ago

Here is full code:

class InotifyTree(object):
    def __init__(self, path, mask=inotify.constants.IN_ALL_EVENTS, 
                 block_duration_s=_DEFAULT_EPOLL_BLOCK_DURATION_S):

        self.__root_path = path

        # No matter what we actually received as the mask, make sure we have 
        # the minimum that we require to curate our list of watches.
        self.__mask = mask | \
                        inotify.constants.IN_ISDIR | \
                        inotify.constants.IN_CREATE | \
                        inotify.constants.IN_DELETE

        self.__i = Inotify(block_duration_s=block_duration_s)

        self.__load_tree(path)

    def __load_tree(self, path):
        _LOGGER.debug("Adding initial watches on tree: [%s]", path)

        q = [path]
        while q:
            current_path = q[0]
            del q[0]

            self.__i.add_watch(current_path, self.__mask)

            for filename in os.listdir(current_path):
                entry_filepath = os.path.join(current_path, filename)
                if os.path.isdir(entry_filepath) is False:
                    continue

                q.append(entry_filepath)

    def event_gen(self):
        """This is a secondary generator that wraps the principal one, and 
        adds/removes watches as directories are added/removed.
        """

        for event in self.__i.event_gen():
            if event is not None:
                (header, type_names, path, filename) = event

                if header.mask & inotify.constants.IN_ISDIR:
                    full_path = os.path.join(path, filename)

                    if header.mask & inotify.constants.IN_CREATE:
                        _LOGGER.debug("A directory has been created. We're "
                                      "adding a watch on it (because we're "
                                      "being recursive): [%s]", full_path)

                        self.__i.add_watch(full_path, self.__mask)
                    elif header.mask & inotify.constants.IN_DELETE:
                        _LOGGER.debug("A directory has been removed. We're "
                                      "being recursive, but it would have "
                                      "automatically been deregistered: [%s]", 
                                      full_path)

                        # The watch would've already been cleaned-up internally.
                        self.__i.remove_watch(full_path, superficial=True)
                    elif header.mask & inotify.constants.IN_MOVED_FROM:
                        _LOGGER.debug("A directory has been renamed. We're "
                                      "being recursive, but it would have "
                                      "automatically been deregistered: [%s]",
                                      full_path)

                        self.__i.remove_watch(full_path, superficial=True)
                    elif header.mask & inotify.constants.IN_MOVED_TO:
                        _LOGGER.debug("A directory has been renamed. We're "
                                      "adding a watch on it (because we're "
                                      "being recursive): [%s]", full_path)

                        self.__i.add_watch(full_path, self.__mask)

            yield event
dsoprea commented 8 years ago

It looks great. Please fork and submit it as a PR so that I can more formally review it.

dsoprea commented 7 years ago

@vaniatoperich Bump. Can you do this? It'll be five-minutes of work on your part.