From 5864551aef50295addd1c8aa690a52870f70626d Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Wed, 1 Oct 1997 23:32:22 +0000
Subject: OPLOCK CHECK-IN - oplocks are now *OPERATIONAL* !!!! Yipeee. At least
 as far as I can check in a short time :-).

local.h: Changed OPLOCK_BREAK_TIMEOUT to 30 seconds.
locking.c: Big changes to delete oplocks on a share mode entry.
proto.h: updated.
reply.c: Added oplock break code in lockingX reply & readbraw reply.
server.c: Add batch oplock code. Force server shutdown if client fails
          to respond to oplock break.
smb.h: Fix silly slow share mode oplock define bug.
status.c: Add oplock status info.

Jeremy (jallison@whistle.com)
(This used to be commit 4c83d37239f15f855fc10f01d7b4bf4217fb9eda)
---
 source3/smbd/reply.c  |  57 ++++++++++++++-
 source3/smbd/server.c | 199 +++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 226 insertions(+), 30 deletions(-)

(limited to 'source3/smbd')

diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 8987e7c0c2..1a896aa02a 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -1254,10 +1254,13 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize)
 
   if (oplock_request && lp_fake_oplocks(SNUM(cnum))) {
     smb_action |= EXTENDED_OPLOCK_GRANTED;
+    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
   }
 
-  if(fsp->granted_oplock)
+  if(fsp->granted_oplock) {
     smb_action |= EXTENDED_OPLOCK_GRANTED;
+    CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
+  }
 
   set_message(outbuf,15,0,True);
   SSVAL(outbuf,smb_vwv2,fnum);
@@ -1609,6 +1612,22 @@ int reply_readbraw(char *inbuf, char *outbuf)
   int fd;
   char *fname;
 
+#ifdef USE_OPLOCKS
+  /*
+   * Special check if an oplock break has been issued
+   * and the readraw request croses on the wire, we must
+   * return a zero length response here.
+   */
+
+  if(global_oplock_break)
+  {
+    _smb_setlen(header,0);
+    transfer_file(0,Client,0,header,4,0);
+    DEBUG(5,("readbraw - oplock break finished\n"));
+    return -1;
+  }
+#endif
+
   cnum = SVAL(inbuf,smb_tid);
   fnum = GETFNUM(inbuf,smb_vwv0);
 
@@ -3342,7 +3361,10 @@ int reply_setdir(char *inbuf,char *outbuf)
 int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
 {
   int fnum = GETFNUM(inbuf,smb_vwv2);
-  uint16 locktype = SVAL(inbuf,smb_vwv3);
+  unsigned char locktype = CVAL(inbuf,smb_vwv3);
+#if 0
+  unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1);
+#endif /* USE_OPLOCKS */
   uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
   uint16 num_locks = SVAL(inbuf,smb_vwv7);
   uint32 count, offset;
@@ -3359,6 +3381,35 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
   CHECK_ERROR(fnum);
 
   data = smb_buf(inbuf);
+
+#ifdef USE_OPLOCKS
+  /* Check if this is an oplock break on a file
+     we have granted an oplock on.
+   */
+  if((locktype == LOCKING_ANDX_OPLOCK_RELEASE) && 
+     (num_ulocks == 0) && (num_locks == 0) &&
+     (CVAL(inbuf,smb_vwv0) == 0xFF))
+  {
+    DEBUG(5,("reply_lockingX: oplock break reply from client for fnum = %d\n",
+              fnum));
+    /*
+     * Make sure we have granted an oplock on this file.
+     */
+    if(!Files[fnum].granted_oplock)
+    {
+      DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \
+oplock granted on this file.\n", fnum));
+      return ERROR(ERRDOS,ERRlock);
+    }
+
+    /* Just clear the granted flag and return. oplock_break()
+       will handle changing the share_mode_entry. */
+
+    Files[fnum].granted_oplock = 0;
+    return -1;
+  }
+#endif /* USE_OPLOCKS */
+
   /* Data now points at the beginning of the list
      of smb_unlkrng structs */
   for(i = 0; i < (int)num_ulocks; i++) {
@@ -3393,7 +3444,7 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
   set_message(outbuf,2,0,True);
   
   DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n",
-	timestring(),fnum,cnum,locktype,num_locks,num_ulocks));
+	timestring(),fnum,cnum,(unsigned int)locktype,num_locks,num_ulocks));
 
   chain_fnum = fnum;
 
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 708a2c272b..5f59af1bf1 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -1458,23 +1458,74 @@ BOOL check_file_sharing(int cnum,char *fname)
   struct stat sbuf;
   share_lock_token token;
   int pid = getpid();
+  uint32 dev, inode;
 
   if(!lp_share_modes(SNUM(cnum)))
     return True;
 
   if (stat(fname,&sbuf) == -1) return(True);
 
-  lock_share_entry(cnum, (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, &token);
-  num_share_modes = get_share_modes(cnum, token, 
-                     (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, &old_shares);
+  dev = (uint32)sbuf.st_dev;
+  inode = (uint32)sbuf.st_ino;
 
-  for( i = 0; i < num_share_modes; i++)
+  lock_share_entry(cnum, dev, inode, &token);
+  num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares);
+
+  /*
+   * Check if the share modes will give us access.
+   */
+
+  if(num_share_modes != 0)
   {
-    if (old_shares[i].share_mode != DENY_DOS)
-      goto free_and_exit;
+    BOOL broke_oplock;
+
+    do
+    {
+
+      broke_oplock = False;
+      for(i = 0; i < num_share_modes; i++)
+      {
+        min_share_mode_entry *share_entry = &old_shares[i];
+
+        /* someone else has a share lock on it, check to see 
+           if we can too */
+        if ((share_entry->share_mode != DENY_DOS) || (share_entry->pid != pid))
+          goto free_and_exit;
+
+#ifdef USE_OPLOCKS
+        /* 
+         * The share modes would give us access. Check if someone
+         * has an oplock on this file. If so we must break it before
+         * continuing. 
+         */
+        if(share_entry->op_type & BATCH_OPLOCK)
+        {
+
+          DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
 
-    if(old_shares[i].pid != pid)
-      goto free_and_exit;
+          /* Oplock break.... */
+          unlock_share_entry(cnum, dev, inode, token);
+          if(request_oplock_break(share_entry, dev, inode) == False)
+          {
+            free((char *)old_shares);
+            DEBUG(0,("check_file_sharing: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
+            return False;
+          }
+          lock_share_entry(cnum, dev, inode, &token);
+          broke_oplock = True;
+          break;
+        }
+#endif /* USE_OPLOCKS */
+      } /* end for */
+
+      if(broke_oplock)
+      {
+        free((char *)old_shares);
+        num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares);
+      }
+    } while(broke_oplock);
   }
 
   /* XXXX exactly what share mode combinations should be allowed for
@@ -1485,7 +1536,7 @@ BOOL check_file_sharing(int cnum,char *fname)
 
 free_and_exit:
 
-  unlock_share_entry(cnum, (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, token);
+  unlock_share_entry(cnum, dev, inode, token);
   if(old_shares != NULL)
     free((char *)old_shares);
   return(ret);
@@ -1576,6 +1627,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
   share_lock_token token;
   uint32 dev = 0;
   uint32 inode = 0;
+  int num_share_modes = 0;
 
   fs_p->open = False;
   fs_p->fd_ptr = 0;
@@ -1647,7 +1699,6 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
 
   if (lp_share_modes(SNUM(cnum))) 
   {
-    int num_shares = 0;
     int i;
     min_share_mode_entry *old_shares = 0;
 
@@ -1657,14 +1708,14 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
       inode = (uint32)sbuf.st_ino;
       lock_share_entry(cnum, dev, inode, &token);
       share_locked = True;
-      num_shares = get_share_modes(cnum, token, dev, inode, &old_shares);
+      num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares);
     }
 
     /*
      * Check if the share modes will give us access.
      */
 
-    if(share_locked && (num_shares != 0))
+    if(share_locked && (num_share_modes != 0))
     {
       BOOL broke_oplock;
 
@@ -1672,7 +1723,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
       {
 
         broke_oplock = False;
-        for(i = 0; i < num_shares; i++)
+        for(i = 0; i < num_share_modes; i++)
         {
           min_share_mode_entry *share_entry = &old_shares[i];
 
@@ -1696,7 +1747,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
           if(share_entry->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
           {
 
-            DEBUG(5,("open file shared: breaking oplock (%x) on file %s, \
+            DEBUG(5,("open_file_shared: breaking oplock (%x) on file %s, \
 dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
 
             /* Oplock break.... */
@@ -1704,7 +1755,7 @@ dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
             if(request_oplock_break(share_entry, dev, inode) == False)
             {
               free((char *)old_shares);
-              DEBUG(0,("open file shared: FAILED when breaking oplock (%x) on file %s, \
+              DEBUG(0,("open_file_shared: FAILED when breaking oplock (%x) on file %s, \
 dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
               errno = EACCES;
               unix_ERR_class = ERRDOS;
@@ -1721,7 +1772,7 @@ dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
         if(broke_oplock)
         {
           free((char *)old_shares);
-          num_shares = get_share_modes(cnum, token, dev, inode, &old_shares);
+          num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares);
         }
       } while(broke_oplock);
     }
@@ -1782,7 +1833,27 @@ dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
        file (which expects the share_mode_entry to be there).
      */
     if (lp_share_modes(SNUM(cnum)))
-      set_share_mode(token, fnum, 0, 0);
+    {
+      uint16 port = 0;
+#ifdef USE_OPLOCKS
+      /* JRA. Currently this only services Exlcusive and batch
+         oplocks (no other opens on this file). This needs to
+         be extended to level II oplocks (multiple reader
+         oplocks). */
+
+      if(oplock_request && (num_share_modes == 0))
+      {
+        fs_p->granted_oplock = True;
+        global_oplocks_open++;
+        port = oplock_port;
+
+        DEBUG(5,("open_file_shared: granted oplock (%x) on file %s, \
+dev = %x, inode = %x\n", oplock_request, fname, dev, inode));
+      }
+
+#endif /* USE_OPLOCKS */
+      set_share_mode(token, fnum, port, oplock_request);
+    }
 
     if ((flags2&O_TRUNC) && file_existed)
       truncate_unless_locked(fnum,cnum,token,&share_locked);
@@ -2444,7 +2515,7 @@ should be %d).\n", msg_len, OPLOCK_BREAK_MSG_LEN));
         struct sockaddr_in toaddr;
 
         DEBUG(5,("process_local_message: oplock break request from \
-pid %d, dev %d, inode %d\n", remotepid, dev, inode));
+pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
 
         /*
          * If we have no record of any currently open oplocks,
@@ -2483,6 +2554,11 @@ oplocks. Returning success.\n"));
                     remotepid, strerror(errno)));
           return False;
         }
+
+        DEBUG(5,("process_local_message: oplock break reply sent to \
+pid %d, port %d, for file dev = %x, inode = %x\n", remotepid, 
+                from_port, dev, inode));
+
       }
       break;
     default:
@@ -2503,6 +2579,9 @@ BOOL oplock_break(uint32 dev, uint32 inode)
   static char *outbuf = NULL;
   files_struct *fsp = NULL;
   int fnum;
+  share_lock_token token;
+  time_t start_time;
+  BOOL shutdown_server = False;
 
   if(inbuf == NULL)
   {
@@ -2579,24 +2658,90 @@ allowing break to succeed.\n", dev, inode, fnum));
   global_oplock_break = True;
  
   /* Process incoming messages. */
-  while(global_oplock_break && OPEN_FNUM(fnum))
+
+  /* JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT
+     seconds we should just die.... */
+
+  start_time = time(NULL);
+
+  while(OPEN_FNUM(fnum) && fsp->granted_oplock)
   {
     if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
     {
+      /*
+       * Die if we got an error.
+       */
+
       if (smb_read_error == READ_EOF)
-      {
-        DEBUG(3,("oplock_break: end of file from client\n"));
-        return False;
-      }
+        DEBUG(0,("oplock_break: end of file from client\n"));
  
       if (smb_read_error == READ_ERROR)
-      {
-        DEBUG(3,("oplock_break: receive_smb error (%s)\n",
+        DEBUG(0,("oplock_break: receive_smb error (%s)\n",
                   strerror(errno)));
-        return False;
-      }
+
+      if (smb_read_error == READ_TIMEOUT)
+        DEBUG(0,("oplock_break: receive_smb timed out after %d seconds.\n",
+                  OPLOCK_BREAK_TIMEOUT));
+
+      DEBUG(0,("oplock_break failed for file %s (fnum = %d, dev = %x, \
+inode = %x).\n", fsp->name, fnum, dev, inode));
+      shutdown_server = True;
+      break;
     }
     process_smb(inbuf, outbuf);
+
+    /* We only need this in case a readraw crossed on the wire. */
+    global_oplock_break = False;
+
+    /*
+     * Die if we go over the time limit.
+     */
+
+    if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT)
+    {
+      DEBUG(0,("oplock_break: no break received from client within \
+%d seconds.\n", OPLOCK_BREAK_TIMEOUT));
+      DEBUG(0,("oplock_break failed for file %s (fnum = %d, dev = %x, \
+inode = %x).\n", fsp->name, fnum, dev, inode));
+      shutdown_server = True;
+      break;
+    }
+  }
+
+  /*
+   * If the client did not respond we must die.
+   */
+
+  if(shutdown_server)
+  {
+    DEBUG(0,("oplock_break: client failure in break - shutting down this smbd.\n"));
+    close_sockets();
+    close(oplock_sock);
+    exit_server("oplock break failure");
+  }
+
+  if(OPEN_FNUM(fnum))
+  {
+    /* Remove the oplock flag from the sharemode. */
+    lock_share_entry(fsp->cnum, dev, inode, &token);
+    if(remove_share_oplock( fnum, token)==False)
+    {
+      DEBUG(0,("oplock_break: failed to remove share oplock for fnum %d, \
+dev = %x, inode = %x\n", fnum, dev, inode));
+      unlock_share_entry(fsp->cnum, dev, inode, token);
+      return False;
+    }
+    unlock_share_entry(fsp->cnum, dev, inode, token);
+  }
+
+  global_oplocks_open--;
+
+  /* Santity check - remove this later. JRA */
+  if(global_oplocks_open < 0)
+  {
+    DEBUG(0,("oplock_break: global_oplocks_open < 0 (%d). PANIC ERROR\n",
+              global_oplocks_open));
+    abort();
   }
 
   return True;
-- 
cgit