summaryrefslogtreecommitdiff
path: root/source3/smbd/notify.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/notify.c')
-rw-r--r--source3/smbd/notify.c401
1 files changed, 117 insertions, 284 deletions
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index 3af9da238f..40867a71ee 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -3,8 +3,8 @@
Unix SMB/Netbios implementation.
Version 3.0
change notify handling
- Copyright (C) Jeremy Allison 1994-1998
Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Jeremy Allison 1994-1998
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
@@ -25,221 +25,128 @@
extern int DEBUGLEVEL;
-/****************************************************************************
- This is the structure to keep the information needed to
- determine if a directory has changed.
-*****************************************************************************/
-
-typedef struct {
- time_t modify_time; /* Info from the directory we're monitoring. */
- time_t 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. */
-} change_hash_data;
+static struct cnotify_fns *cnotify;
/****************************************************************************
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 the time to check next.
+ Plus the fid to examine and notify private data
*****************************************************************************/
-typedef struct {
- ubi_slNode msg_next;
- files_struct *fsp;
- connection_struct *conn;
- uint32 flags;
- time_t next_check_time;
- change_hash_data change_data;
- char request_buf[smb_size];
-} change_notify_buf;
+struct change_notify {
+ struct change_notify *next, *prev;
+ files_struct *fsp;
+ connection_struct *conn;
+ uint32 flags;
+ char request_buf[smb_size];
+ void *change_data;
+};
-static ubi_slList change_notify_queue = { NULL, (ubi_slNodePtr)&change_notify_queue, 0};
+static struct change_notify *change_notify_list;
/****************************************************************************
Setup the common parts of the return packet and send it.
*****************************************************************************/
-
-static void change_notify_reply_packet(char *inbuf, int error_class, uint32 error_code)
+static void change_notify_reply_packet(char *inbuf, uint32 error_code)
{
- char outbuf[smb_size+38];
-
- memset(outbuf, '\0', sizeof(outbuf));
- construct_reply_common(inbuf, outbuf);
+ char outbuf[smb_size+38];
- /*
- * If we're returning a 'too much in the directory changed' we need to
- * set this is an NT error status flags. If we don't then the (probably
- * untested) code in the NT redirector has a bug in that it doesn't re-issue
- * the change notify.... Ah - I *love* it when I get so deeply into this I
- * can even determine how MS failed to test stuff and why.... :-). JRA.
- */
+ memset(outbuf, '\0', sizeof(outbuf));
+ construct_reply_common(inbuf, outbuf);
- if(error_class == 0) /* NT Error. */
- SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
-
- ERROR(error_class,error_code);
+ /*
+ * If we're returning a 'too much in the directory changed' we need to
+ * set this is an NT error status flags. If we don't then the (probably
+ * untested) code in the NT redirector has a bug in that it doesn't re-issue
+ * the change notify.... Ah - I *love* it when I get so deeply into this I
+ * can even determine how MS failed to test stuff and why.... :-). JRA.
+ */
+
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ ERROR(0,error_code);
- /*
- * Seems NT needs a transact command with an error code
- * in it. This is a longer packet than a simple error.
- */
- set_message(outbuf,18,0,False);
+ /*
+ * Seems NT needs a transact command with an error code
+ * in it. This is a longer packet than a simple error.
+ */
+ set_message(outbuf,18,0,False);
- send_smb(smbd_server_fd(),outbuf);
+ send_smb(smbd_server_fd(),outbuf);
}
/****************************************************************************
- Create the hash we will use to determine if the contents changed.
+remove an entry from the list and free it, also closing any
+directory handle if necessary
+Notice the horrible stuff we have to do because this is a singly linked list.
*****************************************************************************/
-
-static BOOL create_directory_notify_hash( change_notify_buf *cnbp, change_hash_data *change_data)
+static void change_notify_remove(struct change_notify *cnbp)
{
- SMB_STRUCT_STAT st;
- files_struct *fsp = cnbp->fsp;
-
- memset((char *)change_data, '\0', sizeof(change_data));
-
- /*
- * Store the current timestamp on the directory we are monitoring.
- */
-
- if(dos_stat(fsp->fsp_name, &st) < 0) {
- DEBUG(0,("create_directory_notify_hash: Unable to stat name = %s. \
-Error was %s\n", fsp->fsp_name, strerror(errno) ));
- return False;
- }
-
- change_data->modify_time = st.st_mtime;
- change_data->status_time = st.st_ctime;
-
- /*
- * If we are to watch for changes that are only stored
- * in inodes of files, not in the directory inode, we must
- * scan the directory and produce a unique identifier with
- * which we can determine if anything changed. We use the
- * modify and change times from all the files in the
- * directory, added together (ignoring wrapping if it's
- * larger than the max time_t value).
- */
-
- if(cnbp->flags & (FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE)) {
- pstring full_name;
- char *p;
- char *fname;
- size_t remaining_len;
- size_t fullname_len;
- void *dp = OpenDir(cnbp->conn, fsp->fsp_name, True);
-
- if(dp == NULL) {
- DEBUG(0,("create_directory_notify_hash: Unable to open directory = %s. \
-Error was %s\n", fsp->fsp_name, strerror(errno) ));
- return False;
- }
-
- change_data->num_entries = 0;
-
- pstrcpy(full_name, fsp->fsp_name);
- pstrcat(full_name, "/");
-
- fullname_len = strlen(full_name);
- remaining_len = sizeof(full_name) - fullname_len - 1;
- p = &full_name[fullname_len];
-
- while ((fname = ReadDirName(dp))) {
- if(strequal(fname, ".") || strequal(fname, ".."))
- continue;
-
- change_data->num_entries++;
- safe_strcpy( p, fname, remaining_len);
-
- memset(&st, '\0', sizeof(st));
-
- /*
- * Do the stat - but ignore errors.
- */
-
- if(dos_stat(full_name, &st) < 0) {
- DEBUG(5,("create_directory_notify_hash: Unable to stat content file = %s. \
-Error was %s\n", fsp->fsp_name, strerror(errno) ));
- }
- change_data->total_time += (st.st_mtime + st.st_ctime);
- }
-
- CloseDir(dp);
- }
-
- return True;
+ cnotify->remove_notify(cnbp->change_data);
+ DLIST_REMOVE(change_notify_list, cnbp);
+ ZERO_STRUCTP(cnbp);
+ free(cnbp);
}
+
/****************************************************************************
Delete entries by fnum from the change notify pending queue.
*****************************************************************************/
-
void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
{
- change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
- change_notify_buf *prev = NULL;
-
- while(cnbp != NULL) {
- if(cnbp->fsp->fnum == fsp->fnum) {
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- continue;
- }
-
- prev = cnbp;
- cnbp = (change_notify_buf *)ubi_slNext(cnbp);
- }
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if (cnbp->fsp->fnum == fsp->fnum) {
+ change_notify_remove(cnbp);
+ }
+ }
}
/****************************************************************************
Delete entries by mid from the change notify pending queue. Always send reply.
*****************************************************************************/
-
void remove_pending_change_notify_requests_by_mid(int mid)
{
- change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
- change_notify_buf *prev = NULL;
-
- while(cnbp != NULL) {
- if(SVAL(cnbp->request_buf,smb_mid) == mid) {
- change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED);
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- continue;
- }
-
- prev = cnbp;
- cnbp = (change_notify_buf *)ubi_slNext(cnbp);
- }
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if(SVAL(cnbp->request_buf,smb_mid) == mid) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
}
/****************************************************************************
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)
{
- change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
- change_notify_buf *prev = NULL;
-
- while(cnbp != NULL) {
- /*
- * 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,0,0xC0000000 |NT_STATUS_CANCELLED);
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- continue;
- }
-
- prev = cnbp;
- cnbp = (change_notify_buf *)ubi_slNext(cnbp);
- }
+ 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,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Return true if there are pending change notifies.
+****************************************************************************/
+BOOL change_notifies_pending(void)
+{
+ return (change_notify_list != NULL);
}
/****************************************************************************
@@ -247,121 +154,36 @@ void remove_pending_change_notify_requests_by_filename(files_struct *fsp)
Returns True if there are still outstanding change notify requests on the
queue.
*****************************************************************************/
-
BOOL process_pending_change_notify_queue(time_t t)
{
- change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
- change_notify_buf *prev = NULL;
-
- if(cnbp == NULL)
- return False;
-
- if(cnbp->next_check_time >= t)
- return True;
-
- /*
- * It's time to check. Go through the queue and see if
- * the timestamps changed.
- */
-
- while((cnbp != NULL) && (cnbp->next_check_time <= t)) {
- change_hash_data change_data;
- connection_struct *conn = cnbp->conn;
- uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
- SVAL(cnbp->request_buf,smb_uid);
-
- ZERO_STRUCT(change_data);
-
- /*
- * Ensure we don't have any old chain_fsp values
- * sitting around....
- */
- chain_size = 0;
- file_chain_reset();
-
- if(!become_user(conn,vuid)) {
- DEBUG(0,("process_pending_change_notify_queue: Unable to become user vuid=%d.\n",
- vuid ));
- /*
- * Remove the entry and return an error to the client.
- */
- change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- continue;
- }
-
- if(!become_service(conn,True)) {
- DEBUG(0,("process_pending_change_notify_queue: Unable to become service Error was %s.\n", strerror(errno) ));
- /*
- * Remove the entry and return an error to the client.
- */
- change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- unbecome_user();
- continue;
- }
-
- if(!create_directory_notify_hash( cnbp, &change_data)) {
- DEBUG(0,("process_pending_change_notify_queue: Unable to create change data for \
-directory %s\n", cnbp->fsp->fsp_name ));
- /*
- * Remove the entry and return an error to the client.
- */
- change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- unbecome_user();
- continue;
- }
-
- if(memcmp( (char *)&cnbp->change_data, (char *)&change_data, sizeof(change_data))) {
- /*
- * Remove the entry and return a change notify to the client.
- */
- DEBUG(5,("process_pending_change_notify_queue: directory name = %s changed.\n",
- cnbp->fsp->fsp_name ));
- change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
- free((char *)ubi_slRemNext( &change_notify_queue, prev));
- cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
- unbecome_user();
- continue;
- }
-
- unbecome_user();
-
- /*
- * Move to the next in the list.
- */
- prev = cnbp;
- cnbp = (change_notify_buf *)ubi_slNext(cnbp);
- }
-
- return (cnbp != NULL);
-}
+ 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 (cnotify->check_notify(cnbp->conn, vuid, cnbp->fsp->fsp_name, cnbp->flags, cnbp->change_data, t)) {
+ change_notify_reply_packet(cnbp->request_buf,STATUS_NOTIFY_ENUM_DIR);
+ change_notify_remove(cnbp);
+ }
+ }
-/****************************************************************************
- Return true if there are pending change notifies.
-****************************************************************************/
-BOOL change_notifies_pending(void)
-{
- change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
- return (cnbp != NULL);
+ return (change_notify_list != NULL);
}
/****************************************************************************
- * Now queue an entry on the notify change stack. We timestamp
- * the entry we are adding so that we know when to scan next.
+ * 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)
{
- change_notify_buf *cnbp;
+ struct change_notify *cnbp;
- if((cnbp = (change_notify_buf *)malloc(sizeof(change_notify_buf))) == NULL) {
+ if((cnbp = (struct change_notify *)malloc(sizeof(*cnbp))) == NULL) {
DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" ));
return -1;
}
@@ -371,23 +193,34 @@ BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn,
memcpy(cnbp->request_buf, inbuf, smb_size);
cnbp->fsp = fsp;
cnbp->conn = conn;
- cnbp->next_check_time = time(NULL) + lp_change_notify_timeout();
cnbp->flags = flags;
+ cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, flags);
- if (!create_directory_notify_hash(cnbp, &cnbp->change_data)) {
- free((char *)cnbp);
+ if (!cnbp->change_data) {
+ free(cnbp);
return False;
}
+
+ DLIST_ADD(change_notify_list, cnbp);
+
+ return True;
+}
+
+
+/****************************************************************************
+initialise the change notify subsystem
+****************************************************************************/
+BOOL init_change_notify(void)
+{
+ cnotify = hash_notify_init();
- /*
- * Adding to the tail enables us to check only
- * the head when scanning for change, as this entry
- * is forced to have the first timeout expiration.
- */
-
- ubi_slAddTail(&change_notify_queue, cnbp);
+ if (!cnotify) {
+ DEBUG(0,("Failed to init change notify system\n"));
+ return False;
+ }
return True;
}
+
#undef OLD_NTDOMAIN