diff options
Diffstat (limited to 'source/smbd/notify.c')
-rw-r--r-- | source/smbd/notify.c | 226 |
1 files changed, 48 insertions, 178 deletions
diff --git a/source/smbd/notify.c b/source/smbd/notify.c index 2c762bd7590..ce7680b49a1 100644 --- a/source/smbd/notify.c +++ b/source/smbd/notify.c @@ -3,6 +3,7 @@ change notify handling Copyright (C) Andrew Tridgell 2000 Copyright (C) Jeremy Allison 1994-1998 + Copyright (C) Volker Lendecke 2007 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 @@ -24,22 +25,6 @@ static struct cnotify_fns *cnotify; static struct notify_mid_map *notify_changes_by_mid; -/**************************************************************************** - This is the structure to queue to implement NT change - notify. It consists of smb_size bytes stored from the - transact command (to keep the mid, tid etc around). - Plus the fid to examine and notify private data. -*****************************************************************************/ - -struct change_notify { - struct change_notify *next, *prev; - files_struct *fsp; - uint32 flags; - uint32 max_param_count; - char request_buf[smb_size]; - void *change_data; -}; - /* * For NTCancel, we need to find the notify_change_request indexed by * mid. Separate list here. @@ -51,9 +36,7 @@ struct notify_mid_map { uint16 mid; }; -static struct change_notify *change_notify_list; - -static BOOL notify_marshall_changes(unsigned num_changes, +static BOOL notify_marshall_changes(int num_changes, struct notify_change *changes, prs_struct *ps) { @@ -132,12 +115,17 @@ static void change_notify_reply_packet(const char *request_buf, } void change_notify_reply(const char *request_buf, uint32 max_param_count, - unsigned num_changes, struct notify_change *changes) + int num_changes, struct notify_change *changes) { char *outbuf = NULL; prs_struct ps; size_t buflen = smb_size+38+max_param_count; + if (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)) { change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY); @@ -170,19 +158,6 @@ void change_notify_reply(const char *request_buf, uint32 max_param_count, prs_mem_free(&ps); } -/**************************************************************************** - Remove an entry from the list and free it, also closing any - directory handle if necessary. -*****************************************************************************/ - -static void change_notify_remove(struct change_notify *cnbp) -{ - cnotify->remove_notify(cnbp->change_data); - DLIST_REMOVE(change_notify_list, cnbp); - ZERO_STRUCTP(cnbp); - SAFE_FREE(cnbp); -} - NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count, uint32 filter, struct files_struct *fsp) { @@ -202,6 +177,10 @@ NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count, request->max_param_count = max_param_count; request->filter = filter; request->fsp = fsp; + + request->backend_data = cnotify->notify_add(NULL, smbd_event_context(), + fsp, &request->filter); + DLIST_ADD_END(fsp->notify->requests, request, struct notify_change_request *); @@ -240,6 +219,7 @@ static void change_notify_remove_request(struct notify_change_request *remove_re DLIST_REMOVE(fsp->notify->requests, req); DLIST_REMOVE(notify_changes_by_mid, req->mid_map); SAFE_FREE(req->mid_map); + TALLOC_FREE(req->backend_data); SAFE_FREE(req); } @@ -283,141 +263,6 @@ void remove_pending_change_notify_requests_by_fid(files_struct *fsp, } } -/**************************************************************************** - Delete entries by filename and cnum from the change notify pending queue. - Always send reply. -*****************************************************************************/ - -void remove_pending_change_notify_requests_by_filename(files_struct *fsp, NTSTATUS status) -{ - struct change_notify *cnbp, *next; - - for (cnbp=change_notify_list; cnbp; cnbp=next) { - next=cnbp->next; - /* - * We know it refers to the same directory if the connection number and - * the filename are identical. - */ - if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) { - change_notify_reply_packet(cnbp->request_buf, status); - change_notify_remove(cnbp); - } - } -} - -/**************************************************************************** - Set the current change notify timeout to the lowest value across all service - values. -****************************************************************************/ - -void set_change_notify_timeout(int val) -{ - if (val > 0) { - cnotify->select_time = MIN(cnotify->select_time, val); - } -} - -/**************************************************************************** - Longest time to sleep for before doing a change notify scan. -****************************************************************************/ - -int change_notify_timeout(void) -{ - return cnotify->select_time; -} - -/**************************************************************************** - Process the change notify queue. Note that this is only called as root. - Returns True if there are still outstanding change notify requests on the - queue. -*****************************************************************************/ - -BOOL process_pending_change_notify_queue(time_t t) -{ - struct change_notify *cnbp, *next; - uint16 vuid; - - for (cnbp=change_notify_list; cnbp; cnbp=next) { - next=cnbp->next; - - vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid); - - if (cnbp->fsp->notify->num_changes != 0) { - DEBUG(10,("process_pending_change_notify_queue: %s " - "has %d changes!\n", cnbp->fsp->fsp_name, - cnbp->fsp->notify->num_changes)); - change_notify_reply(cnbp->request_buf, - cnbp->max_param_count, - cnbp->fsp->notify->num_changes, - cnbp->fsp->notify->changes); - change_notify_remove(cnbp); - continue; - } - - if (cnotify->check_notify(cnbp->fsp->conn, vuid, - cnbp->fsp->fsp_name, cnbp->flags, - cnbp->change_data, t)) { - DEBUG(10,("process_pending_change_notify_queue: dir " - "%s changed !\n", cnbp->fsp->fsp_name )); - change_notify_reply(cnbp->request_buf, - cnbp->max_param_count, - cnbp->fsp->notify->num_changes, - cnbp->fsp->notify->changes); - change_notify_remove(cnbp); - } - } - - return (change_notify_list != NULL); -} - -/**************************************************************************** - Now queue an entry on the notify change list. - We only need to save smb_size bytes from this incoming packet - as we will always by returning a 'read the directory yourself' - error. -****************************************************************************/ - -BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, - uint32 flags, uint32 max_param_count) -{ - struct change_notify *cnbp; - - if((cnbp = SMB_MALLOC_P(struct change_notify)) == NULL) { - DEBUG(0,("change_notify_set: malloc fail !\n" )); - return False; - } - - ZERO_STRUCTP(cnbp); - - memcpy(cnbp->request_buf, inbuf, smb_size); - cnbp->fsp = fsp; - cnbp->flags = flags; - cnbp->max_param_count = max_param_count; - cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, - flags); - - if (!cnbp->change_data) { - SAFE_FREE(cnbp); - return False; - } - - DLIST_ADD(change_notify_list, cnbp); - - /* Push the MID of this packet on the signing queue. */ - srv_defer_sign_response(SVAL(inbuf,smb_mid)); - - return True; -} - -int change_notify_fd(void) -{ - if (cnotify) { - return cnotify->notification_fd; - } - - return -1; -} - /* notify message definition Offset Data length. @@ -571,7 +416,7 @@ void notify_fname(connection_struct *conn, const char *path, TALLOC_FREE(parent); } -static void notify_fsp(files_struct *fsp, struct notify_message *msg) +void notify_fsp(files_struct *fsp, uint32 action, char *name) { struct notify_change *change, *changes; @@ -582,8 +427,7 @@ static void notify_fsp(files_struct *fsp, struct notify_message *msg) return; } - if ((fsp->notify->requests != NULL) - && (fsp->notify->requests->filter & msg->filter)) { + if (fsp->notify->requests != NULL) { /* * Someone is waiting for the change, trigger the reply * immediately. @@ -594,8 +438,18 @@ static void notify_fsp(files_struct *fsp, struct notify_message *msg) struct notify_change_request *req = fsp->notify->requests; struct notify_change onechange; - onechange.action = msg->action; - onechange.name = msg->name; + if (name == NULL) { + /* + * Catch-all change, possibly from notify_hash.c + */ + change_notify_reply(req->request_buf, + req->max_param_count, + -1, NULL); + return; + } + + onechange.action = action; + onechange.name = name; change_notify_reply(req->request_buf, req->max_param_count, 1, &onechange); @@ -609,6 +463,19 @@ static void notify_fsp(files_struct *fsp, struct notify_message *msg) * apply here. Do we have to store them? */ + if ((fsp->notify->num_changes > 30) || (name == NULL)) { + /* + * W2k3 seems to store at most 30 changes. + */ + TALLOC_FREE(fsp->notify->changes); + fsp->notify->num_changes = -1; + return; + } + + if (fsp->notify->num_changes == -1) { + return; + } + if (!(changes = TALLOC_REALLOC_ARRAY( fsp->notify, fsp->notify->changes, struct notify_change, fsp->notify->num_changes+1))) { @@ -620,11 +487,11 @@ static void notify_fsp(files_struct *fsp, struct notify_message *msg) change = &(fsp->notify->changes[fsp->notify->num_changes]); - if (!(change->name = talloc_strdup(changes, msg->name))) { + if (!(change->name = talloc_strdup(changes, name))) { DEBUG(0, ("talloc_strdup failed\n")); return; } - change->action = msg->action; + change->action = action; fsp->notify->num_changes += 1; return; @@ -645,7 +512,10 @@ static void notify_message_callback(int msgtype, struct process_id pid, for(fsp = fsp_find_di_first(msg.dev, msg.inode); fsp; fsp = fsp_find_di_next(fsp)) { - notify_fsp(fsp, &msg); + if ((fsp->notify->requests != NULL) + && (fsp->notify->requests->filter & msg.filter)) { + notify_fsp(fsp, msg.action, msg.name); + } } } @@ -659,11 +529,11 @@ BOOL init_change_notify(void) #if HAVE_KERNEL_CHANGE_NOTIFY if (cnotify == NULL && lp_kernel_change_notify()) - cnotify = kernel_notify_init(); + cnotify = kernel_notify_init(smbd_event_context()); #endif #if HAVE_FAM_CHANGE_NOTIFY if (cnotify == NULL && lp_fam_change_notify()) - cnotify = fam_notify_init(); + cnotify = fam_notify_init(smbd_event_context()); #endif if (!cnotify) cnotify = hash_notify_init(); |