mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
s3:smbd: Implementation of SMB2.1 and SMB3.0 leases.
Pair-Programmed-With: Jeremy Allison <jra@samba.org> Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> Signed-off-by: Volker Lendecke <vl@samba.org> Signed-off-by: Jeremy Allison <jra@samba.org> Signed-off-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
91b2488c9a
commit
02f2684dd8
@ -724,6 +724,32 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
||||
fsp->aio_write_behind = false;
|
||||
fsp->oplock_type = e->op_type;
|
||||
|
||||
if (fsp->oplock_type == LEASE_OPLOCK) {
|
||||
struct share_mode_lease *l = &lck->data->leases[e->lease_idx];
|
||||
struct smb2_lease_key key;
|
||||
|
||||
key.data[0] = l->lease_key.data[0];
|
||||
key.data[1] = l->lease_key.data[1];
|
||||
|
||||
fsp->lease = find_fsp_lease(fsp, &key, l);
|
||||
if (fsp->lease == NULL) {
|
||||
TALLOC_FREE(lck);
|
||||
fsp_free(fsp);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure the existing client guid matches the
|
||||
* stored one in the share_mode_lease.
|
||||
*/
|
||||
if (!GUID_equal(fsp_client_guid(fsp),
|
||||
&l->client_guid)) {
|
||||
TALLOC_FREE(lck);
|
||||
fsp_free(fsp);
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
fsp->initial_allocation_size = cookie.initial_allocation_size;
|
||||
fsp->fh->position_information = cookie.position_information;
|
||||
fsp->update_write_time_triggered = cookie.update_write_time_triggered;
|
||||
|
@ -490,6 +490,14 @@ void fsp_free(files_struct *fsp)
|
||||
fsp->fh->ref_count--;
|
||||
}
|
||||
|
||||
if (fsp->lease != NULL) {
|
||||
if (fsp->lease->ref_count == 1) {
|
||||
TALLOC_FREE(fsp->lease);
|
||||
} else {
|
||||
fsp->lease->ref_count--;
|
||||
}
|
||||
}
|
||||
|
||||
fsp->conn->num_files_open--;
|
||||
|
||||
/* this is paranoia, just in case someone tries to reuse the
|
||||
|
@ -305,7 +305,9 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
|
||||
struct deferred_open_record;
|
||||
|
||||
/* SMB1 -> SMB2 glue. */
|
||||
void send_break_message_smb2(files_struct *fsp, int level);
|
||||
void send_break_message_smb2(files_struct *fsp,
|
||||
uint32_t break_from,
|
||||
uint32_t break_to);
|
||||
struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req);
|
||||
bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
|
||||
struct smb_request *req,
|
||||
|
@ -1258,6 +1258,10 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
|
||||
share_mode_entry_to_message(msg, exclusive);
|
||||
|
||||
/* Overload entry->op_type */
|
||||
/*
|
||||
* This is a cut from uint32 to uint16, but so far only the lower 3
|
||||
* bits (LEASE_WRITE/HANDLE/READ are used anyway.
|
||||
*/
|
||||
SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
|
||||
|
||||
status = messaging_send_buf(msg_ctx, exclusive->pid,
|
||||
@ -1377,56 +1381,19 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
|
||||
|
||||
static bool delay_for_oplock(files_struct *fsp,
|
||||
int oplock_request,
|
||||
const struct smb2_lease *lease,
|
||||
struct share_mode_lock *lck,
|
||||
bool have_sharing_violation,
|
||||
uint32_t create_disposition)
|
||||
uint32_t create_disposition,
|
||||
bool first_open_attempt)
|
||||
{
|
||||
struct share_mode_data *d = lck->data;
|
||||
struct share_mode_entry *entry;
|
||||
uint32_t num_non_stat_opens = 0;
|
||||
uint32_t i;
|
||||
uint16_t break_to;
|
||||
bool delay = false;
|
||||
bool will_overwrite;
|
||||
|
||||
if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
|
||||
return false;
|
||||
}
|
||||
for (i=0; i<d->num_share_modes; i++) {
|
||||
struct share_mode_entry *e = &d->share_modes[i];
|
||||
if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
|
||||
continue;
|
||||
}
|
||||
num_non_stat_opens += 1;
|
||||
|
||||
/*
|
||||
* We found the a non-stat open, which in the exclusive/batch
|
||||
* case will be inspected further down.
|
||||
*/
|
||||
entry = e;
|
||||
}
|
||||
if (num_non_stat_opens == 0) {
|
||||
/*
|
||||
* Nothing to wait for around
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (num_non_stat_opens != 1) {
|
||||
/*
|
||||
* More than one open around. There can't be any exclusive or
|
||||
* batch left, this is all level2.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (server_id_is_disconnected(&entry->pid)) {
|
||||
/*
|
||||
* TODO: clean up.
|
||||
* This could be achieved by sending a break message
|
||||
* to ourselves. Special considerations for files
|
||||
* with delete_on_close flag set!
|
||||
*
|
||||
* For now we keep it simple and do not
|
||||
* allow delete on close for durable handles.
|
||||
*/
|
||||
if ((oplock_request & INTERNAL_OPEN_ONLY) ||
|
||||
is_stat_open(fsp->access_mask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1434,50 +1401,98 @@ static bool delay_for_oplock(files_struct *fsp,
|
||||
case FILE_SUPERSEDE:
|
||||
case FILE_OVERWRITE:
|
||||
case FILE_OVERWRITE_IF:
|
||||
break_to = NO_OPLOCK;
|
||||
will_overwrite = true;
|
||||
break;
|
||||
default:
|
||||
break_to = LEVEL_II_OPLOCK;
|
||||
will_overwrite = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
|
||||
if (share_mode_stale_pid(d, 0)) {
|
||||
return false;
|
||||
}
|
||||
send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
|
||||
return true;
|
||||
}
|
||||
if (have_sharing_violation) {
|
||||
/*
|
||||
* Non-batch exclusive is not broken if we have a sharing
|
||||
* violation
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
|
||||
(break_to == NO_OPLOCK)) {
|
||||
if (share_mode_stale_pid(d, 0)) {
|
||||
return false;
|
||||
}
|
||||
DEBUG(10, ("Asynchronously breaking level2 oplock for "
|
||||
"create_disposition=%u\n",
|
||||
(unsigned)create_disposition));
|
||||
send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
|
||||
return false;
|
||||
}
|
||||
if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
|
||||
/*
|
||||
* No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (share_mode_stale_pid(d, 0)) {
|
||||
return false;
|
||||
for (i=0; i<d->num_share_modes; i++) {
|
||||
struct share_mode_entry *e = &d->share_modes[i];
|
||||
struct share_mode_lease *l = NULL;
|
||||
uint32_t e_lease_type = get_lease_type(d, e);
|
||||
uint32_t break_to;
|
||||
uint32_t delay_mask = 0;
|
||||
|
||||
if (e->op_type == LEASE_OPLOCK) {
|
||||
l = &d->leases[e->lease_idx];
|
||||
}
|
||||
|
||||
send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
|
||||
return true;
|
||||
if (have_sharing_violation) {
|
||||
delay_mask = SMB2_LEASE_HANDLE;
|
||||
} else {
|
||||
delay_mask = SMB2_LEASE_WRITE;
|
||||
}
|
||||
|
||||
break_to = e_lease_type & ~delay_mask;
|
||||
|
||||
if (will_overwrite) {
|
||||
/*
|
||||
* we'll decide about SMB2_LEASE_READ later.
|
||||
*
|
||||
* Maybe the break will be defered
|
||||
*/
|
||||
break_to &= ~SMB2_LEASE_HANDLE;
|
||||
}
|
||||
|
||||
DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
|
||||
(unsigned)i, (unsigned)e_lease_type,
|
||||
(unsigned)will_overwrite));
|
||||
|
||||
if (lease != NULL && l != NULL) {
|
||||
bool ign;
|
||||
|
||||
ign = smb2_lease_equal(fsp_client_guid(fsp),
|
||||
&lease->lease_key,
|
||||
&l->client_guid,
|
||||
&l->lease_key);
|
||||
if (ign) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((e_lease_type & ~break_to) == 0) {
|
||||
if (l != NULL && l->breaking) {
|
||||
delay = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (share_mode_stale_pid(d, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (will_overwrite) {
|
||||
/*
|
||||
* If we break anyway break to NONE directly.
|
||||
* Otherwise vfs_set_filelen() will trigger the
|
||||
* break.
|
||||
*/
|
||||
break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
|
||||
}
|
||||
|
||||
if (e->op_type != LEASE_OPLOCK) {
|
||||
/*
|
||||
* Oplocks only support breaking to R or NONE.
|
||||
*/
|
||||
break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
|
||||
}
|
||||
|
||||
DEBUG(10, ("breaking from %d to %d\n",
|
||||
(int)e_lease_type, (int)break_to));
|
||||
send_break_message(fsp->conn->sconn->msg_ctx, e,
|
||||
break_to);
|
||||
if (e_lease_type & delay_mask) {
|
||||
delay = true;
|
||||
}
|
||||
if (l != NULL && l->breaking && !first_open_attempt) {
|
||||
delay = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
static bool file_has_brlocks(files_struct *fsp)
|
||||
@ -1557,7 +1572,6 @@ struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
|
||||
return new_fsp->lease;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
|
||||
struct share_mode_lock *lck,
|
||||
const struct smb2_lease *lease,
|
||||
@ -1696,76 +1710,138 @@ static bool is_same_lease(const files_struct *fsp,
|
||||
&d->leases[e->lease_idx].client_guid,
|
||||
&d->leases[e->lease_idx].lease_key);
|
||||
}
|
||||
#endif
|
||||
|
||||
static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
|
||||
struct files_struct *fsp,
|
||||
struct share_mode_lock *lck,
|
||||
int oplock_request)
|
||||
int oplock_request,
|
||||
struct smb2_lease *lease)
|
||||
{
|
||||
bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
|
||||
lp_level2_oplocks(SNUM(fsp->conn));
|
||||
bool got_level2_oplock, got_a_none_oplock;
|
||||
struct share_mode_data *d = lck->data;
|
||||
bool got_handle_lease = false;
|
||||
bool got_oplock = false;
|
||||
uint32_t i;
|
||||
uint32_t granted;
|
||||
uint32_t lease_idx = UINT32_MAX;
|
||||
bool ok;
|
||||
NTSTATUS status;
|
||||
|
||||
/* Start by granting what the client asked for,
|
||||
but ensure no SAMBA_PRIVATE bits can be set. */
|
||||
fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
|
||||
|
||||
if (fsp->oplock_type == NO_OPLOCK) {
|
||||
goto type_selected;
|
||||
}
|
||||
|
||||
if (oplock_request & INTERNAL_OPEN_ONLY) {
|
||||
/* No oplocks on internal open. */
|
||||
fsp->oplock_type = NO_OPLOCK;
|
||||
goto type_selected;
|
||||
oplock_request = NO_OPLOCK;
|
||||
DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
|
||||
fsp->oplock_type, fsp_str_dbg(fsp)));
|
||||
}
|
||||
|
||||
if (oplock_request == LEASE_OPLOCK) {
|
||||
if (lease == NULL) {
|
||||
/*
|
||||
* The SMB2 layer should have checked this
|
||||
*/
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
granted = lease->lease_state;
|
||||
|
||||
if (lp_kernel_oplocks(SNUM(fsp->conn))) {
|
||||
DEBUG(10, ("No lease granted because kernel oplocks are enabled\n"));
|
||||
granted = SMB2_LEASE_NONE;
|
||||
}
|
||||
if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
|
||||
DEBUG(10, ("No read or write lease requested\n"));
|
||||
granted = SMB2_LEASE_NONE;
|
||||
}
|
||||
if (granted == SMB2_LEASE_WRITE) {
|
||||
DEBUG(10, ("pure write lease requested\n"));
|
||||
granted = SMB2_LEASE_NONE;
|
||||
}
|
||||
if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) {
|
||||
DEBUG(10, ("write and handle lease requested\n"));
|
||||
granted = SMB2_LEASE_NONE;
|
||||
}
|
||||
} else {
|
||||
granted = map_oplock_to_lease_type(
|
||||
oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
|
||||
}
|
||||
|
||||
if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
|
||||
DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
|
||||
fsp_str_dbg(fsp)));
|
||||
fsp->oplock_type = NO_OPLOCK;
|
||||
goto type_selected;
|
||||
granted &= ~SMB2_LEASE_READ;
|
||||
}
|
||||
|
||||
got_level2_oplock = false;
|
||||
got_a_none_oplock = false;
|
||||
for (i=0; i<d->num_share_modes; i++) {
|
||||
struct share_mode_entry *e = &d->share_modes[i];
|
||||
uint32_t e_lease_type;
|
||||
|
||||
for (i=0; i<lck->data->num_share_modes; i++) {
|
||||
int op_type = lck->data->share_modes[i].op_type;
|
||||
|
||||
if (LEVEL_II_OPLOCK_TYPE(op_type)) {
|
||||
got_level2_oplock = true;
|
||||
}
|
||||
if (op_type == NO_OPLOCK) {
|
||||
got_a_none_oplock = true;
|
||||
}
|
||||
}
|
||||
e_lease_type = get_lease_type(d, e);
|
||||
|
||||
if ((granted & SMB2_LEASE_WRITE) &&
|
||||
!is_same_lease(fsp, d, e, lease) &&
|
||||
!share_mode_stale_pid(d, i)) {
|
||||
/*
|
||||
* Match what was requested (fsp->oplock_type) with
|
||||
* what was found in the existing share modes.
|
||||
* Can grant only one writer
|
||||
*/
|
||||
granted &= ~SMB2_LEASE_WRITE;
|
||||
}
|
||||
|
||||
if (got_level2_oplock || got_a_none_oplock) {
|
||||
if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
|
||||
if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
|
||||
!share_mode_stale_pid(d, i)) {
|
||||
got_handle_lease = true;
|
||||
}
|
||||
|
||||
if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
|
||||
!share_mode_stale_pid(d, i)) {
|
||||
got_oplock = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((granted & SMB2_LEASE_READ) && !(granted & SMB2_LEASE_WRITE)) {
|
||||
bool allow_level2 =
|
||||
(global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
|
||||
lp_level2_oplocks(SNUM(fsp->conn));
|
||||
|
||||
if (!allow_level2) {
|
||||
granted = SMB2_LEASE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (oplock_request == LEASE_OPLOCK) {
|
||||
if (got_oplock) {
|
||||
granted &= ~SMB2_LEASE_HANDLE;
|
||||
}
|
||||
|
||||
fsp->oplock_type = LEASE_OPLOCK;
|
||||
|
||||
status = grant_fsp_lease(fsp, lck, lease, &lease_idx,
|
||||
granted);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
|
||||
}
|
||||
*lease = fsp->lease->lease;
|
||||
DEBUG(10, ("lease_state=%d\n", lease->lease_state));
|
||||
} else {
|
||||
if (got_handle_lease) {
|
||||
granted = SMB2_LEASE_NONE;
|
||||
}
|
||||
|
||||
switch (granted) {
|
||||
case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
|
||||
fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
|
||||
break;
|
||||
case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
|
||||
fsp->oplock_type = EXCLUSIVE_OPLOCK;
|
||||
break;
|
||||
case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
|
||||
case SMB2_LEASE_READ:
|
||||
fsp->oplock_type = LEVEL_II_OPLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't grant level2 to clients that don't want them
|
||||
* or if we've turned them off.
|
||||
*/
|
||||
if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
|
||||
break;
|
||||
default:
|
||||
fsp->oplock_type = NO_OPLOCK;
|
||||
goto type_selected;
|
||||
break;
|
||||
}
|
||||
|
||||
type_selected:
|
||||
status = set_file_oplock(fsp);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/*
|
||||
@ -1773,11 +1849,12 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
|
||||
*/
|
||||
fsp->oplock_type = NO_OPLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
|
||||
req ? req->mid : 0,
|
||||
fsp->oplock_type,
|
||||
UINT32_MAX);
|
||||
lease_idx);
|
||||
if (!ok) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -2683,8 +2760,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
|
||||
smb_panic("validate_oplock_types failed");
|
||||
}
|
||||
|
||||
if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
|
||||
schedule_defer_open(lck, fsp->file_id, request_time, req);
|
||||
if (delay_for_oplock(fsp, 0, lease, lck, false,
|
||||
create_disposition, first_open_attempt)) {
|
||||
schedule_defer_open(lck, fsp->file_id, request_time,
|
||||
req);
|
||||
TALLOC_FREE(lck);
|
||||
DEBUG(10, ("Sent oplock break request to kernel "
|
||||
"oplock holder\n"));
|
||||
@ -2805,9 +2884,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
|
||||
|
||||
if ((req != NULL) &&
|
||||
delay_for_oplock(
|
||||
fsp, oplock_request, lck,
|
||||
fsp, oplock_request, lease, lck,
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
|
||||
create_disposition)) {
|
||||
create_disposition, first_open_attempt)) {
|
||||
schedule_defer_open(lck, fsp->file_id, request_time, req);
|
||||
TALLOC_FREE(lck);
|
||||
fd_close(fsp);
|
||||
@ -3016,7 +3095,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
|
||||
|
||||
if (file_existed) {
|
||||
/*
|
||||
* stat opens on existing files don't get oplocks.
|
||||
* stat opens on existing files don't get oplocks or leases.
|
||||
*
|
||||
* Note that we check for stat open on the *open_access_mask*,
|
||||
* i.e. the access mask we actually used to do the open,
|
||||
@ -3026,9 +3105,13 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
|
||||
* which adds FILE_WRITE_DATA to open_access_mask.
|
||||
*/
|
||||
if (is_stat_open(open_access_mask)) {
|
||||
if (lease) {
|
||||
lease->lease_state = SMB2_LEASE_NONE;
|
||||
} else {
|
||||
oplock_request = NO_OPLOCK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_file_created) {
|
||||
info = FILE_WAS_CREATED;
|
||||
@ -3048,7 +3131,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
|
||||
* Setup the oplock info in both the shared memory and
|
||||
* file structs.
|
||||
*/
|
||||
status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
|
||||
status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(lck);
|
||||
fd_close(fsp);
|
||||
|
@ -196,15 +196,10 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
|
||||
|
||||
for (i=0; i<d->num_share_modes; i++) {
|
||||
struct share_mode_entry *e = &d->share_modes[i];
|
||||
uint32_t e_lease_type = get_lease_type(d, e);
|
||||
|
||||
if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
|
||||
if (e_lease_type & SMB2_LEASE_READ) {
|
||||
num_read_oplocks += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
|
||||
num_read_oplocks += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -772,14 +767,15 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
|
||||
{
|
||||
struct share_mode_entry msg;
|
||||
files_struct *fsp;
|
||||
bool break_to_level2 = False;
|
||||
bool use_kernel;
|
||||
struct smbd_server_connection *sconn =
|
||||
talloc_get_type_abort(private_data,
|
||||
struct smbd_server_connection);
|
||||
struct server_id self = messaging_server_id(sconn->msg_ctx);
|
||||
struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
|
||||
uint16_t break_from;
|
||||
uint16_t break_to;
|
||||
bool break_needed = true;
|
||||
|
||||
if (data->data == NULL) {
|
||||
DEBUG(0, ("Got NULL buffer\n"));
|
||||
@ -809,27 +805,118 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
|
||||
return;
|
||||
}
|
||||
|
||||
break_from = fsp_lease_type(fsp);
|
||||
|
||||
if (fsp->oplock_type != LEASE_OPLOCK) {
|
||||
if (fsp->sent_oplock_break != NO_BREAK_SENT) {
|
||||
/*
|
||||
* Nothing to do anymore
|
||||
*/
|
||||
DEBUG(10, ("fsp->sent_oplock_break = %d\n",
|
||||
fsp->sent_oplock_break));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) {
|
||||
DEBUG(10, ("client_caps without level2 oplocks\n"));
|
||||
break_to &= ~SMB2_LEASE_READ;
|
||||
}
|
||||
|
||||
use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
|
||||
if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) {
|
||||
DEBUG(10, ("Kernel oplocks don't allow level2\n"));
|
||||
break_to &= ~SMB2_LEASE_READ;
|
||||
}
|
||||
|
||||
if (!lp_level2_oplocks(SNUM(fsp->conn))) {
|
||||
DEBUG(10, ("no level2 oplocks by config\n"));
|
||||
break_to &= ~SMB2_LEASE_READ;
|
||||
}
|
||||
|
||||
if (fsp->oplock_type == LEASE_OPLOCK) {
|
||||
struct share_mode_lock *lck;
|
||||
int idx;
|
||||
|
||||
lck = get_existing_share_mode_lock(
|
||||
talloc_tos(), fsp->file_id);
|
||||
if (lck == NULL) {
|
||||
/*
|
||||
* We hit a race here. Break messages are sent, and
|
||||
* before we get to process this message, we have closed
|
||||
* the file.
|
||||
*/
|
||||
DEBUG(3, ("Did not find share_mode\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (break_to == fsp->oplock_type) {
|
||||
DEBUG(3, ("Already downgraded oplock on %s: %s\n",
|
||||
idx = find_share_mode_lease(
|
||||
lck->data,
|
||||
fsp_client_guid(fsp),
|
||||
&fsp->lease->lease.lease_key);
|
||||
if (idx != -1) {
|
||||
struct share_mode_lease *l;
|
||||
l = &lck->data->leases[idx];
|
||||
|
||||
break_from = l->current_state;
|
||||
break_to &= l->current_state;
|
||||
|
||||
if (l->breaking) {
|
||||
break_to &= l->breaking_to_required;
|
||||
if (l->breaking_to_required != break_to) {
|
||||
/*
|
||||
* Note we don't increment the epoch
|
||||
* here, which might be a bug in
|
||||
* Windows too...
|
||||
*/
|
||||
l->breaking_to_required = break_to;
|
||||
lck->data->modified = true;
|
||||
}
|
||||
break_needed = false;
|
||||
} else if (l->current_state == break_to) {
|
||||
break_needed = false;
|
||||
} else if (l->current_state == SMB2_LEASE_READ) {
|
||||
l->current_state = SMB2_LEASE_NONE;
|
||||
/* Need to increment the epoch */
|
||||
l->epoch += 1;
|
||||
lck->data->modified = true;
|
||||
} else {
|
||||
l->breaking = true;
|
||||
l->breaking_to_required = break_to;
|
||||
l->breaking_to_requested = break_to;
|
||||
/* Need to increment the epoch */
|
||||
l->epoch += 1;
|
||||
lck->data->modified = true;
|
||||
}
|
||||
|
||||
/* Ensure we're in sync with current lease state. */
|
||||
fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
|
||||
}
|
||||
|
||||
TALLOC_FREE(lck);
|
||||
}
|
||||
|
||||
if (!break_needed) {
|
||||
DEBUG(10,("%s: skip break\n", __func__));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((break_from == SMB2_LEASE_NONE) && !break_needed) {
|
||||
DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
|
||||
file_id_string_tos(&fsp->file_id),
|
||||
fsp_str_dbg(fsp)));
|
||||
return;
|
||||
}
|
||||
|
||||
use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
|
||||
DEBUG(10, ("break_from=%u, break_to=%u\n",
|
||||
(unsigned)break_from, (unsigned)break_to));
|
||||
|
||||
if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
|
||||
(break_to != NO_OPLOCK) &&
|
||||
!(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
|
||||
lp_level2_oplocks(SNUM(fsp->conn))) {
|
||||
break_to_level2 = True;
|
||||
if ((break_from == break_to) && !break_needed) {
|
||||
DEBUG(3, ("Already downgraded oplock to %u on %s: %s\n",
|
||||
(unsigned)break_to,
|
||||
file_id_string_tos(&fsp->file_id),
|
||||
fsp_str_dbg(fsp)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Need to wait before sending a break
|
||||
@ -839,21 +926,31 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
|
||||
}
|
||||
|
||||
if (sconn->using_smb2) {
|
||||
send_break_message_smb2(fsp, break_to_level2 ?
|
||||
OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
|
||||
send_break_message_smb2(fsp, break_from, break_to);
|
||||
} else {
|
||||
send_break_message_smb1(fsp, break_to_level2 ?
|
||||
send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ?
|
||||
OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
|
||||
}
|
||||
|
||||
if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
|
||||
if ((break_from == SMB2_LEASE_READ) &&
|
||||
(break_to == SMB2_LEASE_NONE)) {
|
||||
/*
|
||||
* This is an async break without a reply and thus no timeout
|
||||
*
|
||||
* leases are handled above.
|
||||
*/
|
||||
if (fsp->oplock_type != LEASE_OPLOCK) {
|
||||
remove_oplock(fsp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
|
||||
if (fsp->oplock_type == LEASE_OPLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
|
||||
LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
|
||||
|
||||
add_oplock_timeout_handler(fsp);
|
||||
}
|
||||
|
||||
@ -908,7 +1005,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
|
||||
}
|
||||
|
||||
if (sconn->using_smb2) {
|
||||
send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
|
||||
send_break_message_smb2(fsp, 0, OPLOCKLEVEL_NONE);
|
||||
} else {
|
||||
send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
|
||||
}
|
||||
@ -921,6 +1018,8 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
|
||||
struct break_to_none_state {
|
||||
struct smbd_server_connection *sconn;
|
||||
struct file_id id;
|
||||
struct smb2_lease_key lease_key;
|
||||
struct GUID client_guid;
|
||||
};
|
||||
static void do_break_to_none(struct tevent_context *ctx,
|
||||
struct tevent_immediate *im,
|
||||
@ -975,7 +1074,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
|
||||
* anyway, so we postpone this into an immediate event.
|
||||
*/
|
||||
|
||||
state = talloc(sconn, struct break_to_none_state);
|
||||
state = talloc_zero(sconn, struct break_to_none_state);
|
||||
if (state == NULL) {
|
||||
DEBUG(1, ("talloc failed\n"));
|
||||
return;
|
||||
@ -983,6 +1082,14 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
|
||||
state->sconn = sconn;
|
||||
state->id = fsp->file_id;
|
||||
|
||||
if (fsp->oplock_type == LEASE_OPLOCK) {
|
||||
state->client_guid = *fsp_client_guid(fsp);
|
||||
state->lease_key = fsp->lease->lease.lease_key;
|
||||
DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
|
||||
state->lease_key.data[0],
|
||||
state->lease_key.data[1]));
|
||||
}
|
||||
|
||||
im = tevent_create_immediate(state);
|
||||
if (im == NULL) {
|
||||
DEBUG(1, ("tevent_create_immediate failed\n"));
|
||||
@ -1023,8 +1130,50 @@ static void do_break_to_none(struct tevent_context *ctx,
|
||||
}
|
||||
d = lck->data;
|
||||
|
||||
DEBUG(10,("%s: num_share_modes = %d\n", __func__,
|
||||
lck->data->num_share_modes ));
|
||||
/*
|
||||
* Walk leases and oplocks separately: We have to send one break per
|
||||
* lease. If we have multiple share_mode_entry having a common lease,
|
||||
* we would break the lease twice if we don't walk the leases list
|
||||
* separately.
|
||||
*/
|
||||
|
||||
for (i=0; i<d->num_leases; i++) {
|
||||
struct share_mode_lease *l = &d->leases[i];
|
||||
struct share_mode_entry *e;
|
||||
uint32_t j;
|
||||
|
||||
if ((l->current_state & SMB2_LEASE_READ) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (smb2_lease_equal(&state->client_guid,
|
||||
&state->lease_key,
|
||||
&l->client_guid,
|
||||
&l->lease_key)) {
|
||||
DEBUG(10, ("Don't break our own lease\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j=0; j<d->num_share_modes; j++) {
|
||||
e = &d->share_modes[j];
|
||||
|
||||
if (!is_valid_share_mode_entry(e)) {
|
||||
continue;
|
||||
}
|
||||
if (e->lease_idx == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == d->num_share_modes) {
|
||||
DEBUG(0, ("leases[%"PRIu32"] has no share mode\n",
|
||||
i));
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG(10, ("Breaking lease# %"PRIu32" with share_entry# "
|
||||
"%"PRIu32"\n", i, j));
|
||||
|
||||
send_break_to_none(state->sconn->msg_ctx, e);
|
||||
}
|
||||
|
||||
for(i = 0; i < d->num_share_modes; i++) {
|
||||
struct share_mode_entry *e = &d->share_modes[i];
|
||||
@ -1032,6 +1181,12 @@ static void do_break_to_none(struct tevent_context *ctx,
|
||||
if (!is_valid_share_mode_entry(e)) {
|
||||
continue;
|
||||
}
|
||||
if (e->op_type == LEASE_OPLOCK) {
|
||||
/*
|
||||
* Took care of those in the loop above
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* As there could have been multiple writes waiting at the
|
||||
|
@ -24,6 +24,10 @@
|
||||
#include "smbd/globals.h"
|
||||
#include "../libcli/smb/smb_common.h"
|
||||
#include "../lib/util/tevent_ntstatus.h"
|
||||
#include "locking/leases_db.h"
|
||||
|
||||
static NTSTATUS smbd_smb2_request_process_lease_break(
|
||||
struct smbd_smb2_request *req);
|
||||
|
||||
static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
@ -45,6 +49,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
|
||||
struct tevent_req *subreq;
|
||||
|
||||
status = smbd_smb2_request_verify_sizes(req, 0x18);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
|
||||
/*
|
||||
* Retry as a lease break
|
||||
*/
|
||||
return smbd_smb2_request_process_lease_break(req);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return smbd_smb2_request_error(req, status);
|
||||
}
|
||||
@ -222,16 +232,213 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
|
||||
|
||||
static struct tevent_req *smbd_smb2_lease_break_send(
|
||||
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
|
||||
struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
|
||||
uint32_t in_lease_state);
|
||||
static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
|
||||
uint32_t *out_lease_state);
|
||||
|
||||
|
||||
static NTSTATUS smbd_smb2_request_process_lease_break(
|
||||
struct smbd_smb2_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
const uint8_t *inbody;
|
||||
struct smb2_lease_key in_lease_key;
|
||||
uint32_t in_lease_state;
|
||||
struct tevent_req *subreq;
|
||||
|
||||
status = smbd_smb2_request_verify_sizes(req, 0x24);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return smbd_smb2_request_error(req, status);
|
||||
}
|
||||
|
||||
inbody = SMBD_SMB2_IN_BODY_PTR(req);
|
||||
|
||||
in_lease_key.data[0] = BVAL(inbody, 8);
|
||||
in_lease_key.data[1] = BVAL(inbody, 16);
|
||||
in_lease_state = IVAL(inbody, 24);
|
||||
|
||||
subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
|
||||
in_lease_key, in_lease_state);
|
||||
if (subreq == NULL) {
|
||||
return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
|
||||
}
|
||||
tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
|
||||
|
||||
return smbd_smb2_request_pending_queue(req, subreq, 500);
|
||||
}
|
||||
|
||||
static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
|
||||
{
|
||||
struct smbd_smb2_request *req = tevent_req_callback_data(
|
||||
subreq, struct smbd_smb2_request);
|
||||
const uint8_t *inbody;
|
||||
struct smb2_lease_key in_lease_key;
|
||||
uint32_t out_lease_state = 0;
|
||||
DATA_BLOB outbody;
|
||||
NTSTATUS status;
|
||||
NTSTATUS error; /* transport error */
|
||||
|
||||
status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
|
||||
TALLOC_FREE(subreq);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
error = smbd_smb2_request_error(req, status);
|
||||
if (!NT_STATUS_IS_OK(error)) {
|
||||
smbd_server_connection_terminate(req->xconn,
|
||||
nt_errstr(error));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inbody = SMBD_SMB2_IN_BODY_PTR(req);
|
||||
|
||||
in_lease_key.data[0] = BVAL(inbody, 8);
|
||||
in_lease_key.data[1] = BVAL(inbody, 16);
|
||||
|
||||
outbody = smbd_smb2_generate_outbody(req, 0x24);
|
||||
if (outbody.data == NULL) {
|
||||
error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
|
||||
if (!NT_STATUS_IS_OK(error)) {
|
||||
smbd_server_connection_terminate(req->xconn,
|
||||
nt_errstr(error));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SSVAL(outbody.data, 0x00, 0x24); /* struct size */
|
||||
SSVAL(outbody.data, 0x02, 0); /* reserved */
|
||||
SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */
|
||||
SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
|
||||
SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
|
||||
SIVAL(outbody.data, 0x18, out_lease_state);
|
||||
SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */
|
||||
|
||||
error = smbd_smb2_request_done(req, outbody, NULL);
|
||||
if (!NT_STATUS_IS_OK(error)) {
|
||||
smbd_server_connection_terminate(req->xconn,
|
||||
nt_errstr(error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct smbd_smb2_lease_break_state {
|
||||
uint32_t lease_state;
|
||||
};
|
||||
|
||||
struct lease_lookup_state {
|
||||
TALLOC_CTX *mem_ctx;
|
||||
/* Return parameters. */
|
||||
uint32_t num_file_ids;
|
||||
struct file_id *ids;
|
||||
NTSTATUS status;
|
||||
};
|
||||
|
||||
static void lease_parser(
|
||||
uint32_t num_file_ids,
|
||||
struct file_id *ids, const char *filename, const char *stream_name,
|
||||
void *private_data)
|
||||
{
|
||||
struct lease_lookup_state *lls =
|
||||
(struct lease_lookup_state *)private_data;
|
||||
|
||||
lls->status = NT_STATUS_OK;
|
||||
lls->num_file_ids = num_file_ids;
|
||||
lls->ids = talloc_memdup(lls->mem_ctx,
|
||||
ids,
|
||||
num_file_ids * sizeof(struct file_id));
|
||||
if (lls->ids == NULL) {
|
||||
lls->status = NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tevent_req *smbd_smb2_lease_break_send(
|
||||
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
|
||||
struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
|
||||
uint32_t in_lease_state)
|
||||
{
|
||||
struct tevent_req *req;
|
||||
struct smbd_smb2_lease_break_state *state;
|
||||
struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
|
||||
NTSTATUS status;
|
||||
|
||||
req = tevent_req_create(mem_ctx, &state,
|
||||
struct smbd_smb2_lease_break_state);
|
||||
if (req == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
state->lease_state = in_lease_state;
|
||||
|
||||
/* Find any file ids with this lease key. */
|
||||
status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
|
||||
&in_lease_key,
|
||||
lease_parser,
|
||||
&lls);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
DEBUG(10, ("No record for lease key found\n"));
|
||||
}
|
||||
} else if (!NT_STATUS_IS_OK(lls.status)) {
|
||||
status = lls.status;
|
||||
} else if (lls.num_file_ids == 0) {
|
||||
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
tevent_req_nterror(req, status);
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
|
||||
status = downgrade_lease(smb2_req->xconn,
|
||||
lls.num_file_ids,
|
||||
lls.ids,
|
||||
&in_lease_key,
|
||||
in_lease_state);
|
||||
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
|
||||
tevent_req_done(req);
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
DEBUG(10, ("downgrade_lease returned %s\n",
|
||||
nt_errstr(status)));
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
|
||||
tevent_req_done(req);
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
|
||||
static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
|
||||
uint32_t *out_lease_state)
|
||||
{
|
||||
struct smbd_smb2_lease_break_state *state = tevent_req_data(
|
||||
req, struct smbd_smb2_lease_break_state);
|
||||
NTSTATUS status;
|
||||
|
||||
if (tevent_req_is_nterror(req, &status)) {
|
||||
return status;
|
||||
}
|
||||
*out_lease_state = state->lease_state;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*********************************************************
|
||||
Create and send an asynchronous
|
||||
SMB2 OPLOCK_BREAK_NOTIFICATION.
|
||||
*********************************************************/
|
||||
|
||||
void send_break_message_smb2(files_struct *fsp, int level)
|
||||
void send_break_message_smb2(files_struct *fsp,
|
||||
uint32_t break_from,
|
||||
uint32_t break_to)
|
||||
{
|
||||
uint8_t smb2_oplock_level = (level == OPLOCKLEVEL_II) ?
|
||||
SMB2_OPLOCK_LEVEL_II :
|
||||
SMB2_OPLOCK_LEVEL_NONE;
|
||||
NTSTATUS status;
|
||||
struct smbXsrv_connection *xconn = NULL;
|
||||
struct smbXsrv_session *session = NULL;
|
||||
@ -257,7 +464,7 @@ void send_break_message_smb2(files_struct *fsp, int level)
|
||||
"for file %s, %s, smb2 level %u session %llu not found\n",
|
||||
fsp_str_dbg(fsp),
|
||||
fsp_fnum_dbg(fsp),
|
||||
(unsigned int)smb2_oplock_level,
|
||||
(unsigned int)break_to,
|
||||
(unsigned long long)fsp->vuid));
|
||||
return;
|
||||
}
|
||||
@ -266,13 +473,35 @@ void send_break_message_smb2(files_struct *fsp, int level)
|
||||
"for file %s, %s, smb2 level %u\n",
|
||||
fsp_str_dbg(fsp),
|
||||
fsp_fnum_dbg(fsp),
|
||||
(unsigned int)smb2_oplock_level ));
|
||||
(unsigned int)break_to ));
|
||||
|
||||
if (fsp->oplock_type == LEASE_OPLOCK) {
|
||||
uint32_t break_flags = 0;
|
||||
uint16_t new_epoch;
|
||||
|
||||
if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
|
||||
break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
|
||||
}
|
||||
|
||||
if (fsp->lease->lease.lease_version > 1) {
|
||||
new_epoch = fsp->lease->lease.lease_epoch;
|
||||
} else {
|
||||
new_epoch = 0;
|
||||
}
|
||||
|
||||
status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags,
|
||||
&fsp->lease->lease.lease_key,
|
||||
break_from, break_to);
|
||||
} else {
|
||||
uint8_t smb2_oplock_level;
|
||||
smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
|
||||
SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
|
||||
status = smbd_smb2_send_oplock_break(xconn,
|
||||
session,
|
||||
fsp->conn->tcon,
|
||||
fsp->op,
|
||||
smb2_oplock_level);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smbd_server_connection_terminate(xconn,
|
||||
nt_errstr(status));
|
||||
|
Loading…
Reference in New Issue
Block a user