diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/smbd/open.c | 100 |
1 files changed, 60 insertions, 40 deletions
diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 99a7894762..edc5bc98b6 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -608,7 +608,7 @@ static BOOL is_delete_request(files_struct *fsp) { * 3) Only level2 around: Grant level2 and do nothing else. */ -static BOOL delay_for_oplocks(struct share_mode_lock *lck, files_struct *fsp) +static BOOL delay_for_oplocks(struct share_mode_lock *lck, files_struct *fsp, int pass_number) { int i; struct share_mode_entry *exclusive = NULL; @@ -630,9 +630,16 @@ static BOOL delay_for_oplocks(struct share_mode_lock *lck, files_struct *fsp) /* At least one entry is not an invalid or deferred entry. */ valid_entry = True; - if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) { - SMB_ASSERT(exclusive == NULL); - exclusive = &lck->share_modes[i]; + if (pass_number == 1) { + if (BATCH_OPLOCK_TYPE(lck->share_modes[i].op_type)) { + SMB_ASSERT(exclusive == NULL); + exclusive = &lck->share_modes[i]; + } + } else { + if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) { + SMB_ASSERT(exclusive == NULL); + exclusive = &lck->share_modes[i]; + } } if (lck->share_modes[i].op_type == LEVEL_II_OPLOCK) { @@ -1024,6 +1031,42 @@ BOOL map_open_params_to_ntcreate(const char *fname, int deny_mode, int open_func } +static void schedule_defer_open(struct share_mode_lock *lck, struct timeval request_time) +{ + struct deferred_open_record state; + + /* This is a relative time, added to the absolute + request_time value to get the absolute timeout time. + Note that if this is the second or greater time we enter + this codepath for this particular request mid then + request_time is left as the absolute time of the *first* + time this request mid was processed. This is what allows + the request to eventually time out. */ + + struct timeval timeout; + + /* Normally the smbd we asked should respond within + * OPLOCK_BREAK_TIMEOUT seconds regardless of whether + * the client did, give twice the timeout as a safety + * measure here in case the other smbd is stuck + * somewhere else. */ + + timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0); + + /* Nothing actually uses state.delayed_for_oplocks + but it's handy to differentiate in debug messages + between a 30 second delay due to oplock break, and + a 1 second delay for share mode conflicts. */ + + state.delayed_for_oplocks = True; + state.dev = lck->dev; + state.inode = lck->ino; + + if (!request_timed_out(request_time, timeout)) { + defer_open(lck, request_time, timeout, &state); + } +} + /**************************************************************************** Open a file with a share mode. ****************************************************************************/ @@ -1310,7 +1353,6 @@ files_struct *open_file_ntcreate(connection_struct *conn, } if (file_existed) { - dev = psbuf->st_dev; inode = psbuf->st_ino; @@ -1324,41 +1366,9 @@ files_struct *open_file_ntcreate(connection_struct *conn, return NULL; } - if (delay_for_oplocks(lck, fsp)) { - struct deferred_open_record state; - - /* This is a relative time, added to the absolute - request_time value to get the absolute timeout time. - Note that if this is the second or greater time we enter - this codepath for this particular request mid then - request_time is left as the absolute time of the *first* - time this request mid was processed. This is what allows - the request to eventually time out. */ - - struct timeval timeout; - - /* Normally the smbd we asked should respond within - * OPLOCK_BREAK_TIMEOUT seconds regardless of whether - * the client did, give twice the timeout as a safety - * measure here in case the other smbd is stuck - * somewhere else. */ - - timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0); - - /* Nothing actually uses state.delayed_for_oplocks - but it's handy to differentiate in debug messages - between a 30 second delay due to oplock break, and - a 1 second delay for share mode conflicts. */ - - state.delayed_for_oplocks = True; - state.dev = dev; - state.inode = inode; - - if (!request_timed_out(request_time, timeout)) { - defer_open(lck, request_time, timeout, - &state); - } - + /* First pass - send break only on batch oplocks. */ + if (delay_for_oplocks(lck, fsp, 1)) { + schedule_defer_open(lck, request_time); TALLOC_FREE(lck); return NULL; } @@ -1367,6 +1377,16 @@ files_struct *open_file_ntcreate(connection_struct *conn, access_mask, share_access, create_options, &file_existed); + if (NT_STATUS_IS_OK(status)) { + /* We might be going to allow this open. Check oplock status again. */ + /* Second pass - send break for both batch or exclusive oplocks. */ + if (delay_for_oplocks(lck, fsp, 2)) { + schedule_defer_open(lck, request_time); + TALLOC_FREE(lck); + return NULL; + } + } + if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { /* DELETE_PENDING is not deferred for a second */ set_saved_ntstatus(status); |