Merge branch 'master' of ssh://git.samba.org/data/git/samba
[gd/samba/.git] / source3 / smbd / open.c
index d22eda2bb5490b8bd9afa141ebdbce40a22421f7..f00361979d5ffa20921053b40f8b53735f427307 100644 (file)
@@ -741,13 +741,52 @@ static bool is_delete_request(files_struct *fsp) {
                (fsp->oplock_type == NO_OPLOCK));
 }
 
+/*
+ * Send a break message to the oplock holder and delay the open for
+ * our client.
+ */
+
+static NTSTATUS send_break_message(files_struct *fsp,
+                                       struct share_mode_entry *exclusive,
+                                       uint16 mid,
+                                       int oplock_request)
+{
+       NTSTATUS status;
+       char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+
+       DEBUG(10, ("Sending break request to PID %s\n",
+                  procid_str_static(&exclusive->pid)));
+       exclusive->op_mid = mid;
+
+       /* Create the message. */
+       share_mode_entry_to_message(msg, exclusive);
+
+       /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We
+          don't want this set in the share mode struct pointed to by lck. */
+
+       if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) {
+               SSVAL(msg,6,exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE);
+       }
+
+       status = messaging_send_buf(smbd_messaging_context(), exclusive->pid,
+                                   MSG_SMB_BREAK_REQUEST,
+                                   (uint8 *)msg,
+                                   MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(3, ("Could not send oplock break message: %s\n",
+                         nt_errstr(status)));
+       }
+
+       return status;
+}
+
 /*
  * 1) No files open at all or internal open: Grant whatever the client wants.
  *
  * 2) Exclusive (or batch) oplock around: If the requested access is a delete
  *    request, break if the oplock around is a batch oplock. If it's another
  *    requested access type, break.
- * 
+ *
  * 3) Only level2 around: Grant level2 and do nothing else.
  */
 
@@ -757,20 +796,21 @@ static bool delay_for_oplocks(struct share_mode_lock *lck,
                              int pass_number,
                              int oplock_request)
 {
+       extern uint32 global_client_caps;
        int i;
        struct share_mode_entry *exclusive = NULL;
-       bool valid_entry = False;
-       bool delay_it = False;
-       bool have_level2 = False;
-       NTSTATUS status;
-       char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+       bool valid_entry = false;
+       bool have_level2 = false;
+       bool have_a_none_oplock = false;
+       bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+                           lp_level2_oplocks(SNUM(fsp->conn));
 
        if (oplock_request & INTERNAL_OPEN_ONLY) {
                fsp->oplock_type = NO_OPLOCK;
        }
 
        if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
-               return False;
+               return false;
        }
 
        for (i=0; i<lck->num_share_modes; i++) {
@@ -780,86 +820,80 @@ static bool delay_for_oplocks(struct share_mode_lock *lck,
                }
 
                /* At least one entry is not an invalid or deferred entry. */
-               valid_entry = True;
+               valid_entry = true;
 
                if (pass_number == 1) {
                        if (BATCH_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
-                               SMB_ASSERT(exclusive == NULL);                  
+                               SMB_ASSERT(exclusive == NULL);
                                exclusive = &lck->share_modes[i];
                        }
                } else {
                        if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
-                               SMB_ASSERT(exclusive == NULL);                  
+                               SMB_ASSERT(exclusive == NULL);
                                exclusive = &lck->share_modes[i];
                        }
                }
 
-               if (lck->share_modes[i].op_type == LEVEL_II_OPLOCK) {
-                       SMB_ASSERT(exclusive == NULL);                  
-                       have_level2 = True;
+               if (LEVEL_II_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
+                       SMB_ASSERT(exclusive == NULL);
+                       have_level2 = true;
                }
-       }
 
-       if (!valid_entry) {
-               /* All entries are placeholders or deferred.
-                * Directly grant whatever the client wants. */
-               if (fsp->oplock_type == NO_OPLOCK) {
-                       /* Store a level2 oplock, but don't tell the client */
-                       fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+               if (lck->share_modes[i].op_type == NO_OPLOCK) {
+                       have_a_none_oplock = true;
                }
-               return False;
        }
 
        if (exclusive != NULL) { /* Found an exclusive oplock */
+               bool delay_it = is_delete_request(fsp) ?
+                               BATCH_OPLOCK_TYPE(exclusive->op_type) : true;
                SMB_ASSERT(!have_level2);
-               delay_it = is_delete_request(fsp) ?
-                       BATCH_OPLOCK_TYPE(exclusive->op_type) : True;
-       }
-
-       if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
-               /* We can at most grant level2 as there are other
-                * level2 or NO_OPLOCK entries. */
-               fsp->oplock_type = LEVEL_II_OPLOCK;
+               if (delay_it) {
+                       send_break_message(fsp, exclusive, mid, oplock_request);
+                       return true;
+               }
        }
 
-       if ((fsp->oplock_type == NO_OPLOCK) && have_level2) {
-               /* Store a level2 oplock, but don't tell the client */
-               fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
-       }
+       /*
+        * Match what was requested (fsp->oplock_type) with
+        * what was found in the existing share modes.
+        */
 
-       if (!delay_it) {
-               return False;
+       if (!valid_entry) {
+               /* All entries are placeholders or deferred.
+                * Directly grant whatever the client wants. */
+               if (fsp->oplock_type == NO_OPLOCK) {
+                       /* Store a level2 oplock, but don't tell the client */
+                       fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+               }
+       } else if (have_a_none_oplock) {
+               fsp->oplock_type = NO_OPLOCK;
+       } else if (have_level2) {
+               if (fsp->oplock_type == NO_OPLOCK ||
+                               fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) {
+                       /* Store a level2 oplock, but don't tell the client */
+                       fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+               } else {
+                       fsp->oplock_type = LEVEL_II_OPLOCK;
+               }
+       } else {
+               /* This case can never happen. */
+               SMB_ASSERT(1);
        }
 
        /*
-        * Send a break message to the oplock holder and delay the open for
-        * our client.
+        * Don't grant level2 to clients that don't want them
+        * or if we've turned them off.
         */
-
-       DEBUG(10, ("Sending break request to PID %s\n",
-                  procid_str_static(&exclusive->pid)));
-       exclusive->op_mid = mid;
-
-       /* Create the message. */
-       share_mode_entry_to_message(msg, exclusive);
-
-       /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We
-          don't want this set in the share mode struct pointed to by lck. */
-
-       if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) {
-               SSVAL(msg,6,exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE);
+       if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
+               fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
        }
 
-       status = messaging_send_buf(smbd_messaging_context(), exclusive->pid,
-                                   MSG_SMB_BREAK_REQUEST,
-                                   (uint8 *)msg,
-                                   MSG_SMB_SHARE_MODE_ENTRY_SIZE);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(3, ("Could not send oplock break message: %s\n",
-                         nt_errstr(status)));
-       }
+       DEBUG(10,("delay_for_oplocks: oplock type 0x%x on file %s\n",
+               fsp->oplock_type, fsp->fsp_name));
 
-       return True;
+       /* No delay. */
+       return false;
 }
 
 bool request_timed_out(struct timeval request_time,
@@ -1968,12 +2002,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         * file structs.
         */
 
-       if ((fsp->oplock_type != NO_OPLOCK) &&
-           (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) {
-               if (!set_file_oplock(fsp, fsp->oplock_type)) {
-                       /* Could not get the kernel oplock */
-                       fsp->oplock_type = NO_OPLOCK;
-               }
+       if (!set_file_oplock(fsp, fsp->oplock_type)) {
+               /* Could not get the kernel oplock */
+               fsp->oplock_type = NO_OPLOCK;
        }
 
        if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED || info == FILE_WAS_SUPERSEDED) {