[PATCH] Factor out smbd_smb2_create_file from smbd_smb2_create_send

Jeremy Allison jra at samba.org
Tue Jul 18 21:07:12 UTC 2017


On Sat, Jul 15, 2017 at 10:07:02PM +0200, Ralph Böhme wrote:
> Hi!
> 
> From the attic.
> 
> Please review&push if ok. Just passed a private autobuild.

Nice cleanup Ralph, thanks !

Reviewed-by: Jeremy Allison <jra at samba.org>

> From 3e6a39a8810854610995b8642cb4166ecc19b9ea Mon Sep 17 00:00:00 2001
> From: Ralph Boehme <slow at samba.org>
> Date: Sat, 15 Jul 2017 15:23:46 +0200
> Subject: [PATCH] s3/smbd: factor out smbd_smb2_create_file from
>  smbd_smb2_create_send
> 
> No change in behaviour, smbd_smb2_create_send() has grown really large
> with insane indentation levels, so this had to be split up.
> 
> Best viewed with
> 
> $ git show -w
> 
> One subtlety to note is the new use of NT_STATUS_RETRY as return code
> from smbd_smb2_create_file() to tell the caller smbd_smb2_create_send()
> an open was deferred and cause it to directly return the tevent request.
> 
> While at it, update the DEBUG macros to the new form.
> 
> Signed-off-by: Ralph Boehme <slow at samba.org>
> ---
>  source3/smbd/smb2_create.c | 1426 ++++++++++++++++++++++----------------------
>  1 file changed, 727 insertions(+), 699 deletions(-)
> 
> diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
> index c4fe247..969bb16 100644
> --- a/source3/smbd/smb2_create.c
> +++ b/source3/smbd/smb2_create.c
> @@ -449,6 +449,705 @@ struct smbd_smb2_create_state {
>  	struct smb2_create_blobs *out_context_blobs;
>  };
>  
> +static NTSTATUS smbd_smb2_create_file(
> +	TALLOC_CTX *mem_ctx,
> +	struct tevent_context *ev,
> +	struct smbd_smb2_request *smb2req,
> +	const uint8_t in_oplock_level,
> +	const uint32_t in_impersonation_level,
> +	const uint32_t in_desired_access,
> +	const uint32_t in_share_access,
> +	const uint32_t in_create_disposition,
> +	const char *in_name,
> +	const struct smb2_create_blobs in_context_blobs,
> +	struct smbd_smb2_create_state *state,
> +	struct smb_request *smb1req,
> +	struct smb2_create_blob *dhnc,
> +	struct smb2_create_blob *dh2c,
> +	struct smb2_create_blob *dhnq,
> +	struct smb2_create_blob *dh2q,
> +	struct smb2_create_blob *rqls,
> +	uint32_t *_in_file_attributes,
> +	uint32_t *_in_create_options,
> +	int *_requested_oplock_level,
> +	bool *_replay_operation,
> +	int *_info,
> +	files_struct **_result)
> +{
> +	char *fname = NULL;
> +	struct smb2_create_blob *exta = NULL;
> +	struct ea_list *ea_list = NULL;
> +	struct smb2_create_blob *mxac = NULL;
> +	NTTIME max_access_time = 0;
> +	struct smb2_create_blob *secd = NULL;
> +	struct security_descriptor *sec_desc = NULL;
> +	struct smb2_create_blob *alsi = NULL;
> +	uint64_t allocation_size = 0;
> +	struct smb2_create_blob *twrp = NULL;
> +	struct smb2_create_blob *qfid = NULL;
> +	struct GUID _create_guid = GUID_zero();
> +	struct GUID *create_guid = NULL;
> +	bool update_open = false;
> +	bool durable_requested = false;
> +	uint32_t durable_timeout_msec = 0;
> +	bool do_durable_reconnect = false;
> +	uint64_t persistent_id = 0;
> +	struct smb2_lease lease;
> +	struct smb2_lease *lease_ptr = NULL;
> +	ssize_t lease_len = -1;
> +	bool need_replay_cache = false;
> +	struct smbXsrv_open *op = NULL;
> +#if 0
> +	struct smb2_create_blob *svhdx = NULL;
> +#endif
> +	files_struct *result = NULL;
> +	uint32_t in_file_attributes = *_in_file_attributes;
> +	uint32_t in_create_options = *_in_create_options;
> +	int requested_oplock_level = *_requested_oplock_level;
> +	bool replay_operation = *_replay_operation;
> +	int info;
> +	NTSTATUS status;
> +
> +	*_result = NULL;
> +	*_info = 0;
> +
> +	exta = smb2_create_blob_find(&in_context_blobs,
> +				     SMB2_CREATE_TAG_EXTA);
> +	mxac = smb2_create_blob_find(&in_context_blobs,
> +				     SMB2_CREATE_TAG_MXAC);
> +	secd = smb2_create_blob_find(&in_context_blobs,
> +				     SMB2_CREATE_TAG_SECD);
> +	alsi = smb2_create_blob_find(&in_context_blobs,
> +				     SMB2_CREATE_TAG_ALSI);
> +	twrp = smb2_create_blob_find(&in_context_blobs,
> +				     SMB2_CREATE_TAG_TWRP);
> +	qfid = smb2_create_blob_find(&in_context_blobs,
> +				     SMB2_CREATE_TAG_QFID);
> +#if 0
> +	if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
> +		/*
> +		 * This was introduced with SMB3_02
> +		 */
> +		svhdx = smb2_create_blob_find(&in_context_blobs,
> +					      SVHDX_OPEN_DEVICE_CONTEXT);
> +	}
> +#endif
> +
> +	fname = talloc_strdup(state, in_name);
> +	if (fname == NULL) {
> +		return NT_STATUS_NO_MEMORY;
> +	}
> +
> +	if (exta) {
> +		if (!lp_ea_support(SNUM(smb2req->tcon->compat))) {
> +			return NT_STATUS_EAS_NOT_SUPPORTED;
> +		}
> +
> +		ea_list = read_nttrans_ea_list(mem_ctx,
> +					       (const char *)exta->data.data,
> +					       exta->data.length);
> +		if (ea_list == NULL) {
> +			DBG_ERR("read_ea_name_list failed.\n");
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		/*
> +		 * NB. When SMB2+ unix extensions are added,
> +		 * we need to relax this check in invalid
> +		 * names - we used to not do this if
> +		 * lp_posix_pathnames() was false.
> +		 */
> +		if (ea_list_has_invalid_name(ea_list)) {
> +			return STATUS_INVALID_EA_NAME;
> +		}
> +	}
> +
> +	if (mxac) {
> +		if (mxac->data.length == 0) {
> +			max_access_time = 0;
> +		} else if (mxac->data.length == 8) {
> +			max_access_time = BVAL(mxac->data.data, 0);
> +		} else {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +	}
> +
> +	if (secd) {
> +		enum ndr_err_code ndr_err;
> +
> +		sec_desc = talloc_zero(state, struct security_descriptor);
> +		if (sec_desc == NULL) {
> +			return NT_STATUS_NO_MEMORY;
> +		}
> +
> +		ndr_err = ndr_pull_struct_blob(
> +			&secd->data,
> +			sec_desc, sec_desc,
> +			(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
> +		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
> +			DBG_NOTICE("ndr_pull_security_descriptor failed: %s\n",
> +				   ndr_errstr(ndr_err));
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +	}
> +
> +	if (dhnq) {
> +		if (dhnq->data.length != 16) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		if (dh2q) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		/*
> +		 * durable handle request is processed below.
> +		 */
> +		durable_requested = true;
> +		/*
> +		 * Set the timeout to 16 mins.
> +		 *
> +		 * TODO: test this against Windows 2012
> +		 *       as the default for durable v2 is 1 min.
> +		 */
> +		durable_timeout_msec = (16*60*1000);
> +	}
> +
> +	if (dh2q) {
> +		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) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		if (dhnq) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		durable_v2_timeout = IVAL(p, 0);
> +		create_guid_blob = data_blob_const(p + 16, 16);
> +
> +		status = GUID_from_ndr_blob(&create_guid_blob,
> +					    &_create_guid);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +		create_guid = &_create_guid;
> +		/*
> +		 * we need to store the create_guid later
> +		 */
> +		update_open = true;
> +
> +		/*
> +		 * And we need to create a cache for replaying the
> +		 * create.
> +		 */
> +		need_replay_cache = true;
> +
> +		/*
> +		 * durable handle v2 request processed below
> +		 */
> +		durable_requested = true;
> +		durable_timeout_msec = durable_v2_timeout;
> +		if (durable_timeout_msec == 0) {
> +			/*
> +			 * Set the timeout to 1 min as default.
> +			 *
> +			 * This matches Windows 2012.
> +			 */
> +			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 (!NT_STATUS_IS_OK(status)) {
> +			DBG_WARNING("smb2srv_open_lookup_replay_cache "
> +				    "failed: %s\n", nt_errstr(status));
> +			return status;
> +		} 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.
> +			 */
> +			return NT_STATUS_DUPLICATE_OBJECTID;
> +		}
> +	}
> +
> +	if (dhnc) {
> +		persistent_id = BVAL(dhnc->data.data, 0);
> +
> +		do_durable_reconnect = true;
> +	}
> +
> +	if (dh2c) {
> +		const uint8_t *p = dh2c->data.data;
> +		DATA_BLOB create_guid_blob;
> +
> +		persistent_id = BVAL(p, 0);
> +		create_guid_blob = data_blob_const(p + 16, 16);
> +
> +		status = GUID_from_ndr_blob(&create_guid_blob,
> +					    &_create_guid);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +		create_guid = &_create_guid;
> +
> +		do_durable_reconnect = true;
> +	}
> +
> +	if (alsi) {
> +		if (alsi->data.length != 8) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +		allocation_size = BVAL(alsi->data.data, 0);
> +	}
> +
> +	if (twrp) {
> +		NTTIME nttime;
> +		time_t t;
> +		struct tm *tm;
> +
> +		if (twrp->data.length != 8) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		nttime = BVAL(twrp->data.data, 0);
> +		t = nt_time_to_unix(nttime);
> +		tm = gmtime(&t);
> +
> +		TALLOC_FREE(fname);
> +		fname = talloc_asprintf(state,
> +					"%s\\@GMT-%04u.%02u.%02u-%02u.%02u.%02u",
> +					in_name,
> +					tm->tm_year + 1900,
> +					tm->tm_mon + 1,
> +					tm->tm_mday,
> +					tm->tm_hour,
> +					tm->tm_min,
> +					tm->tm_sec);
> +		if (fname == NULL) {
> +			return NT_STATUS_NO_MEMORY;
> +		}
> +		/*
> +		 * Tell filename_create_ucf_flags() this
> +		 * is an @GMT path.
> +		 */
> +		smb1req->flags2 |= FLAGS2_REPARSE_PATH;
> +	}
> +
> +	if (qfid) {
> +		if (qfid->data.length != 0) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +	}
> +
> +	if (rqls) {
> +		lease_len = smb2_lease_pull(rqls->data.data,
> +					    rqls->data.length,
> +					    &lease);
> +		if (lease_len == -1) {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +		lease_ptr = &lease;
> +
> +		if (DEBUGLEVEL >= 10) {
> +			DBG_DEBUG("Got lease request size %d\n",
> +				  (int)lease_len);
> +			NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
> +		}
> +
> +		if (!smb2_lease_key_valid(&lease.lease_key)) {
> +			lease_ptr = NULL;
> +			requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
> +		}
> +
> +		if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) &&
> +		    (lease.lease_version != 1))
> +		{
> +			DBG_DEBUG("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)
> +			{
> +				return NT_STATUS_ACCESS_DENIED;
> +			}
> +			if (!smb2_lease_key_equal(&lease.lease_key,
> +						  &op_ls->lease_key))
> +			{
> +				return NT_STATUS_ACCESS_DENIED;
> +			}
> +		}
> +	}
> +
> +	/* these are ignored for SMB2 */
> +	in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
> +	in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
> +
> +	in_file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS;
> +
> +	DBG_DEBUG("open execution phase\n");
> +
> +	/*
> +	 * For the backend file open procedure, there are
> +	 * three possible modes: replay operation (in which case
> +	 * there is nothing else to do), durable_reconnect or
> +	 * new open.
> +	 */
> +	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);
> +
> +		status = smb2srv_open_recreate(smb2req->xconn,
> +					       smb1req->conn->session_info,
> +					       persistent_id, create_guid,
> +					       now, &op);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			DBG_NOTICE("smb2srv_open_recreate failed: %s\n",
> +				  nt_errstr(status));
> +			return status;
> +		}
> +
> +		DBG_DEBUG("durable handle recreate %s\n",
> +			   op->global->durable ? "succeeded" : "failed");
> +
> +		if (!op->global->durable) {
> +			talloc_free(op);
> +			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
> +		}
> +
> +		status = SMB_VFS_DURABLE_RECONNECT(smb1req->conn,
> +						   smb1req,
> +						   op, /* smbXsrv_open input */
> +						   op->global->backend_cookie,
> +						   op, /* TALLOC_CTX */
> +						   &result, &new_cookie);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			NTSTATUS return_status;
> +
> +			return_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
> +			DBG_NOTICE("durable_reconnect failed: %s => %s\n",
> +				   nt_errstr(status),
> +				   nt_errstr(return_status));
> +			return return_status;
> +		}
> +
> +		DBG_DEBUG("result->oplock_type=%u, lease_ptr==%p\n",
> +			  (unsigned)result->oplock_type, lease_ptr);
> +
> +		status = smbd_smb2_create_durable_lease_check(
> +			smb1req, fname, result, lease_ptr);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			close_file(smb1req, result, SHUTDOWN_CLOSE);
> +			return status;
> +		}
> +
> +		data_blob_free(&op->global->backend_cookie);
> +		op->global->backend_cookie = new_cookie;
> +
> +		op->status = NT_STATUS_OK;
> +		op->global->disconnect_time = 0;
> +
> +		/* save the timout for later update */
> +		durable_timeout_msec = op->global->durable_timeout_msec;
> +
> +		update_open = true;
> +
> +		info = FILE_WAS_OPENED;
> +	} else {
> +		struct smb_filename *smb_fname = NULL;
> +		uint32_t ucf_flags;
> +
> +		if (requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
> +			if (lease_ptr == NULL) {
> +				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
> +			}
> +		} else {
> +			lease_ptr = NULL;
> +		}
> +
> +		/*
> +		 * For a DFS path the function parse_dfs_path()
> +		 * will do the path processing.
> +		 */
> +
> +		if (!(smb1req->flags2 & FLAGS2_DFS_PATHNAMES)) {
> +			/* convert '\\' into '/' */
> +			status = check_path_syntax(fname);
> +			if (!NT_STATUS_IS_OK(status)) {
> +				return status;
> +			}
> +		}
> +
> +		ucf_flags = filename_create_ucf_flags(smb1req, in_create_disposition);
> +		status = filename_convert(state,
> +					  smb1req->conn,
> +					  fname,
> +					  ucf_flags,
> +					  NULL, /* ppath_contains_wcards */
> +					  &smb_fname);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +
> +		/*
> +		 * MS-SMB2: 2.2.13 SMB2 CREATE Request
> +		 * ImpersonationLevel ... MUST contain one of the
> +		 * following values. The server MUST validate this
> +		 * field, but otherwise ignore it.
> +		 *
> +		 * NB. The source4/torture/smb2/durable_open.c test
> +		 * shows this check is only done on real opens, not
> +		 * on durable handle-reopens.
> +		 */
> +
> +		if (in_impersonation_level > SMB2_IMPERSONATION_DELEGATE) {
> +			return NT_STATUS_BAD_IMPERSONATION_LEVEL;
> +		}
> +
> +		/*
> +		 * We know we're going to do a local open, so now
> +		 * we must be protocol strict. JRA.
> +		 *
> +		 * MS-SMB2: 3.3.5.9 - Receiving an SMB2 CREATE Request
> +		 * If the file name length is greater than zero and the
> +		 * first character is a path separator character, the
> +		 * server MUST fail the request with
> +		 * STATUS_INVALID_PARAMETER.
> +		 */
> +		if (in_name[0] == '\\' || in_name[0] == '/') {
> +			return NT_STATUS_INVALID_PARAMETER;
> +		}
> +
> +		status = SMB_VFS_CREATE_FILE(smb1req->conn,
> +					     smb1req,
> +					     0, /* root_dir_fid */
> +					     smb_fname,
> +					     in_desired_access,
> +					     in_share_access,
> +					     in_create_disposition,
> +					     in_create_options,
> +					     in_file_attributes,
> +					     map_smb2_oplock_levels_to_samba(requested_oplock_level),
> +					     lease_ptr,
> +					     allocation_size,
> +					     0, /* private_flags */
> +					     sec_desc,
> +					     ea_list,
> +					     &result,
> +					     &info,
> +					     &in_context_blobs,
> +					     state->out_context_blobs);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
> +				SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);
> +				return NT_STATUS_RETRY;
> +			}
> +			return status;
> +		}
> +		op = result->op;
> +	}
> +
> +	/*
> +	 * here we have op == result->op
> +	 */
> +
> +	DBG_DEBUG("response construction phase\n");
> +
> +	if (mxac) {
> +		NTTIME last_write_time;
> +
> +		last_write_time = unix_timespec_to_nt_time(
> +			result->fsp_name->st.st_ex_mtime);
> +		if (last_write_time != max_access_time) {
> +			uint8_t p[8];
> +			uint32_t max_access_granted;
> +			DATA_BLOB blob = data_blob_const(p, sizeof(p));
> +
> +			status = smbd_calculate_access_mask(smb1req->conn,
> +							    result->fsp_name,
> +							    false,
> +							    SEC_FLAG_MAXIMUM_ALLOWED,
> +							    &max_access_granted);
> +
> +			SIVAL(p, 0, NT_STATUS_V(status));
> +			SIVAL(p, 4, max_access_granted);
> +
> +			status = smb2_create_blob_add(
> +				state->out_context_blobs,
> +				state->out_context_blobs,
> +				SMB2_CREATE_TAG_MXAC,
> +				blob);
> +			if (!NT_STATUS_IS_OK(status)) {
> +				return status;
> +			}
> +		}
> +	}
> +
> +	if (!replay_operation && durable_requested &&
> +	    (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
> +	{
> +		status = SMB_VFS_DURABLE_COOKIE(result,
> +						op,
> +						&op->global->backend_cookie);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			op->global->backend_cookie = data_blob_null;
> +		}
> +	}
> +	if (!replay_operation && op->global->backend_cookie.length > 0)
> +	{
> +		update_open = true;
> +
> +		op->global->durable = true;
> +		op->global->durable_timeout_msec = durable_timeout_msec;
> +	}
> +
> +	if (update_open) {
> +		op->global->create_guid = _create_guid;
> +		if (need_replay_cache) {
> +			op->flags |= SMBXSRV_OPEN_NEED_REPLAY_CACHE;
> +		}
> +
> +		status = smbXsrv_open_update(op);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			DBG_ERR("smbXsrv_open_update returned %s\n",
> +				nt_errstr(status));
> +			return status;
> +		}
> +	}
> +
> +	if (dhnq && op->global->durable) {
> +		uint8_t p[8] = { 0, };
> +		DATA_BLOB blob = data_blob_const(p, sizeof(p));
> +
> +		status = smb2_create_blob_add(state->out_context_blobs,
> +					      state->out_context_blobs,
> +					      SMB2_CREATE_TAG_DHNQ,
> +					      blob);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +	}
> +
> +	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;
> +
> +		SIVAL(p, 0, op->global->durable_timeout_msec);
> +		SIVAL(p, 4, durable_v2_response_flags);
> +
> +		status = smb2_create_blob_add(state->out_context_blobs,
> +					      state->out_context_blobs,
> +					      SMB2_CREATE_TAG_DH2Q,
> +					      blob);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +	}
> +
> +	if (qfid) {
> +		uint8_t p[32];
> +		uint64_t file_index = get_FileIndex(result->conn,
> +						    &result->fsp_name->st);
> +		DATA_BLOB blob = data_blob_const(p, sizeof(p));
> +
> +		ZERO_STRUCT(p);
> +
> +		/* From conversations with Microsoft engineers at
> +		   the MS plugfest. The first 8 bytes are the "volume index"
> +		   == inode, the second 8 bytes are the "volume id",
> +		   == dev. This will be updated in the SMB2 doc. */
> +		SBVAL(p, 0, file_index);
> +		SIVAL(p, 8, result->fsp_name->st.st_ex_dev);/* FileIndexHigh */
> +
> +		status = smb2_create_blob_add(state->out_context_blobs,
> +					      state->out_context_blobs,
> +					      SMB2_CREATE_TAG_QFID,
> +					      blob);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +	}
> +
> +	if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) {
> +		uint8_t buf[52];
> +
> +		lease = result->lease->lease;
> +
> +		lease_len = sizeof(buf);
> +		if (lease.lease_version == 1) {
> +			lease_len = 32;
> +		}
> +
> +		if (!smb2_lease_push(&lease, buf, lease_len)) {
> +			return NT_STATUS_INTERNAL_ERROR;
> +		}
> +
> +		status = smb2_create_blob_add(
> +			state, state->out_context_blobs,
> +			SMB2_CREATE_TAG_RQLS,
> +			data_blob_const(buf, lease_len));
> +		if (!NT_STATUS_IS_OK(status)) {
> +			return status;
> +		}
> +	}
> +
> +	*_in_file_attributes = in_file_attributes;
> +	*_in_create_options = in_create_options;
> +	*_requested_oplock_level = requested_oplock_level;
> +	*_replay_operation = replay_operation;
> +	*_result = result;
> +	*_info = info;
> +	return NT_STATUS_OK;
> +}
> +
>  static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
>  			struct tevent_context *ev,
>  			struct smbd_smb2_request *smb2req,
> @@ -650,706 +1349,35 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
>  		}
>  		info = FILE_WAS_CREATED;
>  	} else {
> -		char *fname;
> -		struct smb2_create_blob *exta = NULL;
> -		struct ea_list *ea_list = NULL;
> -		struct smb2_create_blob *mxac = NULL;
> -		NTTIME max_access_time = 0;
> -		struct smb2_create_blob *secd = NULL;
> -		struct security_descriptor *sec_desc = NULL;
> -		struct smb2_create_blob *alsi = NULL;
> -		uint64_t allocation_size = 0;
> -		struct smb2_create_blob *twrp = NULL;
> -		struct smb2_create_blob *qfid = NULL;
> -		struct GUID _create_guid = GUID_zero();
> -		struct GUID *create_guid = NULL;
> -		bool update_open = false;
> -		bool durable_requested = false;
> -		uint32_t durable_timeout_msec = 0;
> -		bool do_durable_reconnect = false;
> -		uint64_t persistent_id = 0;
> -		struct smb2_lease lease;
> -		struct smb2_lease *lease_ptr = NULL;
> -		ssize_t lease_len = -1;
> -		bool need_replay_cache = false;
> -		struct smbXsrv_open *op = NULL;
> -#if 0
> -		struct smb2_create_blob *svhdx = NULL;
> -#endif
> -
> -		exta = smb2_create_blob_find(&in_context_blobs,
> -					     SMB2_CREATE_TAG_EXTA);
> -		mxac = smb2_create_blob_find(&in_context_blobs,
> -					     SMB2_CREATE_TAG_MXAC);
> -		secd = smb2_create_blob_find(&in_context_blobs,
> -					     SMB2_CREATE_TAG_SECD);
> -		alsi = smb2_create_blob_find(&in_context_blobs,
> -					     SMB2_CREATE_TAG_ALSI);
> -		twrp = smb2_create_blob_find(&in_context_blobs,
> -					     SMB2_CREATE_TAG_TWRP);
> -		qfid = smb2_create_blob_find(&in_context_blobs,
> -					     SMB2_CREATE_TAG_QFID);
> -#if 0
> -		if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
> -			/*
> -			 * This was introduced with SMB3_02
> -			 */
> -			svhdx = smb2_create_blob_find(&in_context_blobs,
> -						      SVHDX_OPEN_DEVICE_CONTEXT);
> -		}
> -#endif
> -
> -		fname = talloc_strdup(state, in_name);
> -		if (tevent_req_nomem(fname, req)) {
> -			return tevent_req_post(req, ev);
> -		}
> -
> -		if (exta) {
> -			if (!lp_ea_support(SNUM(smb2req->tcon->compat))) {
> -				tevent_req_nterror(req,
> -					NT_STATUS_EAS_NOT_SUPPORTED);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			ea_list = read_nttrans_ea_list(mem_ctx,
> -				(const char *)exta->data.data, exta->data.length);
> -			if (!ea_list) {
> -				DEBUG(10,("smbd_smb2_create_send: read_ea_name_list failed.\n"));
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			/*
> -			 * NB. When SMB2+ unix extensions are added,
> -			 * we need to relax this check in invalid
> -			 * names - we used to not do this if
> -			 * lp_posix_pathnames() was false.
> -			 */
> -			if (ea_list_has_invalid_name(ea_list)) {
> -				tevent_req_nterror(req, STATUS_INVALID_EA_NAME);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if (mxac) {
> -			if (mxac->data.length == 0) {
> -				max_access_time = 0;
> -			} else if (mxac->data.length == 8) {
> -				max_access_time = BVAL(mxac->data.data, 0);
> -			} else {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if (secd) {
> -			enum ndr_err_code ndr_err;
> -
> -			sec_desc = talloc_zero(state, struct security_descriptor);
> -			if (tevent_req_nomem(sec_desc, req)) {
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			ndr_err = ndr_pull_struct_blob(&secd->data,
> -				sec_desc, sec_desc,
> -				(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
> -			if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
> -				DEBUG(2,("ndr_pull_security_descriptor failed: %s\n",
> -					 ndr_errstr(ndr_err)));
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if (dhnq) {
> -			if (dhnq->data.length != 16) {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			if (dh2q) {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			/*
> -			 * durable handle request is processed below.
> -			 */
> -			durable_requested = true;
> -			/*
> -			 * Set the timeout to 16 mins.
> -			 *
> -			 * TODO: test this against Windows 2012
> -			 *       as the default for durable v2 is 1 min.
> -			 */
> -			durable_timeout_msec = (16*60*1000);
> -		}
> -
> -		if (dh2q) {
> -			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);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			if (dhnq) {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			durable_v2_timeout = IVAL(p, 0);
> -			create_guid_blob = data_blob_const(p + 16, 16);
> -
> -			status = GUID_from_ndr_blob(&create_guid_blob,
> -						    &_create_guid);
> -			if (tevent_req_nterror(req, status)) {
> -				return tevent_req_post(req, ev);
> -			}
> -			create_guid = &_create_guid;
> -			/*
> -			 * we need to store the create_guid later
> -			 */
> -			update_open = true;
> -
> -			/*
> -			 * And we need to create a cache for replaying the
> -			 * create.
> -			 */
> -			need_replay_cache = true;
> -
> -			/*
> -			 * durable handle v2 request processed below
> -			 */
> -			durable_requested = true;
> -			durable_timeout_msec = durable_v2_timeout;
> -			if (durable_timeout_msec == 0) {
> -				/*
> -				 * Set the timeout to 1 min as default.
> -				 *
> -				 * This matches Windows 2012.
> -				 */
> -				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) {
> -			persistent_id = BVAL(dhnc->data.data, 0);
> -
> -			do_durable_reconnect = true;
> -		}
> -
> -		if (dh2c) {
> -			const uint8_t *p = dh2c->data.data;
> -			DATA_BLOB create_guid_blob;
> -
> -			persistent_id = BVAL(p, 0);
> -			create_guid_blob = data_blob_const(p + 16, 16);
> -
> -			status = GUID_from_ndr_blob(&create_guid_blob,
> -						    &_create_guid);
> -			if (tevent_req_nterror(req, status)) {
> -				return tevent_req_post(req, ev);
> -			}
> -			create_guid = &_create_guid;
> -
> -			do_durable_reconnect = true;
> -		}
> -
> -		if (alsi) {
> -			if (alsi->data.length != 8) {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -			allocation_size = BVAL(alsi->data.data, 0);
> -		}
> -
> -		if (twrp) {
> -			NTTIME nttime;
> -			time_t t;
> -			struct tm *tm;
> -
> -			if (twrp->data.length != 8) {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			nttime = BVAL(twrp->data.data, 0);
> -			t = nt_time_to_unix(nttime);
> -			tm = gmtime(&t);
> -
> -			TALLOC_FREE(fname);
> -			fname = talloc_asprintf(state,
> -					"%s\\@GMT-%04u.%02u.%02u-%02u.%02u.%02u",
> -					in_name,
> -					tm->tm_year + 1900,
> -					tm->tm_mon + 1,
> -					tm->tm_mday,
> -					tm->tm_hour,
> -					tm->tm_min,
> -					tm->tm_sec);
> -			if (tevent_req_nomem(fname, req)) {
> -				return tevent_req_post(req, ev);
> -			}
> -			/*
> -			 * Tell filename_create_ucf_flags() this
> -			 * is an @GMT path.
> -			 */
> -			smb1req->flags2 |= FLAGS2_REPARSE_PATH;
> -		}
> -
> -		if (qfid) {
> -			if (qfid->data.length != 0) {
> -				tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if (rqls) {
> -			lease_len = smb2_lease_pull(
> -				rqls->data.data, rqls->data.length, &lease);
> -			if (lease_len == -1) {
> -				tevent_req_nterror(
> -					req, NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -			lease_ptr = &lease;
> -
> -			if (DEBUGLEVEL >= 10) {
> -				DEBUG(10, ("Got lease request size %d\n",
> -					   (int)lease_len));
> -				NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
> -			}
> -
> -			if (!smb2_lease_key_valid(&lease.lease_key)) {
> -				lease_ptr = NULL;
> -				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
> -			}
> -
> -			if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) &&
> -			    (lease.lease_version != 1)) {
> -				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 */
> -		in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
> -		in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
> -
> -		in_file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS;
> -
> -		DEBUG(10, ("smbd_smb2_create_send: open execution phase\n"));
> -
> -		/*
> -		 * For the backend file open procedure, there are
> -		 * three possible modes: replay operation (in which case
> -		 * there is nothing else to do), durable_reconnect or
> -		 * new open.
> -		 */
> -		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);
> -
> -			status = smb2srv_open_recreate(smb2req->xconn,
> -						smb1req->conn->session_info,
> -						persistent_id, create_guid,
> -						now, &op);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				DEBUG(3, ("smbd_smb2_create_send: "
> -					  "smb2srv_open_recreate failed: %s\n",
> -					  nt_errstr(status)));
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			DEBUG(10, ("smb2_create_send: %s to recreate the "
> -				   "smb2srv_open struct for a durable handle.\n",
> -				   op->global->durable ? "succeeded" : "failed"));
> -
> -			if (!op->global->durable) {
> -				talloc_free(op);
> -				tevent_req_nterror(req,
> -					NT_STATUS_OBJECT_NAME_NOT_FOUND);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			status = SMB_VFS_DURABLE_RECONNECT(smb1req->conn,
> -						smb1req,
> -						op, /* smbXsrv_open input */
> -						op->global->backend_cookie,
> -						op, /* TALLOC_CTX */
> -						&result, &new_cookie);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				NTSTATUS return_status;
> -
> -				return_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
> -
> -				DEBUG(3, ("smbd_smb2_create_send: "
> -					  "durable_reconnect failed: %s => %s\n",
> -					  nt_errstr(status),
> -					  nt_errstr(return_status)));
> -
> -				tevent_req_nterror(req, return_status);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
> -				   (unsigned)result->oplock_type, lease_ptr));
> -
> -			status = smbd_smb2_create_durable_lease_check(
> -				smb1req, fname, result, lease_ptr);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				close_file(smb1req, result, SHUTDOWN_CLOSE);
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			data_blob_free(&op->global->backend_cookie);
> -			op->global->backend_cookie = new_cookie;
> -
> -			op->status = NT_STATUS_OK;
> -			op->global->disconnect_time = 0;
> -
> -			/* save the timout for later update */
> -			durable_timeout_msec = op->global->durable_timeout_msec;
> -
> -			update_open = true;
> -
> -			info = FILE_WAS_OPENED;
> -		} else {
> -			struct smb_filename *smb_fname = NULL;
> -			uint32_t ucf_flags;
> -
> -			if (requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
> -				if (lease_ptr == NULL) {
> -					requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
> -				}
> -			} else {
> -				lease_ptr = NULL;
> -			}
> -
> -			/*
> -			 * For a DFS path the function parse_dfs_path()
> -			 * will do the path processing.
> -			 */
> -
> -			if (!(smb1req->flags2 & FLAGS2_DFS_PATHNAMES)) {
> -				/* convert '\\' into '/' */
> -				status = check_path_syntax(fname);
> -				if (!NT_STATUS_IS_OK(status)) {
> -					tevent_req_nterror(req, status);
> -					return tevent_req_post(req, ev);
> -				}
> -			}
> -
> -			ucf_flags = filename_create_ucf_flags(smb1req, in_create_disposition);
> -			status = filename_convert(req,
> -						  smb1req->conn,
> -						  fname,
> -						  ucf_flags,
> -						  NULL, /* ppath_contains_wcards */
> -						  &smb_fname);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			/*
> -			 * MS-SMB2: 2.2.13 SMB2 CREATE Request
> -			 * ImpersonationLevel ... MUST contain one of the
> -			 * following values. The server MUST validate this
> -			 * field, but otherwise ignore it.
> -			 *
> -			 * NB. The source4/torture/smb2/durable_open.c test
> -			 * shows this check is only done on real opens, not
> -			 * on durable handle-reopens.
> -			 */
> -
> -			if (in_impersonation_level >
> -					SMB2_IMPERSONATION_DELEGATE) {
> -				tevent_req_nterror(req,
> -					NT_STATUS_BAD_IMPERSONATION_LEVEL);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			/*
> -			 * We know we're going to do a local open, so now
> -			 * we must be protocol strict. JRA.
> -			 *
> -			 * MS-SMB2: 3.3.5.9 - Receiving an SMB2 CREATE Request
> -			 * If the file name length is greater than zero and the
> -			 * first character is a path separator character, the
> -			 * server MUST fail the request with
> -			 * STATUS_INVALID_PARAMETER.
> -			 */
> -			if (in_name[0] == '\\' || in_name[0] == '/') {
> -				tevent_req_nterror(req,
> -					NT_STATUS_INVALID_PARAMETER);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			status = SMB_VFS_CREATE_FILE(smb1req->conn,
> -						     smb1req,
> -						     0, /* root_dir_fid */
> -						     smb_fname,
> -						     in_desired_access,
> -						     in_share_access,
> -						     in_create_disposition,
> -						     in_create_options,
> -						     in_file_attributes,
> -						     map_smb2_oplock_levels_to_samba(requested_oplock_level),
> -						     lease_ptr,
> -						     allocation_size,
> -						     0, /* private_flags */
> -						     sec_desc,
> -						     ea_list,
> -						     &result,
> -						     &info,
> -						     &in_context_blobs,
> -						     state->out_context_blobs);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
> -					SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);
> -					return req;
> -				}
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -			op = result->op;
> -		}
> -
> -		/*
> -		 * here we have op == result->op
> -		 */
> -
> -		DEBUG(10, ("smbd_smb2_create_send: "
> -			   "response construction phase\n"));
> -
> -		if (mxac) {
> -			NTTIME last_write_time;
> -
> -			last_write_time = unix_timespec_to_nt_time(
> -						 result->fsp_name->st.st_ex_mtime);
> -			if (last_write_time != max_access_time) {
> -				uint8_t p[8];
> -				uint32_t max_access_granted;
> -				DATA_BLOB blob = data_blob_const(p, sizeof(p));
> -
> -				status = smbd_calculate_access_mask(smb1req->conn,
> -							result->fsp_name,
> -							false,
> -							SEC_FLAG_MAXIMUM_ALLOWED,
> -							&max_access_granted);
> -
> -				SIVAL(p, 0, NT_STATUS_V(status));
> -				SIVAL(p, 4, max_access_granted);
> -
> -				status = smb2_create_blob_add(
> -				    state->out_context_blobs,
> -				    state->out_context_blobs,
> -				    SMB2_CREATE_TAG_MXAC,
> -				    blob);
> -				if (!NT_STATUS_IS_OK(status)) {
> -					tevent_req_nterror(req, status);
> -					return tevent_req_post(req, ev);
> -				}
> -			}
> -		}
> -
> -		if (!replay_operation && durable_requested &&
> -		    (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
> -		{
> -			status = SMB_VFS_DURABLE_COOKIE(result,
> -						op,
> -						&op->global->backend_cookie);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				op->global->backend_cookie = data_blob_null;
> -			}
> -		}
> -		if (!replay_operation && op->global->backend_cookie.length > 0)
> -		{
> -			update_open = true;
> -
> -			op->global->durable = true;
> -			op->global->durable_timeout_msec = durable_timeout_msec;
> -		}
> -
> -		if (update_open) {
> -			op->global->create_guid = _create_guid;
> -			if (need_replay_cache) {
> -				op->flags |= SMBXSRV_OPEN_NEED_REPLAY_CACHE;
> -			}
> -
> -			status = smbXsrv_open_update(op);
> -			DEBUG(10, ("smb2_create_send: smbXsrv_open_update "
> -				   "returned %s\n",
> -				   nt_errstr(status)));
> -			if (!NT_STATUS_IS_OK(status)) {
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if (dhnq && op->global->durable) {
> -			uint8_t p[8] = { 0, };
> -			DATA_BLOB blob = data_blob_const(p, sizeof(p));
> -
> -			status = smb2_create_blob_add(state->out_context_blobs,
> -						      state->out_context_blobs,
> -						      SMB2_CREATE_TAG_DHNQ,
> -						      blob);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		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;
> -
> -			SIVAL(p, 0, op->global->durable_timeout_msec);
> -			SIVAL(p, 4, durable_v2_response_flags);
> -
> -			status = smb2_create_blob_add(state->out_context_blobs,
> -						      state->out_context_blobs,
> -						      SMB2_CREATE_TAG_DH2Q,
> -						      blob);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if (qfid) {
> -			uint8_t p[32];
> -			uint64_t file_index = get_FileIndex(result->conn,
> -							&result->fsp_name->st);
> -			DATA_BLOB blob = data_blob_const(p, sizeof(p));
> -
> -			ZERO_STRUCT(p);
> -
> -			/* From conversations with Microsoft engineers at
> -			   the MS plugfest. The first 8 bytes are the "volume index"
> -			   == inode, the second 8 bytes are the "volume id",
> -			   == dev. This will be updated in the SMB2 doc. */
> -			SBVAL(p, 0, file_index);
> -			SIVAL(p, 8, result->fsp_name->st.st_ex_dev);/* FileIndexHigh */
> -
> -			status = smb2_create_blob_add(state->out_context_blobs,
> -						      state->out_context_blobs,
> -						      SMB2_CREATE_TAG_QFID,
> -						      blob);
> -			if (!NT_STATUS_IS_OK(status)) {
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> -			}
> -		}
> -
> -		if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) {
> -			uint8_t buf[52];
> -
> -			lease = result->lease->lease;
> -
> -			lease_len = sizeof(buf);
> -			if (lease.lease_version == 1) {
> -				lease_len = 32;
> -			}
> -
> -			if (!smb2_lease_push(&lease, buf, lease_len)) {
> -				tevent_req_nterror(
> -					req, NT_STATUS_INTERNAL_ERROR);
> -				return tevent_req_post(req, ev);
> -			}
> -
> -			status = smb2_create_blob_add(
> -				state, state->out_context_blobs,
> -				SMB2_CREATE_TAG_RQLS,
> -				data_blob_const(buf, lease_len));
> -			if (!NT_STATUS_IS_OK(status)) {
> -				tevent_req_nterror(req, status);
> -				return tevent_req_post(req, ev);
> +		status = smbd_smb2_create_file(mem_ctx,
> +					       ev,
> +					       smb2req,
> +					       in_oplock_level,
> +					       in_impersonation_level,
> +					       in_desired_access,
> +					       in_share_access,
> +					       in_create_disposition,
> +					       in_name,
> +					       in_context_blobs,
> +					       state,
> +					       smb1req,
> +					       dhnc,
> +					       dh2c,
> +					       dhnq,
> +					       dh2q,
> +					       rqls,
> +					       &in_file_attributes,
> +					       &in_create_options,
> +					       &requested_oplock_level,
> +					       &replay_operation,
> +					       &info,
> +					       &result);
> +		if (!NT_STATUS_IS_OK(status)) {
> +			if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
> +				return req;
>  			}
> +			tevent_req_nterror(req, status);
> +			return tevent_req_post(req, ev);
>  		}
>  	}
>  
> -- 
> 2.9.4
> 




More information about the samba-technical mailing list