struct smb2_create_blob *dh2q = NULL;
struct smb2_create_blob *rqls = NULL;
struct smbXsrv_open *op = NULL;
+ bool replay_operation = false;
if(lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
const uint8_t *p = dh2q->data.data;
uint32_t durable_v2_timeout = 0;
DATA_BLOB create_guid_blob;
+ const uint8_t *hdr;
+ uint32_t flags;
if (dh2q->data.length != 32) {
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
*/
durable_timeout_msec = (60*1000);
}
+
+ /*
+ * Check for replay operation.
+ * Only consider it when we have dh2q.
+ * If we do not have a replay operation, verify that
+ * the create_guid is not cached for replay.
+ */
+ hdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
+ flags = IVAL(hdr, SMB2_HDR_FLAGS);
+ replay_operation =
+ !!(flags & SMB2_HDR_FLAG_REPLAY_OPERATION);
+
+ status = smb2srv_open_lookup_replay_cache(
+ smb2req->xconn, create_guid,
+ 0 /* now */, &op);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ replay_operation = false;
+ } else if (tevent_req_nterror(req, status)) {
+ DBG_WARNING("smb2srv_open_lookup_replay_cache "
+ "failed: %s\n", nt_errstr(status));
+ return tevent_req_post(req, ev);
+ } else if (!replay_operation) {
+ /*
+ * If a create without replay operation flag
+ * is sent but with a create_guid that is
+ * currently in the replay cache -- fail.
+ */
+ status = NT_STATUS_DUPLICATE_OBJECTID;
+ (void)tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
}
if (dhnc) {
DEBUG(10, ("v2 lease key only for SMB3\n"));
lease_ptr = NULL;
}
+
+ /*
+ * Replay with a lease is only allowed if the
+ * established open carries a lease with the
+ * same lease key.
+ */
+ if (replay_operation) {
+ struct smb2_lease *op_ls =
+ &op->compat->lease->lease;
+ int op_oplock = op->compat->oplock_type;
+
+ if (map_samba_oplock_levels_to_smb2(op_oplock)
+ != SMB2_OPLOCK_LEVEL_LEASE)
+ {
+ status = NT_STATUS_ACCESS_DENIED;
+ (void)tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ if (!smb2_lease_key_equal(&lease.lease_key,
+ &op_ls->lease_key))
+ {
+ status = NT_STATUS_ACCESS_DENIED;
+ (void)tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ }
}
/* these are ignored for SMB2 */
/*
* For the backend file open procedure, there are
- * two possible modes: durable_reconnect or not.
+ * three possible modes: replay operation (in which case
+ * there is nothing else to do), durable_reconnect or
+ * new open.
*/
- if (do_durable_reconnect) {
+ if (replay_operation) {
+ result = op->compat;
+ result->op = op;
+ update_open = false;
+ info = op->create_action;
+ } else if (do_durable_reconnect) {
DATA_BLOB new_cookie = data_blob_null;
NTTIME now = timeval_to_nttime(&smb2req->request_time);
}
}
- if (durable_requested &&
+ if (!replay_operation && durable_requested &&
(fsp_lease_type(result) & SMB2_LEASE_HANDLE))
{
status = SMB_VFS_DURABLE_COOKIE(result,
op->global->backend_cookie = data_blob_null;
}
}
- if (op->global->backend_cookie.length > 0) {
+ if (!replay_operation && op->global->backend_cookie.length > 0)
+ {
update_open = true;
op->global->durable = true;
}
}
- if (dh2q && op->global->durable) {
+ if (dh2q && op->global->durable &&
+ /*
+ * For replay operations, we return the dh2q blob
+ * in the case of oplocks not based on the state of
+ * the open, but on whether it could have been granted
+ * for the request data. In the case of leases instead,
+ * the state of the open is used...
+ */
+ (!replay_operation ||
+ in_oplock_level == SMB2_OPLOCK_LEVEL_BATCH ||
+ in_oplock_level == SMB2_OPLOCK_LEVEL_LEASE))
+ {
uint8_t p[8] = { 0, };
DATA_BLOB blob = data_blob_const(p, sizeof(p));
uint32_t durable_v2_response_flags = 0;
smb2req->compat_chain_fsp = smb1req->chain_fsp;
- if(lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
+ if (replay_operation) {
+ state->out_oplock_level = in_oplock_level;
+ } else if (lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
state->out_oplock_level = in_oplock_level;
} else {
state->out_oplock_level = map_samba_oplock_levels_to_smb2(result->oplock_type);
} else {
state->out_create_action = info;
}
+ result->op->create_action = state->out_create_action;
state->out_file_attributes = dos_mode(result->conn,
result->fsp_name);