summaryrefslogtreecommitdiffstats
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)
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");