summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/open.c79
1 files changed, 79 insertions, 0 deletions
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 0f34992454..d4babd40f7 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1929,6 +1929,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
bool def_acl = False;
bool posix_open = False;
bool new_file_created = False;
+ bool first_open_attempt = true;
NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED;
mode_t new_unx_mode = (mode_t)0;
mode_t unx_mode = (mode_t)0;
@@ -2035,6 +2036,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
/* Ensure we don't reprocess this message. */
remove_deferred_open_message_smb(req->sconn, req->mid);
+
+ first_open_attempt = false;
}
}
@@ -2191,6 +2194,24 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
flags2 &= ~(O_CREAT|O_TRUNC);
}
+ if (first_open_attempt && lp_kernel_oplocks(SNUM(conn))) {
+ /*
+ * With kernel oplocks the open breaking an oplock
+ * blocks until the oplock holder has given up the
+ * oplock or closed the file. We prevent this by first
+ * trying to open the file with O_NONBLOCK (see "man
+ * fcntl" on Linux). For the second try, triggered by
+ * an oplock break response, we do not need this
+ * anymore.
+ *
+ * This is true under the assumption that only Samba
+ * requests kernel oplocks. Once someone else like
+ * NFSv4 starts to use that API, we will have to
+ * modify this by communicating with the NFSv4 server.
+ */
+ flags2 |= O_NONBLOCK;
+ }
+
/*
* Ensure we can't write on a read-only share or file.
*/
@@ -2239,6 +2260,64 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
flags|flags2, unx_mode, access_mask,
open_access_mask, &new_file_created);
+ if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
+ struct deferred_open_record state;
+
+ /*
+ * EWOULDBLOCK/EAGAIN maps to NETWORK_BUSY.
+ */
+ if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
+ DEBUG(10, ("FIFO busy\n"));
+ return NT_STATUS_NETWORK_BUSY;
+ }
+ if (req == NULL) {
+ DEBUG(10, ("Internal open busy\n"));
+ return NT_STATUS_NETWORK_BUSY;
+ }
+
+ /*
+ * From here on we assume this is an oplock break triggered
+ */
+
+ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
+ if (lck == NULL) {
+ state.delayed_for_oplocks = false;
+ state.async_open = false;
+ state.id = fsp->file_id;
+ defer_open(NULL, request_time, timeval_set(0, 0),
+ req, &state);
+ DEBUG(10, ("No share mode lock found after "
+ "EWOULDBLOCK, retrying sync\n"));
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ find_oplock_types(fsp, 0, lck, &batch_entry, &exclusive_entry,
+ &got_level2_oplock, &got_a_none_oplock);
+
+ if (delay_for_batch_oplocks(fsp, req->mid, 0, batch_entry) ||
+ delay_for_exclusive_oplocks(fsp, req->mid, 0,
+ exclusive_entry)) {
+ schedule_defer_open(lck, request_time, req);
+ TALLOC_FREE(lck);
+ DEBUG(10, ("Sent oplock break request to kernel "
+ "oplock holder\n"));
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ /*
+ * No oplock from Samba around. Immediately retry with
+ * a blocking open.
+ */
+ state.delayed_for_oplocks = false;
+ state.async_open = false;
+ state.id = lck->data->id;
+ defer_open(lck, request_time, timeval_set(0, 0), req, &state);
+ TALLOC_FREE(lck);
+ DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
+ "Retrying sync\n"));
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
if (!NT_STATUS_IS_OK(fsp_open)) {
if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
schedule_async_open(request_time, req);