Sync up critical kernel oplock bugfix. I don't want to lose
[samba.git] / source / smbd / oplock.c
index 0cd6f0bef61050981e1c8aca762e55c9082cf695..1455b4d8e5803b951c2ec71bf45b8f7548271186 100644 (file)
@@ -39,6 +39,29 @@ extern int smb_read_error;
 
 static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval);
 
+/****************************************************************************
+ Setup the kernel level oplock backchannel for this process.
+****************************************************************************/
+
+BOOL setup_kernel_oplock_pipe(void)
+{
+#if defined(HAVE_KERNEL_OPLOCKS)
+  if(lp_kernel_oplocks()) {
+    int pfd[2];
+
+    if(pipe(pfd) != 0) {
+      DEBUG(0,("setup_kernel_oplock_pipe: Unable to create pipe. Error was %s\n",
+            strerror(errno) ));
+      return False;
+    }
+
+    oplock_pipe_read = pfd[0];
+    oplock_pipe_write = pfd[1];
+  }
+#endif /* HAVE_KERNEL_OPLOCKS */
+  return True;
+}
+
 /****************************************************************************
   open the oplock IPC socket communication
 ****************************************************************************/
@@ -54,7 +77,7 @@ BOOL open_oplock_ipc(void)
   if (oplock_sock == -1)
   {
     DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
-address %x. Error was %s\n", htonl(INADDR_LOOPBACK), strerror(errno)));
+address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno)));
     global_oplock_port = 0;
     return(False);
   }
@@ -71,6 +94,9 @@ address %x. Error was %s\n", htonl(INADDR_LOOPBACK), strerror(errno)));
   }
   global_oplock_port = ntohs(sock_name.sin_port);
 
+  if(!setup_kernel_oplock_pipe())
+    return False;
+
   DEBUG(3,("open_oplock ipc: pid = %d, global_oplock_port = %u\n", 
             (int)getpid(), global_oplock_port));
 
@@ -158,6 +184,13 @@ Error was %s.\n", strerror(errno) ));
     if(fcntl(oplock_pipe_read, F_OPLKSTAT, &os) < 0) {
       DEBUG(0,("receive_local_message: fcntl of kernel notification failed. \
 Error was %s.\n", strerror(errno) ));
+      if(errno == EAGAIN) {
+        /*
+         * Duplicate kernel break message - ignore.
+         */
+        memset(buffer, '\0', KERNEL_OPLOCK_BREAK_MSG_LEN);
+        return True;
+      }
       smb_read_error = READ_ERROR;
       return False;
     }
@@ -166,7 +199,7 @@ Error was %s.\n", strerror(errno) ));
     inode = (SMB_DEV_T)os.os_ino;
 
     DEBUG(5,("receive_local_message: kernel oplock break request received for \
-dev = %x, inode = %0.f\n", (unsigned int)dev, (double)inode ));
+dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
 
     /*
      * Create a kernel oplock break message.
@@ -220,7 +253,7 @@ dev = %x, inode = %0.f\n", (unsigned int)dev, (double)inode ));
   /* Validate message from address (must be localhost). */
   if(from.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
     DEBUG(0,("receive_local_message: invalid 'from' address \
-(was %x should be 127.0.0.1\n", from.sin_addr.s_addr));
+(was %lx should be 127.0.0.1\n", (long)from.sin_addr.s_addr));
    return False;
   }
 
@@ -240,19 +273,24 @@ BOOL set_file_oplock(files_struct *fsp)
 {
 #if defined(HAVE_KERNEL_OPLOCKS)
   if(lp_kernel_oplocks()) {
-    if(fcntl(fsp->fd_ptr->fd, F_OPLKREG, oplock_pipe_write) < 0) {
+
+    if(fcntl(fsp->fd_ptr->fd, F_OPLKREG, oplock_pipe_write) < 0 ) {
       if(errno != EAGAIN) {
         DEBUG(0,("set_file_oplock: Unable to get kernel oplock on file %s, dev = %x, \
-inode = %0.f. Error was %s\n", 
+inode = %.0f. Error was %s\n", 
               fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode,
                strerror(errno) ));
       } else {
-        DEBUG(5,("set_file_oplock: Refused oplock on file %s, dev = %x, \
-inode = %0.f. Another process had the file open.\n",
-              fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode ));
+        DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f. Another process had the file open.\n",
+              fsp->fsp_name, fsp->fd_ptr->fd, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode ));
       }
       return False;
     }
+
+    DEBUG(10,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f\n",
+          fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode));
+
   }
 #endif /* HAVE_KERNEL_OPLOCKS */
 
@@ -274,7 +312,8 @@ inode = %0.f. Another process had the file open.\n",
 static void release_file_oplock(files_struct *fsp)
 {
 #if defined(HAVE_KERNEL_OPLOCKS)
-  if(fsp->granted_oplock && lp_kernel_oplocks())
+
+  if(lp_kernel_oplocks())
   {
     if( DEBUGLVL( 10 ))
     {
@@ -289,7 +328,7 @@ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev,
     }
 
     /*
-     * Remove the kernel oplock on this file.
+     * Remote the kernel oplock on this file.
      */
 
     if(fcntl(fsp->fd_ptr->fd, F_OPLKACK, OP_REVOKE) < 0)
@@ -414,8 +453,8 @@ should be %d).\n", msg_len, OPLOCK_BREAK_MSG_LEN));
 
         dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
 
-        tval.tv_sec = IVAL(msg_start, OPLOCK_BREAK_SEC_OFFSET);
-        tval.tv_usec = IVAL(msg_start, OPLOCK_BREAK_USEC_OFFSET);
+        tval.tv_sec = (time_t)IVAL(msg_start, OPLOCK_BREAK_SEC_OFFSET);
+        tval.tv_usec = (long)IVAL(msg_start, OPLOCK_BREAK_USEC_OFFSET);
 
         ptval = &tval;
 
@@ -539,6 +578,7 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval)
   connection_struct *saved_conn;
   int saved_vuid;
   pstring saved_dir; 
+  int break_counter = OPLOCK_BREAK_RESENDS;
 
   if( DEBUGLVL( 3 ) )
   {
@@ -658,15 +698,27 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval)
    */
   saved_conn = fsp->conn;
   saved_vuid = current_user.vuid;
-  GetWd(saved_dir);
+  dos_GetWd(saved_dir);
   unbecome_user();
   /* Save the chain fnum. */
   file_chain_save();
 
   while(OPEN_FSP(fsp) && fsp->granted_oplock)
   {
-    if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
+    if(receive_smb(Client,inbuf,
+                  (OPLOCK_BREAK_TIMEOUT/OPLOCK_BREAK_RESENDS) * 1000) == False)
     {
+
+           /* Isaac suggestd that if a MS client doesn't respond to a
+              oplock break request then we might try resending
+              it. Certainly it's no worse than just dropping the
+              socket! */
+           if (smb_read_error == READ_TIMEOUT && break_counter--) {
+                   DEBUG(2, ( "oplock_break resend\n" ) );
+                   send_smb(Client, outbuf);
+                   continue;
+           }
+
       /*
        * Die if we got an error.
        */
@@ -730,7 +782,7 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval)
     exit_server("unable to re-become user");
   }
   /* Including the directory. */
-  ChDir(saved_dir);
+  dos_ChDir(saved_dir);
 
   /* Restore the chain fnum. */
   file_chain_restore();
@@ -989,7 +1041,7 @@ BOOL attempt_close_oplocked_file(files_struct *fsp)
 
   DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name));
 
-  if (fsp->open && fsp->granted_oplock && !fsp->sent_oplock_break) {
+  if (fsp->open && fsp->granted_oplock && !fsp->sent_oplock_break && (fsp->fd_ptr != NULL)) {
 
     /* Try and break the oplock. */
     file_fd_struct *fd_ptr = fsp->fd_ptr;
@@ -1026,8 +1078,10 @@ void check_kernel_oplocks(void)
     int pfd[2];
     pstring tmpname;
 
-    slprintf( tmpname, sizeof(tmpname)-1, "/tmp/ot.%d.XXXXXX", getpid());
-    mktemp(tmpname);
+    set_process_capability(KERNEL_OPLOCK_CAPABILITY,True);
+    set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,True);
+
+       slprintf(tmpname,sizeof(tmpname)-1, "%s/koplock.%d", lp_lockdir(), (int)getpid());
 
     if(pipe(pfd) != 0) {
       DEBUG(0,("check_kernel_oplocks: Unable to create pipe. Error was %s\n",
@@ -1035,7 +1089,7 @@ void check_kernel_oplocks(void)
       return;
     }
 
-    if((fd = open(tmpname, O_RDWR)) < 0) {
+    if((fd = sys_open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600)) < 0) {
       DEBUG(0,("check_kernel_oplocks: Unable to open temp test file %s. Error was %s\n",
             tmpname, strerror(errno) ));
       unlink( tmpname );
@@ -1048,7 +1102,7 @@ void check_kernel_oplocks(void)
 
     if(fcntl(fd, F_OPLKREG, pfd[1]) == -1) {
       DEBUG(0,("check_kernel_oplocks: Kernel oplocks are not available on this machine. \
-Disabling kernel oplock supprt.\n" ));
+Disabling kernel oplock support.\n" ));
       close(pfd[0]);
       close(pfd[1]);
       close(fd);
@@ -1057,20 +1111,22 @@ Disabling kernel oplock supprt.\n" ));
 
     if(fcntl(fd, F_OPLKACK, OP_REVOKE) < 0 ) {
       DEBUG(0,("check_kernel_oplocks: Error when removing kernel oplock. Error was %s. \
-Disabling kernel oplock supprt.\n" ));
+Disabling kernel oplock support.\n", strerror(errno) ));
       close(pfd[0]);
       close(pfd[1]);
       close(fd);
       return;
     }
 
-    oplock_pipe_read = pfd[0];
-    oplock_pipe_write = pfd[1];
+    close(pfd[0]);
+    close(pfd[1]);
     close(fd);
 
-    DEBUG(3,("check_kernel_oplocks: Kernel oplocks enabled.\n"));
-
     lp_set_kernel_oplocks(True);
+
+    DEBUG(0,("check_kernel_oplocks: Kernel oplocks available and set to %s.\n",
+          lp_kernel_oplocks() ? "True" : "False" ));
+
   }
 #endif /* HAVE_KERNEL_OPLOCKS */
 }