summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2014-07-02 20:51:24 -0700
committerKarolin Seeger <kseeger@samba.org>2014-07-15 12:46:16 +0200
commit2f118b639cf1eb1579b8554d42b18b31a5b222dd (patch)
tree62cfa34cd47d78cfef398cba90c82a3efac39dc6
parent01753e8409497f8f82dd637a0ad8402da9464d5c (diff)
downloadsamba-2f118b639cf1eb1579b8554d42b18b31a5b222dd.tar.gz
s3: smbd: Locking - re-add pending lock records if we fail to acquire a lock (and the lock hasn't timed out).
Keep the blocking lock record and the pending lock records consistent if we are dealing with multiple blocking lock requests in one SMB1 LockingX request. Ensure we re-add the records under the record lock, to avoid race conditions. Bug #10684 - SMB1 blocking locks can fail notification on unlock, causing client timeout. https://bugzilla.samba.org/show_bug.cgi?id=10684 Signed-off-by: Jeremy Allison <jra@samba.org> Reviewed-by: Volker Lendecke <Volker.Lendecke@SerNet.DE> (cherry picked from commit 954401f8b2b16b3e2ef9655e8ce94d657becce36)
-rw-r--r--source3/smbd/blocking.c97
1 files changed, 80 insertions, 17 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c
index b6edb65c236..d5b8347469a 100644
--- a/source3/smbd/blocking.c
+++ b/source3/smbd/blocking.c
@@ -459,8 +459,6 @@ static bool process_lockingX(struct blocking_lock_record *blr)
files_struct *fsp = blr->fsp;
uint16 num_ulocks = SVAL(blr->req->vwv+6, 0);
uint16 num_locks = SVAL(blr->req->vwv+7, 0);
- uint64_t count = (uint64_t)0, offset = (uint64_t)0;
- uint64_t smblctx;
bool large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
uint8_t *data;
NTSTATUS status = NT_STATUS_OK;
@@ -478,9 +476,14 @@ static bool process_lockingX(struct blocking_lock_record *blr)
struct byte_range_lock *br_lck = NULL;
bool err;
- smblctx = get_lock_pid( data, blr->lock_num, large_file_format);
- count = get_lock_count( data, blr->lock_num, large_file_format);
- offset = get_lock_offset( data, blr->lock_num, large_file_format, &err);
+ /*
+ * Ensure the blr record gets updated with
+ * any lock we might end up blocked on.
+ */
+
+ blr->smblctx = get_lock_pid( data, blr->lock_num, large_file_format);
+ blr->count = get_lock_count( data, blr->lock_num, large_file_format);
+ blr->offset = get_lock_offset( data, blr->lock_num, large_file_format, &err);
/*
* We know err cannot be set as if it was the lock
@@ -489,9 +492,9 @@ static bool process_lockingX(struct blocking_lock_record *blr)
errno = 0;
br_lck = do_lock(fsp->conn->sconn->msg_ctx,
fsp,
- smblctx,
- count,
- offset,
+ blr->smblctx,
+ blr->count,
+ blr->offset,
((locktype & LOCKING_ANDX_SHARED_LOCK) ?
READ_LOCK : WRITE_LOCK),
WINDOWS_LOCK,
@@ -500,6 +503,34 @@ static bool process_lockingX(struct blocking_lock_record *blr)
&blr->blocking_smblctx,
blr);
+ if (ERROR_WAS_LOCK_DENIED(status) && !lock_timeout) {
+ /*
+ * If we didn't timeout, but still need to wait,
+ * re-add the pending lock entry whilst holding
+ * the brlock db lock.
+ */
+ NTSTATUS status1 =
+ brl_lock(blr->fsp->conn->sconn->msg_ctx,
+ br_lck,
+ blr->smblctx,
+ messaging_server_id(
+ blr->fsp->conn->sconn->msg_ctx),
+ blr->offset,
+ blr->count,
+ blr->lock_type == READ_LOCK ?
+ PENDING_READ_LOCK :
+ PENDING_WRITE_LOCK,
+ blr->lock_flav,
+ true, /* Blocking lock. */
+ NULL,
+ blr);
+
+ if (!NT_STATUS_IS_OK(status1)) {
+ DEBUG(0,("failed to add PENDING_LOCK "
+ "record.\n"));
+ }
+ }
+
TALLOC_FREE(br_lck);
if (NT_STATUS_IS_ERR(status)) {
@@ -573,6 +604,33 @@ static bool process_trans2(struct blocking_lock_record *blr)
&status,
&blr->blocking_smblctx,
blr);
+ if (ERROR_WAS_LOCK_DENIED(status) && !lock_timeout) {
+ /*
+ * If we didn't timeout, but still need to wait,
+ * re-add the pending lock entry whilst holding
+ * the brlock db lock.
+ */
+ NTSTATUS status1 =
+ brl_lock(blr->fsp->conn->sconn->msg_ctx,
+ br_lck,
+ blr->smblctx,
+ messaging_server_id(
+ blr->fsp->conn->sconn->msg_ctx),
+ blr->offset,
+ blr->count,
+ blr->lock_type == READ_LOCK ?
+ PENDING_READ_LOCK :
+ PENDING_WRITE_LOCK,
+ blr->lock_flav,
+ true, /* Blocking lock. */
+ NULL,
+ blr);
+
+ if (!NT_STATUS_IS_OK(status1)) {
+ DEBUG(0,("failed to add PENDING_LOCK record.\n"));
+ }
+ }
+
TALLOC_FREE(br_lck);
if (!NT_STATUS_IS_OK(status)) {
@@ -805,16 +863,13 @@ void process_blocking_lock_queue(struct smbd_server_connection *sconn)
SVAL(blr->req->inbuf,smb_flg),
false);
- if(!blocking_lock_record_process(blr)) {
- DEBUG(10, ("still waiting for lock. BLR = %p\n", blr));
- continue;
- }
+ /*
+ * Remove the pending lock we're waiting on.
+ * If we need to keep waiting blocking_lock_record_process()
+ * will re-add it.
+ */
br_lck = brl_get_locks(talloc_tos(), blr->fsp);
-
- DEBUG(10, ("BLR_process returned true: cancelling and "
- "removing lock. BLR = %p\n", blr));
-
if (br_lck) {
brl_lock_cancel(br_lck,
blr->smblctx,
@@ -823,8 +878,16 @@ void process_blocking_lock_queue(struct smbd_server_connection *sconn)
blr->count,
blr->lock_flav,
blr);
- TALLOC_FREE(br_lck);
}
+ TALLOC_FREE(br_lck);
+
+ if(!blocking_lock_record_process(blr)) {
+ DEBUG(10, ("still waiting for lock. BLR = %p\n", blr));
+ continue;
+ }
+
+ DEBUG(10, ("BLR_process returned true: removing BLR = %p\n",
+ blr));
DLIST_REMOVE(sconn->smb1.locks.blocking_lock_queue, blr);
TALLOC_FREE(blr);