summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2000-06-12 15:53:31 +0000
committerAndrew Tridgell <tridge@samba.org>2000-06-12 15:53:31 +0000
commitb2d01bd2dbfed8b35cc324fad42eac562fcad3b4 (patch)
tree6d2708c53b61bbd441d7c4a65945b65d543b2f73
parent6de513cef43ad83ecd1823bde5a4e05c22224b0f (diff)
downloadsamba-b2d01bd2dbfed8b35cc324fad42eac562fcad3b4.tar.gz
samba-b2d01bd2dbfed8b35cc324fad42eac562fcad3b4.tar.bz2
samba-b2d01bd2dbfed8b35cc324fad42eac562fcad3b4.zip
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 used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
-rw-r--r--source3/Makefile.in8
-rwxr-xr-xsource3/configure2
-rw-r--r--source3/configure.in2
-rw-r--r--source3/include/config.h.in3
-rw-r--r--source3/include/nterr.h2
-rw-r--r--source3/include/proto.h30
-rw-r--r--source3/include/smb.h10
-rw-r--r--source3/lib/debug.c4
-rw-r--r--source3/lib/system.c109
-rw-r--r--source3/smbd/notify.c401
-rw-r--r--source3/smbd/notify_hash.c184
-rw-r--r--source3/smbd/notify_kernel.c170
-rw-r--r--source3/smbd/oplock.c55
-rw-r--r--source3/smbd/oplock_irix.c8
-rw-r--r--source3/smbd/oplock_linux.c29
-rw-r--r--source3/smbd/process.c253
-rw-r--r--source3/smbd/server.c12
17 files changed, 698 insertions, 584 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index af46419312..091e43bf6d 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -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 \
diff --git a/source3/configure b/source3/configure
index 49f95411d1..8150594d77 100755
--- a/source3/configure
+++ b/source3/configure
@@ -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
diff --git a/source3/configure.in b/source3/configure.in
index 6d0926cdb2..c28beb2429 100644
--- a/source3/configure.in
+++ b/source3/configure.in
@@ -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)
diff --git a/source3/include/config.h.in b/source3/include/config.h.in
index b4a4f134e4..e3afd6a66a 100644
--- a/source3/include/config.h.in
+++ b/source3/include/config.h.in
@@ -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
diff --git a/source3/include/nterr.h b/source3/include/nterr.h
index 08e7457376..07e874dce0 100644
--- a/source3/include/nterr.h
+++ b/source3/include/nterr.h
@@ -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 */
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 4f5f3d42d3..eabacd4d21 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -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
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 68df3f250c..b64fa0bd02 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -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. */
diff --git a/source3/lib/debug.c b/source3/lib/debug.c
index 675c2d8cfc..a388956d42 100644
--- a/source3/lib/debug.c
+++ b/source3/lib/debug.c
@@ -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
diff --git a/source3/lib/system.c b/source3/lib/system.c
index 46b01b747a..479bce1965 100644
--- a/source3/lib/system.c
+++ b/source3/lib/system.c
@@ -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.
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
diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c
new file mode 100644
index 0000000000..e01f660700
--- /dev/null
+++ b/source3/smbd/notify_hash.c
@@ -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
diff --git a/source3/smbd/notify_kernel.c b/source3/smbd/notify_kernel.c
new file mode 100644
index 0000000000..7732bc646f
--- /dev/null
+++ b/source3/smbd/notify_kernel.c
@@ -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
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 5e63b4d4ff..59c3c83f6f 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -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.
diff --git a/source3/smbd/oplock_irix.c b/source3/smbd/oplock_irix.c
index 8d55a3d4a0..c4d528c835 100644
--- a/source3/smbd/oplock_irix.c
+++ b/source3/smbd/oplock_irix.c
@@ -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;
}
diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c
index 46290683d2..de2a4300a7 100644
--- a/source3/smbd/oplock_linux.c
+++ b/source3/smbd/oplock_linux.c
@@ -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;
}
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 30d03747d8..b84e55343e 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -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;
-
- do
- {
- ret = receive_message_or_smb(inbuf,bufsize,timeout,&got_smb);
+ BOOL got_keepalive;
+ BOOL ret;
- 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);
+ do {
+ ret = receive_message_or_smb(inbuf,bufsize,timeout);
+
+ got_keepalive = (ret && (CVAL(inbuf,0) == 0x85));
+ } while (ret && got_keepalive);
- 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();
-
- if (deadtime <= 0)
- deadtime = DEFAULT_SMBD_TIMEOUT;
-
- errno = 0;
-
- /* free up temporary memory */
- lp_talloc_free();
-
- /*
- * 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;
- }
-
- 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. */
- }
-
- 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;
-
- process_smb(InBuffer, OutBuffer);
-
- 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++;
+ while (True) {
+ int deadtime = lp_deadtime()*60;
+ int select_timeout = setup_select_timeout();
+ int num_echos;
- /*
- * 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 (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
- 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);
- }
+ errno = 0;
+
+ /* 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. */
+ }
+
+ /*
+ * 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;
+
+ process_smb(InBuffer, OutBuffer);
+
+ 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. */
+ }
+ }
+ }
}
#undef OLD_NTDOMAIN
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index a5da156250..b28ba6d4ef 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -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");