totally rewrote the async signal, notification and oplock notification
authorAndrew Tridgell <tridge@samba.org>
Mon, 12 Jun 2000 15:53:31 +0000 (15:53 +0000)
committerAndrew Tridgell <tridge@samba.org>
Mon, 12 Jun 2000 15:53:31 +0000 (15:53 +0000)
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!

17 files changed:
source/Makefile.in
source/configure
source/configure.in
source/include/config.h.in
source/include/nterr.h
source/include/proto.h
source/include/smb.h
source/lib/debug.c
source/lib/system.c
source/smbd/notify.c
source/smbd/notify_hash.c [new file with mode: 0644]
source/smbd/notify_kernel.c [new file with mode: 0644]
source/smbd/oplock.c
source/smbd/oplock_irix.c
source/smbd/oplock_linux.c
source/smbd/process.c
source/smbd/server.c

index af46419312cd01266ef551135c5832d109ce228d..091e43bf6dab04ce3f9e6ce4229b69a14d309749 100644 (file)
@@ -108,7 +108,7 @@ LIB_OBJ = lib/charcnv.o lib/charset.o lib/debug.o lib/fault.o \
          lib/util_unistr.o lib/util_file.o \
          lib/util.o lib/util_sock.o lib/util_sec.o smbd/ssl.o \
          lib/talloc.o lib/hash.o lib/substitute.o lib/fsusage.o \
-         lib/ms_fnmatch.o lib/util_seaccess.o \
+         lib/ms_fnmatch.o lib/util_seaccess.o lib/select.o \
          $(TDB_OBJ)
 
 UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \
@@ -159,10 +159,12 @@ PROFILE_OBJ = profile/profile.o
 
 OPLOCK_OBJ = smbd/oplock.o smbd/oplock_irix.o smbd/oplock_linux.o
 
+NOTIFY_OBJ = smbd/notify.o smbd/notify_hash.o 
+
 SMBD_OBJ1 = smbd/server.o smbd/files.o smbd/chgpasswd.o smbd/connection.o \
             smbd/dfree.o smbd/dir.o smbd/password.o smbd/conn.o smbd/fileio.o \
             smbd/ipc.o smbd/lanman.o smbd/mangle.o smbd/negprot.o \
-            smbd/message.o smbd/nttrans.o smbd/notify.o smbd/pipes.o \
+            smbd/message.o smbd/nttrans.o smbd/pipes.o \
             smbd/reply.o smbd/trans2.o smbd/uid.o \
            smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o smbd/blocking.o \
            smbd/vfs.o smbd/vfs-wrap.o smbd/statcache.o \
@@ -180,7 +182,7 @@ MSDFS_OBJ = msdfs/msdfs.o
 SMBD_OBJ = $(SMBD_OBJ1) $(MSDFS_OBJ) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
            $(RPC_SERVER_OBJ) $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) \
            $(LOCKING_OBJ) $(PASSDB_OBJ) $(PRINTING_OBJ) $(PROFILE_OBJ) $(LIB_OBJ) \
-          $(PRINTBACKEND_OBJ) $(QUOTAOBJS) $(OPLOCK_OBJ)
+          $(PRINTBACKEND_OBJ) $(QUOTAOBJS) $(OPLOCK_OBJ) $(NOTIFY_OBJ)
 
 
 NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \
index 49f95411d108f31036cf11962e373470fa7146e2..8150594d77e44ea2278d3987d3d99b90567d2bec 100755 (executable)
@@ -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
index 6d0926cdb27e62786f96e73e21e991d4c1eb9c66..c28beb2429c389fc084bf67b0256d91590ff5fde 100644 (file)
@@ -361,7 +361,7 @@ AC_CHECK_FUNCS(waitpid getcwd strdup strtoul strerror chown chmod chroot)
 AC_CHECK_FUNCS(fstat strchr utime utimes getrlimit fsync bzero memset)
 AC_CHECK_FUNCS(memmove vsnprintf snprintf setsid glob strpbrk pipe crypt16 getauthuid)
 AC_CHECK_FUNCS(strftime sigprocmask sigblock sigaction innetgr setnetgrent getnetgrent endnetgrent)
-AC_CHECK_FUNCS(initgroups select rdchk getgrnam getgrent pathconf)
+AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf)
 AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate stat64 fstat64)
 AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
 AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid yp_get_default_domain getpwanam)
index b4a4f134e4851937099689f2c2e830d338b44af6..e3afd6a66a9ba60e2150bb504cdf75b7804f60b0 100644 (file)
 /* 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
 
index 08e7457376ca589c1bf7bc7ebbab6c3e8f16f0c0..07e874dce019cef4dbb43bf31520fbf68efcf899 100644 (file)
@@ -5,6 +5,7 @@
 #define ERROR_INVALID_PARAMETER                 (87)
 #define ERROR_INSUFFICIENT_BUFFER      (122)
 #define STATUS_1804                   (1804)
+#define STATUS_NOTIFY_ENUM_DIR (0x10C)
 
 
 /* these are the NT error codes less than 1000. They are here for when
 #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 */
index 4f5f3d42d3e9fb52f168adf11631b9843e5f5649..eabacd4d21f868bb43ffda3735b29a04c5da9ab3 100644 (file)
@@ -183,6 +183,12 @@ void pidfile_create(char *name);
 
 char *rep_inet_ntoa(struct in_addr ip);
 
+/*The following definitions come from  lib/select.c  */
+
+void sys_select_signal(void);
+int sys_select(int maxfd, fd_set *fds,struct timeval *tval);
+int sys_select_intr(int maxfd, fd_set *fds,struct timeval *tval);
+
 /*The following definitions come from  lib/signal.c  */
 
 void BlockSignals(BOOL block,int signum);
@@ -212,9 +218,6 @@ void standard_sub_vsnum(char *str, user_struct *vuser, int snum);
 
 /*The following definitions come from  lib/system.c  */
 
-int sys_select(int maxfd, fd_set *fds,struct timeval *tval);
-int sys_select(int maxfd, fd_set *fds,struct timeval *tval);
-int sys_select_intr(int maxfd, fd_set *fds,struct timeval *tval);
 int sys_usleep(long usecs);
 int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf);
 int sys_fstat(int fd,SMB_STRUCT_STAT *sbuf);
@@ -3251,9 +3254,16 @@ BOOL disk_quotas(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT
 void remove_pending_change_notify_requests_by_fid(files_struct *fsp);
 void remove_pending_change_notify_requests_by_mid(int mid);
 void remove_pending_change_notify_requests_by_filename(files_struct *fsp);
-BOOL process_pending_change_notify_queue(time_t t);
 BOOL change_notifies_pending(void);
+BOOL process_pending_change_notify_queue(time_t t);
 BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags);
+BOOL init_change_notify(void);
+#endif
+
+/*The following definitions come from  smbd/notify_hash.c  */
+
+#if OLD_NTDOMAIN
+struct cnotify_fns *hash_notify_init(void) ;
 #endif
 
 /*The following definitions come from  smbd/nttrans.c  */
@@ -3301,6 +3311,18 @@ BOOL attempt_close_oplocked_file(files_struct *fsp);
 BOOL init_oplocks(void);
 #endif
 
+/*The following definitions come from  smbd/oplock_irix.c  */
+
+#if OLD_NTDOMAIN
+struct kernel_oplocks *irix_init_kernel_oplocks(void) ;
+#endif
+
+/*The following definitions come from  smbd/oplock_linux.c  */
+
+#if OLD_NTDOMAIN
+struct kernel_oplocks *linux_init_kernel_oplocks(void) ;
+#endif
+
 /*The following definitions come from  smbd/password.c  */
 
 #if OLD_NTDOMAIN
index 68df3f250c744edff4efb8dfd6e4f8a58e159544..b64fa0bd02e6bb6ae8f6f3021e47fa73020ed983 100644 (file)
@@ -1636,6 +1636,16 @@ struct kernel_oplocks {
 
 #define CMD_REPLY 0x8000
 
+/* this structure defines the functions for doing change notify in
+   various implementations */
+struct cnotify_fns {
+       void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
+       BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
+       void (*remove_notify)(void *data);
+};
+
+
+
 #include "smb_macros.h"
 
 /* A netbios name structure. */
index 675c2d8cfcb190bcca275ab66ddc3b3f23544136..a388956d426cf150e2e65d190079535fcd923311 100644 (file)
@@ -132,6 +132,8 @@ void sig_usr2( int sig )
 
   DEBUG( 0, ( "Got SIGUSR2; set debug level to %d.\n", DEBUGLEVEL ) );
 
+  sys_select_signal();
+
 #if !defined(HAVE_SIGACTION)
   CatchSignal( SIGUSR2, SIGNAL_CAST sig_usr2 );
 #endif
@@ -154,6 +156,8 @@ void sig_usr1( int sig )
 
   DEBUG( 0, ( "Got SIGUSR1; set debug level to %d.\n", DEBUGLEVEL ) );
 
+  sys_select_signal();
+
 #if !defined(HAVE_SIGACTION)
   CatchSignal( SIGUSR1, SIGNAL_CAST sig_usr1 );
 #endif
index 46b01b747a2352e43086dec252034fe9fc5e9f86..479bce19659909ffc49c75a8dde74dda35f40d5b 100644 (file)
@@ -39,115 +39,6 @@ extern int DEBUGLEVEL;
 */
 
 
-/*******************************************************************
-this replaces the normal select() system call
-return if some data has arrived on one of the file descriptors
-return -1 means error
-********************************************************************/
-#ifndef HAVE_SELECT
-static int pollfd(int fd)
-{
-  int     r=0;
-
-#ifdef HAS_RDCHK
-  r = rdchk(fd);
-#elif defined(TCRDCHK)
-  (void)ioctl(fd, TCRDCHK, &r);
-#else
-  (void)ioctl(fd, FIONREAD, &r);
-#endif
-
-  return(r);
-}
-
-int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
-{
-  fd_set fds2;
-  int counter=0;
-  int found=0;
-
-  FD_ZERO(&fds2);
-
-  while (1) 
-  {
-    int i;
-    for (i=0;i<maxfd;i++) {
-      if (FD_ISSET(i,fds) && pollfd(i)>0) {
-        found++;
-        FD_SET(i,&fds2);
-      }
-    }
-
-    if (found) {
-      memcpy((void *)fds,(void *)&fds2,sizeof(fds2));
-      return(found);
-    }
-      
-    if (tval && tval->tv_sec < counter) return(0);
-      sleep(1);
-      counter++;
-  }
-}
-
-#else /* !NO_SELECT */
-int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
-{
-#ifdef USE_POLL
-  struct pollfd pfd[256];
-  int i;
-  int maxpoll;
-  int timeout;
-  int pollrtn;
-
-  maxpoll = 0;
-  for( i = 0; i < maxfd; i++) {
-    if(FD_ISSET(i,fds)) {
-      struct pollfd *pfdp = &pfd[maxpoll++];
-      pfdp->fd = i;
-      pfdp->events = POLLIN;
-      pfdp->revents = 0;
-    }
-  }
-
-  timeout = (tval != NULL) ? (tval->tv_sec * 1000) + (tval->tv_usec/1000) :
-                -1;
-  errno = 0;
-  pollrtn = poll( &pfd[0], maxpoll, timeout);
-
-  FD_ZERO(fds);
-
-  for( i = 0; i < maxpoll; i++)
-    if( pfd[i].revents & POLLIN )
-      FD_SET(pfd[i].fd,fds);
-
-  return pollrtn;
-#else /* USE_POLL */
-
-  struct timeval t2;
-  int selrtn;
-
-  if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2));
-  errno = 0;
-  selrtn = select(maxfd,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL);
-
-  return(selrtn);
-}
-#endif /* USE_POLL */
-#endif /* NO_SELECT */
-
-/*******************************************************************
-similar to sys_select() but catch EINTR and continue
-this is what sys_select() used to do in Samba
-********************************************************************/
-int sys_select_intr(int maxfd, fd_set *fds,struct timeval *tval)
-{
-       int ret;
-       do {
-               ret = sys_select(maxfd, fds, tval);
-       } while (ret == -1 && errno == EINTR);
-       return ret;
-}
-
 
 /*******************************************************************
  A wrapper for usleep in case we don't have one.
index 3af9da238fce9a569a838f90dd0b118ddf62fae7..40867a71eeb652e13afa48e0a32eb547f18962f1 100644 (file)
@@ -3,8 +3,8 @@
    Unix SMB/Netbios implementation.
    Version 3.0
    change notify handling
-   Copyright (C) Jeremy Allison 1994-1998
    Copyright (C) Andrew Tridgell 2000
+   Copyright (C) Jeremy Allison 1994-1998
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 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/source/smbd/notify_hash.c b/source/smbd/notify_hash.c
new file mode 100644 (file)
index 0000000..e01f660
--- /dev/null
@@ -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/source/smbd/notify_kernel.c b/source/smbd/notify_kernel.c
new file mode 100644 (file)
index 0000000..7732bc6
--- /dev/null
@@ -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
index 5e63b4d4ff5976687e30fdf098d3a8f11472fb5a..59c3c83f6f99ceba145755554f09b3cd2202047b 100644 (file)
@@ -71,44 +71,47 @@ BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeou
   smb_read_error = 0;
 
   if(timeout != 0) {
-    struct timeval to;
-    int selrtn;
-    int maxfd = oplock_sock;
+         struct timeval to;
+         int selrtn;
+         int maxfd = oplock_sock;
 
-    if (koplocks && koplocks->notification_fd != -1) {
-           FD_SET(koplocks->notification_fd, fds);
-    }
+         if (koplocks && koplocks->notification_fd != -1) {
+                 FD_SET(koplocks->notification_fd, fds);
+                 maxfd = MAX(maxfd, koplocks->notification_fd);
+         }
 
-    to.tv_sec = timeout / 1000;
-    to.tv_usec = (timeout % 1000) * 1000;
+         to.tv_sec = timeout / 1000;
+         to.tv_usec = (timeout % 1000) * 1000;
 
-    selrtn = sys_select(maxfd+1,fds,&to);
+         selrtn = sys_select(maxfd+1,fds,&to);
 
-    if (selrtn == -1 && errno == EINTR) {
-           /* could be a kernel oplock interrupt */
-           if (koplocks && koplocks->msg_waiting(fds)) {
-                   return koplocks->receive_message(fds, buffer, buffer_len);
-           }
-    }
+         if (selrtn == -1 && errno == EINTR) {
+                 /* could be a kernel oplock interrupt */
+                 if (koplocks && koplocks->msg_waiting(fds)) {
+                         return koplocks->receive_message(fds, buffer, buffer_len);
+                 }
+         }
 
-    /* Check if error */
-    if(selrtn == -1) {
-      /* something is wrong. Maybe the socket is dead? */
-      smb_read_error = READ_ERROR;
-      return False;
-    }
+         /* Check if error */
+         if(selrtn == -1) {
+                 /* something is wrong. Maybe the socket is dead? */
+                 smb_read_error = READ_ERROR;
+                 return False;
+         }
 
-    /* Did we timeout ? */
-    if (selrtn == 0) {
-      smb_read_error = READ_TIMEOUT;
-      return False;
-    }
+         /* Did we timeout ? */
+         if (selrtn == 0) {
+                 smb_read_error = READ_TIMEOUT;
+                 return False;
+         }
   }
 
   if (koplocks && koplocks->msg_waiting(fds)) {
          return koplocks->receive_message(fds, buffer, buffer_len);
   }
 
+  if (!FD_ISSET(oplock_sock, fds)) return False;
+
   /*
    * From here down we deal with the smbd <--> smbd
    * oplock break protocol only.
index 8d55a3d4a051e1a227a1ba9fb82c58c97d3a6d19..c4d528c83546be75e46308d5b0cff10ce615ad0f 100644 (file)
@@ -218,16 +218,16 @@ static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *in
 {
        /* Ensure that the msg length is correct. */
        if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
-               DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
-should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
+               DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n", 
+                        msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
                return False;
        }
 
         memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
         memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
 
-        DEBUG(5,("process_local_message: kernel oplock break request for \
-file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
+        DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f\n", 
+                (unsigned int)*dev, (double)*inode));
 
        return True;
 }
index 46290683d257765565edc1110e4bfdef15b31c63..de2a4300a7a0d2c8967faed337d359bea8deae83 100644 (file)
@@ -29,7 +29,7 @@ extern int DEBUGLEVEL;
 
 static unsigned signals_received;
 static unsigned signals_processed;
-static int fd_pending; /* the fd of the current pending SIGIO */
+static int fd_pending; /* the fd of the current pending signal */
 
 #ifndef F_SETLEASE
 #define F_SETLEASE     1024
@@ -43,14 +43,19 @@ static int fd_pending; /* the fd of the current pending SIGIO */
 #define CAP_LEASE 28
 #endif
 
+#ifndef RT_SIGNAL_LEASE
+#define RT_SIGNAL_LEASE 33
+#endif
+
 /****************************************************************************
-handle a SIGIO, incrementing the signals_received and blocking SIGIO
+handle a LEASE signal, incrementing the signals_received and blocking the signal
 ****************************************************************************/
-static void sigio_handler(int signal, siginfo_t *info, void *unused)
+static void signal_handler(int signal, siginfo_t *info, void *unused)
 {
+       BlockSignals(True, signal);
        fd_pending = info->si_fd;
        signals_received++;
-       BlockSignals(True, SIGIO);
+       sys_select_signal();
 }
 
 /****************************************************************************
@@ -150,7 +155,7 @@ dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
        /* now we can receive more signals */
        fd_pending = -1;
        signals_processed++;
-       BlockSignals(False, SIGIO);
+       BlockSignals(False, RT_SIGNAL_LEASE);
      
        return True;
 }
@@ -213,16 +218,16 @@ static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *i
 {
        /* Ensure that the msg length is correct. */
        if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
-               DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
-should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
+               DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n", 
+                        msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
                return False;
        }
 
         memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
         memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
 
-        DEBUG(5,("process_local_message: kernel oplock break request for \
-file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
+        DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f\n", 
+                (unsigned int)*dev, (double)*inode));
 
        return True;
 }
@@ -264,10 +269,10 @@ struct kernel_oplocks *linux_init_kernel_oplocks(void)
        }
 
         act.sa_handler = NULL;
-        act.sa_sigaction = sigio_handler;
+        act.sa_sigaction = signal_handler;
         act.sa_flags = SA_SIGINFO;
-        if (sigaction(SIGIO, &act, NULL) != 0) {
-               DEBUG(0,("Failed to setup SIGIO handler\n"));
+        if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
+               DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
                return NULL;
         }
 
index 30d03747d852936916c58a4593555ed6830cc2d8..b84e55343ece8d0918f463f8ab47e01887a6cc13 100644 (file)
@@ -107,7 +107,30 @@ static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len)
 
 BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
 {
-  return push_message(&smb_oplock_queue, buf, msg_len);
+       return push_message(&smb_oplock_queue, buf, msg_len);
+}
+
+/****************************************************************************
+do all async processing in here. This includes UDB oplock messages, kernel
+oplock messages, change notify events etc.
+****************************************************************************/
+static void async_processing(fd_set *fds, char *buffer, int buffer_len)
+{
+       /* check for oplock messages (both UDP and kernel) */
+       if (receive_local_message(fds, buffer, buffer_len, 0)) {
+               process_local_message(buffer, buffer_len);
+       }
+
+       /* check for async change notify events */
+       process_pending_change_notify_queue(0);
+
+       /* check for sighup processing */
+       if (reload_after_sighup) {
+               unbecome_user();
+               DEBUG(1,("Reloading services after SIGHUP\n"));
+               reload_services(False);
+               reload_after_sighup = False;
+       }
 }
 
 /****************************************************************************
@@ -115,7 +138,7 @@ BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
 
   If a local udp message has been pushed onto the
   queue (this can only happen during oplock break
-  processing) return this first.
+  processing) call async_processing()
 
   If a pending smb message has been pushed onto the
   queue (this can only happen during oplock break
@@ -131,8 +154,7 @@ BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
 The timeout is in milli seconds
 ****************************************************************************/
 
-static BOOL receive_message_or_smb(char *buffer, int buffer_len, 
-                                   int timeout, BOOL *got_smb)
+static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
 {
        fd_set fds;
        int selrtn;
@@ -141,30 +163,28 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
 
        smb_read_error = 0;
 
-       *got_smb = False;
-
        /*
         * Check to see if we already have a message on the smb queue.
         * If so - copy and return it.
         */
-  
-       if(ubi_slCount(&smb_oplock_queue) != 0) {
+       if(ubi_slCount(&smb_oplock_queue) != 0) {
                pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
                memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
   
                /* Free the message we just copied. */
                free((char *)msg->msg_buf);
                free((char *)msg);
-               *got_smb = True;
                
                DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
                return True;
        }
 
+
        /*
         * Setup the select read fd set.
         */
 
+ again:
        FD_ZERO(&fds);
        FD_SET(smbd_server_fd(),&fds);
        maxfd = setup_oplock_select_set(&fds);
@@ -175,16 +195,16 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
        selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,timeout>0?&to:NULL);
 
        /* if we get EINTR then maybe we have received an oplock
-       signal - treat this as select returning 1. This is ugly, but
-       is the best we can do until the oplock code knows more about
-       signals */
+          signal - treat this as select returning 1. This is ugly, but
+          is the best we can do until the oplock code knows more about
+          signals */
        if (selrtn == -1 && errno == EINTR) {
-               FD_ZERO(&fds);
-               selrtn = 1;
+               async_processing(&fds, buffer, buffer_len);
+               goto again;
        }
 
        /* Check if error */
-       if(selrtn == -1 && errno != EINTR) {
+       if (selrtn == -1) {
                /* something is wrong. Maybe the socket is dead? */
                smb_read_error = READ_ERROR;
                return False;
@@ -195,13 +215,13 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len,
                smb_read_error = READ_TIMEOUT;
                return False;
        }
-       
-       if (FD_ISSET(smbd_server_fd(),&fds)) {
-               *got_smb = True;
-               return receive_smb(smbd_server_fd(), buffer, 0);
-       } else {
-               return receive_local_message(&fds, buffer, buffer_len, 0);
+
+       if (!FD_ISSET(smbd_server_fd(),&fds) || selrtn > 1) {
+               async_processing(&fds, buffer, buffer_len);
+               if (!FD_ISSET(smbd_server_fd(),&fds)) goto again;
        }
+       
+       return receive_smb(smbd_server_fd(), buffer, 0);
 }
 
 /****************************************************************************
@@ -210,30 +230,16 @@ Get the next SMB packet, doing the local message processing automatically.
 
 BOOL receive_next_smb(char *inbuf, int bufsize, int timeout)
 {
-  BOOL got_smb = False;
-  BOOL ret;
-
-  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
index a5da15625063c457edc02273bccba4980d0397a0..b28ba6d4ef0090ca7989c52b786dd113cf3dfd2d 100644 (file)
@@ -350,12 +350,7 @@ static void sig_hup(int sig)
        BlockSignals(True,SIGHUP);
        DEBUG(0,("Got SIGHUP\n"));
 
-       /*
-        * Fix from <branko.cibej@hermes.si> here.
-        * We used to reload in the signal handler - this
-        * is a *BIG* no-no.
-        */
-
+       sys_select_signal();
        reload_after_sighup = True;
        BlockSignals(False,SIGHUP);
 }
@@ -758,6 +753,11 @@ static void usage(char *pname)
                exit(1);
        }
 
+       /* Setup change notify */
+       if (!init_change_notify()) {
+               exit(1);
+       }
+
        smbd_process();
        
        exit_server("normal exit");