summaryrefslogtreecommitdiff
path: root/source3/smbd/notify.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/notify.c')
-rw-r--r--source3/smbd/notify.c202
1 files changed, 136 insertions, 66 deletions
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index 674505ac5b..9957c1fc5d 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -22,6 +22,7 @@
#include "includes.h"
static struct cnotify_fns *cnotify;
+static struct notify_mid_map *notify_changes_by_mid;
/****************************************************************************
This is the structure to queue to implement NT change
@@ -41,14 +42,15 @@ struct change_notify {
static struct change_notify *change_notify_list;
-static BOOL notify_marshall_changes(struct notify_changes *changes,
+static BOOL notify_marshall_changes(unsigned num_changes,
+ struct notify_change *changes,
prs_struct *ps)
{
int i;
UNISTR uni_name;
- for (i=0; i<changes->num_changes; i++) {
- struct notify_change *c = &changes->changes[i];
+ for (i=0; i<num_changes; i++) {
+ struct notify_change *c = &changes[i];
size_t namelen;
uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid
* signed/unsigned issues */
@@ -66,7 +68,7 @@ static BOOL notify_marshall_changes(struct notify_changes *changes,
* Offset to next entry, only if there is one
*/
- u32_tmp = (i == changes->num_changes-1) ? 0 : namelen + 12;
+ u32_tmp = (i == num_changes-1) ? 0 : namelen + 12;
if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail;
u32_tmp = c->action;
@@ -96,8 +98,7 @@ static BOOL notify_marshall_changes(struct notify_changes *changes,
Setup the common parts of the return packet and send it.
*****************************************************************************/
-static void change_notify_reply_packet(const char *request_buf,
- NTSTATUS error_code)
+void change_notify_reply_packet(const char *request_buf, NTSTATUS error_code)
{
char outbuf[smb_size+38];
@@ -118,14 +119,14 @@ static void change_notify_reply_packet(const char *request_buf,
}
void change_notify_reply(const char *request_buf, uint32 max_param_count,
- files_struct *fsp)
+ unsigned num_changes, struct notify_change *changes)
{
char *outbuf = NULL;
prs_struct ps;
size_t buflen = smb_size+38+max_param_count;
if (!prs_init(&ps, 0, NULL, False)
- || !notify_marshall_changes(fsp->notify, &ps)) {
+ || !notify_marshall_changes(num_changes, changes, &ps)) {
change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY);
goto done;
}
@@ -154,8 +155,6 @@ void change_notify_reply(const char *request_buf, uint32 max_param_count,
done:
SAFE_FREE(outbuf);
prs_mem_free(&ps);
- fsp->notify->num_changes = 0;
- TALLOC_FREE(fsp->notify->changes);
}
/****************************************************************************
@@ -171,39 +170,100 @@ static void change_notify_remove(struct change_notify *cnbp)
SAFE_FREE(cnbp);
}
-/****************************************************************************
- Delete entries by fnum from the change notify pending queue.
-*****************************************************************************/
+NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count,
+ struct files_struct *fsp)
+{
+ struct notify_change_request *request = NULL;
+ struct notify_mid_map *map = NULL;
+
+ if (!(request = SMB_MALLOC_P(struct notify_change_request))
+ || !(map = SMB_MALLOC_P(struct notify_mid_map))) {
+ SAFE_FREE(request);
+ return NT_STATUS_NO_MEMORY;
+ }
-void remove_pending_change_notify_requests_by_fid(files_struct *fsp, NTSTATUS status)
+ request->mid_map = map;
+ map->req = request;
+
+ memcpy(request->request_buf, inbuf, sizeof(request->request_buf));
+ request->max_param_count = max_param_count;
+ request->fsp = fsp;
+ DLIST_ADD_END(fsp->notify->requests, request,
+ struct notify_change_request *);
+
+ map->mid = SVAL(inbuf, smb_mid);
+ DLIST_ADD(notify_changes_by_mid, map);
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(inbuf,smb_mid));
+
+ return NT_STATUS_OK;
+}
+
+static void change_notify_remove_request(struct notify_change_request *remove_req)
{
- struct change_notify *cnbp, *next;
+ files_struct *fsp;
+ struct notify_change_request *req;
- for (cnbp=change_notify_list; cnbp; cnbp=next) {
- next=cnbp->next;
- if (cnbp->fsp->fnum == fsp->fnum) {
- change_notify_reply_packet(cnbp->request_buf, status);
- change_notify_remove(cnbp);
+ /*
+ * Paranoia checks, the fsp referenced must must have the request in
+ * its list of pending requests
+ */
+
+ fsp = remove_req->fsp;
+ SMB_ASSERT(fsp->notify != NULL);
+
+ for (req = fsp->notify->requests; req; req = req->next) {
+ if (req == remove_req) {
+ break;
}
}
+ SMB_ASSERT(req != NULL);
+
+ DLIST_REMOVE(fsp->notify->requests, req);
+ DLIST_REMOVE(notify_changes_by_mid, req->mid_map);
+ SAFE_FREE(req->mid_map);
+ SAFE_FREE(req);
}
/****************************************************************************
Delete entries by mid from the change notify pending queue. Always send reply.
*****************************************************************************/
-void remove_pending_change_notify_requests_by_mid(int mid)
+void remove_pending_change_notify_requests_by_mid(uint16 mid)
{
- struct change_notify *cnbp, *next;
+ struct notify_mid_map *map;
- for (cnbp=change_notify_list; cnbp; cnbp=next) {
- next=cnbp->next;
- if(SVAL(cnbp->request_buf,smb_mid) == mid) {
- change_notify_reply_packet(cnbp->request_buf,
- NT_STATUS_CANCELLED);
- change_notify_remove(cnbp);
+ for (map = notify_changes_by_mid; map; map = map->next) {
+ if (map->mid == mid) {
+ break;
}
}
+
+ if (map == NULL) {
+ return;
+ }
+
+ change_notify_reply_packet(map->req->request_buf, NT_STATUS_CANCELLED);
+ change_notify_remove_request(map->req);
+}
+
+/****************************************************************************
+ Delete entries by fnum from the change notify pending queue.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
+ NTSTATUS status)
+{
+ if (fsp->notify == NULL) {
+ return;
+ }
+
+ while (fsp->notify->requests != NULL) {
+ change_notify_reply_packet(
+ fsp->notify->requests->request_buf, status);
+ change_notify_remove_request(fsp->notify->requests);
+ }
}
/****************************************************************************
@@ -271,7 +331,8 @@ BOOL process_pending_change_notify_queue(time_t t)
cnbp->fsp->notify->num_changes));
change_notify_reply(cnbp->request_buf,
cnbp->max_param_count,
- cnbp->fsp);
+ cnbp->fsp->notify->num_changes,
+ cnbp->fsp->notify->changes);
change_notify_remove(cnbp);
continue;
}
@@ -283,7 +344,8 @@ BOOL process_pending_change_notify_queue(time_t t)
"%s changed !\n", cnbp->fsp->fsp_name ));
change_notify_reply(cnbp->request_buf,
cnbp->max_param_count,
- cnbp->fsp);
+ cnbp->fsp->notify->num_changes,
+ cnbp->fsp->notify->changes);
change_notify_remove(cnbp);
}
}
@@ -472,43 +534,42 @@ void notify_action(connection_struct *conn, const char *parent,
TALLOC_FREE(lck);
}
-static void notify_message_callback(int msgtype, struct process_id pid,
- void *buf, size_t len)
+static void notify_fsp(files_struct *fsp, struct notify_message *msg)
{
- struct notify_message msg;
- files_struct *fsp;
- struct notify_change *changes, *change;
- struct change_notify *cnbp;
+ struct notify_change *change, *changes;
- if (!buf_to_notify_message(buf, len, &msg)) {
+ if (fsp->notify == NULL) {
+ /*
+ * Nobody is waiting, don't queue
+ */
return;
}
- DEBUG(10, ("Received notify_message for 0x%x/%.0f: %d\n",
- (unsigned)msg.dev, (double)msg.inode, msg.action));
+ if (fsp->notify->requests != NULL) {
+ /*
+ * Someone is waiting for the change, trigger the reply
+ * immediately
+ */
- fsp = NULL;
+ struct notify_change_request *req = fsp->notify->requests;
+ struct notify_change onechange;
- for (cnbp = change_notify_list; cnbp != NULL; cnbp = cnbp->next) {
- if ((cnbp->fsp->dev == msg.dev)
- && (cnbp->fsp->inode == msg.inode)) {
- break;
- }
- }
+ onechange.action = msg->action;
+ onechange.name = msg->name;
- if (cnbp != NULL) {
- DEBUG(10, ("Found pending change notify for %s\n",
- cnbp->fsp->fsp_name));
- fsp = cnbp->fsp;
- SMB_ASSERT(fsp->notify->num_changes == 0);
- }
+ change_notify_reply(req->request_buf, req->max_param_count,
+ 1, &onechange);
- if ((fsp == NULL)
- && !(fsp = file_find_dir_lowest_id(msg.dev, msg.inode))) {
- DEBUG(10, ("notify_message: did not find fsp\n"));
+ DLIST_REMOVE(fsp->notify->requests, req);
+ SAFE_FREE(req);
return;
}
+ /*
+ * Someone has triggered a notify previously, queue the change for
+ * later. TODO: Limit the number of changes queued.
+ */
+
if (!(changes = TALLOC_REALLOC_ARRAY(
fsp->notify, fsp->notify->changes,
struct notify_change, fsp->notify->num_changes+1))) {
@@ -520,24 +581,33 @@ static void notify_message_callback(int msgtype, struct process_id pid,
change = &(fsp->notify->changes[fsp->notify->num_changes]);
- if (!(change->name = talloc_strdup(changes, msg.name))) {
+ if (!(change->name = talloc_strdup(changes, msg->name))) {
DEBUG(0, ("talloc_strdup failed\n"));
return;
}
- change->action = msg.action;
+ change->action = msg->action;
fsp->notify->num_changes += 1;
- if (cnbp != NULL) {
- /*
- * Respond directly, we have a someone waiting for this change
- */
- DEBUG(10, ("Found pending cn for %s, responding directly\n",
- cnbp->fsp->fsp_name));
- change_notify_reply(cnbp->request_buf, cnbp->max_param_count,
- cnbp->fsp);
- change_notify_remove(cnbp);
+ return;
+}
+
+static void notify_message_callback(int msgtype, struct process_id pid,
+ void *buf, size_t len)
+{
+ struct notify_message msg;
+ files_struct *fsp;
+
+ if (!buf_to_notify_message(buf, len, &msg)) {
return;
}
+
+ DEBUG(10, ("Received notify_message for 0x%x/%.0f: %d\n",
+ (unsigned)msg.dev, (double)msg.inode, msg.action));
+
+ for(fsp = fsp_find_di_first(msg.dev, msg.inode); fsp;
+ fsp = fsp_find_di_next(fsp)) {
+ notify_fsp(fsp, &msg);
+ }
}
/****************************************************************************