summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/open.c231
1 files changed, 160 insertions, 71 deletions
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 5a725c6634..f236243b37 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -915,93 +915,137 @@ static NTSTATUS send_break_message(files_struct *fsp,
}
/*
- * 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.
+ * Return share_mode_entry pointers for :
+ * 1). Batch oplock entry.
+ * 2). Batch or exclusive oplock entry (may be identical to #1).
+ * bool have_level2_oplock
+ * bool have_no_oplock.
+ * Do internal consistency checks on the share mode for a file.
*/
-static bool delay_for_oplocks(struct share_mode_lock *lck,
- files_struct *fsp,
- uint64_t mid,
- int pass_number,
- int oplock_request)
+static void find_oplock_types(struct share_mode_lock *lck,
+ struct share_mode_entry **pp_batch,
+ struct share_mode_entry **pp_ex_or_batch,
+ bool *got_level2,
+ bool *got_no_oplock)
{
int i;
- struct share_mode_entry *exclusive = NULL;
- 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;
- }
+ *pp_batch = NULL;
+ *pp_ex_or_batch = NULL;
+ *got_level2 = false;
+ *got_no_oplock = false;
for (i=0; i<lck->num_share_modes; i++) {
-
if (!is_valid_share_mode_entry(&lck->share_modes[i])) {
continue;
}
- /* At least one entry is not an invalid or deferred entry. */
- valid_entry = true;
-
- if (pass_number == 1) {
- if (BATCH_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
- SMB_ASSERT(exclusive == NULL);
- exclusive = &lck->share_modes[i];
+ if (BATCH_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
+ /* batch - can only be one. */
+ if (*pp_batch || *got_level2 || *got_no_oplock) {
+ smb_panic("Bad batch oplock entry.");
}
- } else {
- if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
- SMB_ASSERT(exclusive == NULL);
- exclusive = &lck->share_modes[i];
+ *pp_batch = &lck->share_modes[i];
+ }
+
+ if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
+ /* Exclusive or batch - can only be one. */
+ if (*pp_ex_or_batch || *got_level2 || *got_no_oplock) {
+ smb_panic("Bad exclusive or batch oplock entry.");
}
+ *pp_ex_or_batch = &lck->share_modes[i];
}
if (LEVEL_II_OPLOCK_TYPE(lck->share_modes[i].op_type)) {
- SMB_ASSERT(exclusive == NULL);
- have_level2 = true;
+ if (*pp_batch || *pp_ex_or_batch) {
+ smb_panic("Bad levelII oplock entry.");
+ }
+ *got_level2 = true;
}
if (lck->share_modes[i].op_type == NO_OPLOCK) {
- have_a_none_oplock = true;
+ if (*pp_batch || *pp_ex_or_batch) {
+ smb_panic("Bad no oplock entry.");
+ }
+ *got_no_oplock = true;
}
}
+}
+
+static bool delay_for_batch_oplocks(files_struct *fsp,
+ uint64_t mid,
+ int oplock_request,
+ struct share_mode_entry *batch_entry)
+{
+ if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
+ return false;
+ }
+
+ if (batch_entry != NULL) {
+ /* Found a batch oplock */
+ send_break_message(fsp, batch_entry, mid, oplock_request);
+ return true;
+ }
+ return false;
+}
+
+static bool delay_for_exclusive_oplocks(files_struct *fsp,
+ uint64_t mid,
+ int oplock_request,
+ struct share_mode_entry *ex_entry)
+{
+ if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
+ return false;
+ }
- if (exclusive != NULL) { /* Found an exclusive oplock */
+ if (ex_entry != NULL) {
+ /* Found an exclusive or batch oplock */
bool delay_it = is_delete_request(fsp) ?
- BATCH_OPLOCK_TYPE(exclusive->op_type) : true;
- SMB_ASSERT(!have_level2);
+ BATCH_OPLOCK_TYPE(ex_entry->op_type) : true;
if (delay_it) {
- send_break_message(fsp, exclusive, mid, oplock_request);
+ send_break_message(fsp, ex_entry, mid, oplock_request);
return true;
}
}
+ return false;
+}
+
+static void grant_fsp_oplock_type(files_struct *fsp,
+ int oplock_request,
+ bool got_level2_oplock,
+ bool got_a_none_oplock)
+{
+ bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ lp_level2_oplocks(SNUM(fsp->conn));
+
+ /* Start by granting what the client asked for,
+ but ensure no SAMBA_PRIVATE bits can be set. */
+ fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
+
+ if (oplock_request & INTERNAL_OPEN_ONLY) {
+ /* No oplocks on internal open. */
+ fsp->oplock_type = NO_OPLOCK;
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+ fsp->oplock_type, fsp_str_dbg(fsp)));
+ return;
+ }
+
+ if (is_stat_open(fsp->access_mask)) {
+ /* Leave the value already set. */
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+ fsp->oplock_type, fsp_str_dbg(fsp)));
+ return;
+ }
/*
* Match what was requested (fsp->oplock_type) with
* what was found in the existing share modes.
*/
- 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) {
+ if (got_a_none_oplock) {
fsp->oplock_type = NO_OPLOCK;
- } else if (have_level2) {
+ } else if (got_level2_oplock) {
if (fsp->oplock_type == NO_OPLOCK ||
fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) {
/* Store a level2 oplock, but don't tell the client */
@@ -1010,8 +1054,13 @@ static bool delay_for_oplocks(struct share_mode_lock *lck,
fsp->oplock_type = LEVEL_II_OPLOCK;
}
} else {
- /* This case can never happen. */
- SMB_ASSERT(1);
+ /* All share_mode_entries are placeholders or deferred.
+ * Silently upgrade to fake levelII if the client didn't
+ * ask for an oplock. */
+ if (fsp->oplock_type == NO_OPLOCK) {
+ /* Store a level2 oplock, but don't tell the client */
+ fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+ }
}
/*
@@ -1022,11 +1071,8 @@ static bool delay_for_oplocks(struct share_mode_lock *lck,
fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
}
- DEBUG(10,("delay_for_oplocks: oplock type 0x%x on file %s\n",
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
fsp->oplock_type, fsp_str_dbg(fsp)));
-
- /* No delay. */
- return false;
}
bool request_timed_out(struct timeval request_time,
@@ -1801,6 +1847,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
}
if (file_existed) {
+ struct share_mode_entry *batch_entry = NULL;
+ struct share_mode_entry *exclusive_entry = NULL;
+ bool got_level2_oplock = false;
+ bool got_a_none_oplock = false;
+
struct timespec old_write_time = smb_fname->st.st_ex_mtime;
id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
@@ -1813,10 +1864,19 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
return NT_STATUS_SHARING_VIOLATION;
}
+ /* Get the types we need to examine. */
+ find_oplock_types(lck,
+ &batch_entry,
+ &exclusive_entry,
+ &got_level2_oplock,
+ &got_a_none_oplock);
+
/* First pass - send break only on batch oplocks. */
- if ((req != NULL)
- && delay_for_oplocks(lck, fsp, req->mid, 1,
- oplock_request)) {
+ if ((req != NULL) &&
+ delay_for_batch_oplocks(fsp,
+ req->mid,
+ oplock_request,
+ batch_entry)) {
schedule_defer_open(lck, request_time, req);
TALLOC_FREE(lck);
return NT_STATUS_SHARING_VIOLATION;
@@ -1833,9 +1893,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* status again. */
/* Second pass - send break for both batch or
* exclusive oplocks. */
- if ((req != NULL)
- && delay_for_oplocks(lck, fsp, req->mid, 2,
- oplock_request)) {
+ if ((req != NULL) &&
+ delay_for_exclusive_oplocks(
+ fsp,
+ req->mid,
+ oplock_request,
+ exclusive_entry)) {
schedule_defer_open(lck, request_time, req);
TALLOC_FREE(lck);
return NT_STATUS_SHARING_VIOLATION;
@@ -1848,6 +1911,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
return status;
}
+ grant_fsp_oplock_type(fsp,
+ oplock_request,
+ got_level2_oplock,
+ got_a_none_oplock);
+
if (!NT_STATUS_IS_OK(status)) {
uint32 can_access_mask;
bool can_access = True;
@@ -2007,6 +2075,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
}
if (!file_existed) {
+ struct share_mode_entry *batch_entry = NULL;
+ struct share_mode_entry *exclusive_entry = NULL;
+ bool got_level2_oplock = false;
+ bool got_a_none_oplock = false;
struct timespec old_write_time = smb_fname->st.st_ex_mtime;
/*
* Deal with the race condition where two smbd's detect the
@@ -2037,10 +2109,19 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
return NT_STATUS_SHARING_VIOLATION;
}
+ /* Get the types we need to examine. */
+ find_oplock_types(lck,
+ &batch_entry,
+ &exclusive_entry,
+ &got_level2_oplock,
+ &got_a_none_oplock);
+
/* First pass - send break only on batch oplocks. */
- if ((req != NULL)
- && delay_for_oplocks(lck, fsp, req->mid, 1,
- oplock_request)) {
+ if ((req != NULL) &&
+ delay_for_batch_oplocks(fsp,
+ req->mid,
+ oplock_request,
+ batch_entry)) {
schedule_defer_open(lck, request_time, req);
TALLOC_FREE(lck);
fd_close(fsp);
@@ -2056,9 +2137,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* status again. */
/* Second pass - send break for both batch or
* exclusive oplocks. */
- if ((req != NULL)
- && delay_for_oplocks(lck, fsp, req->mid, 2,
- oplock_request)) {
+ if ((req != NULL) &&
+ delay_for_exclusive_oplocks(
+ fsp,
+ req->mid,
+ oplock_request,
+ exclusive_entry)) {
schedule_defer_open(lck, request_time, req);
TALLOC_FREE(lck);
fd_close(fsp);
@@ -2089,6 +2173,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
return status;
}
+ grant_fsp_oplock_type(fsp,
+ oplock_request,
+ got_level2_oplock,
+ got_a_none_oplock);
+
/*
* We exit this block with the share entry *locked*.....
*/