inotify: Properly handle cross-dir renames
authorVolker Lendecke <vl@samba.org>
Fri, 5 Dec 2014 15:38:45 +0000 (15:38 +0000)
committerJeremy Allison <jra@samba.org>
Fri, 5 Dec 2014 20:01:54 +0000 (21:01 +0100)
When watching two subdirectories with inotify and a file is moved between both,
we get a IN_MOVED_FROM for the source watch and a IN_MOVED_TO for the
destination watch. Without this patch we create a NOTIFY_ACTION_OLD_NAME for
the old directory. We hold this back in notify_fsp, expecting the NEW_NAME
immediately after it. In the cross-directory rename case this does not work,
we'll not get the NEW_NAME, there is no NEW_NAME in that directory.

This patch changes us to create NOTIFY_ACTION_REMOVED and NOTIFY_ACTION_ADDED
in this case. Not sure this is right, but at least it is better than before: We
get something at all.

This is more likely to happen with the notifyd approach, as there we
inotify-watch many subdirectories from one process. Without nootifyd you had to
have two explorer windows open and do a nfs or local mv between those two
directories to find this.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Fri Dec  5 21:01:54 CET 2014 on sn-devel-104

source3/smbd/notify_inotify.c

index efb659f7c07addd20dd2762cecb54ba463cafe25..1fcd8ecd7356fbb9cd781b4b87eeaea760ad8a36 100644 (file)
@@ -118,6 +118,7 @@ static bool filter_match(struct inotify_watch_context *w,
 */
 static void inotify_dispatch(struct inotify_private *in, 
                             struct inotify_event *e, 
+                            int prev_wd,
                             uint32_t prev_cookie,
                             struct inotify_event *e2)
 {
@@ -140,13 +141,14 @@ static void inotify_dispatch(struct inotify_private *in,
        } else if (e->mask & IN_DELETE) {
                ne.action = NOTIFY_ACTION_REMOVED;
        } else if (e->mask & IN_MOVED_FROM) {
-               if (e2 != NULL && e2->cookie == e->cookie) {
+               if (e2 != NULL && e2->cookie == e->cookie &&
+                   e2->wd == e->wd) {
                        ne.action = NOTIFY_ACTION_OLD_NAME;
                } else {
                        ne.action = NOTIFY_ACTION_REMOVED;
                }
        } else if (e->mask & IN_MOVED_TO) {
-               if (e->cookie == prev_cookie) {
+               if ((e->cookie == prev_cookie) && (e->wd == prev_wd)) {
                        ne.action = NOTIFY_ACTION_NEW_NAME;
                } else {
                        ne.action = NOTIFY_ACTION_ADDED;
@@ -198,6 +200,7 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
        int bufsize = 0;
        struct inotify_event *e0, *e;
        uint32_t prev_cookie=0;
+       int prev_wd = -1;
        NTSTATUS status;
 
        /*
@@ -234,7 +237,8 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
                if (bufsize >= sizeof(*e)) {
                        e2 = (struct inotify_event *)(e->len + sizeof(*e) + (char *)e);
                }
-               inotify_dispatch(in, e, prev_cookie, e2);
+               inotify_dispatch(in, e, prev_wd, prev_cookie, e2);
+               prev_wd = e->wd;
                prev_cookie = e->cookie;
                e = e2;
        }