[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[sfrench/samba-autobuild/.git] / source3 / smbd / notify.c
index 5491f8eaf8adfb5e2cf092008b6efc962473bb18..b8c5085b4174d9c8ef70af2d14492601daa22ac2 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,
@@ -16,8 +16,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
@@ -25,9 +24,9 @@
 struct notify_change_request {
        struct notify_change_request *prev, *next;
        struct files_struct *fsp;       /* backpointer for cancel by mid */
-       char request_buf[smb_size];
+       uint8 request_buf[smb_size];
        uint32 filter;
-       uint32 current_bufsize;
+       uint32 max_param;
        struct notify_mid_map *mid_map;
        void *backend_data;
 };
@@ -47,19 +46,40 @@ 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)
+                               uint32 max_offset,
+                               struct notify_change *changes,
+                               prs_struct *ps)
 {
        int i;
        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);
@@ -90,6 +110,11 @@ static BOOL notify_marshall_changes(int num_changes,
                prs_set_offset(ps, prs_offset(ps)-2);
 
                SAFE_FREE(uni_name.buffer);
+
+               if (prs_offset(ps) > max_offset) {
+                       /* Too much data for client. */
+                       return False;
+               }
        }
 
        return True;
@@ -103,14 +128,13 @@ static BOOL notify_marshall_changes(int num_changes,
  Setup the common parts of the return packet and send it.
 *****************************************************************************/
 
-static void change_notify_reply_packet(const char *request_buf,
+static void change_notify_reply_packet(const uint8 *request_buf,
                                       NTSTATUS error_code)
 {
-       const char *inbuf = request_buf;
        char outbuf[smb_size+38];
 
        memset(outbuf, '\0', sizeof(outbuf));
-       construct_reply_common(request_buf, outbuf);
+       construct_reply_common((char *)request_buf, outbuf);
 
        ERROR_NT(error_code);
 
@@ -118,7 +142,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(inbuf,outbuf,18,0,False);
+       set_message(outbuf,18,0,False);
 
        show_msg(outbuf);
        if (!send_smb(smbd_server_fd(),outbuf))
@@ -126,41 +150,52 @@ static void change_notify_reply_packet(const char *request_buf,
                                    "failed.");
 }
 
-void change_notify_reply(const char *request_buf,
+void change_notify_reply(const uint8 *request_buf, uint32 max_param,
                         struct notify_change_buf *notify_buf)
 {
-       char *outbuf = NULL;
        prs_struct ps;
-       size_t buflen;
+       struct smb_request *req = NULL;
+       uint8 tmp_request[smb_size];
 
        if (notify_buf->num_changes == -1) {
                change_notify_reply_packet(request_buf, NT_STATUS_OK);
+               notify_buf->num_changes = 0;
                return;
        }
 
-       if (!prs_init(&ps, 0, NULL, False)
-           || !notify_marshall_changes(notify_buf->num_changes,
+       prs_init(&ps, 0, NULL, MARSHALL);
+
+       if (!notify_marshall_changes(notify_buf->num_changes, max_param,
                                        notify_buf->changes, &ps)) {
-               change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY);
+               /*
+                * We exceed what the client is willing to accept. Send
+                * nothing.
+                */
+               change_notify_reply_packet(request_buf, NT_STATUS_OK);
                goto done;
        }
 
-       buflen = smb_size+38+prs_offset(&ps) + 4 /* padding */;
-
-       if (!(outbuf = SMB_MALLOC_ARRAY(char, buflen))) {
+       if (!(req = talloc(talloc_tos(), struct smb_request))) {
                change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY);
                goto done;
        }
 
-       construct_reply_common(request_buf, outbuf);
+       memcpy(tmp_request, request_buf, smb_size);
 
-       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.");
-       }
+       /*
+        * We're only interested in the header fields here
+        */
+
+       smb_setlen((char *)tmp_request, smb_size);
+       SCVAL(tmp_request, smb_wct, 0);
+
+       init_smb_request(req, tmp_request);
+
+       send_nt_replies(req, NT_STATUS_OK, prs_data_p(&ps),
+                       prs_offset(&ps), NULL, 0);
 
  done:
-       SAFE_FREE(outbuf);
+       TALLOC_FREE(req);
        prs_mem_free(&ps);
 
        TALLOC_FREE(notify_buf->changes);
@@ -207,7 +242,7 @@ NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
        return status;
 }
 
-NTSTATUS change_notify_add_request(const char *inbuf, 
+NTSTATUS change_notify_add_request(const uint8 *inbuf, uint32 max_param,
                                   uint32 filter, BOOL recursive,
                                   struct files_struct *fsp)
 {
@@ -224,11 +259,11 @@ NTSTATUS change_notify_add_request(const char *inbuf,
        map->req = request;
 
        memcpy(request->request_buf, inbuf, sizeof(request->request_buf));
-       request->current_bufsize = 0;
+       request->max_param = max_param;
        request->filter = filter;
        request->fsp = fsp;
        request->backend_data = NULL;
-       
+
        DLIST_ADD_END(fsp->notify->requests, request,
                      struct notify_change_request *);
 
@@ -261,7 +296,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);
@@ -328,7 +363,7 @@ void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
 static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
 {
        struct notify_change *change, *changes;
-       pstring name2;
+       char *tmp;
 
        if (fsp->notify == NULL) {
                /*
@@ -337,9 +372,6 @@ static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
                return;
        }
 
-       pstrcpy(name2, name);
-       string_replace(name2, '/', '\\');
-
        /*
         * Someone has triggered a notify previously, queue the change for
         * later.
@@ -370,11 +402,14 @@ static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
 
        change = &(fsp->notify->changes[fsp->notify->num_changes]);
 
-       if (!(change->name = talloc_strdup(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;
 
@@ -400,6 +435,7 @@ static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
         */
 
        change_notify_reply(fsp->notify->requests->request_buf,
+                           fsp->notify->requests->max_param,
                            fsp->notify);
 
        change_notify_remove_request(fsp->notify->requests);