summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/event.h5
-rw-r--r--source3/include/smb.h18
-rw-r--r--source3/smbd/close.c5
-rw-r--r--source3/smbd/notify.c226
-rw-r--r--source3/smbd/notify_fam.c548
-rw-r--r--source3/smbd/notify_hash.c191
-rw-r--r--source3/smbd/notify_kernel.c216
-rw-r--r--source3/smbd/nttrans.c14
-rw-r--r--source3/smbd/process.c31
-rw-r--r--source3/smbd/reply.c11
-rw-r--r--source3/smbd/service.c3
-rw-r--r--source3/smbd/trans2.c1
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);