/*
 * Build an SMB2 error response.  [MS-SMB2] 2.2.2
 */
void
smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
{
	DWORD len;

	/*
	 * The common dispatch code writes this when it
	 * updates the SMB2 header before sending.
	 */
	sr->smb2_status = status;

	/* Rewind to the end of the SMB header. */
	sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;

	/*
	 * NB: Must provide at least one byte of error data,
	 * per [MS-SMB2] 2.2.2
	 */
	if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
		(void) smb_mbc_encodef(
		    &sr->reply,
		    "wwlC",
		    9,	/* StructSize */	/* w */
		    0,	/* reserved */		/* w */
		    len,			/* l */
		    mbc);			/* C */
	} else {
		(void) smb_mbc_encodef(
		    &sr->reply,
		    "wwl.",
		    9,	/* StructSize */	/* w */
		    0,	/* reserved */		/* w */
		    0);				/* l. */
	}
}
void
smb_reply_notify_change_request(smb_request_t *sr)
{
	smb_node_t	*node;
	smb_srqueue_t	*srq;
	int		total_bytes, n_setup, n_param, n_data;
	int		param_off, param_pad, data_off, data_pad;
	struct		smb_xa *xa;
	smb_error_t	err;

	SMB_REQ_VALID(sr);
	srq = sr->session->s_srqueue;
	smb_srqueue_waitq_to_runq(srq);

	xa = sr->r_xa;
	node = sr->sr_ncr.nc_node;

	if (--node->waiting_event == 0) {
		node->flags &= ~(NODE_FLAGS_NOTIFY_CHANGE | NODE_FLAGS_CHANGED);
		smb_fem_fcn_uninstall(node);
	}

	mutex_enter(&sr->sr_mutex);
	switch (sr->sr_state) {

	case SMB_REQ_STATE_EVENT_OCCURRED:
		sr->sr_state = SMB_REQ_STATE_ACTIVE;

		/* many things changed */

		(void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0L);

		/* setup the NT transact reply */

		n_setup = MBC_LENGTH(&xa->rep_setup_mb);
		n_param = MBC_LENGTH(&xa->rep_param_mb);
		n_data  = MBC_LENGTH(&xa->rep_data_mb);

		n_setup = (n_setup + 1) / 2; /* Convert to setup words */
		param_pad = 1; /* must be one */
		param_off = param_pad + 32 + 37 + (n_setup << 1) + 2;
		/* Pad to 4 bytes */
		data_pad = (4 - ((param_off + n_param) & 3)) % 4;
		/* Param off from hdr */
		data_off = param_off + n_param + data_pad;
		total_bytes = param_pad + n_param + data_pad + n_data;

		(void) smbsr_encode_result(sr, 18+n_setup, total_bytes,
		    "b3.llllllllbCw#.C#.C",
		    18 + n_setup,	/* wct */
		    n_param,		/* Total Parameter Bytes */
		    n_data,		/* Total Data Bytes */
		    n_param,		/* Total Parameter Bytes this buffer */
		    param_off,		/* Param offset from header start */
		    0,			/* Param displacement */
		    n_data,		/* Total Data Bytes this buffer */
		    data_off,		/* Data offset from header start */
		    0,			/* Data displacement */
		    n_setup,		/* suwcnt */
		    &xa->rep_setup_mb,	/* setup[] */
		    total_bytes,	/* Total data bytes */
		    param_pad,
		    &xa->rep_param_mb,
		    data_pad,
		    &xa->rep_data_mb);
		break;

	case SMB_REQ_STATE_CANCELED:
		err.status   = NT_STATUS_CANCELLED;
		err.errcls   = ERRDOS;
		err.errcode  = ERROR_OPERATION_ABORTED;
		smbsr_set_error(sr, &err);

		(void) smb_mbc_encodef(&sr->reply, "bwbw",
		    (short)0, 0L, (short)0, 0L);
		sr->smb_wct = 0;
		sr->smb_bcc = 0;
		break;
	default:
		ASSERT(0);
	}
	mutex_exit(&sr->sr_mutex);

	/* Setup the header */
	(void) smb_mbc_poke(&sr->reply, 0, SMB_HEADER_ED_FMT,
	    sr->first_smb_com,
	    sr->smb_rcls,
	    sr->smb_reh,
	    sr->smb_err,
	    sr->smb_flg | SMB_FLAGS_REPLY,
	    sr->smb_flg2,
	    sr->smb_pid_high,
	    sr->smb_sig,
	    sr->smb_tid,
	    sr->smb_pid,
	    sr->smb_uid,
	    sr->smb_mid);

	if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
		smb_sign_reply(sr, NULL);

	/* send the reply */
	DTRACE_PROBE1(ncr__reply, struct smb_request *, sr)
	(void) smb_session_send(sr->session, 0, &sr->reply);
	smbsr_cleanup(sr);

	mutex_enter(&sr->sr_mutex);
	sr->sr_state = SMB_REQ_STATE_COMPLETED;
	mutex_exit(&sr->sr_mutex);
	smb_srqueue_runq_exit(srq);
	smb_request_free(sr);
}