r20931: This changes the notify infrastructure from a polling-based to an event-driven
authorVolker Lendecke <vlendec@samba.org>
Sun, 21 Jan 2007 11:49:00 +0000 (11:49 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:17:21 +0000 (12:17 -0500)
based approach. The only remaining hook into the backend is now

void *(*notify_add)(TALLOC_CTX *mem_ctx,
    struct event_context *event_ctx,
    files_struct *fsp, uint32 *filter);

(Should we put this through the VFS, so that others can more easily plug in?)

The trick here is that the backend can pick filter bits that the main smbd
should not handle anymore. Thanks to tridge for this idea.

The backend can notify the main smbd process via

void notify_fsp(files_struct *fsp, uint32 action, char *name);

The core patch is not big, what makes this more than 1800 lines are the
individual backends that are considerably changed but can be reviewed
one by one.

Based on this I'll continue with inotify now.

Volker

12 files changed:
source/include/event.h
source/include/smb.h
source/smbd/close.c
source/smbd/notify.c
source/smbd/notify_fam.c
source/smbd/notify_hash.c
source/smbd/notify_kernel.c
source/smbd/nttrans.c
source/smbd/process.c
source/smbd/reply.c
source/smbd/service.c
source/smbd/trans2.c

index f3c468c9b8d454b2d1b8caaeab980d7c217c991b..ce687eca6d6e13bb92560dcfc25ff2de5096ce12 100644 (file)
    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
index df4f47f12c061b3717d3255594dff222fe62adbb..21643c64a1f9d9ec5f56057ca06d3f6855fa4339 100644 (file)
@@ -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"
index 07f81f988e34cf3a7aa1d1714eac8f109ef9ab7d..714e0615de690e61be68883ee074902d2b6afd85 100644 (file)
@@ -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(
index 2c762bd75907eec98184dfc498132caf81febc95..ce7680b49a1ef4dec3d9d1d773e4b56cdadb3319 100644 (file)
@@ -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
 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();
        
index bfef4ac871ec1ab28343f53fe7083b89542dfd9f..306316e49f7b250b7f6b087ad0f14b05c483473f 100644 (file)
@@ -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 */
index 0787a3eec5e2a7e68acf0bcb4aa2f8077bbf5869..773a7565c563942c2cb041f1458d02815575af60 100644 (file)
@@ -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
 
 #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);
-*/
index 0c20effc3d1122375e4deb023665286fd2e24c4c..0b5784f3d05818d46d9dac037a900336679defdf 100644 (file)
@@ -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
 
 #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);
index 13e290135ac27af07b9da265e08f77bdabb6124d..84ea6a150fcc73f8ad23f4512cc6b00a6d2be11c 100644 (file)
@@ -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;
 }
 
index 2a52da12b38810543ca6497d4f136316aa2f8a9c..cfecd7fecb384aa27bcf05108f18a6b6a34c044d 100644 (file)
@@ -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);
        }
@@ -1458,12 +1433,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.
index 2763924d4f5c5a9af6465e0e6d95c45571b939eb..7cb086841d63e017f00c9b95127c3efdf5a9fc9a 100644 (file)
@@ -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);
index 9b6743f76bbc8e475e45740955051a4d22207c5f..9efe63a82c1552230d06533c33a7952d381a7a65 100644 (file)
@@ -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);
index c9cb2c5b4f82baa4fa175a51b9dabd68fd606c76..b35b27f2763836088f8fbb638705885575ea6f85 100644 (file)
@@ -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);