[SCM] Samba Shared Repository - branch v3-3-test updated - release-3-2-0pre2-4717-gda1852f

Jeremy Allison jra at samba.org
Thu Dec 18 01:23:56 GMT 2008


The branch, v3-3-test has been updated
       via  da1852f65188efc559738c2e5da295b702ad4619 (commit)
      from  801e8bbdebe2d5136faf7348294b3cb5a1d43d7e (commit)

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v3-3-test


- Log -----------------------------------------------------------------
commit da1852f65188efc559738c2e5da295b702ad4619
Author: Jeremy Allison <jra at samba.org>
Date:   Wed Dec 17 17:22:34 2008 -0800

    Fix bug #5979 - Level 2 oplocks being granted improperly,
    Jeremy.

-----------------------------------------------------------------------

Summary of changes:
 source/locking/locking.c |   11 +++-
 source/smbd/open.c       |  161 +++++++++++++++++++++++++++-------------------
 source/smbd/oplock.c     |   23 +++++--
 3 files changed, 124 insertions(+), 71 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source/locking/locking.c b/source/locking/locking.c
index c30a105..d679d88 100644
--- a/source/locking/locking.c
+++ b/source/locking/locking.c
@@ -1186,7 +1186,16 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 	}
 
 	e->op_mid = 0;
-	e->op_type = NO_OPLOCK;
+	if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+		/*
+		 * Going from exclusive or batch,
+ 		 * we always go through FAKE_LEVEL_II
+ 		 * first.
+ 		 */
+		e->op_type = FAKE_LEVEL_II_OPLOCK;
+	} else {
+		e->op_type = NO_OPLOCK;
+	}
 	lck->modified = True;
 	return True;
 }
diff --git a/source/smbd/open.c b/source/smbd/open.c
index 52342fa..a465695 100644
--- a/source/smbd/open.c
+++ b/source/smbd/open.c
@@ -725,12 +725,51 @@ static bool is_delete_request(files_struct *fsp) {
 }
 
 /*
+ * 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.
  */
 
@@ -740,20 +779,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++) {
@@ -763,86 +803,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;
 }
 
 static bool request_timed_out(struct timeval request_time,
@@ -1949,12 +1983,9 @@ static NTSTATUS open_file_ntcreate_internal(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) {
diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c
index 2341129..c98d114 100644
--- a/source/smbd/oplock.c
+++ b/source/smbd/oplock.c
@@ -104,7 +104,10 @@ void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds)
 
 bool set_file_oplock(files_struct *fsp, int oplock_type)
 {
-	if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) {
+	if ((fsp->oplock_type != NO_OPLOCK) &&
+	    (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
+	    koplocks &&
+	    !koplocks->set_oplock(fsp, oplock_type)) {
 		return False;
 	}
 
@@ -112,7 +115,7 @@ bool set_file_oplock(files_struct *fsp, int oplock_type)
 	fsp->sent_oplock_break = NO_BREAK_SENT;
 	if (oplock_type == LEVEL_II_OPLOCK) {
 		level_II_oplocks_open++;
-	} else {
+	} else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
 		exclusive_oplocks_open++;
 	}
 
@@ -145,10 +148,15 @@ void release_file_oplock(files_struct *fsp)
 
 	SMB_ASSERT(exclusive_oplocks_open>=0);
 	SMB_ASSERT(level_II_oplocks_open>=0);
-	
-	fsp->oplock_type = NO_OPLOCK;
+
+	if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+		/* This doesn't matter for close. */
+		fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+	} else {
+		fsp->oplock_type = NO_OPLOCK;
+	}
 	fsp->sent_oplock_break = NO_BREAK_SENT;
-	
+
 	flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
 
 	TALLOC_FREE(fsp->oplock_timeout);
@@ -435,6 +443,11 @@ static void process_oplock_async_level2_break_message(struct messaging_context *
 	/* Ensure we're really at level2 state. */
 	SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK);
 
+	DEBUG(10,("process_oplock_async_level2_break_message: sending break to "
+		"none message for fid %d, file %s\n",
+		fsp->fnum,
+		fsp->fsp_name));
+
 	/* Now send a break to none message to our client. */
 
 	break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE);


-- 
Samba Shared Repository


More information about the samba-cvs mailing list