1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-22 22:04:08 +03:00

smbd: rework async rename check for handle lease breaks

Add a version of delay_rename_for_lease_break() that is usable in other places
where we have to check for handle lease breaks. No change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15608

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
Ralph Boehme 2024-09-16 02:36:00 +02:00
parent 00754add3c
commit f9c9593225
3 changed files with 402 additions and 219 deletions

View File

@ -1204,4 +1204,15 @@ void smb3_file_posix_information_init(
uint32_t dos_attributes,
struct smb3_file_posix_information *dst);
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);
NTSTATUS delay_for_handle_lease_break_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct share_mode_lock **lck);
#endif /* _SMBD_PROTO_H_ */

View File

@ -28,6 +28,7 @@
#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
@ -1368,3 +1369,232 @@ void init_kernel_oplocks(struct smbd_server_connection *sconn)
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;
}

View File

@ -168,188 +168,30 @@ static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq)
}
}
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)
static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req);
static void smbd_smb2_setinfo_cleanup(struct tevent_req *req,
enum tevent_req_state req_state)
{
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;
struct smbd_smb2_setinfo_state *state = tevent_req_data(
req, struct smbd_smb2_setinfo_state);
ours = smb2_lease_equal(fsp_client_guid(fsp),
&fsp->lease->lease.lease_key,
&e->client_guid,
&e->lease_key);
if (ours) {
return false;
if (req_state == TEVENT_REQ_DONE) {
return;
}
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);
send_break_message(
fsp->conn->sconn->msg_ctx, &fsp->file_id, e, break_to);
return false;
TALLOC_FREE(state->lck);
}
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)
{
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;
}
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);
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);
}
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,
@ -363,7 +205,6 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
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;
@ -372,7 +213,12 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
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)));
@ -391,13 +237,13 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
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) {
/*
@ -448,54 +294,12 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
}
}
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:
@ -583,6 +387,144 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
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;