summaryrefslogtreecommitdiffstats
path: root/source3/smbd/blocking.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source3/smbd/blocking.c763
1 files changed, 763 insertions, 0 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c
new file mode 100644
index 0000000..8b41288
--- /dev/null
+++ b/source3/smbd/blocking.c
@@ -0,0 +1,763 @@
+/*
+ Unix SMB/CIFS implementation.
+ Blocking Locking functions
+ Copyright (C) Jeremy Allison 1998-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "locking/share_mode_lock.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "messages.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/dbwrap/dbwrap_watch.h"
+#include "librpc/gen_ndr/ndr_open_files.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
+
+NTSTATUS smbd_do_locks_try(
+ struct files_struct *fsp,
+ uint16_t num_locks,
+ struct smbd_lock_element *locks,
+ uint16_t *blocker_idx,
+ struct server_id *blocking_pid,
+ uint64_t *blocking_smblctx)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ uint16_t i;
+
+ for (i=0; i<num_locks; i++) {
+ struct smbd_lock_element *e = &locks[i];
+
+ status = do_lock(
+ fsp,
+ locks, /* req_mem_ctx */
+ &e->req_guid,
+ e->smblctx,
+ e->count,
+ e->offset,
+ e->brltype,
+ e->lock_flav,
+ blocking_pid,
+ blocking_smblctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ *blocker_idx = i;
+
+ /*
+ * Undo the locks we successfully got
+ */
+ for (i = i-1; i != UINT16_MAX; i--) {
+ struct smbd_lock_element *e = &locks[i];
+ do_unlock(fsp,
+ e->smblctx,
+ e->count,
+ e->offset,
+ e->lock_flav);
+ }
+
+ return status;
+}
+
+static bool smbd_smb1_fsp_add_blocked_lock_req(
+ struct files_struct *fsp, struct tevent_req *req)
+{
+ size_t num_reqs = talloc_array_length(fsp->blocked_smb1_lock_reqs);
+ struct tevent_req **tmp = NULL;
+
+ tmp = talloc_realloc(
+ fsp,
+ fsp->blocked_smb1_lock_reqs,
+ struct tevent_req *,
+ num_reqs+1);
+ if (tmp == NULL) {
+ return false;
+ }
+ fsp->blocked_smb1_lock_reqs = tmp;
+ fsp->blocked_smb1_lock_reqs[num_reqs] = req;
+ return true;
+}
+
+struct smbd_smb1_do_locks_state {
+ struct tevent_context *ev;
+ struct smb_request *smbreq;
+ struct files_struct *fsp;
+ uint32_t timeout;
+ uint32_t polling_msecs;
+ uint32_t retry_msecs;
+ struct timeval endtime;
+ bool large_offset; /* required for correct cancel */
+ uint16_t num_locks;
+ struct smbd_lock_element *locks;
+ uint16_t blocker;
+ NTSTATUS deny_status;
+};
+
+static void smbd_smb1_do_locks_try(struct tevent_req *req);
+static void smbd_smb1_do_locks_retry(struct tevent_req *subreq);
+static void smbd_smb1_blocked_locks_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state);
+static NTSTATUS smbd_smb1_do_locks_check(
+ struct files_struct *fsp,
+ uint16_t num_locks,
+ struct smbd_lock_element *locks,
+ uint16_t *blocker_idx,
+ struct server_id *blocking_pid,
+ uint64_t *blocking_smblctx);
+
+static void smbd_smb1_do_locks_setup_timeout(
+ struct smbd_smb1_do_locks_state *state,
+ const struct smbd_lock_element *blocker)
+{
+ struct files_struct *fsp = state->fsp;
+
+ if (!timeval_is_zero(&state->endtime)) {
+ /*
+ * already done
+ */
+ return;
+ }
+
+ if ((state->timeout != 0) && (state->timeout != UINT32_MAX)) {
+ /*
+ * Windows internal resolution for blocking locks
+ * seems to be about 200ms... Don't wait for less than
+ * that. JRA.
+ */
+ state->timeout = MAX(state->timeout, lp_lock_spin_time());
+ }
+
+ if (state->timeout != 0) {
+ goto set_endtime;
+ }
+
+ if (blocker == NULL) {
+ goto set_endtime;
+ }
+
+ if ((blocker->offset >= 0xEF000000) &&
+ ((blocker->offset >> 63) == 0)) {
+ /*
+ * This must be an optimization of an ancient
+ * application bug...
+ */
+ state->timeout = lp_lock_spin_time();
+ }
+
+ if (fsp->fsp_flags.lock_failure_seen &&
+ (blocker->offset == fsp->lock_failure_offset)) {
+ /*
+ * Delay repeated lock attempts on the same
+ * lock. Maybe a more advanced version of the
+ * above check?
+ */
+ DBG_DEBUG("Delaying lock request due to previous "
+ "failure\n");
+ state->timeout = lp_lock_spin_time();
+ }
+
+set_endtime:
+ /*
+ * Note state->timeout might still 0,
+ * but that's ok, as we don't want to retry
+ * in that case.
+ */
+ state->endtime = timeval_add(&state->smbreq->request_time,
+ state->timeout / 1000,
+ (state->timeout % 1000) * 1000);
+}
+
+static void smbd_smb1_do_locks_update_retry_msecs(
+ struct smbd_smb1_do_locks_state *state)
+{
+ /*
+ * The default lp_lock_spin_time() is 200ms,
+ * we just use half of it to trigger the first retry.
+ *
+ * v_min is in the range of 0.001 to 10 secs
+ * (0.1 secs by default)
+ *
+ * v_max is in the range of 0.01 to 100 secs
+ * (1.0 secs by default)
+ *
+ * The typical steps are:
+ * 0.1, 0.2, 0.3, 0.4, ... 1.0
+ */
+ uint32_t v_min = MAX(2, MIN(20000, lp_lock_spin_time()))/2;
+ uint32_t v_max = 10 * v_min;
+
+ if (state->retry_msecs >= v_max) {
+ state->retry_msecs = v_max;
+ return;
+ }
+
+ state->retry_msecs += v_min;
+}
+
+static void smbd_smb1_do_locks_update_polling_msecs(
+ struct smbd_smb1_do_locks_state *state)
+{
+ /*
+ * The default lp_lock_spin_time() is 200ms.
+ *
+ * v_min is in the range of 0.002 to 20 secs
+ * (0.2 secs by default)
+ *
+ * v_max is in the range of 0.02 to 200 secs
+ * (2.0 secs by default)
+ *
+ * The typical steps are:
+ * 0.2, 0.4, 0.6, 0.8, ... 2.0
+ */
+ uint32_t v_min = MAX(2, MIN(20000, lp_lock_spin_time()));
+ uint32_t v_max = 10 * v_min;
+
+ if (state->polling_msecs >= v_max) {
+ state->polling_msecs = v_max;
+ return;
+ }
+
+ state->polling_msecs += v_min;
+}
+
+struct tevent_req *smbd_smb1_do_locks_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smb_request **smbreq, /* talloc_move()d into our state */
+ struct files_struct *fsp,
+ uint32_t lock_timeout,
+ bool large_offset,
+ uint16_t num_locks,
+ struct smbd_lock_element *locks)
+{
+ struct tevent_req *req = NULL;
+ struct smbd_smb1_do_locks_state *state = NULL;
+ bool ok;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct smbd_smb1_do_locks_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->smbreq = talloc_move(state, smbreq);
+ state->fsp = fsp;
+ state->timeout = lock_timeout;
+ state->large_offset = large_offset;
+ state->num_locks = num_locks;
+ state->locks = locks;
+ state->deny_status = NT_STATUS_LOCK_NOT_GRANTED;
+
+ DBG_DEBUG("state=%p, state->smbreq=%p\n", state, state->smbreq);
+
+ if (num_locks == 0 || locks == NULL) {
+ DBG_DEBUG("no locks\n");
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (state->locks[0].lock_flav == POSIX_LOCK) {
+ /*
+ * SMB1 posix locks always use
+ * NT_STATUS_FILE_LOCK_CONFLICT.
+ */
+ state->deny_status = NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ smbd_smb1_do_locks_try(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ok = smbd_smb1_fsp_add_blocked_lock_req(fsp, req);
+ if (!ok) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_cleanup_fn(req, smbd_smb1_blocked_locks_cleanup);
+ return req;
+}
+
+static void smbd_smb1_blocked_locks_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state)
+{
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+ struct files_struct *fsp = state->fsp;
+ struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
+ size_t num_blocked = talloc_array_length(blocked);
+ size_t i;
+
+ DBG_DEBUG("req=%p, state=%p, req_state=%d\n",
+ req,
+ state,
+ (int)req_state);
+
+ if (req_state == TEVENT_REQ_RECEIVED) {
+ DBG_DEBUG("already received\n");
+ return;
+ }
+
+ for (i=0; i<num_blocked; i++) {
+ if (blocked[i] == req) {
+ break;
+ }
+ }
+ SMB_ASSERT(i<num_blocked);
+
+ ARRAY_DEL_ELEMENT(blocked, i, num_blocked);
+
+ fsp->blocked_smb1_lock_reqs = talloc_realloc(
+ fsp, blocked, struct tevent_req *, num_blocked-1);
+}
+
+static NTSTATUS smbd_smb1_do_locks_check_blocked(
+ uint16_t num_blocked,
+ struct smbd_lock_element *blocked,
+ uint16_t num_locks,
+ struct smbd_lock_element *locks,
+ uint16_t *blocker_idx,
+ uint64_t *blocking_smblctx)
+{
+ uint16_t li;
+
+ for (li=0; li < num_locks; li++) {
+ struct smbd_lock_element *l = &locks[li];
+ uint16_t bi;
+ bool valid;
+
+ valid = byte_range_valid(l->offset, l->count);
+ if (!valid) {
+ return NT_STATUS_INVALID_LOCK_RANGE;
+ }
+
+ for (bi = 0; bi < num_blocked; bi++) {
+ struct smbd_lock_element *b = &blocked[li];
+ bool overlap;
+
+ /* Read locks never conflict. */
+ if (l->brltype == READ_LOCK && b->brltype == READ_LOCK) {
+ continue;
+ }
+
+ overlap = byte_range_overlap(l->offset,
+ l->count,
+ b->offset,
+ b->count);
+ if (!overlap) {
+ continue;
+ }
+
+ *blocker_idx = li;
+ *blocking_smblctx = b->smblctx;
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbd_smb1_do_locks_check(
+ struct files_struct *fsp,
+ uint16_t num_locks,
+ struct smbd_lock_element *locks,
+ uint16_t *blocker_idx,
+ struct server_id *blocking_pid,
+ uint64_t *blocking_smblctx)
+{
+ struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
+ size_t num_blocked = talloc_array_length(blocked);
+ NTSTATUS status;
+ size_t bi;
+
+ /*
+ * We check the pending/blocked requests
+ * from the oldest to the youngest request.
+ *
+ * Note due to the retry logic the current request
+ * might already be in the list.
+ */
+
+ for (bi = 0; bi < num_blocked; bi++) {
+ struct smbd_smb1_do_locks_state *blocked_state =
+ tevent_req_data(blocked[bi],
+ struct smbd_smb1_do_locks_state);
+
+ if (blocked_state->locks == locks) {
+ SMB_ASSERT(blocked_state->num_locks == num_locks);
+
+ /*
+ * We found ourself...
+ */
+ break;
+ }
+
+ status = smbd_smb1_do_locks_check_blocked(
+ blocked_state->num_locks,
+ blocked_state->locks,
+ num_locks,
+ locks,
+ blocker_idx,
+ blocking_smblctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ *blocking_pid = messaging_server_id(
+ fsp->conn->sconn->msg_ctx);
+ return status;
+ }
+ }
+
+ status = smbd_do_locks_try(
+ fsp,
+ num_locks,
+ locks,
+ blocker_idx,
+ blocking_pid,
+ blocking_smblctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void smbd_smb1_do_locks_try(struct tevent_req *req)
+{
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+ struct files_struct *fsp = state->fsp;
+ struct share_mode_lock *lck;
+ struct timeval endtime = { 0 };
+ struct server_id blocking_pid = { 0 };
+ uint64_t blocking_smblctx = 0;
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+ bool ok;
+ bool expired;
+
+ lck = get_existing_share_mode_lock(state, fsp->file_id);
+ if (tevent_req_nomem(lck, req)) {
+ DBG_DEBUG("Could not get share mode lock\n");
+ return;
+ }
+
+ status = smbd_smb1_do_locks_check(
+ fsp,
+ state->num_locks,
+ state->locks,
+ &state->blocker,
+ &blocking_pid,
+ &blocking_smblctx);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+ /*
+ * We got NT_STATUS_RETRY,
+ * we reset polling_msecs so that
+ * that the retries based on LOCK_NOT_GRANTED
+ * will later start with small intervals again.
+ */
+ state->polling_msecs = 0;
+
+ /*
+ * The backend wasn't able to decide yet.
+ * We need to wait even for non-blocking
+ * locks.
+ *
+ * The backend uses blocking_smblctx == UINT64_MAX
+ * to indicate that we should use retry timers.
+ *
+ * It uses blocking_smblctx == 0 to indicate
+ * it will use share_mode_wakeup_waiters()
+ * to wake us. Note that unrelated changes in
+ * locking.tdb may cause retries.
+ */
+
+ if (blocking_smblctx != UINT64_MAX) {
+ SMB_ASSERT(blocking_smblctx == 0);
+ goto setup_retry;
+ }
+
+ smbd_smb1_do_locks_update_retry_msecs(state);
+
+ DBG_DEBUG("Waiting for a backend decision. "
+ "Retry in %"PRIu32" msecs\n",
+ state->retry_msecs);
+
+ /*
+ * We completely ignore state->endtime here
+ * we we'll wait for a backend decision forever.
+ * If the backend is smart enough to implement
+ * some NT_STATUS_RETRY logic, it has to
+ * switch to any other status after in order
+ * to avoid waiting forever.
+ */
+ endtime = timeval_current_ofs_msec(state->retry_msecs);
+ goto setup_retry;
+ }
+ if (!ERROR_WAS_LOCK_DENIED(status)) {
+ goto done;
+ }
+ /*
+ * We got LOCK_NOT_GRANTED, make sure
+ * a following STATUS_RETRY will start
+ * with short intervals again.
+ */
+ state->retry_msecs = 0;
+
+ smbd_smb1_do_locks_setup_timeout(state, &state->locks[state->blocker]);
+ DBG_DEBUG("timeout=%"PRIu32", blocking_smblctx=%"PRIu64"\n",
+ state->timeout,
+ blocking_smblctx);
+
+ /*
+ * The client specified timeout expired
+ * avoid further retries.
+ *
+ * Otherwise keep waiting either waiting
+ * for changes in locking.tdb or the polling
+ * mode timers waiting for posix locks.
+ *
+ * If the endtime is not expired yet,
+ * it means we'll retry after a timeout.
+ * In that case we'll have to return
+ * NT_STATUS_FILE_LOCK_CONFLICT
+ * instead of NT_STATUS_LOCK_NOT_GRANTED.
+ */
+ expired = timeval_expired(&state->endtime);
+ if (expired) {
+ status = state->deny_status;
+ goto done;
+ }
+ state->deny_status = NT_STATUS_FILE_LOCK_CONFLICT;
+
+ endtime = state->endtime;
+
+ if (blocking_smblctx == UINT64_MAX) {
+ struct timeval tmp;
+
+ smbd_smb1_do_locks_update_polling_msecs(state);
+
+ DBG_DEBUG("Blocked on a posix lock. Retry in %"PRIu32" msecs\n",
+ state->polling_msecs);
+
+ tmp = timeval_current_ofs_msec(state->polling_msecs);
+ endtime = timeval_min(&endtime, &tmp);
+ }
+
+setup_retry:
+ subreq = share_mode_watch_send(
+ state, state->ev, lck, blocking_pid);
+ if (tevent_req_nomem(subreq, req)) {
+ goto done;
+ }
+ TALLOC_FREE(lck);
+ tevent_req_set_callback(subreq, smbd_smb1_do_locks_retry, req);
+
+ if (timeval_is_zero(&endtime)) {
+ return;
+ }
+
+ ok = tevent_req_set_endtime(subreq, state->ev, endtime);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ return;
+done:
+ TALLOC_FREE(lck);
+ smbd_smb1_brl_finish_by_req(req, status);
+}
+
+static void smbd_smb1_do_locks_retry(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+ NTSTATUS status;
+ bool ok;
+
+ /*
+ * Make sure we run as the user again
+ */
+ ok = change_to_user_and_service_by_fsp(state->fsp);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ status = share_mode_watch_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+
+ DBG_DEBUG("share_mode_watch_recv returned %s\n",
+ nt_errstr(status));
+
+ /*
+ * We ignore any errors here, it's most likely
+ * we just get NT_STATUS_OK or NT_STATUS_IO_TIMEOUT.
+ *
+ * In any case we can just give it a retry.
+ */
+
+ smbd_smb1_do_locks_try(req);
+}
+
+NTSTATUS smbd_smb1_do_locks_recv(struct tevent_req *req)
+{
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+ NTSTATUS status = NT_STATUS_OK;
+ bool err;
+
+ err = tevent_req_is_nterror(req, &status);
+
+ DBG_DEBUG("err=%d, status=%s\n", (int)err, nt_errstr(status));
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct files_struct *fsp = state->fsp;
+ struct smbd_lock_element *blocker =
+ &state->locks[state->blocker];
+
+ DBG_DEBUG("Setting lock_failure_offset=%"PRIu64"\n",
+ blocker->offset);
+
+ fsp->fsp_flags.lock_failure_seen = true;
+ fsp->lock_failure_offset = blocker->offset;
+ return status;
+ }
+
+ tevent_req_received(req);
+
+ return NT_STATUS_OK;
+}
+
+bool smbd_smb1_do_locks_extract_smbreq(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_request **psmbreq)
+{
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+
+ DBG_DEBUG("req=%p, state=%p, state->smbreq=%p\n",
+ req,
+ state,
+ state->smbreq);
+
+ if (state->smbreq == NULL) {
+ return false;
+ }
+ *psmbreq = talloc_move(mem_ctx, &state->smbreq);
+ return true;
+}
+
+void smbd_smb1_brl_finish_by_req(struct tevent_req *req, NTSTATUS status)
+{
+ DBG_DEBUG("req=%p, status=%s\n", req, nt_errstr(status));
+
+ if (NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_nterror(req, status);
+ }
+}
+
+bool smbd_smb1_brl_finish_by_lock(
+ struct files_struct *fsp,
+ bool large_offset,
+ struct smbd_lock_element lock,
+ NTSTATUS finish_status)
+{
+ struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
+ size_t num_blocked = talloc_array_length(blocked);
+ size_t i;
+
+ DBG_DEBUG("num_blocked=%zu\n", num_blocked);
+
+ for (i=0; i<num_blocked; i++) {
+ struct tevent_req *req = blocked[i];
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+ uint16_t j;
+
+ DBG_DEBUG("i=%zu, req=%p\n", i, req);
+
+ if (state->large_offset != large_offset) {
+ continue;
+ }
+
+ for (j=0; j<state->num_locks; j++) {
+ struct smbd_lock_element *l = &state->locks[j];
+
+ if ((lock.smblctx == l->smblctx) &&
+ (lock.offset == l->offset) &&
+ (lock.count == l->count)) {
+ smbd_smb1_brl_finish_by_req(
+ req, finish_status);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static struct files_struct *smbd_smb1_brl_finish_by_mid_fn(
+ struct files_struct *fsp, void *private_data)
+{
+ struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
+ size_t num_blocked = talloc_array_length(blocked);
+ uint64_t mid = *((uint64_t *)private_data);
+ size_t i;
+
+ DBG_DEBUG("fsp=%p, num_blocked=%zu\n", fsp, num_blocked);
+
+ for (i=0; i<num_blocked; i++) {
+ struct tevent_req *req = blocked[i];
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
+ req, struct smbd_smb1_do_locks_state);
+ struct smb_request *smbreq = state->smbreq;
+
+ if (smbreq->mid == mid) {
+ tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * This walks the list of fsps, we store the blocked reqs attached to
+ * them. It can be expensive, but this is legacy SMB1 and trying to
+ * remember looking at traces I don't really see many of those calls.
+ */
+
+bool smbd_smb1_brl_finish_by_mid(
+ struct smbd_server_connection *sconn, uint64_t mid)
+{
+ struct files_struct *found = files_forall(
+ sconn, smbd_smb1_brl_finish_by_mid_fn, &mid);
+ return (found != NULL);
+}