#include "messages.h"
#include "locking/leases_db.h"
#include "../librpc/gen_ndr/ndr_open_files.h"
+#include "lib/util/tevent_ntstatus.h"
/*
* helper function used by the kernel oplock backends to post the break message
sconn->oplocks.kernel_ops = koplocks;
}
}
+
+struct delay_for_handle_lease_break_state {
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct timeval timeout;
+ struct files_struct *fsp;
+ struct share_mode_lock *lck;
+ bool delay;
+};
+
+static void delay_for_handle_lease_break_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct delay_for_handle_lease_break_state *state =
+ tevent_req_data(req, struct delay_for_handle_lease_break_state);
+
+ if (req_state == TEVENT_REQ_DONE) {
+ return;
+ }
+ TALLOC_FREE(state->lck);
+}
+
+static void delay_for_handle_lease_break_check(struct tevent_req *req);
+
+struct tevent_req *delay_for_handle_lease_break_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct timeval timeout,
+ struct files_struct *fsp,
+ struct share_mode_lock **lck)
+{
+ struct tevent_req *req = NULL;
+ struct delay_for_handle_lease_break_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct delay_for_handle_lease_break_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ tevent_req_set_cleanup_fn(req, delay_for_handle_lease_break_cleanup);
+
+ *state = (struct delay_for_handle_lease_break_state) {
+ .mem_ctx = mem_ctx,
+ .ev = ev,
+ .timeout = timeout,
+ .fsp = fsp,
+ .lck = talloc_move(state, lck),
+ };
+
+ delay_for_handle_lease_break_check(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* Ensure we can't be closed in flight. */
+ if (!aio_add_req_to_fsp(fsp, req)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static bool delay_for_handle_lease_break_fn(struct share_mode_entry *e,
+ void *private_data)
+{
+ struct delay_for_handle_lease_break_state *state = talloc_get_type_abort(
+ private_data, struct delay_for_handle_lease_break_state);
+ struct files_struct *fsp = state->fsp;
+ struct server_id_buf buf;
+ uint32_t lease_type;
+ bool ours, stale;
+
+ if (fsp->lease != NULL) {
+ ours = smb2_lease_equal(fsp_client_guid(fsp),
+ &fsp->lease->lease.lease_key,
+ &e->client_guid,
+ &e->lease_key);
+ if (ours) {
+ return false;
+ }
+ }
+
+ lease_type = get_lease_type(e, fsp->file_id);
+ if ((lease_type & SMB2_LEASE_HANDLE) == 0) {
+ return false;
+ }
+
+ stale = share_entry_stale_pid(e);
+ if (stale) {
+ return false;
+ }
+
+ state->delay = true;
+
+ DBG_DEBUG("Breaking h-lease on [%s] pid [%s]\n",
+ fsp_str_dbg(fsp),
+ server_id_str_buf(e->pid, &buf));
+
+ send_break_message(fsp->conn->sconn->msg_ctx,
+ &fsp->file_id,
+ e,
+ lease_type & ~SMB2_LEASE_HANDLE);
+
+ return false;
+}
+
+static void delay_for_handle_lease_break_fsp_done(struct tevent_req *subreq);
+
+static void delay_for_handle_lease_break_fsp_check(struct tevent_req *req)
+{
+ struct delay_for_handle_lease_break_state *state = tevent_req_data(
+ req, struct delay_for_handle_lease_break_state);
+ struct tevent_req *subreq = NULL;
+ bool ok;
+
+ DBG_DEBUG("fsp [%s]\n", fsp_str_dbg(state->fsp));
+
+ ok = share_mode_forall_leases(state->lck,
+ delay_for_handle_lease_break_fn,
+ state);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ if (state->delay) {
+ DBG_DEBUG("Delaying fsp [%s]\n", fsp_str_dbg(state->fsp));
+
+ subreq = share_mode_watch_send(state,
+ state->ev,
+ state->lck,
+ (struct server_id){0});
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ delay_for_handle_lease_break_fsp_done,
+ req);
+
+ if (!tevent_req_set_endtime(subreq, state->ev, state->timeout)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ return;
+ }
+}
+
+static void delay_for_handle_lease_break_fsp_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct delay_for_handle_lease_break_state *state = tevent_req_data(
+ req, struct delay_for_handle_lease_break_state);
+ NTSTATUS status;
+
+ DBG_DEBUG("Watch returned for fsp [%s]\n", fsp_str_dbg(state->fsp));
+
+ status = share_mode_watch_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("share_mode_watch_recv returned %s\n",
+ nt_errstr(status));
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /*
+ * The sharemode-watch timer fired because a client
+ * didn't respond to the lease break.
+ */
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ state->lck = get_existing_share_mode_lock(state, state->fsp->file_id);
+ if (state->lck == NULL) {
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ /*
+ * This could potentially end up looping for some if a client
+ * aggressively reaquires H-leases on the file, but we have a
+ * timeout on the tevent req as upper bound.
+ */
+ delay_for_handle_lease_break_check(req);
+}
+
+static void delay_for_handle_lease_break_check(struct tevent_req *req)
+{
+ struct delay_for_handle_lease_break_state *state = tevent_req_data(
+ req, struct delay_for_handle_lease_break_state);
+
+ state->delay = false;
+
+ DBG_DEBUG("fsp [%s]\n", fsp_str_dbg(state->fsp));
+
+ delay_for_handle_lease_break_fsp_check(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ if (state->delay) {
+ DBG_DEBUG("Delaying fsp [%s]\n", fsp_str_dbg(state->fsp));
+ TALLOC_FREE(state->lck);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS delay_for_handle_lease_break_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct share_mode_lock **lck)
+{
+ NTSTATUS status;
+
+ struct delay_for_handle_lease_break_state *state =
+ tevent_req_data(req, struct delay_for_handle_lease_break_state);
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *lck = talloc_move(mem_ctx, &state->lck);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
}
}
-struct defer_rename_state {
- struct tevent_req *req;
- struct smbd_smb2_request *smb2req;
+struct smbd_smb2_setinfo_state {
struct tevent_context *ev;
+ struct smbd_smb2_request *smb2req;
struct files_struct *fsp;
+ struct share_mode_lock *lck;
+ uint16_t file_info_level;
DATA_BLOB data;
-};
-
-static void defer_rename_done(struct tevent_req *subreq);
-
-struct delay_rename_lease_break_state {
- struct files_struct *fsp;
bool delay;
};
-static bool delay_rename_lease_break_fn(
- struct share_mode_entry *e,
- void *private_data)
-{
- struct delay_rename_lease_break_state *state = private_data;
- struct files_struct *fsp = state->fsp;
- uint32_t e_lease_type, break_to;
- bool ours, stale;
-
- ours = smb2_lease_equal(fsp_client_guid(fsp),
- &fsp->lease->lease.lease_key,
- &e->client_guid,
- &e->lease_key);
- if (ours) {
- return false;
- }
-
- e_lease_type = get_lease_type(e, fsp->file_id);
-
- if ((e_lease_type & SMB2_LEASE_HANDLE) == 0) {
- return false;
- }
-
- stale = share_entry_stale_pid(e);
- if (stale) {
- return false;
- }
-
- state->delay = true;
- break_to = (e_lease_type & ~SMB2_LEASE_HANDLE);
+static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req);
- send_break_message(
- fsp->conn->sconn->msg_ctx, &fsp->file_id, e, break_to);
-
- return false;
-}
-
-static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
- struct smbd_smb2_request *smb2req,
- struct tevent_context *ev,
- struct files_struct *fsp,
- struct share_mode_lock *lck,
- DATA_BLOB *data)
+static void smbd_smb2_setinfo_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
{
- struct tevent_req *subreq;
- struct defer_rename_state *rename_state;
- struct delay_rename_lease_break_state state = { .fsp = fsp };
- struct timeval timeout;
- bool ok;
-
- ok = share_mode_forall_leases(
- lck, delay_rename_lease_break_fn, &state);
- if (!ok) {
- return NULL;
- }
-
- if (!state.delay) {
- return NULL;
- }
-
- /* Setup a watch on this record. */
- rename_state = talloc_zero(req, struct defer_rename_state);
- if (rename_state == NULL) {
- return NULL;
- }
+ struct smbd_smb2_setinfo_state *state = tevent_req_data(
+ req, struct smbd_smb2_setinfo_state);
- rename_state->req = req;
- rename_state->smb2req = smb2req;
- rename_state->ev = ev;
- rename_state->fsp = fsp;
- rename_state->data = *data;
-
- subreq = share_mode_watch_send(
- rename_state,
- ev,
- lck,
- (struct server_id){0});
-
- if (subreq == NULL) {
- exit_server("Could not watch share mode record for rename\n");
- }
-
- tevent_req_set_callback(subreq, defer_rename_done, rename_state);
-
- timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT * 2, 0);
- if (!tevent_req_set_endtime(subreq,
- ev,
- timeval_sum(&smb2req->request_time, &timeout))) {
- exit_server("Could not set rename timeout\n");
- }
-
- return subreq;
-}
-
-static void defer_rename_done(struct tevent_req *subreq)
-{
- struct defer_rename_state *state = tevent_req_callback_data(
- subreq, struct defer_rename_state);
- NTSTATUS status;
- struct share_mode_lock *lck;
- int ret_size = 0;
- bool ok;
-
- status = share_mode_watch_recv(subreq, NULL, NULL);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
- nt_errstr(status)));
- tevent_req_nterror(state->req, status);
- return;
- }
-
- /*
- * Make sure we run as the user again
- */
- ok = change_to_user_and_service(
- state->smb2req->tcon->compat,
- state->smb2req->session->global->session_wire_id);
- if (!ok) {
- tevent_req_nterror(state->req, NT_STATUS_ACCESS_DENIED);
- return;
- }
-
- /* Do we still need to wait ? */
- lck = get_existing_share_mode_lock(state->req, state->fsp->file_id);
- if (lck == NULL) {
- tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL);
- return;
- }
- subreq = delay_rename_for_lease_break(state->req,
- state->smb2req,
- state->ev,
- state->fsp,
- lck,
- &state->data);
- if (subreq) {
- /* Yep - keep waiting. */
- TALLOC_FREE(state);
- TALLOC_FREE(lck);
+ if (req_state == TEVENT_REQ_DONE) {
return;
}
-
- /* Do the rename under the lock. */
- status = smbd_do_setfilepathinfo(state->fsp->conn,
- state->smb2req->smb1req,
- state,
- SMB2_FILE_RENAME_INFORMATION_INTERNAL,
- state->fsp,
- state->fsp->fsp_name,
- (char *)state->data.data,
- state->data.length,
- &ret_size);
-
- TALLOC_FREE(lck);
-
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(state->req, status);
- return;
- }
-
- tevent_req_done(state->req);
+ TALLOC_FREE(state->lck);
}
-struct smbd_smb2_setinfo_state {
- struct smbd_smb2_request *smb2req;
-};
-
static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct smbd_smb2_request *smb2req,
struct smbd_smb2_setinfo_state *state = NULL;
struct smb_request *smbreq = NULL;
connection_struct *conn = smb2req->tcon->compat;
- struct share_mode_lock *lck = NULL;
NTSTATUS status;
int ret;
if (req == NULL) {
return NULL;
}
+ state->ev = ev;
state->smb2req = smb2req;
+ state->fsp = fsp;
+ state->data = in_input_buffer;
+
+ tevent_req_set_cleanup_fn(req, smbd_smb2_setinfo_cleanup);
DEBUG(10,("smbd_smb2_setinfo_send: %s - %s\n",
fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
case SMB2_0_INFO_FILE:
{
uint16_t file_info_level;
- int ret_size = 0;
file_info_level = in_file_info_class + 1000;
if (file_info_level == SMB_FILE_RENAME_INFORMATION) {
/* SMB2_FILE_RENAME_INFORMATION_INTERNAL == 0xFF00 + in_file_info_class */
file_info_level = SMB2_FILE_RENAME_INFORMATION_INTERNAL;
}
+ state->file_info_level = file_info_level;
if (fsp_get_pathref_fd(fsp) == -1) {
/*
}
}
- if (file_info_level == SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
- struct tevent_req *subreq;
-
- lck = get_existing_share_mode_lock(mem_ctx,
- fsp->file_id);
- if (lck == NULL) {
- tevent_req_nterror(req,
- NT_STATUS_UNSUCCESSFUL);
- return tevent_req_post(req, ev);
- }
-
- subreq = delay_rename_for_lease_break(req,
- smb2req,
- ev,
- fsp,
- lck,
- &in_input_buffer);
- if (subreq) {
- /* Wait for lease break response. */
-
- /* Ensure we can't be closed in flight. */
- if (!aio_add_req_to_fsp(fsp, req)) {
- TALLOC_FREE(lck);
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return tevent_req_post(req, ev);
- }
-
- TALLOC_FREE(lck);
- return req;
- }
- }
-
- status = smbd_do_setfilepathinfo(conn, smbreq, state,
- file_info_level,
- fsp,
- fsp->fsp_name,
- (char *)in_input_buffer.data,
- in_input_buffer.length,
- &ret_size);
- TALLOC_FREE(lck);
- if (!NT_STATUS_IS_OK(status)) {
- if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
- status = NT_STATUS_INVALID_INFO_CLASS;
- }
- tevent_req_nterror(req, status);
+ smbd_smb2_setinfo_lease_break_check(req);
+ if (!tevent_req_is_in_progress(req)) {
return tevent_req_post(req, ev);
}
- break;
+ SMB_ASSERT(state->delay);
+ return req;
}
case SMB2_0_INFO_FILESYSTEM:
return tevent_req_post(req, ev);
}
+static void smbd_smb2_setinfo_lease_break_fsp_check(struct tevent_req *req);
+static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq);
+
+static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req)
+{
+ struct smbd_smb2_setinfo_state *state = tevent_req_data(
+ req, struct smbd_smb2_setinfo_state);
+ int ret_size;
+ NTSTATUS status;
+
+ state->delay = false;
+
+ smbd_smb2_setinfo_lease_break_fsp_check(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ if (state->delay) {
+ TALLOC_FREE(state->lck);
+ DBG_DEBUG("Waiting for h-lease breaks on fsp [%s]\n",
+ fsp_str_dbg(state->fsp));
+ return;
+ }
+
+ status = smbd_do_setfilepathinfo(state->fsp->conn,
+ state->smb2req->smb1req,
+ state,
+ state->file_info_level,
+ state->fsp,
+ state->fsp->fsp_name,
+ (char *)state->data.data,
+ state->data.length,
+ &ret_size);
+ TALLOC_FREE(state->lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void smbd_smb2_setinfo_lease_break_fsp_check(struct tevent_req *req)
+{
+ struct smbd_smb2_setinfo_state *state = tevent_req_data(
+ req, struct smbd_smb2_setinfo_state);
+ struct smbd_smb2_request *smb2req = state->smb2req;
+ struct files_struct *fsp = state->fsp;
+ uint16_t file_info_level = state->file_info_level;
+ struct tevent_req *subreq = NULL;
+ struct timeval timeout;
+ bool rename;
+ NTSTATUS status;
+
+ rename = (file_info_level == SMB2_FILE_RENAME_INFORMATION_INTERNAL);
+ if (!rename) {
+ return;
+ }
+
+ state->lck = get_existing_share_mode_lock(state, fsp->file_id);
+ if (state->lck == NULL) {
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
+ timeout = timeval_sum(&smb2req->request_time, &timeout);
+
+ subreq = delay_for_handle_lease_break_send(state,
+ state->ev,
+ timeout,
+ fsp,
+ &state->lck);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ if (tevent_req_is_in_progress(subreq)) {
+ state->delay = true;
+ tevent_req_set_callback(subreq,
+ smbd_smb2_setinfo_lease_break_fsp_done,
+ req);
+ return;
+ }
+
+ status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+}
+
+static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbd_smb2_setinfo_state *state = tevent_req_data(
+ req, struct smbd_smb2_setinfo_state);
+ int ret_size;
+ NTSTATUS status;
+ bool ok;
+
+ status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * Make sure we run as the user again
+ */
+ ok = change_to_user_and_service(
+ state->smb2req->tcon->compat,
+ state->smb2req->session->global->session_wire_id);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ /* Do the rename under the lock. */
+ status = smbd_do_setfilepathinfo(state->fsp->conn,
+ state->smb2req->smb1req,
+ state,
+ state->file_info_level,
+ state->fsp,
+ state->fsp->fsp_name,
+ (char *)state->data.data,
+ state->data.length,
+ &ret_size);
+ TALLOC_FREE(state->lck);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
{
NTSTATUS status;