mirror of
https://github.com/samba-team/samba.git
synced 2025-02-28 01:58:17 +03:00
split all the change notify code out into a separate module
smbd/notify.c. All the data structures are now private to that module. this is in preparation for Linux kernel support for change notify (coming soon to a CVS tree near you) (This used to be commit 1bb0aad4f66dbfa2d0f767ea90f926affff20b17)
This commit is contained in:
parent
a4ba9bb939
commit
6db12f7180
@ -162,7 +162,7 @@ OPLOCK_OBJ = smbd/oplock.o smbd/oplock_irix.o smbd/oplock_linux.o
|
||||
SMBD_OBJ1 = smbd/server.o smbd/files.o smbd/chgpasswd.o smbd/connection.o \
|
||||
smbd/dfree.o smbd/dir.o smbd/password.o smbd/conn.o smbd/fileio.o \
|
||||
smbd/ipc.o smbd/lanman.o smbd/mangle.o smbd/negprot.o \
|
||||
smbd/message.o smbd/nttrans.o smbd/pipes.o \
|
||||
smbd/message.o smbd/nttrans.o smbd/notify.o smbd/pipes.o \
|
||||
smbd/reply.o smbd/trans2.o smbd/uid.o \
|
||||
smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o smbd/blocking.o \
|
||||
smbd/vfs.o smbd/vfs-wrap.o smbd/statcache.o \
|
||||
|
@ -3245,6 +3245,17 @@ int reply_negprot(connection_struct *conn,
|
||||
|
||||
BOOL disk_quotas(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize);
|
||||
|
||||
/*The following definitions come from smbd/notify.c */
|
||||
|
||||
#if OLD_NTDOMAIN
|
||||
void remove_pending_change_notify_requests_by_fid(files_struct *fsp);
|
||||
void remove_pending_change_notify_requests_by_mid(int mid);
|
||||
void remove_pending_change_notify_requests_by_filename(files_struct *fsp);
|
||||
BOOL process_pending_change_notify_queue(time_t t);
|
||||
BOOL change_notifies_pending(void);
|
||||
BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags);
|
||||
#endif
|
||||
|
||||
/*The following definitions come from smbd/nttrans.c */
|
||||
|
||||
#if OLD_NTDOMAIN
|
||||
@ -3256,10 +3267,6 @@ int reply_ntcancel(connection_struct *conn,
|
||||
char *inbuf,char *outbuf,int length,int bufsize);
|
||||
int reply_nttranss(connection_struct *conn,
|
||||
char *inbuf,char *outbuf,int length,int bufsize);
|
||||
void remove_pending_change_notify_requests_by_fid(files_struct *fsp);
|
||||
void remove_pending_change_notify_requests_by_filename(files_struct *fsp);
|
||||
BOOL process_pending_change_notify_queue(time_t t);
|
||||
BOOL change_notifies_pending(void);
|
||||
int reply_nttrans(connection_struct *conn,
|
||||
char *inbuf,char *outbuf,int length,int bufsize);
|
||||
#endif
|
||||
@ -3294,18 +3301,6 @@ BOOL attempt_close_oplocked_file(files_struct *fsp);
|
||||
BOOL init_oplocks(void);
|
||||
#endif
|
||||
|
||||
/*The following definitions come from smbd/oplock_irix.c */
|
||||
|
||||
#if OLD_NTDOMAIN
|
||||
struct kernel_oplocks *irix_init_kernel_oplocks(void) ;
|
||||
#endif
|
||||
|
||||
/*The following definitions come from smbd/oplock_linux.c */
|
||||
|
||||
#if OLD_NTDOMAIN
|
||||
struct kernel_oplocks *linux_init_kernel_oplocks(void) ;
|
||||
#endif
|
||||
|
||||
/*The following definitions come from smbd/password.c */
|
||||
|
||||
#if OLD_NTDOMAIN
|
||||
|
392
source3/smbd/notify.c
Normal file
392
source3/smbd/notify.c
Normal file
@ -0,0 +1,392 @@
|
||||
#define OLD_NTDOMAIN 1
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 1.9.
|
||||
SMB NT transaction handling
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
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;
|
||||
|
||||
/****************************************************************************
|
||||
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.
|
||||
*****************************************************************************/
|
||||
|
||||
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;
|
||||
|
||||
static ubi_slList change_notify_queue = { NULL, (ubi_slNodePtr)&change_notify_queue, 0};
|
||||
|
||||
/****************************************************************************
|
||||
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)
|
||||
{
|
||||
char outbuf[smb_size+38];
|
||||
|
||||
memset(outbuf, '\0', sizeof(outbuf));
|
||||
construct_reply_common(inbuf, outbuf);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if(error_class == 0) /* NT Error. */
|
||||
SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
|
||||
|
||||
ERROR(error_class,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);
|
||||
|
||||
send_smb(smbd_server_fd(),outbuf);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Create the hash we will use to determine if the contents changed.
|
||||
*****************************************************************************/
|
||||
|
||||
static BOOL create_directory_notify_hash( change_notify_buf *cnbp, change_hash_data *change_data)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Now queue an entry on the notify change stack. We timestamp
|
||||
* the entry we are adding so that we know when to scan next.
|
||||
* 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;
|
||||
|
||||
if((cnbp = (change_notify_buf *)malloc(sizeof(change_notify_buf))) == NULL) {
|
||||
DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" ));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZERO_STRUCTP(cnbp);
|
||||
|
||||
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;
|
||||
|
||||
if (!create_directory_notify_hash(cnbp, &cnbp->change_data)) {
|
||||
free((char *)cnbp);
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
#undef OLD_NTDOMAIN
|
@ -30,8 +30,6 @@ extern BOOL case_sensitive;
|
||||
extern BOOL case_preserve;
|
||||
extern BOOL short_case_preserve;
|
||||
|
||||
static void remove_pending_change_notify_requests_by_mid(int mid);
|
||||
|
||||
static char *known_nt_pipes[] = {
|
||||
"\\LANMAN",
|
||||
"\\srvsvc",
|
||||
@ -1407,6 +1405,41 @@ int reply_nttranss(connection_struct *conn,
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a notify change - queue the request and
|
||||
don't allow a directory to be opened.
|
||||
****************************************************************************/
|
||||
static int call_nt_transact_notify_change(connection_struct *conn,
|
||||
char *inbuf, char *outbuf, int length,
|
||||
int bufsize,
|
||||
char **ppsetup,
|
||||
char **ppparams, char **ppdata)
|
||||
{
|
||||
char *setup = *ppsetup;
|
||||
files_struct *fsp;
|
||||
uint32 flags;
|
||||
|
||||
fsp = file_fsp(setup,4);
|
||||
flags = IVAL(setup, 0);
|
||||
|
||||
DEBUG(3,("call_nt_transact_notify_change\n"));
|
||||
|
||||
if(!fsp)
|
||||
return(ERROR(ERRDOS,ERRbadfid));
|
||||
|
||||
if((!fsp->is_directory) || (conn != fsp->conn))
|
||||
return(ERROR(ERRDOS,ERRbadfid));
|
||||
|
||||
if (!change_notify_set(inbuf, fsp, conn, flags)) {
|
||||
return(UNIXERROR(ERRDOS,ERRbadfid));
|
||||
}
|
||||
|
||||
DEBUG(3,("call_nt_transact_notify_change: notify change called on directory \
|
||||
name = %s\n", fsp->fsp_name ));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to an NT transact rename command.
|
||||
****************************************************************************/
|
||||
@ -1445,396 +1478,6 @@ static int call_nt_transact_rename(connection_struct *conn,
|
||||
return(outsize);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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;
|
||||
|
||||
/****************************************************************************
|
||||
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.
|
||||
*****************************************************************************/
|
||||
|
||||
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;
|
||||
|
||||
static ubi_slList change_notify_queue = { NULL, (ubi_slNodePtr)&change_notify_queue, 0};
|
||||
|
||||
/****************************************************************************
|
||||
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)
|
||||
{
|
||||
char outbuf[smb_size+38];
|
||||
|
||||
memset(outbuf, '\0', sizeof(outbuf));
|
||||
construct_reply_common(inbuf, outbuf);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if(error_class == 0) /* NT Error. */
|
||||
SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
|
||||
|
||||
ERROR(error_class,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);
|
||||
|
||||
send_smb(smbd_server_fd(),outbuf);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Create the hash we will use to determine if the contents changed.
|
||||
*****************************************************************************/
|
||||
|
||||
static BOOL create_directory_notify_hash( change_notify_buf *cnbp, change_hash_data *change_data)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Delete entries by mid from the change notify pending queue. Always send reply.
|
||||
*****************************************************************************/
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to a notify change - queue the request and
|
||||
don't allow a directory to be opened.
|
||||
****************************************************************************/
|
||||
|
||||
static int call_nt_transact_notify_change(connection_struct *conn,
|
||||
char *inbuf, char *outbuf, int length,
|
||||
int bufsize,
|
||||
char **ppsetup,
|
||||
char **ppparams, char **ppdata)
|
||||
{
|
||||
char *setup = *ppsetup;
|
||||
files_struct *fsp;
|
||||
change_notify_buf *cnbp;
|
||||
|
||||
fsp = file_fsp(setup,4);
|
||||
|
||||
DEBUG(3,("call_nt_transact_notify_change\n"));
|
||||
|
||||
if(!fsp)
|
||||
return(ERROR(ERRDOS,ERRbadfid));
|
||||
|
||||
if((!fsp->is_directory) || (conn != fsp->conn))
|
||||
return(ERROR(ERRDOS,ERRbadfid));
|
||||
|
||||
/*
|
||||
* Now queue an entry on the notify change stack. We timestamp
|
||||
* the entry we are adding so that we know when to scan next.
|
||||
* We only need to save smb_size bytes from this incoming packet
|
||||
* as we will always by returning a 'read the directory yourself'
|
||||
* error.
|
||||
*/
|
||||
|
||||
if((cnbp = (change_notify_buf *)malloc(sizeof(change_notify_buf))) == NULL) {
|
||||
DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" ));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset((char *)cnbp, '\0', sizeof(change_notify_buf));
|
||||
|
||||
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 = IVAL(setup, 0);
|
||||
|
||||
if(!create_directory_notify_hash( cnbp, &cnbp->change_data )) {
|
||||
free((char *)cnbp);
|
||||
return(UNIXERROR(ERRDOS,ERRbadfid));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
DEBUG(3,("call_nt_transact_notify_change: notify change called on directory \
|
||||
name = %s\n", fsp->fsp_name ));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Reply to query a security descriptor - currently this is not implemented (it
|
||||
|
Loading…
x
Reference in New Issue
Block a user