Linux kernel oplocks now seem to work, but need a _lot_ of testing
authorAndrew Tridgell <tridge@samba.org>
Sun, 11 Jun 2000 05:57:58 +0000 (05:57 +0000)
committerAndrew Tridgell <tridge@samba.org>
Sun, 11 Jun 2000 05:57:58 +0000 (05:57 +0000)
I had to modify sys_select() to not loop on EINTR. I added a wrapper
called sys_select_intr() which gives the old behaviour.

16 files changed:
source/client/client.c
source/include/includes.h
source/include/proto.h
source/lib/system.c
source/lib/util.c
source/lib/util_sock.c
source/libsmb/nmblib.c
source/nmbd/nmbd_packets.c
source/param/loadparm.c
source/rpcclient/rpcclient.c
source/smbd/oplock.c
source/smbd/oplock_irix.c
source/smbd/oplock_linux.c
source/smbd/process.c
source/smbd/server.c
source/utils/smbfilter.c

index 6449b1335c65751fe8953acc1c21b7db3c21180c..cbc4eb6f7cd5f308c5498d65d5738b078428e9b5 100644 (file)
@@ -1751,8 +1751,8 @@ static void wait_keyboard(void)
 
                timeout.tv_sec = 20;
                timeout.tv_usec = 0;
-               sys_select(MAX(cli->fd,fileno(stdin))+1,&fds,&timeout);
-      
+               sys_select_intr(MAX(cli->fd,fileno(stdin))+1,&fds,&timeout);
+               
                if (FD_ISSET(fileno(stdin),&fds))
                        return;
 
index 432fd09f0bc2becbfacc65b63a8e84cc083f130c..a30a8448ad5dd35f955b58353c74930c64c96187 100644 (file)
@@ -868,6 +868,18 @@ int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
 #include <dlfcn.h>
 #endif
 
+#if HAVE_KERNEL_OPLOCKS_LINUX
+#ifndef F_SETLEASE
+#define F_SETLEASE     1024
+#endif
+#ifndef F_GETLEASE
+#define F_GETLEASE     1025
+#endif
+#ifndef CAP_LEASE
+#define CAP_LEASE 28
+#endif
+#endif
+
 extern int DEBUGLEVEL;
 
 #endif /* _INCLUDES_H */
index b6aeb19232449aa6b0dc7240c269b6c86e6558be..f874f0e1a19c4142d8009d656ab8f6d9860f1476 100644 (file)
@@ -214,6 +214,7 @@ void standard_sub_vsnum(char *str, user_struct *vuser, int snum);
 
 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);
index 2a99ae779e4e22551d5ed69c3bfebdab72ee9bf7..46b01b747a2352e43086dec252034fe9fc5e9f86 100644 (file)
@@ -112,9 +112,7 @@ int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
   timeout = (tval != NULL) ? (tval->tv_sec * 1000) + (tval->tv_usec/1000) :
                 -1;
   errno = 0;
-  do {
-    pollrtn = poll( &pfd[0], maxpoll, timeout);
-  } while (pollrtn<0 && errno == EINTR);
+  pollrtn = poll( &pfd[0], maxpoll, timeout);
 
   FD_ZERO(fds);
 
@@ -128,17 +126,29 @@ int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
   struct timeval t2;
   int selrtn;
 
-  do {
-    if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2));
-    errno = 0;
-    selrtn = select(maxfd,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL);
-  } while (selrtn<0 && errno == EINTR);
+  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 e5aa20a972a9296ddab2e7652c8b95af56b0443a..f2d89eebb71ebddb0cf29fbd0ccaa099aba30294 100644 (file)
@@ -728,7 +728,7 @@ void msleep(int t)
  
     FD_ZERO(&fds);
     errno = 0;
-    sys_select(0,&fds,&tval);
+    sys_select_intr(0,&fds,&tval);
 
     GetTimeOfDay(&t2);
     tdiff = TvalDiff(&t1,&t2);
index bb62442bebb57e99abc3e12edf7549bb131646b9..e6aef16d16dbb6cdcfa6f632c70dc50cfe78eb7a 100644 (file)
@@ -241,7 +241,7 @@ static ssize_t read_socket_with_timeout(int fd,char *buf,size_t mincnt,size_t ma
     FD_ZERO(&fds);
     FD_SET(fd,&fds);
       
-    selrtn = sys_select(fd+1,&fds,&timeout);
+    selrtn = sys_select_intr(fd+1,&fds,&timeout);
 
     /* Check if error */
     if(selrtn == -1) {
@@ -345,7 +345,7 @@ ssize_t read_with_timeout(int fd,char *buf,size_t mincnt,size_t maxcnt,unsigned
     FD_ZERO(&fds);
     FD_SET(fd,&fds);
       
-    selrtn = sys_select(fd+1,&fds,&timeout);
+    selrtn = sys_select_intr(fd+1,&fds,&timeout);
 
     if(selrtn <= 0)
       return selrtn;
index d4955fa6a62d9da549d6848868516dda47f164b5..e290ee5d4f0a4b6fdb683bf76f67787ac047b68b 100644 (file)
@@ -967,7 +967,7 @@ struct packet_struct *receive_packet(int fd,enum packet_type type,int t)
        timeout.tv_sec = t/1000;
        timeout.tv_usec = 1000*(t%1000);
 
-       if ((ret = sys_select(fd+1,&fds,&timeout)) == -1) {
+       if ((ret = sys_select_intr(fd+1,&fds,&timeout)) == -1) {
                /* errno should be EBADF or EINVAL. */
                DEBUG(0,("select returned -1, errno = %s (%d)\n", strerror(errno), errno));
                return NULL;
index ee2ba2e240143d98f475e3eefdb663222465943b..05b75e984ef44f453beefc362d7c383fed9b6673 100644 (file)
@@ -1793,7 +1793,7 @@ BOOL listen_for_packets(BOOL run_election)
   BlockSignals(False, SIGUSR2);
 #endif /* SIGUSR2 */
 
-  selrtn = sys_select(FD_SETSIZE,&fds,&timeout);
+  selrtn = sys_select_intr(FD_SETSIZE,&fds,&timeout);
 
   /* We can only take signals when we are in the select - block them again here. */
 
index 28dd455e51a1e5ed5afa73ee46192d3f353424dd..20eec790dd956c7574b9706bedcb4d3474982a12 100644 (file)
@@ -1014,7 +1014,6 @@ static void init_globals(void)
        string_set(&Globals.szPasswdProgram, PASSWD_PROGRAM);
        string_set(&Globals.szPrintcapname, PRINTCAP_NAME);
        string_set(&Globals.szLockDir, LOCKDIR);
-       string_set(&Globals.szRootdir, "/");
 #ifdef WITH_UTMP
        string_set(&Globals.szUtmpDir, "");
 #endif /* WITH_UTMP */
index b0fa85b8a4d96208751670e8f22c8a60d95ede20..eccb2fd06ada436ff8a554b7c505061aa5839aea 100644 (file)
@@ -224,7 +224,7 @@ static void wait_keyboard(struct cli_state *cli)
 
       timeout.tv_sec = 20;
       timeout.tv_usec = 0;
-      sys_select(MAX(cli->fd,fileno(stdin))+1,&fds,&timeout);
+      sys_select_intr(MAX(cli->fd,fileno(stdin))+1,&fds,&timeout);
       
       if (FD_ISSET(fileno(stdin),&fds))
        return;
index 44a8e9b0716207b22c783c8be64bc0dbc45e0c13..5e63b4d4ff5976687e30fdf098d3a8f11472fb5a 100644 (file)
@@ -84,6 +84,13 @@ BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeou
 
     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);
+           }
+    }
+
     /* Check if error */
     if(selrtn == -1) {
       /* something is wrong. Maybe the socket is dead? */
@@ -1120,6 +1127,8 @@ address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno)));
        if (lp_kernel_oplocks()) {
 #if HAVE_KERNEL_OPLOCKS_IRIX
                koplocks = irix_init_kernel_oplocks();
+#elif HAVE_KERNEL_OPLOCKS_LINUX
+               koplocks = linux_init_kernel_oplocks();
 #endif
        }
 
index 6eb8ff919159268e827993bb26021283c2dca076..8d55a3d4a051e1a227a1ba9fb82c58c97d3a6d19 100644 (file)
@@ -1,5 +1,4 @@
 #define OLD_NTDOMAIN 1
-#if HAVE_KERNEL_OPLOCKS_IRIX
 
 /* 
    Unix SMB/Netbios implementation.
@@ -24,6 +23,7 @@
 
 #include "includes.h"
 
+#if HAVE_KERNEL_OPLOCKS_IRIX
 extern int DEBUGLEVEL;
 
 
@@ -227,7 +227,7 @@ should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
         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));
+file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
 
        return True;
 }
index d8496ca9ca3caa5e116110c741732cc56632533a..73a14b3e88fe36687b38fbb0ecdb2123ea1d46ab 100644 (file)
@@ -1,5 +1,4 @@
 #define OLD_NTDOMAIN 1
-#if HAVE_KERNEL_OPLOCKS_LINUX
 
 /* 
    Unix SMB/Netbios implementation.
 
 #include "includes.h"
 
+#if HAVE_KERNEL_OPLOCKS_LINUX
+
 extern int DEBUGLEVEL;
 
 static unsigned signals_received;
 static unsigned signals_processed;
 static int fd_pending; /* the fd of the current pending SIGIO */
 
+/* these can be removed when they are in libc */
+typedef struct __user_cap_header_struct {
+        uint32 version;
+        int pid;
+} *cap_user_header_t;
+typedef struct __user_cap_data_struct {
+        uint32 effective;
+        uint32 permitted;
+        uint32 inheritable;
+} *cap_user_data_t;
+
+
 /****************************************************************************
 handle a SIGIO, incrementing the signals_received and blocking SIGIO
 ****************************************************************************/
@@ -40,6 +54,41 @@ static void sigio_handler(int signal, siginfo_t *info, void *unused)
        BlockSignals(True, SIGIO);
 }
 
+/****************************************************************************
+try to gain the CAP_LEASE capability
+****************************************************************************/
+static void set_lease_capability(void)
+{
+       cap_user_header_t header;
+       cap_user_data_t data;
+       if (capget(header, data) == -1) {
+               DEBUG(3,("Unable to get kernel capabilities\n"));
+               return;
+       }
+       data->effective |= (1<<CAP_LEASE);
+       if (capset(header, data) == -1) {
+               DEBUG(3,("Unable to set CAP_LEASE capability\n"));
+       }
+}
+
+
+/****************************************************************************
+call SETLEASE. If we get EACCES then we try setting up the right capability and
+try again
+****************************************************************************/
+static int linux_setlease(int fd, int leasetype)
+{
+       int ret;
+       ret = fcntl(fd, F_SETLEASE, leasetype);
+       if (ret == -1 && errno == EACCES) {
+               set_lease_capability();
+               ret = fcntl(fd, F_SETLEASE, leasetype);
+       }
+
+       return ret;
+}
+
+
 /****************************************************************************
  * Deal with the Linux kernel <--> smbd
  * oplock break protocol.
@@ -95,10 +144,11 @@ dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
 ****************************************************************************/
 static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
 {
-       if (fcntl(fsp->fd, F_SETLEASE, F_WRLCK) == -1) {
+       if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
                DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
-inode = %.0f.\n",
-                        fsp->fsp_name, fsp->fd, (unsigned int)fsp->dev, (double)fsp->inode));
+inode = %.0f. (%s)\n",
+                        fsp->fsp_name, fsp->fd, 
+                        (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
                return False;
        }
        
@@ -128,7 +178,7 @@ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
        /*
         * Remove the kernel oplock on this file.
         */
-       if (fcntl(fsp->fd, F_SETLEASE, F_UNLCK) == -1) {
+       if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
                if (DEBUGLVL(0)) {
                        dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
                        dbgtext("%s, dev = %x, inode = %.0f. Error was %s\n",
@@ -155,7 +205,7 @@ should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
         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));
+file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
 
        return True;
 }
@@ -203,3 +253,4 @@ struct kernel_oplocks *linux_init_kernel_oplocks(void)
 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */
 
 #undef OLD_NTDOMAIN
+
index 82e61a138e8e17b6a906f573a597e80da10d038c..30d03747d852936916c58a4593555ed6830cc2d8 100644 (file)
@@ -134,69 +134,74 @@ The timeout is in milli seconds
 static BOOL receive_message_or_smb(char *buffer, int buffer_len, 
                                    int timeout, BOOL *got_smb)
 {
-  fd_set fds;
-  int selrtn;
-  struct timeval to;
-  int maxfd;
+       fd_set fds;
+       int selrtn;
+       struct timeval to;
+       int maxfd;
 
-  smb_read_error = 0;
+       smb_read_error = 0;
 
-  *got_smb = False;
+       *got_smb = False;
 
-  /*
-   * Check to see if we already have a message on the smb queue.
-   * If so - copy and return it.
-   */
+       /*
+        * 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)
-  {
-    pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
-    memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+       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;
+               /* 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;
+       }
 
-    DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
-    return True;
-  }
+       /*
+        * Setup the select read fd set.
+        */
 
-  /*
-   * Setup the select read fd set.
-   */
+       FD_ZERO(&fds);
+       FD_SET(smbd_server_fd(),&fds);
+       maxfd = setup_oplock_select_set(&fds);
 
-  FD_ZERO(&fds);
-  FD_SET(smbd_server_fd(),&fds);
-  maxfd = setup_oplock_select_set(&fds);
+       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(MAX(maxfd,smbd_server_fd())+1,&fds,timeout>0?&to:NULL);
 
-  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 */
+       if (selrtn == -1 && errno == EINTR) {
+               FD_ZERO(&fds);
+               selrtn = 1;
+       }
 
-  /* 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 && errno != EINTR) {
+               /* 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;
-  }
-
-  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);
-  }
+       /* Did we timeout ? */
+       if (selrtn == 0) {
+               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);
+       }
 }
 
 /****************************************************************************
index ecf1deb05af0116658f84d655f253108d0944c81..a5da15625063c457edc02273bccba4980d0397a0 100644 (file)
@@ -203,7 +203,7 @@ max can be %d\n",
                memcpy((char *)&lfds, (char *)&listen_set, 
                       sizeof(listen_set));
                
-               num = sys_select(FD_SETSIZE,&lfds,NULL);
+               num = sys_select_intr(FD_SETSIZE,&lfds,NULL);
                
                if (num == -1 && errno == EINTR)
                        continue;
index 81b10e4519a21ced5369f4163ee12d8f671f4b6a..5d11c74d14992c11f06a5a3964d93a5d398f561c 100644 (file)
@@ -120,7 +120,7 @@ static void filter_child(int c, struct in_addr dest_ip)
                if (s != -1) FD_SET(s, &fds);
                if (c != -1) FD_SET(c, &fds);
 
-               num = sys_select(MAX(s+1, c+1),&fds,NULL);
+               num = sys_select_intr(MAX(s+1, c+1),&fds,NULL);
                if (num <= 0) continue;
                
                if (c != -1 && FD_ISSET(c, &fds)) {
@@ -184,7 +184,7 @@ static void start_filter(char *desthost)
                FD_ZERO(&fds);
                FD_SET(s, &fds);
 
-               num = sys_select(s+1,&fds,NULL);
+               num = sys_select_intr(s+1,&fds,NULL);
                if (num > 0) {
                        c = accept(s, &addr, &in_addrlen);
                        if (c != -1) {