diff options
-rw-r--r-- | source3/include/event.h | 5 | ||||
-rw-r--r-- | source3/include/smb.h | 18 | ||||
-rw-r--r-- | source3/smbd/close.c | 5 | ||||
-rw-r--r-- | source3/smbd/notify.c | 226 | ||||
-rw-r--r-- | source3/smbd/notify_fam.c | 548 | ||||
-rw-r--r-- | source3/smbd/notify_hash.c | 191 | ||||
-rw-r--r-- | source3/smbd/notify_kernel.c | 216 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 14 | ||||
-rw-r--r-- | source3/smbd/process.c | 31 | ||||
-rw-r--r-- | source3/smbd/reply.c | 11 | ||||
-rw-r--r-- | source3/smbd/service.c | 3 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 1 |
12 files changed, 484 insertions, 785 deletions
diff --git a/source3/include/event.h b/source3/include/event.h index f3c468c9b8..ce687eca6d 100644 --- a/source3/include/event.h +++ b/source3/include/event.h @@ -19,11 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -struct event_context; -struct timed_event; - -struct fd_event; - /* bits for file descriptor event flags */ #define EVENT_FD_READ 1 #define EVENT_FD_WRITE 2 diff --git a/source3/include/smb.h b/source3/include/smb.h index df4f47f12c..21643c64a1 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -421,6 +421,8 @@ struct fd_handle { unsigned long file_id; }; +struct event_context; +struct fd_event; struct timed_event; struct idle_event; struct share_mode_entry; @@ -451,6 +453,7 @@ struct notify_change_request { uint32 filter; uint32 max_param_count; struct notify_mid_map *mid_map; + void *backend_data; }; struct notify_change_buf { @@ -458,7 +461,12 @@ struct notify_change_buf { * If no requests are pending, changes are queued here. Simple array, * we only append. */ - unsigned num_changes; + + /* + * num_changes == -1 means that we have got a catch-all change, when + * asked we just return NT_STATUS_OK without specific changes. + */ + int num_changes; struct notify_change *changes; /* @@ -1672,11 +1680,9 @@ struct kernel_oplocks { /* this structure defines the functions for doing change notify in various implementations */ struct cnotify_fns { - void * (*register_notify)(connection_struct *conn, char *path, uint32 flags); - BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t); - void (*remove_notify)(void *data); - int select_time; - int notification_fd; + void *(*notify_add)(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + files_struct *fsp, uint32 *filter); }; #include "smb_macros.h" diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 07f81f988e..714e0615de 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -291,8 +291,6 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, /* unbecome user. */ pop_sec_ctx(); - process_pending_change_notify_queue((time_t)0); - TALLOC_FREE(lck); return status; } @@ -486,10 +484,7 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type) if(ok) { remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING); - remove_pending_change_notify_requests_by_filename(fsp, NT_STATUS_DELETE_PENDING); - } - process_pending_change_notify_queue((time_t)0); } else { TALLOC_FREE(lck); remove_pending_change_notify_requests_by_fid( diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c index 2c762bd759..ce7680b49a 100644 --- a/source3/smbd/notify.c +++ b/source3/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(); diff --git a/source3/smbd/notify_fam.c b/source3/smbd/notify_fam.c index bfef4ac871..306316e49f 100644 --- a/source3/smbd/notify_fam.c +++ b/source3/smbd/notify_fam.c @@ -2,6 +2,7 @@ * FAM file notification support. * * Copyright (c) James Peach 2005 + * 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 @@ -43,422 +44,241 @@ typedef enum FAMCodes FAMCodes; * http://sourceforge.net/projects/bsdfam/ */ -struct fam_req_info -{ - FAMRequest req; - int generation; - FAMCodes code; - enum - { - /* We are waiting for an event. */ - FAM_REQ_MONITORING, - /* An event has been receive, but we haven't been able to send it back - * to the client yet. It is stashed in the code member. - */ - FAM_REQ_FIRED - } state; -}; - -/* Don't initialise this until the first register request. We want a single - * FAM connection for each worker smbd. If we allow the master (parent) smbd to - * open a FAM connection, multiple processes talking on the same socket will - * undoubtedly create havoc. - */ -static FAMConnection global_fc; -static int global_fc_generation; - -#define FAM_TRACE 8 -#define FAM_TRACE_LOW 10 +static void *fam_notify_add(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + files_struct *fsp, uint32 *filter); -#define FAM_EVENT_DRAIN ((uint32_t)(-1)) - -static void * fam_register_notify(connection_struct * conn, - char * path, - uint32 flags); +/* ------------------------------------------------------------------------- */ -static BOOL fam_check_notify(connection_struct * conn, - uint16_t vuid, - char * path, - uint32_t flags, - void * data, - time_t when); +struct fam_notify_ctx { + struct fam_notify_ctx *prev, *next; + FAMConnection *fam_connection; + struct FAMRequest fr; + files_struct *fsp; + char *path; + uint32 filter; +}; -static void fam_remove_notify(void * data); +static struct fam_notify_ctx *fam_notify_list; +static FAMConnection fam_connection; +static void fam_handler(struct event_context *event_ctx, + struct fd_event *fd_event, + uint16 flags, + void *private_data); -static struct cnotify_fns global_fam_notify = +static NTSTATUS fam_open_connection(FAMConnection *fam_conn, + struct event_context *event_ctx) { - fam_register_notify, - fam_check_notify, - fam_remove_notify, - -1, - -1 -}; + int res; + char *name; -/* Turn a FAM event code into a string. Don't rely on specific code values, - * because that might not work across all flavours of FAM. - */ -static const char * -fam_event_str(FAMCodes code) -{ - static const struct { FAMCodes code; const char * name; } evstr[] = - { - { FAMChanged, "FAMChanged"}, - { FAMDeleted, "FAMDeleted"}, - { FAMStartExecuting, "FAMStartExecuting"}, - { FAMStopExecuting, "FAMStopExecuting"}, - { FAMCreated, "FAMCreated"}, - { FAMMoved, "FAMMoved"}, - { FAMAcknowledge, "FAMAcknowledge"}, - { FAMExists, "FAMExists"}, - { FAMEndExist, "FAMEndExist"} - }; - - int i; - - for (i = 0; i < ARRAY_SIZE(evstr); ++i) { - if (code == evstr[i].code) - return(evstr[i].name); - } - - return("<unknown>"); -} + ZERO_STRUCTP(fam_conn); + FAMCONNECTION_GETFD(fam_conn) = -1; -static BOOL -fam_check_reconnect(void) -{ - if (FAMCONNECTION_GETFD(&global_fc) < 0) { - fstring name; + if (asprintf(&name, "smbd (%lu)", (unsigned long)sys_getpid()) == -1) { + DEBUG(0, ("No memory\n")); + return NT_STATUS_NO_MEMORY; + } - global_fc_generation++; - snprintf(name, sizeof(name), "smbd (%lu)", (unsigned long)sys_getpid()); + res = FAMOpen2(fam_conn, name); + SAFE_FREE(name); - if (FAMOpen2(&global_fc, name) < 0) { - DEBUG(0, ("failed to connect to FAM service\n")); - return(False); + if (res < 0) { + DEBUG(5, ("FAM file change notifications not available\n")); + /* + * No idea how to get NT_STATUS from a FAM result + */ + FAMCONNECTION_GETFD(fam_conn) = -1; + return NT_STATUS_UNEXPECTED_IO_ERROR; } - } - global_fam_notify.notification_fd = FAMCONNECTION_GETFD(&global_fc); - return(True); -} + if (event_add_fd(event_ctx, event_ctx, + FAMCONNECTION_GETFD(fam_conn), + EVENT_FD_READ, fam_handler, + (void *)fam_conn) == NULL) { + DEBUG(0, ("event_add_fd failed\n")); + FAMClose(fam_conn); + FAMCONNECTION_GETFD(fam_conn) = -1; + return NT_STATUS_NO_MEMORY; + } -static BOOL -fam_monitor_path(connection_struct * conn, - struct fam_req_info * info, - const char * path, - uint32 flags) -{ - SMB_STRUCT_STAT st; - pstring fullpath; - - DEBUG(FAM_TRACE, ("requesting FAM notifications for '%s'\n", path)); - - /* FAM needs an absolute pathname. */ - - /* It would be better to use reduce_name() here, but reduce_name does not - * actually return the reduced result. How utterly un-useful. - */ - pstrcpy(fullpath, path); - if (!canonicalize_path(conn, fullpath)) { - DEBUG(0, ("failed to canonicalize path '%s'\n", path)); - return(False); - } - - if (*fullpath != '/') { - DEBUG(0, ("canonicalized path '%s' into `%s`\n", path, fullpath)); - DEBUGADD(0, ("but expected an absolute path\n")); - return(False); - } - - if (SMB_VFS_STAT(conn, path, &st) < 0) { - DEBUG(0, ("stat of '%s' failed: %s\n", path, strerror(errno))); - return(False); - } - /* Start monitoring this file or directory. We hand the state structure to - * both the caller and the FAM library so we can match up the caller's - * status requests with FAM notifications. - */ - if (S_ISDIR(st.st_mode)) { - FAMMonitorDirectory(&global_fc, fullpath, &(info->req), info); - } else { - FAMMonitorFile(&global_fc, fullpath, &(info->req), info); - } - - /* Grr. On IRIX, neither of the monitor functions return a status. */ - - /* We will stay in initialising state until we see the FAMendExist message - * for this file. - */ - info->state = FAM_REQ_MONITORING; - info->generation = global_fc_generation; - return(True); + return NT_STATUS_OK; } -static BOOL -fam_handle_event(const FAMCodes code, uint32 flags) +static void fam_reopen(FAMConnection *fam_conn, + struct event_context *event_ctx, + struct fam_notify_ctx *notify_list) { -#define F_CHANGE_MASK (FILE_NOTIFY_CHANGE_FILE | \ - FILE_NOTIFY_CHANGE_ATTRIBUTES | \ - FILE_NOTIFY_CHANGE_SIZE | \ - FILE_NOTIFY_CHANGE_LAST_WRITE | \ - FILE_NOTIFY_CHANGE_LAST_ACCESS | \ - FILE_NOTIFY_CHANGE_CREATION | \ - FILE_NOTIFY_CHANGE_EA | \ - FILE_NOTIFY_CHANGE_SECURITY) - -#define F_DELETE_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | \ - FILE_NOTIFY_CHANGE_DIR_NAME) - -#define F_CREATE_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | \ - FILE_NOTIFY_CHANGE_DIR_NAME) - - switch (code) { - case FAMChanged: - if (flags & F_CHANGE_MASK) - return(True); - break; - case FAMDeleted: - if (flags & F_DELETE_MASK) - return(True); - break; - case FAMCreated: - if (flags & F_CREATE_MASK) - return(True); - break; - default: - /* Ignore anything else. */ - break; - } - - return(False); - -#undef F_CHANGE_MASK -#undef F_DELETE_MASK -#undef F_CREATE_MASK -} + struct fam_notify_ctx *ctx; -static BOOL -fam_pump_events(struct fam_req_info * info, uint32_t flags) -{ - FAMEvent ev; + DEBUG(5, ("Re-opening FAM connection\n")); - for (;;) { + FAMClose(fam_conn); - /* If we are draining the event queue we must keep going until we find - * the correct FAMAcknowledge event or the connection drops. Otherwise - * we should stop when there are no more events pending. - */ - if (flags != FAM_EVENT_DRAIN && !FAMPending(&global_fc)) { - break; + if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) { + DEBUG(5, ("Re-opening fam connection failed\n")); + return; } - if (FAMNextEvent(&global_fc, &ev) < 0) { - DEBUG(0, ("failed to fetch pending FAM event\n")); - DEBUGADD(0, ("resetting FAM connection\n")); - FAMClose(&global_fc); - FAMCONNECTION_GETFD(&global_fc) = -1; - return(False); + for (ctx = notify_list; ctx; ctx = ctx->next) { + FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL); } +} - DEBUG(FAM_TRACE_LOW, ("FAM event %s on '%s' for request %d\n", - fam_event_str(ev.code), ev.filename, ev.fr.reqnum)); +static void fam_handler(struct event_context *event_ctx, + struct fd_event *fd_event, + uint16 flags, + void *private_data) +{ + FAMConnection *fam_conn = (FAMConnection *)private_data; + FAMEvent fam_event; + struct fam_notify_ctx *ctx; + char *name; + + if (FAMPending(fam_conn) == 0) { + DEBUG(10, ("fam_handler called but nothing pending\n")); + return; + } - switch (ev.code) { - case FAMAcknowledge: - /* FAM generates an ACK event when we cancel a monitor. We need - * this to know when it is safe to free out request state - * structure. - */ - if (info->generation == global_fc_generation && - info->req.reqnum == ev.fr.reqnum && - flags == FAM_EVENT_DRAIN) { - return(True); - } + if (FAMNextEvent(fam_conn, &fam_event) != 1) { + DEBUG(10, ("FAMNextEvent returned an error\n")); + TALLOC_FREE(fd_event); + fam_reopen(fam_conn, event_ctx, fam_notify_list); + return; + } - case FAMEndExist: - case FAMExists: - /* Ignore these. FAM sends these enumeration events when we - * start monitoring. If we are monitoring a directory, we will - * get a FAMExists event for each directory entry. - */ + if ((fam_event.code != FAMCreated) && (fam_event.code != FAMDeleted)) { + DEBUG(10, ("Ignoring code FAMCode %d for file %s\n", + (int)fam_event.code, fam_event.filename)); + return; + } - /* TODO: we might be able to use these to implement recursive - * monitoring of entire subtrees. - */ - case FAMMoved: - /* These events never happen. A move or rename shows up as a - * create/delete pair. - */ - case FAMStartExecuting: - case FAMStopExecuting: - /* We might get these, but we just don't care. */ - break; - - case FAMChanged: - case FAMDeleted: - case FAMCreated: - if (info->generation != global_fc_generation) { - /* Ignore this; the req number can't be matched. */ - break; + for (ctx = fam_notify_list; ctx; ctx = ctx->next) { + if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) { + break; } + } - if (info->req.reqnum == ev.fr.reqnum) { - /* This is the event the caller was interested in. */ - DEBUG(FAM_TRACE, ("handling FAM %s event on '%s'\n", - fam_event_str(ev.code), ev.filename)); - /* Ignore events if we are draining this request. */ - if (flags != FAM_EVENT_DRAIN) { - return(fam_handle_event(ev.code, flags)); - } - break; - } else { - /* Caller doesn't want this event. Stash the result so we - * can come back to it. Unfortunately, FAM doesn't - * guarantee to give us back evinfo. - */ - struct fam_req_info * evinfo = - (struct fam_req_info *)ev.userdata; - - if (evinfo) { - DEBUG(FAM_TRACE, ("storing FAM %s event for winter\n", - fam_event_str(ev.code))); - evinfo->state = FAM_REQ_FIRED; - evinfo->code = ev.code; - } else { - DEBUG(2, ("received FAM %s notification for %s, " - "but userdata was unexpectedly NULL\n", - fam_event_str(ev.code), ev.filename)); - } - break; - } + if (ctx == NULL) { + DEBUG(5, ("Discarding event for file %s\n", + fam_event.filename)); + return; + } - default: - DEBUG(0, ("ignoring unknown FAM event code %d for `%s`\n", - ev.code, ev.filename)); + if ((name = strrchr_m(fam_event.filename, '\\')) == NULL) { + name = fam_event.filename; } - } - /* No more notifications pending. */ - return(False); + notify_fsp(ctx->fsp, + fam_event.code == FAMCreated + ? NOTIFY_ACTION_ADDED : NOTIFY_ACTION_REMOVED, + name); } -static BOOL -fam_test_connection(void) +static int fam_notify_ctx_destructor(struct fam_notify_ctx *ctx) { - FAMConnection fc; + if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) { + FAMCancelMonitor(&fam_connection, &ctx->fr); + } + DLIST_REMOVE(fam_notify_list, ctx); + return 0; +} - /* On IRIX FAMOpen2 leaks 960 bytes in 48 blocks. It's a deliberate leak - * in the library and there's nothing we can do about it here. - */ - if (FAMOpen2(&fc, "smbd probe") < 0) - return(False); +static void *fam_notify_add(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + files_struct *fsp, uint32 *filter) +{ + struct fam_notify_ctx *ctx; + pstring fullpath; - FAMClose(&fc); - return(True); -} + if ((*filter & FILE_NOTIFY_CHANGE_FILE) == 0) { + DEBUG(10, ("filter = %u, no FILE_NOTIFY_CHANGE_FILE\n", + *filter)); + return NULL; + } -/* ------------------------------------------------------------------------- */ + /* FAM needs an absolute pathname. */ -static void * -fam_register_notify(connection_struct * conn, - char * path, - uint32 flags) -{ - struct fam_req_info * info; - - if (!fam_check_reconnect()) { - return(False); - } - - if ((info = SMB_MALLOC_P(struct fam_req_info)) == NULL) { - DEBUG(0, ("malloc of %u bytes failed\n", (unsigned int)sizeof(struct fam_req_info))); - return(NULL); - } - - if (fam_monitor_path(conn, info, path, flags)) { - return(info); - } else { - SAFE_FREE(info); - return(NULL); - } -} + pstrcpy(fullpath, fsp->fsp_name); + if (!canonicalize_path(fsp->conn, fullpath)) { + DEBUG(0, ("failed to canonicalize path '%s'\n", fullpath)); + return NULL; + } -static BOOL -fam_check_notify(connection_struct * conn, - uint16_t vuid, - char * path, - uint32_t flags, - void * data, - time_t when) -{ - struct fam_req_info * info; + if (*fullpath != '/') { + DEBUG(0, ("canonicalized path '%s' into `%s`\n", fsp->fsp_name, + fullpath)); + DEBUGADD(0, ("but expected an absolute path\n")); + return NULL; + } + + if (!(ctx = TALLOC_P(mem_ctx, struct fam_notify_ctx))) { + return NULL; + } - info = (struct fam_req_info *)data; - SMB_ASSERT(info != NULL); + ctx->fsp = fsp; + ctx->fam_connection = &fam_connection; - DEBUG(10, ("checking FAM events for `%s`\n", path)); + /* + * The FAM module in this early state will only take care of + * FAMCreated and FAMDeleted events + */ - if (info->state == FAM_REQ_FIRED) { - DEBUG(FAM_TRACE, ("handling previously fired FAM %s event\n", - fam_event_str(info->code))); - info->state = FAM_REQ_MONITORING; - return(fam_handle_event(info->code, flags)); - } + ctx->filter = FILE_NOTIFY_CHANGE_FILE; - if (!fam_check_reconnect()) { - return(False); - } + if (!(ctx->path = talloc_strdup(ctx, fullpath))) { + DEBUG(0, ("talloc_strdup failed\n")); + TALLOC_FREE(ctx); + return NULL; + } - if (info->generation != global_fc_generation) { - DEBUG(FAM_TRACE, ("reapplying stale FAM monitor to %s\n", path)); - fam_monitor_path(conn, info, path, flags); - return(False); - } + /* + * Leave the rest to smbd itself + */ - return(fam_pump_events(info, flags)); -} + *filter &= ~FILE_NOTIFY_CHANGE_FILE; -static void -fam_remove_notify(void * data) -{ - struct fam_req_info * info; - - if ((info = (struct fam_req_info *)data) == NULL) - return; - - /* No need to reconnect. If the FAM connection is gone, there's no need to - * cancel and we can safely let FAMCancelMonitor fail. If it we - * reconnected, then the generation check will stop us cancelling the wrong - * request. - */ - - if ((FAMCONNECTION_GETFD(&global_fc) != -1) - && (info->generation == global_fc_generation)) { - DEBUG(FAM_TRACE, ("removing FAM notification for request %d\n", - info->req.reqnum)); - FAMCancelMonitor(&global_fc, &(info->req)); - - /* Soak up all events until the FAMAcknowledge. We can't free - * our request state until we are sure there are no more events in - * flight. + DLIST_ADD(fam_notify_list, ctx); + talloc_set_destructor(ctx, fam_notify_ctx_destructor); + + /* + * Only directories monitored so far */ - fam_pump_events(info, FAM_EVENT_DRAIN); - } - SAFE_FREE(info); + if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) { + FAMMonitorDirectory(ctx->fam_connection, ctx->path, &ctx->fr, + NULL); + } + else { + /* + * If the re-open is successful, this will establish the + * FAMMonitor from the list + */ + fam_reopen(ctx->fam_connection, event_ctx, fam_notify_list); + } + + return ctx; } -struct cnotify_fns * fam_notify_init(void) +static struct cnotify_fns global_fam_notify = +{ + fam_notify_add, +}; + +struct cnotify_fns *fam_notify_init(struct event_context *event_ctx) { - FAMCONNECTION_GETFD(&global_fc) = -1; - if (!fam_test_connection()) { - DEBUG(0, ("FAM file change notifications not available\n")); - return(NULL); - } + ZERO_STRUCT(fam_connection); + FAMCONNECTION_GETFD(&fam_connection) = -1; + + if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection, + event_ctx))) { + DEBUG(0, ("FAM file change notifications not available\n")); + return NULL; + } - DEBUG(FAM_TRACE, ("enabling FAM change notifications\n")); - return &global_fam_notify; + DEBUG(10, ("enabling FAM change notifications\n")); + return &global_fam_notify; } #endif /* HAVE_FAM_CHANGE_NOTIFY */ diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c index 0787a3eec5..773a7565c5 100644 --- a/source3/smbd/notify_hash.c +++ b/source3/smbd/notify_hash.c @@ -3,6 +3,7 @@ change notify handling - hash based implementation Copyright (C) Jeremy Allison 1994-1998 Copyright (C) Andrew Tridgell 2000 + 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 @@ -21,16 +22,26 @@ #include "includes.h" -struct change_data { +struct hash_change_data { time_t last_check_time; /* time we last checked this entry */ - struct timespec modify_time; /* Info from the directory we're monitoring. */ - struct timespec status_time; /* Info from the directory we're monitoring. */ - time_t total_time; /* Total time of all directory entries - don't care if it wraps. */ - unsigned int num_entries; /* Zero or the number of files in the directory. */ + struct timespec modify_time; /* Info from the directory we're + * monitoring. */ + struct timespec status_time; /* Info from the directory we're + * monitoring. */ + time_t total_time; /* Total time of all directory entries - don't care + * if it wraps. */ + unsigned int num_entries; /* Zero or the number of files in the + * directory. */ unsigned int mode_sum; unsigned char name_hash[16]; }; +struct hash_notify_ctx { + struct hash_change_data *data; + files_struct *fsp; + char *path; + uint32 filter; +}; /* Compare struct timespec. */ #define TIMESTAMP_NEQ(x, y) (((x).tv_sec != (y).tv_sec) || ((x).tv_nsec != (y).tv_nsec)) @@ -40,7 +51,8 @@ struct change_data { *****************************************************************************/ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, - struct change_data *data, struct change_data *old_data) + struct hash_change_data *data, + struct hash_change_data *old_data) { SMB_STRUCT_STAT st; pstring full_name; @@ -71,7 +83,8 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, } if (S_ISDIR(st.st_mode) && - (flags & ~(FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME)) == 0) + (flags & ~(FILE_NOTIFY_CHANGE_FILE_NAME + | FILE_NOTIFY_CHANGE_DIR_NAME)) == 0) { /* This is the case of a client wanting to know only when * the contents of a directory changes. Since any file @@ -81,11 +94,6 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, return True; } - if (lp_change_notify_timeout(SNUM(conn)) <= 0) { - /* It change notify timeout has been disabled, never scan the directory. */ - return True; - } - /* * If we are to watch for changes that are only stored * in inodes of files, not in the directory inode, we must @@ -138,10 +146,13 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, * If requested hash the names. */ - if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) { + if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME + |FILE_NOTIFY_CHANGE_FILE_NAME + |FILE_NOTIFY_CHANGE_FILE)) { int i; unsigned char tmp_hash[16]; - mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname)); + mdfour(tmp_hash, (const unsigned char *)fname, + strlen(fname)); for (i=0;i<16;i++) data->name_hash[i] ^= tmp_hash[i]; } @@ -150,7 +161,8 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, * If requested sum the mode_t's. */ - if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY)) + if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES + |FILE_NOTIFY_CHANGE_SECURITY)) data->mode_sum += st.st_mode; } @@ -159,77 +171,111 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, return True; } -/**************************************************************************** - Register a change notify request. -*****************************************************************************/ - -static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags) +static void hash_change_notify_handler(struct event_context *event_ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) { - struct change_data data; + struct hash_change_data *new_data; + struct hash_notify_ctx *ctx = + talloc_get_type_abort(private_data, + struct hash_notify_ctx); - if (!notify_hash(conn, path, flags, &data, NULL)) - return NULL; + TALLOC_FREE(te); - data.last_check_time = time(NULL); + if (!(new_data = TALLOC_P(ctx, struct hash_change_data))) { + DEBUG(0, ("talloc failed\n")); + /* + * No new timed event; + */ + return; + } - return (void *)memdup(&data, sizeof(data)); + if (!notify_hash(ctx->fsp->conn, ctx->fsp->fsp_name, + ctx->filter, new_data, ctx->data) + || TIMESTAMP_NEQ(new_data->modify_time, ctx->data->modify_time) + || TIMESTAMP_NEQ(new_data->status_time, ctx->data->status_time) + || new_data->total_time != ctx->data->total_time + || new_data->num_entries != ctx->data->num_entries + || new_data->mode_sum != ctx->data->mode_sum + || (memcmp(new_data->name_hash, ctx->data->name_hash, + sizeof(new_data->name_hash)))) { + notify_fsp(ctx->fsp, 0, NULL); + } + + TALLOC_FREE(ctx->data); + ctx->data = new_data; + + event_add_timed( + event_ctx, ctx, + timeval_current_ofs( + lp_change_notify_timeout(SNUM(ctx->fsp->conn)), 0), + "hash_change_notify_handler", + hash_change_notify_handler, ctx); } -/**************************************************************************** - Check if a change notify should be issued. - A time of zero means instantaneous check - don't modify the last check time. -*****************************************************************************/ -static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t) + +static void *hash_notify_add(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + files_struct *fsp, + uint32 *filter) { - struct change_data *data = (struct change_data *)datap; - struct change_data data2; - int cnto = lp_change_notify_timeout(SNUM(conn)); + struct hash_notify_ctx *ctx; + int timeout = lp_change_notify_timeout(SNUM(fsp->conn)); + pstring fullpath; - if (t && cnto <= 0) { - /* Change notify turned off on this share. - * Only scan when (t==0) - we think something changed. */ - return False; + if (timeout <= 0) { + /* It change notify timeout has been disabled, never scan the + * directory. */ + return NULL; } - if (t && t < data->last_check_time + cnto) { - return False; + pstrcpy(fullpath, fsp->fsp_name); + if (!canonicalize_path(fsp->conn, fullpath)) { + DEBUG(0, ("failed to canonicalize path '%s'\n", fullpath)); + return NULL; } - if (!change_to_user(conn,vuid)) - return True; - if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) { - change_to_root_user(); - return True; + if (*fullpath != '/') { + DEBUG(0, ("canonicalized path '%s' into `%s`\n", fsp->fsp_name, + fullpath)); + DEBUGADD(0, ("but expected an absolute path\n")); + return NULL; + } + + if (!(ctx = TALLOC_P(mem_ctx, struct hash_notify_ctx))) { + DEBUG(0, ("talloc failed\n")); + return NULL; } - if (!notify_hash(conn, path, flags, &data2, data) || - TIMESTAMP_NEQ(data2.modify_time, data->modify_time) || - TIMESTAMP_NEQ(data2.status_time, data->status_time) || - data2.total_time != data->total_time || - data2.num_entries != data->num_entries || - data2.mode_sum != data->mode_sum || - memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) { - change_to_root_user(); - return True; + if (!(ctx->path = talloc_strdup(ctx, fullpath))) { + DEBUG(0, ("talloc_strdup failed\n")); + TALLOC_FREE(ctx); + return NULL; } - if (t) { - data->last_check_time = t; + if (!(ctx->data = TALLOC_P(ctx, struct hash_change_data))) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(ctx); + return NULL; } - change_to_root_user(); + ctx->fsp = fsp; - return False; -} + /* + * Don't change the Samba filter, hash can only be a very bad attempt + * anyway. + */ + ctx->filter = *filter; -/**************************************************************************** - Remove a change notify data structure. -*****************************************************************************/ + notify_hash(fsp->conn, ctx->path, ctx->filter, ctx->data, NULL); -static void hash_remove_notify(void *datap) -{ - free(datap); + event_add_timed(event_ctx, ctx, timeval_current_ofs(timeout, 0), + "hash_change_notify_handler", + hash_change_notify_handler, ctx); + + return (void *)ctx; } /**************************************************************************** @@ -240,22 +286,7 @@ struct cnotify_fns *hash_notify_init(void) { static struct cnotify_fns cnotify; - cnotify.register_notify = hash_register_notify; - cnotify.check_notify = hash_check_notify; - cnotify.remove_notify = hash_remove_notify; - cnotify.select_time = 60; /* Start with 1 minute default. */ - cnotify.notification_fd = -1; + cnotify.notify_add = hash_notify_add; return &cnotify; } - -/* - change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess); - change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR); - - chain_size = 0; - file_chain_reset(); - - uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : - SVAL(cnbp->request_buf,smb_uid); -*/ diff --git a/source3/smbd/notify_kernel.c b/source3/smbd/notify_kernel.c index 0c20effc3d..0b5784f3d0 100644 --- a/source3/smbd/notify_kernel.c +++ b/source3/smbd/notify_kernel.c @@ -3,6 +3,7 @@ Version 3.0 change notify handling - linux kernel based implementation Copyright (C) Andrew Tridgell 2000 + 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 @@ -23,10 +24,6 @@ #if HAVE_KERNEL_CHANGE_NOTIFY -#define FD_PENDING_SIZE 20 -static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE]; -static SIG_ATOMIC_T signals_received; - #ifndef DN_ACCESS #define DN_ACCESS 0x00000001 /* File accessed in directory */ #define DN_MODIFY 0x00000002 /* File modified in directory */ @@ -55,10 +52,16 @@ static SIG_ATOMIC_T signals_received; determine if a directory has changed. *****************************************************************************/ -struct change_data { - int directory_handle; +struct dnotify_ctx { + struct dnotify_ctx *prev, *next; + + int fd; + files_struct *fsp; }; +static struct dnotify_ctx *dnotify_list; +static int dnotify_signal_pipe[2]; + /**************************************************************************** The signal handler for change notify. The Linux kernel has a bug in that we should be able to block any @@ -68,107 +71,91 @@ struct change_data { test case for the kernel hackers. JRA. *****************************************************************************/ -static void signal_handler(int sig, siginfo_t *info, void *unused) +static void dnotify_signal_handler(int sig, siginfo_t *info, void *unused) { - if (signals_received < FD_PENDING_SIZE - 1) { - fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd; - signals_received++; - } /* Else signal is lost. */ + int saved_errno; + + /* + * According to http://www.opengroup.org/onlinepubs/009695399/ write + * to a pipe either writes all or nothing, so we can safely write a + * full sizeof(int) and not risk the pipe to become out of sync with + * the receiving end. + * + * We don't care about the result of the write() call. If the pipe is + * full, then this signal is lost, we can't do anything about it. + */ + + saved_errno = errno; + write(dnotify_signal_pipe[1], (const void *)&info->si_fd, sizeof(int)); + errno = saved_errno; + sys_select_signal(RT_SIGNAL_NOTIFY); } /**************************************************************************** - Check if a change notify should be issued. - time non-zero means timeout check (used for hash). Ignore this (async method - where time is zero will be used instead). + The upper level handler informed when the pipe is ready for reading *****************************************************************************/ -static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t) +static void dnotify_pipe_handler(struct event_context *event_ctx, + struct fd_event *event, + uint16 flags, + void *private_data) { - struct change_data *data = (struct change_data *)datap; - int i; - BOOL ret = False; - - if (t) - return False; - - BlockSignals(True, RT_SIGNAL_NOTIFY); - for (i = 0; i < signals_received; i++) { - if (data->directory_handle == (int)fd_pending_array[i]) { - DEBUG(3,("kernel_check_notify: kernel change notify on %s fd[%d]=%d (signals_received=%d)\n", - path, i, (int)fd_pending_array[i], (int)signals_received )); - - close((int)fd_pending_array[i]); - fd_pending_array[i] = (SIG_ATOMIC_T)-1; - if (signals_received - i - 1) { - memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1], - sizeof(SIG_ATOMIC_T)*(signals_received-i-1)); - } - data->directory_handle = -1; - signals_received--; - ret = True; - break; - } + int res, fd; + struct dnotify_ctx *ctx; + + res = read(dnotify_signal_pipe[0], (void *)&fd, sizeof(int)); + + if (res == -1) { + DEBUG(0, ("Read from the dnotify pipe failed: %s\n", + strerror(errno))); + TALLOC_FREE(event); /* Don't try again */ + return; } - BlockSignals(False, RT_SIGNAL_NOTIFY); - return ret; -} -/**************************************************************************** - Remove a change notify data structure. -*****************************************************************************/ + if (res != sizeof(int)) { + smb_panic("read from dnotify pipe gave wrong number of " + "bytes\n"); + } -static void kernel_remove_notify(void *datap) -{ - struct change_data *data = (struct change_data *)datap; - int fd = data->directory_handle; - if (fd != -1) { - int i; - BlockSignals(True, RT_SIGNAL_NOTIFY); - for (i = 0; i < signals_received; i++) { - if (fd == (int)fd_pending_array[i]) { - fd_pending_array[i] = (SIG_ATOMIC_T)-1; - if (signals_received - i - 1) { - memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1], - sizeof(SIG_ATOMIC_T)*(signals_received-i-1)); - } - data->directory_handle = -1; - signals_received--; - break; - } + for (ctx = dnotify_list; ctx; ctx = ctx->next) { + if (ctx->fd == fd) { + notify_fsp(ctx->fsp, 0, NULL); } - close(fd); - BlockSignals(False, RT_SIGNAL_NOTIFY); } - SAFE_FREE(data); - DEBUG(3,("kernel_remove_notify: fd=%d\n", fd)); } /**************************************************************************** Register a change notify request. *****************************************************************************/ -static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags) +static int kernel_register_notify(connection_struct *conn, char *path, + uint32 flags) { - struct change_data data; int fd; unsigned long kernel_flags; fd = sys_open(path,O_RDONLY, 0); if (fd == -1) { - DEBUG(3,("Failed to open directory %s for change notify\n", path)); - return NULL; + DEBUG(3,("Failed to open directory %s for change notify\n", + path)); + return -1; } if (sys_fcntl_long(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) { DEBUG(3,("Failed to set signal handler for change notify\n")); - return NULL; + close(fd); + return -1; } - kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion changes everything! */ + kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion + * changes + * everything! */ if (flags & FILE_NOTIFY_CHANGE_FILE) kernel_flags |= DN_MODIFY; - if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME|DN_DELETE; + if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags + |= DN_RENAME + |DN_DELETE; if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB; if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY; if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY; @@ -176,19 +163,20 @@ static void *kernel_register_notify(connection_struct *conn, char *path, uint32 if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE; if (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB; if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB; - if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME|DN_DELETE; + if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags + |= DN_RENAME + |DN_DELETE; if (sys_fcntl_long(fd, F_NOTIFY, kernel_flags) == -1) { DEBUG(3,("Failed to set async flag for change notify\n")); - return NULL; + close(fd); + return -1; } - data.directory_handle = fd; - - DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) fd=%d\n", - path, (int)flags, (int)kernel_flags, fd)); + DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) " + "fd=%d\n", path, (int)flags, (int)kernel_flags, fd)); - return (void *)memdup(&data, sizeof(data)); + return fd; } /**************************************************************************** @@ -206,18 +194,74 @@ static BOOL kernel_notify_available(void) return ret == 0; } +static int dnotify_ctx_destructor(struct dnotify_ctx *ctx) +{ + close(ctx->fd); + DLIST_REMOVE(dnotify_list, ctx); + return 0; +} + +static void *kernel_notify_add(TALLOC_CTX *mem_ctx, + struct event_context *event_ctx, + files_struct *fsp, + uint32 *filter) +{ + struct dnotify_ctx *ctx; + + if (!(ctx = TALLOC_P(mem_ctx, struct dnotify_ctx))) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + ctx->fsp = fsp; + ctx->fd = kernel_register_notify(fsp->conn, fsp->fsp_name, *filter); + + if (ctx->fd == -1) { + TALLOC_FREE(ctx); + return NULL; + } + + DLIST_ADD(dnotify_list, ctx); + talloc_set_destructor(ctx, dnotify_ctx_destructor); + + return ctx; +} + /**************************************************************************** Setup kernel based change notify. ****************************************************************************/ -struct cnotify_fns *kernel_notify_init(void) +struct cnotify_fns *kernel_notify_init(struct event_context *event_ctx) { static struct cnotify_fns cnotify; struct sigaction act; + if (pipe(dnotify_signal_pipe) == -1) { + DEBUG(0, ("Failed to create signal pipe: %s\n", + strerror(errno))); + return NULL; + } + + if ((set_blocking(dnotify_signal_pipe[0], False) == -1) + || (set_blocking(dnotify_signal_pipe[1], False) == -1)) { + DEBUG(0, ("Failed to set signal pipe to non-blocking: %s\n", + strerror(errno))); + close(dnotify_signal_pipe[0]); + close(dnotify_signal_pipe[1]); + return NULL; + } + + if (event_add_fd(event_ctx, NULL, dnotify_signal_pipe[0], + EVENT_FD_READ, dnotify_pipe_handler, NULL) == NULL) { + DEBUG(0, ("Failed to set signal event handler\n")); + close(dnotify_signal_pipe[0]); + close(dnotify_signal_pipe[1]); + return NULL; + } + ZERO_STRUCT(act); - act.sa_sigaction = signal_handler; + act.sa_sigaction = dnotify_signal_handler; act.sa_flags = SA_SIGINFO; sigemptyset( &act.sa_mask ); if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) { @@ -228,11 +272,7 @@ struct cnotify_fns *kernel_notify_init(void) if (!kernel_notify_available()) return NULL; - cnotify.register_notify = kernel_register_notify; - cnotify.check_notify = kernel_check_notify; - cnotify.remove_notify = kernel_remove_notify; - cnotify.select_time = -1; - cnotify.notification_fd = -1; + cnotify.notify_add = kernel_notify_add; /* the signal can start off blocked due to a bug in bash */ BlockSignals(False, RT_SIGNAL_NOTIFY); diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 13e290135a..84ea6a150f 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -1776,11 +1776,6 @@ int reply_ntrename(connection_struct *conn, return ERROR_NT(status); } - /* - * Win2k needs a changenotify request response before it will - * update after a rename.. - */ - process_pending_change_notify_queue((time_t)0); outsize = set_message(outbuf,0,0,False); END_PROFILE(SMBntrename); @@ -1834,7 +1829,7 @@ static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, } } - if (fsp->notify->num_changes > 0) { + if (fsp->notify->num_changes != 0) { /* * We've got changes pending, respond immediately @@ -1921,13 +1916,6 @@ static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *o DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", fsp->fsp_name, new_name)); - /* - * Win2k needs a changenotify request response before it will - * update after a rename.. - */ - - process_pending_change_notify_queue((time_t)0); - return -1; } diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 2a52da12b3..cfecd7fecb 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -304,9 +304,6 @@ static void async_processing(fd_set *pfds) exit_server_cleanly("termination signal"); } - /* check for async change notify events */ - process_pending_change_notify_queue(0); - /* check for sighup processing */ if (reload_after_sighup) { change_to_root_user(); @@ -468,7 +465,6 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) START_PROFILE(smbd_idle); maxfd = select_on_fd(smbd_server_fd(), maxfd, &r_fds); - maxfd = select_on_fd(change_notify_fd(), maxfd, &r_fds); maxfd = select_on_fd(oplock_notify_fd(), maxfd, &r_fds); selrtn = sys_select(maxfd+1,&r_fds,&w_fds,NULL,&to); @@ -525,20 +521,6 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) goto again; } - if ((change_notify_fd() >= 0) && FD_ISSET(change_notify_fd(), - &r_fds)) { - - process_pending_change_notify_queue((time_t)0); - - /* - * Same comment as for oplock processing applies here. We - * might have done I/O on the client socket. - */ - - goto again; - } - - return receive_smb(smbd_server_fd(), buffer, 0); } @@ -1251,16 +1233,9 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) static int setup_select_timeout(void) { int select_timeout; - int t; select_timeout = blocking_locks_timeout_ms(SMBD_SELECT_TIMEOUT*1000); - t = change_notify_timeout(); - DEBUG(10, ("change_notify_timeout: %d\n", t)); - if (t != -1) { - select_timeout = MIN(select_timeout, t*1000); - } - if (print_notify_messages_pending()) { select_timeout = MIN(select_timeout, 1000); } @@ -1459,12 +1434,6 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup())); update_monitored_printq_cache(); /* - * Check to see if we have any change notifies - * outstanding on the queue. - */ - process_pending_change_notify_queue(t); - - /* * Now we are root, check if the log files need pruning. * Force a log file check. */ diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 2763924d4f..7cb086841d 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1982,12 +1982,6 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(status); } - /* - * Win2k needs a changenotify request response before it will - * update after a rename.. - */ - process_pending_change_notify_queue((time_t)0); - outsize = set_message(outbuf,0,0,False); END_PROFILE(SMBunlink); @@ -4490,11 +4484,6 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - /* - * Win2k needs a changenotify request response before it will - * update after a rename.. - */ - process_pending_change_notify_queue((time_t)0); outsize = set_message(outbuf,0,0,False); END_PROFILE(SMBmv); diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 9b6743f76b..9efe63a82c 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -1074,9 +1074,6 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, dbgtext( "(pid %d)\n", (int)sys_getpid() ); } - /* Setup the minimum value for a change notify wait time (seconds). */ - set_change_notify_timeout(lp_change_notify_timeout(snum)); - /* we've finished with the user stuff - go back to root */ change_to_root_user(); return(conn); diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index c9cb2c5b4f..b35b27f276 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -4487,7 +4487,6 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", return ERROR_NT(status); } - process_pending_change_notify_queue((time_t)0); SSVAL(params,0,0); send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0, max_data_bytes); return(-1); |