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;
/*
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)
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);
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));
* 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))
"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.
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.");
}
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)
{
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;
}
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);
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) {
/*
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;
}
fsp->notify, fsp->notify->changes,
struct notify_change, fsp->notify->num_changes+1))) {
DEBUG(0, ("talloc_realloc failed\n"));
- TALLOC_FREE(name2);
return;
}
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;
* We have to send the two rename events in one reply. So hold
* the first part back.
*/
- return;
+ return;
}
/*
*/
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)
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)
{
}
ctx->ev = ev;
+ ctx->conn = conn;
ctx->private_data = NULL;
return 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);
}