]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: Handle what mkdir_internals() puts on inotify
authorVolker Lendecke <vl@samba.org>
Tue, 27 May 2025 12:29:55 +0000 (14:29 +0200)
committerRalph Boehme <slow@samba.org>
Mon, 2 Jun 2025 17:08:34 +0000 (17:08 +0000)
Properly handle the TMPNAME events

Bug: https://bugzilla.samba.org/show_bug.cgi?id=15864
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/smbd/notify_inotify.c

index 0e546e7c4052481423fd0bee232d9b863bd6b7f2..a8eadf4efbf4a0e7fb76f7e826aea6b3c57b8c7f 100644 (file)
@@ -25,6 +25,7 @@
 #include "../librpc/gen_ndr/notify.h"
 #include "smbd/smbd.h"
 #include "lib/util/sys_rw.h"
+#include "smbd/globals.h"
 
 #include <sys/inotify.h>
 
@@ -55,6 +56,7 @@ struct inotify_watch_context {
        uint32_t filter; /* the windows completion filter */
        const char *path;
 
+       char *last_mkdir;
        struct inotify_event *moved_from_event;
 };
 
@@ -218,7 +220,7 @@ static void save_moved_from(struct tevent_context *ev,
                         w);
 }
 
-static void handle_local_rename(struct inotify_watch_context *w,
+static bool handle_local_rename(struct inotify_watch_context *w,
                                struct inotify_event *to)
 {
        struct inotify_private *in = w->in;
@@ -226,6 +228,42 @@ static void handle_local_rename(struct inotify_watch_context *w,
        struct notify_event ne = {};
        uint32_t filter;
 
+       if ((w->last_mkdir != NULL) && (w->moved_from_event != NULL) &&
+           IS_SMBD_TMPNAME(w->moved_from_event->name, NULL))
+       {
+               if (strcmp(to->name, w->last_mkdir) == 0) {
+                       /*
+                        * Assume this is a rename() from smbd after a
+                        * mkdir of the real target directory. See the
+                        * comment about RENAME_NOREPLACE in
+                        * mkdir_internals(). We have already sent out
+                        * the mkdir notify event, this MOVED_FROM/TO
+                        * pair is just internal fluff that the client
+                        * should not get wind of via notify.
+                        */
+                       TALLOC_FREE(w->last_mkdir);
+                       TALLOC_FREE(w->moved_from_event);
+                       return true;
+               }
+
+               if (strcmp(w->moved_from_event->name, w->last_mkdir) == 0) {
+                       /*
+                        * Assume this is a renameat2() from smbd's
+                        * mkdir_internal().
+                        */
+                       TALLOC_FREE(w->last_mkdir);
+                       TALLOC_FREE(w->moved_from_event);
+
+                       /*
+                        * Pretend this is not a rename but a new
+                        * directory.
+                        */
+                       to->mask = IN_ISDIR | IN_CREATE;
+
+                       return false;
+               }
+       }
+
        ne = (struct notify_event){
                .action = NOTIFY_ACTION_OLD_NAME,
                .path = from->name,
@@ -250,10 +288,10 @@ static void handle_local_rename(struct inotify_watch_context *w,
        }
 
        if (to->mask & IN_ISDIR) {
-               return;
+               return true;
        }
        if ((w->filter & FILE_NOTIFY_CHANGE_CREATION) == 0) {
-               return;
+               return true;
        }
 
        /*
@@ -268,6 +306,8 @@ static void handle_local_rename(struct inotify_watch_context *w,
        if (filter_match(w, to)) {
                w->callback(in->ctx, w->private_data, &ne, filter);
        }
+
+       return true;
 }
 
 /*
@@ -307,12 +347,29 @@ static void inotify_dispatch(struct tevent_context *ev,
 
        if (e->mask & IN_MOVED_TO) {
                for (w = in->watches; w != NULL; w = w->next) {
-                       if ((w->wd == e->wd) && (w->moved_from_event != NULL) &&
+                       if ((w->wd == e->wd) &&
+                           (w->moved_from_event != NULL) &&
                            (w->moved_from_event->cookie == e->cookie))
                        {
-                               handle_local_rename(w, e);
-                               return;
+                               bool handled = handle_local_rename(w, e);
+                               if (handled) {
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       if ((e->mask & IN_CREATE) && (e->mask & IN_ISDIR)) {
+               for (w = in->watches; w != NULL; w = w->next) {
+                       if (w->wd != e->wd) {
+                               continue;
                        }
+                       TALLOC_FREE(w->last_mkdir);
+                       w->last_mkdir = talloc_strdup(w, e->name);
+               }
+
+               if (IS_SMBD_TMPNAME(e->name, NULL)) {
+                       return;
                }
        }