r23779: Change from v2 or later to v3 or later.
[gd/samba/.git] / source3 / smbd / notify.c
index 5c1143a28e2dbce448b1fc6f5d6a19f4e44417d3..eecf9fa57cae5c4708797c2f0b26c88d3cb94cef 100644 (file)
@@ -7,7 +7,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
 #include "includes.h"
 
+/* Max size we can send to client in a notify response. */
+extern int max_send;
+
+struct notify_change_request {
+       struct notify_change_request *prev, *next;
+       struct files_struct *fsp;       /* backpointer for cancel by mid */
+       char request_buf[smb_size];
+       uint32 filter;
+       uint32 current_bufsize;
+       struct notify_mid_map *mid_map;
+       void *backend_data;
+};
+
+static void notify_fsp(files_struct *fsp, uint32 action, const char *name);
+
 static struct notify_mid_map *notify_changes_by_mid;
 
 /*
@@ -35,6 +50,17 @@ struct notify_mid_map {
        uint16 mid;
 };
 
+static BOOL notify_change_record_identical(struct notify_change *c1,
+                                       struct notify_change *c2)
+{
+       /* Note this is deliberately case sensitive. */
+       if (c1->action == c2->action &&
+                       strcmp(c1->name, c2->name) == 0) {
+               return True;
+       }
+       return False;
+}
+
 static BOOL notify_marshall_changes(int num_changes,
                                    struct notify_change *changes,
                                    prs_struct *ps)
@@ -43,11 +69,20 @@ static BOOL notify_marshall_changes(int num_changes,
        UNISTR uni_name;
 
        for (i=0; i<num_changes; i++) {
-               struct notify_change *c = &changes[i];
+               struct notify_change *c;
                size_t namelen;
                uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid
                                 * signed/unsigned issues */
 
+               /* Coalesce any identical records. */
+               while (i+1 < num_changes &&
+                       notify_change_record_identical(&changes[i],
+                                               &changes[i+1])) {
+                       i++;
+               }
+
+               c = &changes[i];
+
                namelen = convert_string_allocate(
                        NULL, CH_UNIX, CH_UTF16LE, c->name, strlen(c->name)+1,
                        &uni_name.buffer, True);
@@ -94,6 +129,7 @@ static BOOL notify_marshall_changes(int num_changes,
 static void change_notify_reply_packet(const char *request_buf,
                                       NTSTATUS error_code)
 {
+       const char *inbuf = request_buf;
        char outbuf[smb_size+38];
 
        memset(outbuf, '\0', sizeof(outbuf));
@@ -105,7 +141,7 @@ static void change_notify_reply_packet(const char *request_buf,
         * Seems NT needs a transact command with an error code
         * in it. This is a longer packet than a simple error.
         */
-       set_message(outbuf,18,0,False);
+       set_message(inbuf,outbuf,18,0,False);
 
        show_msg(outbuf);
        if (!send_smb(smbd_server_fd(),outbuf))
@@ -113,25 +149,28 @@ static void change_notify_reply_packet(const char *request_buf,
                                    "failed.");
 }
 
-void change_notify_reply(const char *request_buf, uint32 max_param_count,
-                        int num_changes, struct notify_change *changes)
+void change_notify_reply(const char *request_buf,
+                        struct notify_change_buf *notify_buf)
 {
        char *outbuf = NULL;
        prs_struct ps;
-       size_t buflen = smb_size+38+max_param_count;
+       size_t buflen;
 
-       if (num_changes == -1) {
+       if (notify_buf->num_changes == -1) {
                change_notify_reply_packet(request_buf, NT_STATUS_OK);
                return;
        }
 
        if (!prs_init(&ps, 0, NULL, False)
-           || !notify_marshall_changes(num_changes, changes, &ps)) {
+           || !notify_marshall_changes(notify_buf->num_changes,
+                                       notify_buf->changes, &ps)) {
                change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY);
                goto done;
        }
 
-       if (prs_offset(&ps) > max_param_count) {
+       buflen = smb_size+38+prs_offset(&ps) + 4 /* padding */;
+
+       if (buflen > max_send) {
                /*
                 * We exceed what the client is willing to accept. Send
                 * nothing.
@@ -147,7 +186,7 @@ void change_notify_reply(const char *request_buf, uint32 max_param_count,
 
        construct_reply_common(request_buf, outbuf);
 
-       if (send_nt_replies(outbuf, buflen, NT_STATUS_OK, prs_data_p(&ps),
+       if (send_nt_replies(request_buf, outbuf, buflen, NT_STATUS_OK, prs_data_p(&ps),
                            prs_offset(&ps), NULL, 0) == -1) {
                exit_server("change_notify_reply_packet: send_smb failed.");
        }
@@ -155,9 +194,52 @@ void change_notify_reply(const char *request_buf, uint32 max_param_count,
  done:
        SAFE_FREE(outbuf);
        prs_mem_free(&ps);
+
+       TALLOC_FREE(notify_buf->changes);
+       notify_buf->num_changes = 0;
+}
+
+static void notify_callback(void *private_data, const struct notify_event *e)
+{
+       files_struct *fsp = (files_struct *)private_data;
+       DEBUG(10, ("notify_callback called for %s\n", fsp->fsp_name));
+       notify_fsp(fsp, e->action, e->path);
+}
+
+NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
+                             BOOL recursive)
+{
+       char *fullpath;
+       struct notify_entry e;
+       NTSTATUS status;
+
+       SMB_ASSERT(fsp->notify == NULL);
+
+       if (!(fsp->notify = TALLOC_ZERO_P(NULL, struct notify_change_buf))) {
+               DEBUG(0, ("talloc failed\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (asprintf(&fullpath, "%s/%s", fsp->conn->connectpath,
+                    fsp->fsp_name) == -1) {
+               DEBUG(0, ("asprintf failed\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       e.path = fullpath;
+       e.filter = filter;
+       e.subdir_filter = 0;
+       if (recursive) {
+               e.subdir_filter = filter;
+       }
+
+       status = notify_add(fsp->conn->notify_ctx, &e, notify_callback, fsp);
+       SAFE_FREE(fullpath);
+
+       return status;
 }
 
-NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count,
+NTSTATUS change_notify_add_request(const char *inbuf, 
                                   uint32 filter, BOOL recursive,
                                   struct files_struct *fsp)
 {
@@ -174,7 +256,7 @@ NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count,
        map->req = request;
 
        memcpy(request->request_buf, inbuf, sizeof(request->request_buf));
-       request->max_param_count = max_param_count;
+       request->current_bufsize = 0;
        request->filter = filter;
        request->fsp = fsp;
        request->backend_data = NULL;
@@ -211,7 +293,7 @@ static void change_notify_remove_request(struct notify_change_request *remove_re
        }
 
        if (req == NULL) {
-               smb_panic("notify_req not found in fsp's requests\n");
+               smb_panic("notify_req not found in fsp's requests");
        }
 
        DLIST_REMOVE(fsp->notify->requests, req);
@@ -275,10 +357,10 @@ void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
        SAFE_FREE(fullpath);
 }
 
-void notify_fsp(files_struct *fsp, uint32 action, const char *name)
+static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
 {
        struct notify_change *change, *changes;
-       char *name2;
+       char *tmp;
 
        if (fsp->notify == NULL) {
                /*
@@ -287,25 +369,17 @@ void notify_fsp(files_struct *fsp, uint32 action, const char *name)
                return;
        }
 
-       if (!(name2 = talloc_strdup(fsp->notify, name))) {
-               DEBUG(0, ("talloc_strdup failed\n"));
-                       return;
-               }
-
-       string_replace(name2, '/', '\\');
-
        /*
         * Someone has triggered a notify previously, queue the change for
-        * later. TODO: Limit the number of changes queued, test how filters
-        * apply here. Do we have to store them?
+        * later.
         */
 
-       if ((fsp->notify->num_changes > 30) || (name == NULL)) {
+       if ((fsp->notify->num_changes > 1000) || (name == NULL)) {
                /*
-                * W2k3 seems to store at most 30 changes.
+                * The real number depends on the client buf, just provide a
+                * guard against a DoS here.
                 */
                TALLOC_FREE(fsp->notify->changes);
-               TALLOC_FREE(name2);
                fsp->notify->num_changes = -1;
                return;
        }
@@ -318,7 +392,6 @@ void notify_fsp(files_struct *fsp, uint32 action, const char *name)
                      fsp->notify, fsp->notify->changes,
                      struct notify_change, fsp->notify->num_changes+1))) {
                DEBUG(0, ("talloc_realloc failed\n"));
-               TALLOC_FREE(name2);
                return;
        }
 
@@ -326,7 +399,14 @@ void notify_fsp(files_struct *fsp, uint32 action, const char *name)
 
        change = &(fsp->notify->changes[fsp->notify->num_changes]);
 
-       change->name = talloc_move(changes, &name2);
+       if (!(tmp = talloc_strdup(changes, name))) {
+               DEBUG(0, ("talloc_strdup failed\n"));
+               return;
+       }
+
+       string_replace(tmp, '/', '\\');
+       change->name = tmp;     
+
        change->action = action;
        fsp->notify->num_changes += 1;
 
@@ -342,7 +422,7 @@ void notify_fsp(files_struct *fsp, uint32 action, const char *name)
                 * We have to send the two rename events in one reply. So hold
                 * the first part back.
                 */
-       return;
+               return;
        }
 
        /*
@@ -352,14 +432,9 @@ void notify_fsp(files_struct *fsp, uint32 action, const char *name)
         */
 
        change_notify_reply(fsp->notify->requests->request_buf,
-                           fsp->notify->requests->max_param_count,
-                           fsp->notify->num_changes,
-                           fsp->notify->changes);
+                           fsp->notify);
 
        change_notify_remove_request(fsp->notify->requests);
-
-       TALLOC_FREE(fsp->notify->changes);
-       fsp->notify->num_changes = 0;
 }
 
 char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter)
@@ -400,7 +475,7 @@ char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter)
        return result;
 }
 
-struct sys_notify_context *sys_notify_context_create(struct share_params *scfg,
+struct sys_notify_context *sys_notify_context_create(connection_struct *conn,
                                                     TALLOC_CTX *mem_ctx, 
                                                     struct event_context *ev)
 {
@@ -412,6 +487,7 @@ struct sys_notify_context *sys_notify_context_create(struct share_params *scfg,
        }
 
        ctx->ev = ev;
+       ctx->conn = conn;
        ctx->private_data = NULL;
        return ctx;
 }
@@ -423,10 +499,7 @@ NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
                                           struct notify_event *ev),
                          void *private_data, void *handle)
 {
-#ifdef HAVE_INOTIFY
-       return inotify_watch(ctx, e, callback, private_data, handle);
-#else
-       return NT_STATUS_OK;
-#endif
+       return SMB_VFS_NOTIFY_WATCH(ctx->conn, ctx, e, callback, private_data,
+                                   handle);
 }