1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00

totally rewrote the async signal, notification and oplock notification

handling in Samba. This was needed due to several limitations and
races in the previous code - as a side effect the new code is much
cleaner :)

in summary:

- changed sys_select() to avoid a signal/select race condition. It is a
  rare race but once we have signals doing notification and oplocks it
  is important.

- changed our main processing loop to take advantage of the new
  sys_select semantics

- split the notify code into implementaion dependent and general
  parts. Added the following structure that defines an implementation:

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);
};


then I wrote two implementations, one using hash/poll (like our old
code) and the other using the new Linux kernel change notify. It
should be easy to add other change notify implementations by creating
a sructure of the above type.

- fixed a bug in change notify where we were returning the wrong error
  code.

- rewrote the core change notify code to be much simpler

- moved to real-time signals for leases and change notify

Amazingly, it all seems to work. I was very surprised!
This commit is contained in:
Andrew Tridgell 0001-01-01 00:00:00 +00:00
parent abf06fa90b
commit 44766c39e0
17 changed files with 693 additions and 579 deletions

View File

@ -108,7 +108,7 @@ LIB_OBJ = lib/charcnv.o lib/charset.o lib/debug.o lib/fault.o \
lib/util_unistr.o lib/util_file.o \
lib/util.o lib/util_sock.o lib/util_sec.o smbd/ssl.o \
lib/talloc.o lib/hash.o lib/substitute.o lib/fsusage.o \
lib/ms_fnmatch.o lib/util_seaccess.o \
lib/ms_fnmatch.o lib/util_seaccess.o lib/select.o \
$(TDB_OBJ)
UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \
@ -159,10 +159,12 @@ PROFILE_OBJ = profile/profile.o
OPLOCK_OBJ = smbd/oplock.o smbd/oplock_irix.o smbd/oplock_linux.o
NOTIFY_OBJ = smbd/notify.o smbd/notify_hash.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/notify.o smbd/pipes.o \
smbd/message.o smbd/nttrans.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 \
@ -180,7 +182,7 @@ MSDFS_OBJ = msdfs/msdfs.o
SMBD_OBJ = $(SMBD_OBJ1) $(MSDFS_OBJ) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
$(RPC_SERVER_OBJ) $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) \
$(LOCKING_OBJ) $(PASSDB_OBJ) $(PRINTING_OBJ) $(PROFILE_OBJ) $(LIB_OBJ) \
$(PRINTBACKEND_OBJ) $(QUOTAOBJS) $(OPLOCK_OBJ)
$(PRINTBACKEND_OBJ) $(QUOTAOBJS) $(OPLOCK_OBJ) $(NOTIFY_OBJ)
NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \

2
source/configure vendored
View File

@ -4680,7 +4680,7 @@ else
fi
done
for ac_func in initgroups select rdchk getgrnam getgrent pathconf
for ac_func in initgroups select poll rdchk getgrnam getgrent pathconf
do
echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
echo "configure:4687: checking for $ac_func" >&5

View File

@ -361,7 +361,7 @@ AC_CHECK_FUNCS(waitpid getcwd strdup strtoul strerror chown chmod chroot)
AC_CHECK_FUNCS(fstat strchr utime utimes getrlimit fsync bzero memset)
AC_CHECK_FUNCS(memmove vsnprintf snprintf setsid glob strpbrk pipe crypt16 getauthuid)
AC_CHECK_FUNCS(strftime sigprocmask sigblock sigaction innetgr setnetgrent getnetgrent endnetgrent)
AC_CHECK_FUNCS(initgroups select rdchk getgrnam getgrent pathconf)
AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf)
AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate stat64 fstat64)
AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid yp_get_default_domain getpwanam)

View File

@ -544,6 +544,9 @@
/* Define if you have the pipe function. */
#undef HAVE_PIPE
/* Define if you have the poll function. */
#undef HAVE_POLL
/* Define if you have the pread function. */
#undef HAVE_PREAD

View File

@ -5,6 +5,7 @@
#define ERROR_INVALID_PARAMETER (87)
#define ERROR_INSUFFICIENT_BUFFER (122)
#define STATUS_1804 (1804)
#define STATUS_NOTIFY_ENUM_DIR (0x10C)
/* these are the NT error codes less than 1000. They are here for when
@ -514,5 +515,4 @@
#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 613)
#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 614)
#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 615)
#define NT_STATUS_NOTIFY_ENUM_DIR (0xC0000000 | 0x10C)
#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */

View File

@ -183,6 +183,12 @@ void pidfile_create(char *name);
char *rep_inet_ntoa(struct in_addr ip);
/*The following definitions come from lib/select.c */
void sys_select_signal(void);
int sys_select(int maxfd, fd_set *fds,struct timeval *tval);
int sys_select_intr(int maxfd, fd_set *fds,struct timeval *tval);
/*The following definitions come from lib/signal.c */
void BlockSignals(BOOL block,int signum);
@ -212,9 +218,6 @@ void standard_sub_vsnum(char *str, user_struct *vuser, int snum);
/*The following definitions come from lib/system.c */
int sys_select(int maxfd, fd_set *fds,struct timeval *tval);
int sys_select(int maxfd, fd_set *fds,struct timeval *tval);
int sys_select_intr(int maxfd, fd_set *fds,struct timeval *tval);
int sys_usleep(long usecs);
int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf);
int sys_fstat(int fd,SMB_STRUCT_STAT *sbuf);
@ -3251,9 +3254,16 @@ BOOL disk_quotas(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT
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 process_pending_change_notify_queue(time_t t);
BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags);
BOOL init_change_notify(void);
#endif
/*The following definitions come from smbd/notify_hash.c */
#if OLD_NTDOMAIN
struct cnotify_fns *hash_notify_init(void) ;
#endif
/*The following definitions come from smbd/nttrans.c */
@ -3301,6 +3311,18 @@ 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

View File

@ -1636,6 +1636,16 @@ struct kernel_oplocks {
#define CMD_REPLY 0x8000
/* 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);
};
#include "smb_macros.h"
/* A netbios name structure. */

View File

@ -132,6 +132,8 @@ void sig_usr2( int sig )
DEBUG( 0, ( "Got SIGUSR2; set debug level to %d.\n", DEBUGLEVEL ) );
sys_select_signal();
#if !defined(HAVE_SIGACTION)
CatchSignal( SIGUSR2, SIGNAL_CAST sig_usr2 );
#endif
@ -154,6 +156,8 @@ void sig_usr1( int sig )
DEBUG( 0, ( "Got SIGUSR1; set debug level to %d.\n", DEBUGLEVEL ) );
sys_select_signal();
#if !defined(HAVE_SIGACTION)
CatchSignal( SIGUSR1, SIGNAL_CAST sig_usr1 );
#endif

View File

@ -39,115 +39,6 @@ extern int DEBUGLEVEL;
*/
/*******************************************************************
this replaces the normal select() system call
return if some data has arrived on one of the file descriptors
return -1 means error
********************************************************************/
#ifndef HAVE_SELECT
static int pollfd(int fd)
{
int r=0;
#ifdef HAS_RDCHK
r = rdchk(fd);
#elif defined(TCRDCHK)
(void)ioctl(fd, TCRDCHK, &r);
#else
(void)ioctl(fd, FIONREAD, &r);
#endif
return(r);
}
int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
{
fd_set fds2;
int counter=0;
int found=0;
FD_ZERO(&fds2);
while (1)
{
int i;
for (i=0;i<maxfd;i++) {
if (FD_ISSET(i,fds) && pollfd(i)>0) {
found++;
FD_SET(i,&fds2);
}
}
if (found) {
memcpy((void *)fds,(void *)&fds2,sizeof(fds2));
return(found);
}
if (tval && tval->tv_sec < counter) return(0);
sleep(1);
counter++;
}
}
#else /* !NO_SELECT */
int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
{
#ifdef USE_POLL
struct pollfd pfd[256];
int i;
int maxpoll;
int timeout;
int pollrtn;
maxpoll = 0;
for( i = 0; i < maxfd; i++) {
if(FD_ISSET(i,fds)) {
struct pollfd *pfdp = &pfd[maxpoll++];
pfdp->fd = i;
pfdp->events = POLLIN;
pfdp->revents = 0;
}
}
timeout = (tval != NULL) ? (tval->tv_sec * 1000) + (tval->tv_usec/1000) :
-1;
errno = 0;
pollrtn = poll( &pfd[0], maxpoll, timeout);
FD_ZERO(fds);
for( i = 0; i < maxpoll; i++)
if( pfd[i].revents & POLLIN )
FD_SET(pfd[i].fd,fds);
return pollrtn;
#else /* USE_POLL */
struct timeval t2;
int selrtn;
if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2));
errno = 0;
selrtn = select(maxfd,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL);
return(selrtn);
}
#endif /* USE_POLL */
#endif /* NO_SELECT */
/*******************************************************************
similar to sys_select() but catch EINTR and continue
this is what sys_select() used to do in Samba
********************************************************************/
int sys_select_intr(int maxfd, fd_set *fds,struct timeval *tval)
{
int ret;
do {
ret = sys_select(maxfd, fds, tval);
} while (ret == -1 && errno == EINTR);
return ret;
}
/*******************************************************************
A wrapper for usleep in case we don't have one.

View File

@ -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,320 +25,120 @@
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];
char outbuf[smb_size+38];
memset(outbuf, '\0', sizeof(outbuf));
construct_reply_common(inbuf, outbuf);
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 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);
if(error_class == 0) /* NT Error. */
SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
/*
* 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);
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);
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;
struct change_notify *cnbp, *next;
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);
}
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;
struct change_notify *cnbp, *next;
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);
}
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;
struct change_notify *cnbp, *next;
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);
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);
}
}
}
/****************************************************************************
@ -346,22 +146,44 @@ directory %s\n", cnbp->fsp->fsp_name ));
****************************************************************************/
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.
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 (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 (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)
{
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;
}
/*
* 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);
DLIST_ADD(change_notify_list, cnbp);
return True;
}
/****************************************************************************
initialise the change notify subsystem
****************************************************************************/
BOOL init_change_notify(void)
{
cnotify = hash_notify_init();
if (!cnotify) {
DEBUG(0,("Failed to init change notify system\n"));
return False;
}
return True;
}
#undef OLD_NTDOMAIN

184
source/smbd/notify_hash.c Normal file
View File

@ -0,0 +1,184 @@
#define OLD_NTDOMAIN 1
/*
Unix SMB/Netbios implementation.
Version 3.0
change notify handling - hash based implementation
Copyright (C) Jeremy Allison 1994-1998
Copyright (C) Andrew Tridgell 2000
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;
struct change_data {
time_t last_check_time; /* time we last checked this entry */
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. */
};
/****************************************************************************
Create the hash we will use to determine if the contents changed.
*****************************************************************************/
static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
struct change_data *data)
{
SMB_STRUCT_STAT st;
pstring full_name;
char *p;
char *fname;
size_t remaining_len;
size_t fullname_len;
void *dp;
ZERO_STRUCTP(data);
if(dos_stat(path, &st) == -1) return False;
data->modify_time = st.st_mtime;
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 (!(flags & (FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE))) return True;
dp = OpenDir(conn, path, True);
if (dp == NULL) return False;
data->num_entries = 0;
pstrcpy(full_name, path);
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;
data->num_entries++;
safe_strcpy(p, fname, remaining_len);
ZERO_STRUCT(st);
/*
* Do the stat - but ignore errors.
*/
dos_stat(full_name, &st);
data->total_time += (st.st_mtime + st.st_ctime);
}
CloseDir(dp);
return True;
}
/****************************************************************************
register a change notify request
*****************************************************************************/
static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
{
struct change_data data;
if (!notify_hash(conn, path, flags, &data)) return NULL;
data.last_check_time = time(NULL);
return (void *)memdup(&data, sizeof(data));
}
/****************************************************************************
check if a change notify should be issued
*****************************************************************************/
static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
{
struct change_data *data = (struct change_data *)datap;
struct change_data data2;
if (t < data->last_check_time + lp_change_notify_timeout()) return False;
if (!become_user(conn,vuid)) return True;
if (!become_service(conn,True)) {
unbecome_user();
return True;
}
if (!notify_hash(conn, path, flags, &data2) ||
data2.modify_time != data->modify_time ||
data2.status_time != data->status_time ||
data2.total_time != data->total_time ||
data2.num_entries != data->num_entries) {
unbecome_user();
return True;
}
data->last_check_time = t;
unbecome_user();
return False;
}
/****************************************************************************
remove a change notify data structure
*****************************************************************************/
static void hash_remove_notify(void *datap)
{
free(datap);
}
/****************************************************************************
setup hash based change notify
****************************************************************************/
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;
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);
*/
#undef OLD_NTDOMAIN

170
source/smbd/notify_kernel.c Normal file
View File

@ -0,0 +1,170 @@
#define OLD_NTDOMAIN 1
/*
Unix SMB/Netbios implementation.
Version 3.0
change notify handling - linux kernel based implementation
Copyright (C) Andrew Tridgell 2000
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"
#if HAVE_KERNEL_CHANGE_NOTIFY
extern int DEBUGLEVEL;
static int fd_pending;
static unsigned signals_received;
static unsigned signals_processed;
#ifndef DN_ACCESS
#define DN_ACCESS 0x00000001 /* File accessed in directory */
#define DN_MODIFY 0x00000002 /* File modified in directory */
#define DN_CREATE 0x00000004 /* File created in directory */
#define DN_DELETE 0x00000008 /* File removed from directory */
#define DN_RENAME 0x00000010 /* File renamed in directory */
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
#endif
#ifndef RT_SIGNAL_NOTIFY
#define RT_SIGNAL_NOTIFY 34
#endif
/****************************************************************************
This is the structure to keep the information needed to
determine if a directory has changed.
*****************************************************************************/
struct change_data {
int directory_handle;
};
/****************************************************************************
the signal handler for change notify
*****************************************************************************/
static void signal_handler(int signal, siginfo_t *info, void *unused)
{
BlockSignals(True, signal);
fd_pending = info->si_fd;
signals_received++;
sys_select_signal();
}
/****************************************************************************
check if a change notify should be issued
*****************************************************************************/
static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
{
struct change_data *data = (struct change_data *)datap;
if (data->directory_handle != fd_pending) return False;
close(fd_pending);
data->directory_handle = fd_pending = -1;
signals_processed++;
BlockSignals(False, RT_SIGNAL_NOTIFY);
return True;
}
/****************************************************************************
remove a change notify data structure
*****************************************************************************/
static void kernel_remove_notify(void *datap)
{
struct change_data *data = (struct change_data *)datap;
if (data->directory_handle != -1) {
if (data->directory_handle == fd_pending) {
data->directory_handle = fd_pending = -1;
signals_processed++;
BlockSignals(False, RT_SIGNAL_NOTIFY);
}
close(data->directory_handle);
}
free(data);
}
/****************************************************************************
register a change notify request
*****************************************************************************/
static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags)
{
struct change_data data;
int fd;
unsigned long kernel_flags;
fd = dos_open(fsp->fsp_name, O_RDONLY, 0);
if (fd == -1) {
DEBUG(3,("Failed to open directory %s for change notify\n", fsp->fsp_name));
return NULL;
}
if (fcntl(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
DEBUG(3,("Failed to set signal handler for change notify\n"));
return NULL;
}
kernel_flags = 0;
if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME;
if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME;
if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_MODIFY;
if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY;
if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY;
if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS;
if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE;
if (fcntl(fd, F_NOTIFY, kernel_flags) == -1) {
DEBUG(3,("Failed to set async flag for change notify\n"));
return NULL;
}
data.directory_handle = fd;
return (void *)memdup(&data, sizeof(data));
}
/****************************************************************************
setup kernel based change notify
****************************************************************************/
struct cnotify_fns *kernel_notify_init(void)
{
static struct cnotify_fns cnotify;
struct sigaction act;
act.sa_handler = NULL;
act.sa_sigaction = signal_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
return NULL;
}
cnotify.register_notify = kernel_register_notify;
cnotify.check_notify = kernel_check_notify;
cnotify.remove_notify = kernel_remove_notify;
return &cnotify;
}
#else
void notify_kernel_dummy(void) {}
#endif /* HAVE_KERNEL_CHANGE_NOTIFY */
#undef OLD_NTDOMAIN

View File

@ -71,44 +71,47 @@ BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeou
smb_read_error = 0;
if(timeout != 0) {
struct timeval to;
int selrtn;
int maxfd = oplock_sock;
struct timeval to;
int selrtn;
int maxfd = oplock_sock;
if (koplocks && koplocks->notification_fd != -1) {
FD_SET(koplocks->notification_fd, fds);
}
if (koplocks && koplocks->notification_fd != -1) {
FD_SET(koplocks->notification_fd, fds);
maxfd = MAX(maxfd, koplocks->notification_fd);
}
to.tv_sec = timeout / 1000;
to.tv_usec = (timeout % 1000) * 1000;
to.tv_sec = timeout / 1000;
to.tv_usec = (timeout % 1000) * 1000;
selrtn = sys_select(maxfd+1,fds,&to);
selrtn = sys_select(maxfd+1,fds,&to);
if (selrtn == -1 && errno == EINTR) {
/* could be a kernel oplock interrupt */
if (koplocks && koplocks->msg_waiting(fds)) {
return koplocks->receive_message(fds, buffer, buffer_len);
}
}
if (selrtn == -1 && errno == EINTR) {
/* could be a kernel oplock interrupt */
if (koplocks && koplocks->msg_waiting(fds)) {
return koplocks->receive_message(fds, buffer, buffer_len);
}
}
/* Check if error */
if(selrtn == -1) {
/* something is wrong. Maybe the socket is dead? */
smb_read_error = READ_ERROR;
return False;
}
/* Check if error */
if(selrtn == -1) {
/* something is wrong. Maybe the socket is dead? */
smb_read_error = READ_ERROR;
return False;
}
/* Did we timeout ? */
if (selrtn == 0) {
smb_read_error = READ_TIMEOUT;
return False;
}
/* Did we timeout ? */
if (selrtn == 0) {
smb_read_error = READ_TIMEOUT;
return False;
}
}
if (koplocks && koplocks->msg_waiting(fds)) {
return koplocks->receive_message(fds, buffer, buffer_len);
}
if (!FD_ISSET(oplock_sock, fds)) return False;
/*
* From here down we deal with the smbd <--> smbd
* oplock break protocol only.

View File

@ -218,16 +218,16 @@ static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *in
{
/* Ensure that the msg length is correct. */
if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
return False;
}
memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
DEBUG(5,("process_local_message: kernel oplock break request for \
file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f\n",
(unsigned int)*dev, (double)*inode));
return True;
}

View File

@ -29,7 +29,7 @@ extern int DEBUGLEVEL;
static unsigned signals_received;
static unsigned signals_processed;
static int fd_pending; /* the fd of the current pending SIGIO */
static int fd_pending; /* the fd of the current pending signal */
#ifndef F_SETLEASE
#define F_SETLEASE 1024
@ -43,14 +43,19 @@ static int fd_pending; /* the fd of the current pending SIGIO */
#define CAP_LEASE 28
#endif
#ifndef RT_SIGNAL_LEASE
#define RT_SIGNAL_LEASE 33
#endif
/****************************************************************************
handle a SIGIO, incrementing the signals_received and blocking SIGIO
handle a LEASE signal, incrementing the signals_received and blocking the signal
****************************************************************************/
static void sigio_handler(int signal, siginfo_t *info, void *unused)
static void signal_handler(int signal, siginfo_t *info, void *unused)
{
BlockSignals(True, signal);
fd_pending = info->si_fd;
signals_received++;
BlockSignals(True, SIGIO);
sys_select_signal();
}
/****************************************************************************
@ -150,7 +155,7 @@ dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
/* now we can receive more signals */
fd_pending = -1;
signals_processed++;
BlockSignals(False, SIGIO);
BlockSignals(False, RT_SIGNAL_LEASE);
return True;
}
@ -213,16 +218,16 @@ static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *i
{
/* Ensure that the msg length is correct. */
if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
return False;
}
memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
DEBUG(5,("process_local_message: kernel oplock break request for \
file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f\n",
(unsigned int)*dev, (double)*inode));
return True;
}
@ -264,10 +269,10 @@ struct kernel_oplocks *linux_init_kernel_oplocks(void)
}
act.sa_handler = NULL;
act.sa_sigaction = sigio_handler;
act.sa_sigaction = signal_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGIO, &act, NULL) != 0) {
DEBUG(0,("Failed to setup SIGIO handler\n"));
if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
return NULL;
}

View File

@ -107,7 +107,30 @@ static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len)
BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
{
return push_message(&smb_oplock_queue, buf, msg_len);
return push_message(&smb_oplock_queue, buf, msg_len);
}
/****************************************************************************
do all async processing in here. This includes UDB oplock messages, kernel
oplock messages, change notify events etc.
****************************************************************************/
static void async_processing(fd_set *fds, char *buffer, int buffer_len)
{
/* check for oplock messages (both UDP and kernel) */
if (receive_local_message(fds, buffer, buffer_len, 0)) {
process_local_message(buffer, buffer_len);
}
/* check for async change notify events */
process_pending_change_notify_queue(0);
/* check for sighup processing */
if (reload_after_sighup) {
unbecome_user();
DEBUG(1,("Reloading services after SIGHUP\n"));
reload_services(False);
reload_after_sighup = False;
}
}
/****************************************************************************
@ -115,7 +138,7 @@ BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
If a local udp message has been pushed onto the
queue (this can only happen during oplock break
processing) return this first.
processing) call async_processing()
If a pending smb message has been pushed onto the
queue (this can only happen during oplock break
@ -131,8 +154,7 @@ BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
The timeout is in milli seconds
****************************************************************************/
static BOOL receive_message_or_smb(char *buffer, int buffer_len,
int timeout, BOOL *got_smb)
static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
{
fd_set fds;
int selrtn;
@ -141,30 +163,28 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
smb_read_error = 0;
*got_smb = False;
/*
* Check to see if we already have a message on the smb queue.
* If so - copy and return it.
*/
if(ubi_slCount(&smb_oplock_queue) != 0) {
if(ubi_slCount(&smb_oplock_queue) != 0) {
pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
/* Free the message we just copied. */
free((char *)msg->msg_buf);
free((char *)msg);
*got_smb = True;
DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
return True;
}
/*
* Setup the select read fd set.
*/
again:
FD_ZERO(&fds);
FD_SET(smbd_server_fd(),&fds);
maxfd = setup_oplock_select_set(&fds);
@ -175,16 +195,16 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,timeout>0?&to:NULL);
/* if we get EINTR then maybe we have received an oplock
signal - treat this as select returning 1. This is ugly, but
is the best we can do until the oplock code knows more about
signals */
signal - treat this as select returning 1. This is ugly, but
is the best we can do until the oplock code knows more about
signals */
if (selrtn == -1 && errno == EINTR) {
FD_ZERO(&fds);
selrtn = 1;
async_processing(&fds, buffer, buffer_len);
goto again;
}
/* Check if error */
if(selrtn == -1 && errno != EINTR) {
if (selrtn == -1) {
/* something is wrong. Maybe the socket is dead? */
smb_read_error = READ_ERROR;
return False;
@ -195,13 +215,13 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
smb_read_error = READ_TIMEOUT;
return False;
}
if (FD_ISSET(smbd_server_fd(),&fds)) {
*got_smb = True;
return receive_smb(smbd_server_fd(), buffer, 0);
} else {
return receive_local_message(&fds, buffer, buffer_len, 0);
if (!FD_ISSET(smbd_server_fd(),&fds) || selrtn > 1) {
async_processing(&fds, buffer, buffer_len);
if (!FD_ISSET(smbd_server_fd(),&fds)) goto again;
}
return receive_smb(smbd_server_fd(), buffer, 0);
}
/****************************************************************************
@ -210,30 +230,16 @@ Get the next SMB packet, doing the local message processing automatically.
BOOL receive_next_smb(char *inbuf, int bufsize, int timeout)
{
BOOL got_smb = False;
BOOL ret;
BOOL got_keepalive;
BOOL ret;
do
{
ret = receive_message_or_smb(inbuf,bufsize,timeout,&got_smb);
do {
ret = receive_message_or_smb(inbuf,bufsize,timeout);
got_keepalive = (ret && (CVAL(inbuf,0) == 0x85));
} while (ret && got_keepalive);
if(ret && !got_smb)
{
/* Deal with oplock break requests from other smbd's. */
process_local_message(inbuf, bufsize);
continue;
}
if(ret && (CVAL(inbuf,0) == 0x85))
{
/* Keepalive packet. */
got_smb = False;
}
}
while(ret && !got_smb);
return ret;
return ret;
}
/****************************************************************************
@ -270,13 +276,12 @@ void respond_to_all_remaining_local_messages(void)
* Keep doing receive_local_message with a 1 ms timeout until
* we have no more messages.
*/
while(receive_local_message(&fds, buffer, sizeof(buffer), 1)) {
/* Deal with oplock break requests from other smbd's. */
process_local_message(buffer, sizeof(buffer));
/* Deal with oplock break requests from other smbd's. */
process_local_message(buffer, sizeof(buffer));
FD_ZERO(&fds);
(void)setup_oplock_select_set(&fds);
FD_ZERO(&fds);
(void)setup_oplock_select_set(&fds);
}
return;
@ -1008,98 +1013,80 @@ machine %s in domain %s.\n", global_myname, global_myworkgroup ));
void smbd_process(void)
{
extern int smb_echo_count;
time_t last_timeout_processing_time = time(NULL);
unsigned int num_smbs = 0;
extern int smb_echo_count;
time_t last_timeout_processing_time = time(NULL);
unsigned int num_smbs = 0;
InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
if ((InBuffer == NULL) || (OutBuffer == NULL))
return;
InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
if ((InBuffer == NULL) || (OutBuffer == NULL))
return;
InBuffer += SMB_ALIGNMENT;
OutBuffer += SMB_ALIGNMENT;
InBuffer += SMB_ALIGNMENT;
OutBuffer += SMB_ALIGNMENT;
max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
/* re-initialise the timezone */
TimeInit();
/* re-initialise the timezone */
TimeInit();
while (True)
{
int deadtime = lp_deadtime()*60;
BOOL got_smb = False;
int select_timeout = setup_select_timeout();
while (True) {
int deadtime = lp_deadtime()*60;
int select_timeout = setup_select_timeout();
int num_echos;
if (deadtime <= 0)
deadtime = DEFAULT_SMBD_TIMEOUT;
if (deadtime <= 0)
deadtime = DEFAULT_SMBD_TIMEOUT;
errno = 0;
errno = 0;
/* free up temporary memory */
lp_talloc_free();
/* free up temporary memory */
lp_talloc_free();
while (!receive_message_or_smb(InBuffer,BUFFER_SIZE,select_timeout)) {
if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
}
/*
* If reload_after_sighup == True then we got a SIGHUP
* and are being asked to reload. Fix from <branko.cibej@hermes.si>
*/
if (reload_after_sighup) {
/* become root */
unbecome_user();
DEBUG(1,("Reloading services after SIGHUP\n"));
reload_services(False);
reload_after_sighup = False;
}
/*
* Ensure we do timeout processing if the SMB we just got was
* only an echo request. This allows us to set the select
* timeout in 'receive_message_or_smb()' to any value we like
* without worrying that the client will send echo requests
* faster than the select timeout, thus starving out the
* essential processing (change notify, blocking locks) that
* the timeout code does. JRA.
*/
num_echos = smb_echo_count;
while(!receive_message_or_smb(InBuffer,BUFFER_SIZE,select_timeout,&got_smb))
{
if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
}
process_smb(InBuffer, OutBuffer);
if(got_smb) {
/*
* Ensure we do timeout processing if the SMB we just got was
* only an echo request. This allows us to set the select
* timeout in 'receive_message_or_smb()' to any value we like
* without worrying that the client will send echo requests
* faster than the select timeout, thus starving out the
* essential processing (change notify, blocking locks) that
* the timeout code does. JRA.
*/
int num_echos = smb_echo_count;
if (smb_echo_count != num_echos) {
if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
}
process_smb(InBuffer, OutBuffer);
num_smbs++;
if(smb_echo_count != num_echos) {
if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
}
num_smbs++;
/*
* If we are getting smb requests in a constant stream
* with no echos, make sure we attempt timeout processing
* every select_timeout milliseconds - but only check for this
* every 200 smb requests.
*/
if((num_smbs % 200) == 0) {
time_t new_check_time = time(NULL);
if(last_timeout_processing_time - new_check_time >= (select_timeout/1000)) {
if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
last_timeout_processing_time = new_check_time; /* Reset time. */
}
}
}
else
process_local_message(InBuffer, BUFFER_SIZE);
}
/*
* If we are getting smb requests in a constant stream
* with no echos, make sure we attempt timeout processing
* every select_timeout milliseconds - but only check for this
* every 200 smb requests.
*/
if ((num_smbs % 200) == 0) {
time_t new_check_time = time(NULL);
if(last_timeout_processing_time - new_check_time >= (select_timeout/1000)) {
if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
last_timeout_processing_time = new_check_time; /* Reset time. */
}
}
}
}
#undef OLD_NTDOMAIN

View File

@ -350,12 +350,7 @@ static void sig_hup(int sig)
BlockSignals(True,SIGHUP);
DEBUG(0,("Got SIGHUP\n"));
/*
* Fix from <branko.cibej@hermes.si> here.
* We used to reload in the signal handler - this
* is a *BIG* no-no.
*/
sys_select_signal();
reload_after_sighup = True;
BlockSignals(False,SIGHUP);
}
@ -758,6 +753,11 @@ static void usage(char *pname)
exit(1);
}
/* Setup change notify */
if (!init_change_notify()) {
exit(1);
}
smbd_process();
exit_server("normal exit");