view usr/src/uts/common/fs/smbsrv/smb_common_transact.c @ 9914:15092dda0737

6847056 smb_ads_computer_op() dumps core when joining Win 2008 R2 domain 6848702 NDR buffer decode support 6797780 return code mismatches 6850931 SMB volume properties Quota tab display 6851425 Unable to create hard links via SMB/CIFS using cygwin 6840353 Fail to set or retrieve the share ACL using Windows test program
author Alan Wright <amw@Sun.COM>
date Fri, 19 Jun 2009 12:13:15 -0600
parents 8ff6afa44187
children 37e5dcdf36d3
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <smbsrv/smb_incl.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_share.h>
#include <smbsrv/oem.h>
#include <smbsrv/nmpipes.h>
#include <smbsrv/mailslot.h>
#include <smbsrv/lmerr.h>
#include <smbsrv/nterror.h>

#define	SMB_QUOTA_UNLIMITED	0xFFFFFFFFFFFFFFFF;

/*
 * count of bytes in server response packet
 * except parameters and data. Note that setup
 * word count is zero.
 */
#define	RESP_HEADER_LEN		24

/*
 * We started by using common functions for transaction/transaction2
 * and transaction_secondary/transaction2_secondary because they
 * are respectively so similar. However, it turned out to be a bad
 * idea because of quirky differences. Be sure if you modify one
 * of these four functions to check and see if the modification should
 * be applied to its peer.
 */

static int smb_trans_ready(struct smb_xa *);
static smb_sdrc_t smb_trans_dispatch(struct smb_request *, struct smb_xa *);
static smb_sdrc_t smb_trans2_dispatch(struct smb_request *, struct smb_xa *);
static smb_sdrc_t smb_nt_transact_query_quota(struct smb_request *,
    struct smb_xa *);

smb_sdrc_t
smb_pre_transaction(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Transaction__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_transaction(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Transaction__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_transaction(smb_request_t *sr)
{
	int		rc;
	unsigned char	msrcnt, suwcnt;
	uint16_t	tpscnt, tdscnt, mprcnt, mdrcnt, flags;
	uint16_t	pscnt, psoff, dscnt, dsoff;
	uint32_t	timeo;
	struct smb_xa *xa;
	char *stn;
	int ready;

	rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT,
	    &tpscnt, &tdscnt, &mprcnt, &mdrcnt, &msrcnt, &flags,
	    &timeo, &pscnt, &psoff, &dscnt, &dsoff, &suwcnt);

	if (rc != 0)
		return (SDRC_ERROR);

	xa = smb_xa_create(sr->session, sr, tpscnt, tdscnt, mprcnt, mdrcnt,
	    msrcnt, suwcnt);
	if (xa == NULL) {
		smbsr_error(sr, 0, ERRSRV, ERRnoroom);
		return (SDRC_ERROR);
	}

	/* Should be some alignment stuff here in SMB? */
	if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) {
		rc = smbsr_decode_data(sr, "%.U", sr, &stn);
	} else {
		rc = smbsr_decode_data(sr, "%s", sr,  &stn);
	}
	if (rc != 0) {
		smb_xa_rele(sr->session, xa);
		return (SDRC_ERROR);
	}
	xa->xa_smb_trans_name = MEM_STRDUP("smb", stn);

	xa->smb_flags  = flags;
	xa->smb_timeout = timeo;
	xa->req_disp_param = pscnt;
	xa->req_disp_data  = dscnt;

	if (MBC_SHADOW_CHAIN(&xa->req_setup_mb, &sr->smb_vwv,
	    sr->smb_vwv.chain_offset, suwcnt * 2)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}

	ready = smb_trans_ready(xa);

	if (smb_xa_open(xa)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRsrverror);
		return (SDRC_ERROR);
	}
	sr->r_xa = xa;

	if (!ready) {
		rc = smbsr_encode_empty_result(sr);
		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
	}

	if (!smb_xa_complete(xa)) {
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}

	return (smb_trans_dispatch(sr, xa));
}

smb_sdrc_t
smb_pre_transaction_secondary(smb_request_t *sr)
{
	DTRACE_SMB_1(op__TransactionSecondary__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_transaction_secondary(smb_request_t *sr)
{
	DTRACE_SMB_1(op__TransactionSecondary__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_transaction_secondary(smb_request_t *sr)
{
	uint16_t tpscnt, tdscnt, pscnt, psdisp;
	uint16_t dscnt, dsoff, dsdisp, psoff;
	smb_xa_t *xa;
	int rc;

	if ((xa = smbsr_lookup_xa(sr)) == 0) {
		smbsr_error(sr, 0, ERRSRV, ERRsrverror);
		return (SDRC_ERROR);
	}

	if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
		if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) {
			smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
			    ERRDOS, ERRnoaccess);
			return (SDRC_ERROR);
		}
	}

	if (xa->smb_com != SMB_COM_TRANSACTION) {
		return (SDRC_DROP_VC);
	}

	rc = smbsr_decode_vwv(sr, SMB_TRANSSHDR_ED_FMT, &tpscnt, &tdscnt,
	    &pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp);

	if (rc != 0)
		return (SDRC_ERROR);

	mutex_enter(&xa->xa_mutex);
	xa->smb_tpscnt = tpscnt;	/* might have shrunk */
	xa->smb_tdscnt = tdscnt;	/* might have shrunk */
	xa->req_disp_param = psdisp+pscnt;
	xa->req_disp_data  = dsdisp+dscnt;

	if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
		mutex_exit(&xa->xa_mutex);
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
		mutex_exit(&xa->xa_mutex);
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	mutex_exit(&xa->xa_mutex);

	if (!smb_trans_ready(xa))
		return (SDRC_NO_REPLY);

	if (!smb_xa_complete(xa))
		return (SDRC_NO_REPLY);

	return (smb_trans_dispatch(sr, xa));
}

smb_sdrc_t
smb_pre_ioctl(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Ioctl__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_ioctl(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Ioctl__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_ioctl(smb_request_t *sr)
{
	uint16_t fid, category, function, tpscnt, tdscnt, mprcnt;
	uint16_t mdrcnt, pscnt, pdoff, dscnt, dsoff;
	uint32_t timeout;
	int rc;

	rc = smbsr_decode_vwv(sr, "wwwwwwwl2.wwww", &fid, &category, &function,
	    &tpscnt, &tdscnt, &mprcnt, &mdrcnt, &timeout, &pscnt,
	    &pdoff, &dscnt, &dsoff);

	if (rc != 0)
		return (SDRC_ERROR);

	return (SDRC_NOT_IMPLEMENTED);
}

smb_sdrc_t
smb_pre_transaction2(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Transaction2__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_transaction2(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Transaction2__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_transaction2(struct smb_request *sr)
{
	unsigned char	msrcnt, suwcnt;
	uint16_t	tpscnt, tdscnt, mprcnt, mdrcnt, flags;
	uint16_t	pscnt, psoff, dscnt, dsoff;
	uint32_t	timeo;
	smb_xa_t *xa;
	int ready;
	int rc;

	rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT, &tpscnt, &tdscnt,
	    &mprcnt, &mdrcnt, &msrcnt, &flags, &timeo, &pscnt, &psoff, &dscnt,
	    &dsoff, &suwcnt);

	if (rc != 0)
		return (SDRC_ERROR);

	xa = smb_xa_create(sr->session, sr, tpscnt, tdscnt, mprcnt, mdrcnt,
	    msrcnt, suwcnt);
	if (xa == 0) {
		smbsr_error(sr, 0, ERRSRV, ERRnoroom);
		return (SDRC_ERROR);
	}

	xa->smb_flags  = flags;
	xa->smb_timeout = timeo;
	xa->req_disp_param = pscnt;
	xa->req_disp_data  = dscnt;

	if (MBC_SHADOW_CHAIN(&xa->req_setup_mb, &sr->smb_vwv,
	    sr->smb_vwv.chain_offset, suwcnt*2)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}

	ready = smb_trans_ready(xa);

	if (smb_xa_open(xa)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRsrverror);
		return (SDRC_ERROR);
	}
	sr->r_xa = xa;

	if (!ready) {
		rc = smbsr_encode_empty_result(sr);
		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
	}

	if (!smb_xa_complete(xa)) {
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}

	return (smb_trans2_dispatch(sr, xa));
}

smb_sdrc_t
smb_pre_transaction2_secondary(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Transaction2Secondary__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_transaction2_secondary(smb_request_t *sr)
{
	DTRACE_SMB_1(op__Transaction2Secondary__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_transaction2_secondary(smb_request_t *sr)
{
	uint16_t tpscnt, tdscnt, fid;
	uint16_t pscnt, psoff, psdisp, dscnt, dsoff, dsdisp;
	smb_xa_t *xa;
	int rc;

	if ((xa = smbsr_lookup_xa(sr)) == 0) {
		smbsr_error(sr, 0, ERRSRV, ERRsrverror);
		return (SDRC_ERROR);
	}

	if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
		if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) {
			smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
			    ERRDOS, ERRnoaccess);
			return (SDRC_ERROR);
		}
	}

	if (xa->smb_com != SMB_COM_TRANSACTION2) {
		return (SDRC_DROP_VC);
	}

	rc = smbsr_decode_vwv(sr, SMB_TRANS2SHDR_ED_FMT, &tpscnt, &tdscnt,
	    &pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp, &fid);

	if (rc != 0)
		return (SDRC_ERROR);

	mutex_enter(&xa->xa_mutex);
	xa->smb_tpscnt = tpscnt;	/* might have shrunk */
	xa->smb_tdscnt = tdscnt;	/* might have shrunk */
	xa->xa_smb_fid = fid;		/* overwrite rules? */
	xa->req_disp_param = psdisp + pscnt;
	xa->req_disp_data  = dsdisp + dscnt;

	if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
		mutex_exit(&xa->xa_mutex);
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
		mutex_exit(&xa->xa_mutex);
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	mutex_exit(&xa->xa_mutex);

	if (!smb_trans_ready(xa))
		return (SDRC_NO_REPLY);

	if (!smb_xa_complete(xa))
		return (SDRC_NO_REPLY);

	return (smb_trans2_dispatch(sr, xa));
}

static smb_sdrc_t
smb_nt_trans_dispatch(struct smb_request *sr, struct smb_xa *xa)
{
	int rc;
	int total_bytes, n_setup, n_param, n_data;
	int param_off, param_pad, data_off, data_pad;

	n_setup = (xa->smb_msrcnt < 200) ? xa->smb_msrcnt : 200;
	n_setup++;
	n_setup = n_setup & ~0x0001;
	n_param = (xa->smb_mprcnt < smb_maxbufsize)
	    ? xa->smb_mprcnt : smb_maxbufsize;
	n_param++;
	n_param = n_param & ~0x0001;
	rc = smb_maxbufsize - (SMBHEADERSIZE + 28 + n_setup + n_param);
	n_data = (xa->smb_mdrcnt < rc) ? xa->smb_mdrcnt : rc;
	MBC_INIT(&xa->rep_setup_mb, n_setup * 2);
	MBC_INIT(&xa->rep_param_mb, n_param);
	MBC_INIT(&xa->rep_data_mb, n_data);

	switch (xa->smb_func) {
	case NT_TRANSACT_CREATE:
		if ((rc = smb_pre_nt_transact_create(sr, xa)) == 0)
			rc = smb_nt_transact_create(sr, xa);
		smb_post_nt_transact_create(sr, xa);
		break;
	case NT_TRANSACT_NOTIFY_CHANGE:
		rc = smb_nt_transact_notify_change(sr, xa);
		break;
	case NT_TRANSACT_QUERY_SECURITY_DESC:
		rc = smb_nt_transact_query_security_info(sr, xa);
		break;
	case NT_TRANSACT_SET_SECURITY_DESC:
		rc = smb_nt_transact_set_security_info(sr, xa);
		break;
	case NT_TRANSACT_IOCTL:
		rc = smb_nt_transact_ioctl(sr, xa);
		break;

	case NT_TRANSACT_QUERY_QUOTA:
		(void) smb_nt_transact_query_quota(sr, xa);
		break;

	case NT_TRANSACT_SET_QUOTA:
		smbsr_error(sr, 0, ERRSRV, ERRaccess);
		return (SDRC_ERROR);

	default:
		smbsr_error(sr, 0, ERRSRV, ERRsmbcmd);
		return (SDRC_ERROR);
	}

	switch (rc) {
	case SDRC_SUCCESS:
		break;

	case SDRC_DROP_VC:
	case SDRC_NO_REPLY:
	case SDRC_ERROR:
	case SDRC_SR_KEPT:
		return (rc);

	case SDRC_NOT_IMPLEMENTED:
		smbsr_error(sr, 0, ERRSRV, ERRsmbcmd);
		return (SDRC_ERROR);

	default:
		break;
	}

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

	if (xa->smb_msrcnt < n_setup ||
	    xa->smb_mprcnt < n_param ||
	    xa->smb_mdrcnt < n_data) {
		smbsr_error(sr, 0, ERRSRV, ERRsmbcmd);
		return (SDRC_ERROR);
	}

	/* neato, blast it over there */

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

	rc = 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);
	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}

/*
 * smb_nt_transact_query_quota
 *
 * Request                    Description
 * ========================== ==================================
 * WORD fid
 * BYTE ReturnSingleEntry     A boolean indicating whether to return
 *                            a single entry or multiple entries.
 * BYTE RestartScan           A boolean indicating whether to continue from
 *                            the previous request or restart a new sequence.
 * DWORD SidListLength        The length in bytes of the SidList or 0 if
 *                            there is no SidList.
 * DWORD StartSidLength       The length in bytes of the StartSid or 0 if
 *                            there is no StartSid.  The server must ignore
 *                            StartSidLength if SidListLength is non-zero.
 * DWORD StartSidOffset       The offset, in bytes, to the StartSid in the
 *                            parameter block.
 *
 * If SidListLength is non-zero, the request contains a list of SIDs
 * for which information is requested.  If StartSidLength is nonzero,
 * the request contains the SID at which the enumeration should start.
 *
 * One of SidListLength and StartSidLength must be 0.  If both are 0,
 * all SIDs are to be enumerated by the server as if they were passed
 * the SidList.
 */
static smb_sdrc_t
smb_nt_transact_query_quota(struct smb_request *sr, struct smb_xa *xa)
{
	smb_sid_t	*sid;
	uint8_t		single, restart;
	uint16_t	fid;
	uint32_t	sidlen, listlen, startlen, startoff;
	uint64_t	limit, used, mtime;

	if (smb_mbc_decodef(&xa->req_param_mb, "%wbblll", sr,
	    &fid, &single, &restart, &listlen, &startlen, &startoff))
		return (SDRC_ERROR);

	if (restart == 0) {
		(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
		return (SDRC_SUCCESS);
	}

	/*
	 * BUILTIN\Administrators
	 */
	if ((sid = smb_sid_fromstr("S-1-5-32-544")) == NULL) {
		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 0, 0);
		return (SDRC_ERROR);
	}

	sidlen = smb_sid_len(sid);
	used = 0;
	mtime = 0xBA7ADAAC0436C601; /* canned dummy timestamp */
	limit = SMB_QUOTA_UNLIMITED;

	/*
	 * The encoded length of "llqqqq" is 40 bytes.
	 */
	(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 40 + sidlen);

	(void) smb_mbc_encodef(&xa->rep_data_mb, "llqqqq",
	    0,		/* next offset */
	    sidlen,	/* sid length */
	    mtime,	/* change time */
	    used,	/* quota used */
	    limit,	/* soft limit */
	    limit);	/* hard limit */

	smb_encode_sid(xa, sid);
	smb_sid_free(sid);
	return (SDRC_SUCCESS);
}

smb_sdrc_t
smb_pre_nt_transact(smb_request_t *sr)
{
	DTRACE_SMB_1(op__NtTransact__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_nt_transact(smb_request_t *sr)
{
	DTRACE_SMB_1(op__NtTransact__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_nt_transact(struct smb_request *sr)
{
	uint16_t	Function;
	unsigned char	MaxSetupCount, SetupCount;
	uint32_t	TotalParameterCount, TotalDataCount;
	uint32_t	MaxParameterCount, MaxDataCount, pscnt;
	uint32_t	psoff, dscnt, dsoff;
	smb_xa_t *xa;
	int ready;
	int rc;

	rc = smbsr_decode_vwv(sr, SMB_NT_TRANSHDR_ED_FMT, &MaxSetupCount,
	    &TotalParameterCount, &TotalDataCount, &MaxParameterCount,
	    &MaxDataCount, &pscnt, &psoff, &dscnt,
	    &dsoff, &SetupCount, &Function);

	if (rc != 0)
		return (SDRC_ERROR);

	xa = smb_xa_create(sr->session, sr, TotalParameterCount, TotalDataCount,
	    MaxParameterCount, MaxDataCount, MaxSetupCount, SetupCount);
	if (xa == 0) {
		smbsr_error(sr, 0, ERRSRV, ERRnoroom);
		return (SDRC_ERROR);
	}

	xa->smb_flags  = 0;
	xa->smb_timeout = 0;
	xa->smb_func = Function;
	xa->req_disp_param = pscnt;
	xa->req_disp_data  = dscnt;

	if (MBC_SHADOW_CHAIN(&xa->req_setup_mb, &sr->smb_vwv,
	    sr->smb_vwv.chain_offset, SetupCount * 2)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}

	ready = smb_trans_ready(xa);

	if (smb_xa_open(xa)) {
		smb_xa_rele(sr->session, xa);
		smbsr_error(sr, 0, ERRDOS, ERRsrverror);
		return (SDRC_ERROR);
	}
	sr->r_xa = xa;

	if (!ready) {
		rc = smbsr_encode_empty_result(sr);
		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
	}

	if (!smb_xa_complete(xa)) {
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}

	return (smb_nt_trans_dispatch(sr, xa));
}

smb_sdrc_t
smb_pre_nt_transact_secondary(smb_request_t *sr)
{
	DTRACE_SMB_1(op__NtTransactSecondary__start, smb_request_t *, sr);
	return (SDRC_SUCCESS);
}

void
smb_post_nt_transact_secondary(smb_request_t *sr)
{
	DTRACE_SMB_1(op__NtTransactSecondary__done, smb_request_t *, sr);
}

smb_sdrc_t
smb_com_nt_transact_secondary(struct smb_request *sr)
{
	uint16_t tpscnt, tdscnt, fid;
	uint16_t pscnt, psoff, psdisp, dscnt, dsoff, dsdisp;
	smb_xa_t *xa;
	int rc;

	if ((xa = smbsr_lookup_xa(sr)) == 0) {
		smbsr_error(sr, 0, ERRSRV, ERRsrverror);
		return (SDRC_ERROR);
	}

	if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
		if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) {
			smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
			    ERRDOS, ERRnoaccess);
			return (SDRC_ERROR);
		}
	}

	if (xa->smb_com != SMB_COM_TRANSACTION2) {
		return (SDRC_DROP_VC);
	}

	rc = smbsr_decode_vwv(sr, SMB_TRANS2SHDR_ED_FMT, &tpscnt, &tdscnt,
	    &pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp, &fid);

	if (rc != 0)
		return (SDRC_ERROR);

	mutex_enter(&xa->xa_mutex);
	xa->smb_tpscnt = tpscnt;	/* might have shrunk */
	xa->smb_tdscnt = tdscnt;	/* might have shrunk */
	xa->xa_smb_fid = fid;		/* overwrite rules? */
	xa->req_disp_param = psdisp+pscnt;
	xa->req_disp_data  = dsdisp+dscnt;

	if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) {
		mutex_exit(&xa->xa_mutex);
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) {
		mutex_exit(&xa->xa_mutex);
		smb_xa_close(xa);
		smbsr_error(sr, 0, ERRDOS, ERRbadformat);
		return (SDRC_ERROR);
	}
	mutex_exit(&xa->xa_mutex);

	if (!smb_trans_ready(xa))
		return (SDRC_NO_REPLY);

	if (!smb_xa_complete(xa))
		return (SDRC_NO_REPLY);

	return (smb_nt_trans_dispatch(sr, xa));
}

static int
smb_trans_ready(struct smb_xa *xa)
{
	int rc;

	mutex_enter(&xa->xa_mutex);
	rc = xa->req_disp_data >= xa->smb_tdscnt &&
	    xa->req_disp_param >= xa->smb_tpscnt;
	mutex_exit(&xa->xa_mutex);

	return (rc);
}

static void
smb_encode_SHARE_INFO_1(struct mbuf_chain *output, struct mbuf_chain *text,
    char *oem_name, uint16_t type, char *comment)
{
	(void) smb_mbc_encodef(output, "13c.wl", oem_name,
	    type, MBC_LENGTH(text));

	(void) smb_mbc_encodef(text, "s", comment ? comment : "");
}

static void
smb_encode_SHARE_INFO_2(struct mbuf_chain *output, struct mbuf_chain *text,
	smb_request_t *sr, char *oem_name, uint16_t type,
	char *comment, uint16_t access, char *path, char *password)
{
	unsigned char pword[9];

	bzero(pword, sizeof (pword));
	(void) strncpy((char *)pword, password, sizeof (pword));
	smb_encode_SHARE_INFO_1(output, text, oem_name, type, comment);
	(void) smb_mbc_encodef(output, "wwwl9c.",
	    access,
	    sr->sr_cfg->skc_maxconnections,
	    smb_server_get_session_count(),
	    MBC_LENGTH(text),
	    pword);
	(void) smb_mbc_encodef(text, "s", path);
}

int
smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa)
{
	door_handle_t dhdl = sr->sr_server->sv_lmshrd;

	/*
	 * Number of data bytes that will
	 * be sent in the current response
	 */
	uint16_t data_scnt;

	/*
	 * Total number of data bytes that
	 * are sent till now. This is only
	 * used for calculating current data
	 * displacement
	 */
	uint16_t tot_data_scnt;

	/*
	 * Number of parameter bytes should
	 * be sent for the current response.
	 * It is 8 for the 1st response and
	 * 0 for others
	 */
	uint16_t param_scnt;

	/* number of setup and parameter bytes */
	uint16_t n_setup, n_param;

	/* data and parameter displacement */
	uint16_t data_disp, param_disp;

	/* parameter and data offset and pad */
	int param_off, param_pad, data_off, data_pad;

	/*
	 * total bytes of parameters and data
	 * in the packet, plus the pad bytes.
	 */
	int tot_packet_bytes;

	boolean_t first_resp;

	char fmt[16];
	struct mbuf_chain reply;

	uint16_t level;
	uint16_t pkt_bufsize;
	smb_enumshare_info_t esi;
	char *sent_buf;

	ASSERT(sr->uid_user);

	/*
	 * Initialize the mbuf chain of reply to zero. If it is not
	 * zero, code inside the while loop will try to free the chain.
	 */
	bzero(&reply, sizeof (struct mbuf_chain));

	if (smb_mbc_decodef(&xa->req_param_mb, "ww", &level,
	    &esi.es_bufsize) != 0)
		return (SDRC_NOT_IMPLEMENTED);

	if (level != 1) {
		/*
		 * Only level 1 is valid for NetShareEnum
		 * None of the error codes in the spec are meaningful
		 * here. This error code is returned by Windows.
		 */
		(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
		    ERROR_INVALID_LEVEL, 0, 0, 0);
		return (SDRC_SUCCESS);
	}

	esi.es_buf = kmem_zalloc(esi.es_bufsize, KM_SLEEP);
	esi.es_username = sr->uid_user->u_name;
	(void) smb_kshare_enum(dhdl, &esi);

	/* client buffer size is not big enough to hold any shares */
	if (esi.es_nsent == 0) {
		(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
		    ERROR_MORE_DATA, 0, esi.es_nsent, esi.es_ntotal);
		kmem_free(esi.es_buf, esi.es_bufsize);
		return (SDRC_SUCCESS);
	}

	/*
	 * The rep_setup_mb is already initialized in smb_trans_dispatch().
	 * Calling MBC_INIT() will initialized the structure and so the
	 * pointer to the mbuf chains will be lost. Therefore, we need
	 * to free the resources before calling MBC_INIT() again.
	 */
	n_setup = 0;	/* Setup count for NetShareEnum SMB is 0 */
	m_freem(xa->rep_setup_mb.chain);
	MBC_INIT(&xa->rep_setup_mb, n_setup * 2);

	n_param = 8;
	pkt_bufsize = sr->session->smb_msg_size -
	    (SMB_HEADER_ED_LEN + RESP_HEADER_LEN + n_param);

	tot_data_scnt = 0;
	sent_buf = esi.es_buf;
	first_resp = B_TRUE;

	while (tot_data_scnt < esi.es_datasize) {
		data_scnt = esi.es_datasize - tot_data_scnt;
		if (data_scnt > pkt_bufsize)
			data_scnt = pkt_bufsize;
		m_freem(xa->rep_data_mb.chain);
		MBC_INIT(&xa->rep_data_mb, data_scnt);

		(void) sprintf(fmt, "%dc", data_scnt);
		(void) smb_mbc_encodef(&xa->rep_data_mb, fmt, sent_buf);

		sent_buf += data_scnt;
		tot_data_scnt += data_scnt;

		/* Only the 1st response packet contains parameters */
		param_scnt = (first_resp) ? n_param : 0;
		param_pad = 1;				/* always one */
		param_off = SMB_HEADER_ED_LEN + RESP_HEADER_LEN;
		param_disp = (first_resp) ? 0 : n_param;

		m_freem(xa->rep_param_mb.chain);
		MBC_INIT(&xa->rep_param_mb, param_scnt);

		if (first_resp) {
			first_resp = B_FALSE;
			(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
			    (esi.es_ntotal > esi.es_nsent)
			    ? ERROR_MORE_DATA : 0,
			    0, esi.es_nsent, esi.es_ntotal);
		}

		data_pad = (param_off + n_param) & 1;	/* Pad to short */

		/* data off from hdr start */
		data_off = param_off + param_scnt + data_pad;
		data_disp = tot_data_scnt - data_scnt;
		tot_packet_bytes = param_pad + param_scnt + data_pad +
		    data_scnt;

		/*
		 * Calling MBC_INIT() will initialized the structure and so the
		 * pointer to the mbuf chains will be lost. Therefore, we need
		 * to free the resources if any before calling MBC_INIT().
		 */
		m_freem(reply.chain);
		MBC_INIT(&reply, SMB_HEADER_ED_LEN
		    + sizeof (uint8_t)		/* word parameters count */
		    + 10*sizeof (uint16_t)	/* word parameters */
		    + n_setup*sizeof (uint16_t)	/* setup parameters */
		    + sizeof (uint16_t)		/* total data byte count */
		    + tot_packet_bytes);

		(void) smb_mbc_encodef(&reply, 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);

		(void) smb_mbc_encodef(&reply,
		    "bww2.wwwwwwb.Cw#.C#.C",
		    10 + n_setup,	/* wct */
		    n_param,		/* Total Parameter Bytes */
		    esi.es_datasize,	/* Total Data Bytes */
		    param_scnt,		/* Total Parameter Bytes this buffer */
		    param_off,		/* Param offset from header start */
		    param_disp,		/* Param displacement */
		    data_scnt,		/* Total Data Bytes this buffer */
		    data_off,		/* Data offset from header start */
		    data_disp,		/* Data displacement */
		    n_setup,		/* suwcnt */
		    &xa->rep_setup_mb, 	/* setup[] */
		    tot_packet_bytes,	/* Total data bytes */
		    param_pad,
		    &xa->rep_param_mb,
		    data_pad,
		    &xa->rep_data_mb);

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

		(void) smb_session_send(sr->session, 0, &reply);
	}

	kmem_free(esi.es_buf, esi.es_bufsize);
	return (SDRC_NO_REPLY);
}

int
smb_trans_net_share_getinfo(smb_request_t *sr, struct smb_xa *xa)
{
	uint16_t		level, max_bytes, access;
	struct mbuf_chain	str_mb;
	char			*share;
	char			*password;
	smb_share_t		si;
	int			rc;

	if (smb_mbc_decodef(&xa->req_param_mb, "%sww", sr,
	    &share, &level, &max_bytes) != 0)
		return (SDRC_NOT_IMPLEMENTED);

	(void) utf8_strlwr(share);
	rc = smb_kshare_getinfo(sr->sr_server->sv_lmshrd, share, &si, NULL);
	if ((rc != NERR_Success) || (si.shr_flags & SMB_SHRF_LONGNAME)) {
		(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
		    NERR_NetNameNotFound, 0, 0);
		return (SDRC_SUCCESS);
	}

	access = SHARE_ACCESS_ALL;
	password = "";

	MBC_INIT(&str_mb, max_bytes);

	switch (level) {
	case 0 :
		(void) smb_mbc_encodef(&xa->rep_data_mb, "13c", si.shr_oemname);
		break;

	case 1 :
		smb_encode_SHARE_INFO_1(&xa->rep_data_mb, &str_mb,
		    si.shr_oemname, si.shr_type, si.shr_cmnt);
		break;

	case 2 :
		smb_encode_SHARE_INFO_2(&xa->rep_data_mb, &str_mb, sr,
		    si.shr_oemname, si.shr_type, si.shr_cmnt, access,
		    si.shr_path, password);
		break;

	default:
		(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
		    ERROR_INVALID_LEVEL, 0, 0);
		m_freem(str_mb.chain);
		return (SDRC_NOT_IMPLEMENTED);
	}

	(void) smb_mbc_encodef(&xa->rep_param_mb, "www", NERR_Success,
	    -MBC_LENGTH(&xa->rep_data_mb),
	    MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&xa->rep_data_mb, "C", &str_mb);
	m_freem(str_mb.chain);
	return (SDRC_SUCCESS);
}

int
smb_trans_net_workstation_getinfo(struct smb_request *sr, struct smb_xa *xa)
{
	uint16_t		level, max_bytes;
	struct mbuf_chain	str_mb;
	char *domain;
	char *hostname;

	if ((smb_mbc_decodef(&xa->req_param_mb, "ww",
	    &level, &max_bytes) != 0) ||
	    (level != 10)) {
		(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
		    NERR_BadTransactConfig, 0, 0, 0);
		return (SDRC_SUCCESS);
	}

	domain = sr->sr_cfg->skc_nbdomain;
	hostname = sr->sr_cfg->skc_hostname;

	MBC_INIT(&str_mb, max_bytes);

	(void) smb_mbc_encodef(&str_mb, "."); /* Prevent NULL pointers */

	(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&str_mb, "s", hostname);
	(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&str_mb, "s", "nobody");
	(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&str_mb, "s", domain);
	(void) smb_mbc_encodef(&xa->rep_data_mb, "bbl",
	    SMB_VERSION_MAJOR, SMB_VERSION_MINOR, MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&str_mb, "s", domain);
	(void) smb_mbc_encodef(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&str_mb, "s", domain);

	(void) smb_mbc_encodef(&xa->rep_param_mb, "www", 0,
	    -MBC_LENGTH(&xa->rep_data_mb),
	    MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb));
	(void) smb_mbc_encodef(&xa->rep_data_mb, "C", &str_mb);
	m_freem(str_mb.chain);
	return (SDRC_SUCCESS);
}

int
smb_trans_net_user_getinfo(struct smb_request *sr, struct smb_xa *xa)
{
	uint16_t		level, max_bytes;
	unsigned char		*user;
	int rc;

	rc = smb_mbc_decodef(&xa->req_param_mb, "%sww", sr,
	    &user,
	    &level,
	    &max_bytes);

	if (rc != 0)
		return (SDRC_NOT_IMPLEMENTED);

	(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
	    NERR_UserNotFound, 0, 0);
	return (SDRC_SUCCESS);
}

smb_sdrc_t
smb_trans_net_server_getinfo(struct smb_request *sr, struct smb_xa *xa)
{
	uint16_t		level, buf_size;
	uint16_t		avail_data, max_data;
	char			server_name[16];
	struct mbuf_chain	str_mb;

	if (smb_mbc_decodef(&xa->req_param_mb, "ww", &level, &buf_size) != 0)
		return (SDRC_ERROR);

	max_data = MBC_MAXBYTES(&xa->rep_data_mb);

	MBC_INIT(&str_mb, buf_size);

	bzero(server_name, sizeof (server_name));
	(void) strncpy(server_name, sr->sr_cfg->skc_hostname,
	    sizeof (server_name));

	/* valid levels are 0 and 1 */
	switch (level) {
	case 0:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "16c", server_name);
		break;

	case 1:
		(void) smb_mbc_encodef(&str_mb, "s",
		    sr->sr_cfg->skc_system_comment);
		(void) smb_mbc_encodef(&xa->rep_data_mb, "16cbbll", server_name,
		    SMB_VERSION_MAJOR, SMB_VERSION_MINOR,
		    MY_SERVER_TYPE, max_data - MBC_LENGTH(&str_mb));
		break;

	default:
		(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
		    ERROR_INVALID_LEVEL, 0, 0);
		m_freem(str_mb.chain);
		return (SDRC_SUCCESS);
	}

	avail_data = MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb);
	(void) smb_mbc_encodef(&xa->rep_param_mb, "www",
	    NERR_Success, max_data - avail_data, avail_data);
	(void) smb_mbc_encodef(&xa->rep_data_mb, "C", &str_mb);
	m_freem(str_mb.chain);
	return (SDRC_SUCCESS);
}

/*
 * 6.4 The NetServerEnum2 RAP Service
 *
 * The NetServerEnum2 RAP service lists all computers of the specified type
 * or types that are visible in the specified domains. It may also
 * enumerate domains.
 *
 * The following definition uses the notation and terminology defined in
 * the CIFS Remote Administration Protocol specification, which is required
 * in order to make it well-defined. The definition is:
 *
 *     uint16_t NetServerEnum2 (
 *         uint16_t  sLevel,
 *         RCVBUF          pbBuffer,
 *         RCVBUFLEN       cbBuffer,
 *         ENTCOUNT        pcEntriesRead,
 *         uint16_t  *pcTotalAvail,
 *         uint32_t   fServerType,
 *         char            *pszDomain,
 *     );
 *
 * where:
 *
 *    sLevel specifies the level of detail (0 or 1) requested.
 *
 *    pbBuffer points to the buffer to receive the returned data. If the
 *    function is successful, the buffer contains a sequence of
 *    server_info_x structures, where x is 0 or 1, depending on the
 *    level of detail requested.
 *
 *    cbBuffer specifies the size, in bytes, of the buffer pointed to by
 *    the pbBuffer parameter.
 *
 *    pcEntriesRead points to a 16 bit variable that receives a count of
 *    the number of servers enumerated in the buffer. This count is
 *    valid only if NetServerEnum2 returns the NERR_Success or
 *    ERROR_MORE_DATA values.
 *
 *    pcTotal Avail points to a 16 bit variable that receives a count of
 *    the total number of available entries. This count is valid only if
 *    NetServerEnum2 returns the NERR_Success or ERROR_MORE_DATA values.
 *
 *     fServerType specifies the type or types of computers to enumerate.
 *     Computers that match at least one of the specified types are
 *     returned in the buffer. Possible values are defined in the request
 *     parameters section.
 *
 *    pszDomain points to a null-terminated string that contains the
 *    name of the workgroup in which to enumerate computers of the
 *    specified type or types. If the pszDomain parameter is a null
 *    string or a null pointer, servers are enumerated for the current
 *    domain of the computer.
 *
 * 6.4.1 Transaction Request Parameters section
 *
 * The Transaction request parameters section in this instance contains:
 * . The 16 bit function number for NetServerEnum2 which is 104.
 * . The parameter descriptor string which is "WrLehDz".
 * . The data descriptor string for the (returned) data which is "B16" for
 *   level detail 0 or "B16BBDz" for level detail 1.
 * . The actual parameters as described by the parameter descriptor
 *   string.
 *
 * The parameters are:
 * . A 16 bit integer with a value of 0 or 1 (corresponding to the "W" in
 *   the parameter descriptor string. This represents the level of detail
 *   the server is expected to return
 * . A 16 bit integer that contains the size of the receive buffer.
 * . A 32 bit integer that represents the type of servers the function
 *   should enumerate. The possible values may be any of the following or
 *   a combination of the following:
 *
 * SV_TYPE_WORKSTATION        0x00000001 All workstations
 * SV_TYPE_SERVER             0x00000002 All servers
 * SV_TYPE_SQLSERVER          0x00000004 Any server running with SQL
 *                                       server
 * SV_TYPE_DOMAIN_CTRL        0x00000008 Primary domain controller
 * SV_TYPE_DOMAIN_BAKCTRL     0x00000010 Backup domain controller
 * SV_TYPE_TIME_SOURCE        0x00000020 Server running the timesource
 *                                       service
 * SV_TYPE_AFP                0x00000040 Apple File Protocol servers
 * SV_TYPE_NOVELL             0x00000080 Novell servers
 * SV_TYPE_DOMAIN_MEMBER      0x00000100 Domain Member
 * SV_TYPE_PRINTQ_SERVER      0x00000200 Server sharing print queue
 * SV_TYPE_DIALIN_SERVER      0x00000400 Server running dialin service.
 * SV_TYPE_XENIX_SERVER       0x00000800 Xenix server
 * SV_TYPE_NT                 0x00001000 NT server
 * SV_TYPE_WFW                0x00002000 Server running Windows for
 *                                       Workgroups
 * SV_TYPE_SERVER_NT          0x00008000 Windows NT non DC server
 * SV_TYPE_POTENTIAL_BROWSER  0x00010000 Server that can run the browser
 *                                       service
 * SV_TYPE_BACKUP_BROWSER     0x00020000 Backup browser server
 * SV_TYPE_MASTER_BROWSER     0x00040000 Master browser server
 * SV_TYPE_DOMAIN_MASTER      0x00080000 Domain Master Browser server
 * SV_TYPE_LOCAL_LIST_ONLY    0x40000000 Enumerate only entries marked
 *                                       "local"
 * SV_TYPE_DOMAIN_ENUM        0x80000000 Enumerate Domains. The pszDomain
 *                                       parameter must be NULL.
 *
 * . A null terminated ASCII string representing the pszDomain parameter
 *   described above
 *
 * 6.4.2 Transaction Request Data section
 *
 * There is no data or auxiliary data to send as part of the request.
 *
 * 6.4.3 Transaction Response Parameters section
 *
 * The transaction response parameters section consists of:
 * . A 16 bit word indicating the return status. The possible values are:
 *
 * Code                   Value  Description
 * NERR_Success           0      No errors encountered
 * ERROR_MORE_DATA        234    Additional data is available
 * NERR_ServerNotStarted  2114   The RAP service on the remote computer
 *                               is not running
 * NERR_BadTransactConfig 2141   The server is not configured for
 *                               transactions, IPC$ is not shared
 *
 * . A 16 bit "converter" word.
 * . A 16 bit number representing the number of entries returned.
 * . A 16 bit number representing the total number of available entries.
 *   If the supplied buffer is large enough, this will equal the number of
 *   entries returned.
 *
 * 6.4.4 Transaction Response Data section
 *
 * The return data section consists of a number of SERVER_INFO_1 structures.
 * The number of such structures present is determined by the third entry
 * (described above) in the return parameters section.
 *
 * At level detail 0, the Transaction response data section contains a
 * number of SERVER_INFO_0 data structure. The number of such structures is
 * equal to the 16 bit number returned by the server in the third parameter
 * in the Transaction response parameter section. The SERVER_INFO_0 data
 * structure is defined as:
 *
 *     struct SERVER_INFO_0 {
 *         char        sv0_name[16];
 *     };
 *
 *  where:
 *
 *    sv0_name is a null-terminated string that specifies the name of a
 *    computer or domain .
 *
 * At level detail 1, the Transaction response data section contains a
 * number of SERVER_INFO_1 data structure. The number of such structures is
 * equal to the 16 bit number returned by the server in the third parameter
 * in the Transaction response parameter section. The SERVER_INFO_1 data
 * structure is defined as:
 *
 *     struct SERVER_INFO_1 {
 *         char            sv1_name[16];
 *         char            sv1_version_major;
 *         char            sv1_version_minor;
 *         uint32_t   sv1_type;
 *         char        *sv1_comment_or_master_browser;
 *     };
 *
 *    sv1_name contains a null-terminated string that specifies the name
 *    of a computer, or a domain name if SV_TYPE_DOMAIN_ENUM is set in
 *    sv1_type.
 *
 *    sv1_version_major whatever was specified in the HostAnnouncement
 *    or DomainAnnouncement frame with which the entry was registered.
 *
 *    sv1_version_minor whatever was specified in the HostAnnouncement
 *    or DomainAnnouncement frame with which the entry was registered.
 *
 *    sv1_type specifies the type of software the computer is running.
 *    The member can be one or a combination of the values defined above
 *    in the Transaction request parameters section for fServerType.
 *
 *
 *    sv1_comment_or_master_browser points to a null-terminated string. If
 *    the sv1_type indicates that the entry is for a domain, this
 *    specifies the name of server running the domain master browser;
 *    otherwise, it specifies a comment describing the server. The comment
 *    can be a null string or the pointer may be a null pointer.
 *
 *    In case there are multiple SERVER_INFO_1 data structures to
 *    return, the server may put all these fixed length structures in
 *    the return buffer, leave some space and then put all the variable
 *    length data (the actual value of the sv1_comment strings) at the
 *    end of the buffer.
 *
 * There is no auxiliary data to receive.
 */

int
smb_trans_net_server_enum2(struct smb_request *sr, struct smb_xa *xa)
{
	uint16_t opcode, level, max_bytes;
	uint32_t server_type;
	unsigned char *domain;
	struct mbuf_chain str_mb;
	char *hostname, *s;
	smb_kmod_cfg_t *si;

	if (smb_mbc_decodef(&xa->req_param_mb,
	    "%wsswwls", sr, &opcode, &s, &s,
	    &level, &max_bytes, &server_type, &domain) != 0)
		return (SDRC_NOT_IMPLEMENTED);

	si = sr->sr_cfg;

	if (utf8_strcasecmp(si->skc_nbdomain, (char *)domain) != 0) {
		(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", 0, 0, 0, 0);
		return (SDRC_SUCCESS);
	}

	if ((server_type & MY_SERVER_TYPE) == 0) {
		(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", 0, 0, 0, 0);
		return (SDRC_SUCCESS);
	}

	MBC_INIT(&str_mb, max_bytes);

	hostname = si->skc_hostname;

	(void) smb_mbc_encodef(&xa->rep_data_mb, "16c", hostname);
	if (level == 1) {
		(void) smb_mbc_encodef(&xa->rep_data_mb, "bbll",
		    SMB_VERSION_MAJOR, SMB_VERSION_MINOR,
		    MY_SERVER_TYPE, MBC_LENGTH(&str_mb));
		(void) smb_mbc_encodef(&str_mb, "s", si->skc_system_comment);
	}

	(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", 0,
	    -MBC_LENGTH(&xa->rep_data_mb), 1, 1);
	(void) smb_mbc_encodef(&xa->rep_data_mb, "m", str_mb.chain);
	return (SDRC_SUCCESS);
}

/*
 * is_supported_pipe
 *
 * Currently, just return 0 if the pipe is \\PIPE\repl otherwise
 * return 1.
 */
int
is_supported_pipe(char *pname)
{
	if (utf8_strcasecmp(pname, PIPE_REPL) == 0)
		return (0);

	return (1);
}

static smb_sdrc_t
smb_trans_dispatch(struct smb_request *sr, struct smb_xa *xa)
{
	int		rc, pos;
	int		total_bytes, n_setup, n_param, n_data;
	int		param_off, param_pad, data_off, data_pad;
	uint16_t	opcode;
	uint16_t	devstate;
	char		*req_fmt;
	char		*rep_fmt;
	smb_vdb_t	vdb;

	n_setup = (xa->smb_msrcnt < 200) ? xa->smb_msrcnt : 200;
	n_setup++;
	n_setup = n_setup & ~0x0001;
	n_param = (xa->smb_mprcnt < smb_maxbufsize)
	    ? xa->smb_mprcnt : smb_maxbufsize;
	n_param++;
	n_param = n_param & ~0x0001;
	rc = smb_maxbufsize - (SMBHEADERSIZE + 28 + n_setup + n_param);
	n_data =  (xa->smb_mdrcnt < rc) ? xa->smb_mdrcnt : rc;
	MBC_INIT(&xa->rep_setup_mb, n_setup * 2);
	MBC_INIT(&xa->rep_param_mb, n_param);
	MBC_INIT(&xa->rep_data_mb, n_data);

	if (xa->smb_suwcnt > 0 && STYPE_ISIPC(sr->tid_tree->t_res_type)) {
		rc = smb_mbc_decodef(&xa->req_setup_mb, "ww", &opcode,
		    &sr->smb_fid);
		if (rc != 0)
			goto trans_err_not_supported;
		switch (opcode) {
		case TRANS_SET_NMPIPE_STATE:
			if ((rc = smb_mbc_decodef(&xa->req_param_mb, "w",
			    &devstate)) != 0)
				goto trans_err_not_supported;

			rc = SDRC_SUCCESS;
			break;

		case TRANS_TRANSACT_NMPIPE:
			smbsr_lookup_file(sr);
			if (sr->fid_ofile == NULL) {
				smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
				    ERRDOS, ERRbadfid);
				return (SDRC_ERROR);
			}

			rc = smb_mbc_decodef(&xa->req_data_mb, "#B",
			    xa->smb_tdscnt, &vdb);
			if (rc != 0)
				goto trans_err_not_supported;

			rc = smb_opipe_transact(sr, &vdb.vdb_uio);
			break;

		case TRANS_WAIT_NMPIPE:
			if (is_supported_pipe(xa->xa_smb_trans_name) == 0) {
				smbsr_error(sr, 0, ERRDOS, ERRbadfile);
				return (SDRC_ERROR);
			}
			rc = SDRC_SUCCESS;
			break;

		default:
			goto trans_err_not_supported;
		}
	} else {
		if ((utf8_strcasecmp(xa->xa_smb_trans_name,
		    PIPE_LANMAN) != 0) &&
		    (utf8_strcasecmp(
		    xa->xa_smb_trans_name, MAILSLOT_LANMAN) != 0) &&
		    (utf8_strcasecmp(
		    xa->xa_smb_trans_name, MAILSLOT_BROWSE) != 0) &&
		    (utf8_strcasecmp(
		    xa->xa_smb_trans_name, MAILSLOT_MSBROWSE) != 0))
			goto trans_err_not_supported;

		if ((rc = smb_mbc_decodef(&xa->req_param_mb, "%wss", sr,
		    &opcode, &req_fmt, &rep_fmt)) != 0)
			goto trans_err_not_supported;

		switch (opcode) {
		case API_WshareEnum:
			rc = smb_trans_net_share_enum(sr, xa);
			break;

		case API_WshareGetInfo:
			rc = smb_trans_net_share_getinfo(sr, xa);
			break;

		case API_WserverGetInfo:
			rc = smb_trans_net_server_getinfo(sr, xa);
			break;

		case API_WUserGetInfo:
			rc = smb_trans_net_user_getinfo(sr, xa);
			break;

		case API_WWkstaGetInfo:
			rc = smb_trans_net_workstation_getinfo(sr, xa);
			break;

		case API_NetServerEnum2:
			rc = smb_trans_net_server_enum2(sr, xa);
			break;

		default:
			goto trans_err_not_supported;
		}
	}

	switch (rc) {
	case SDRC_SUCCESS:
		break;

	case SDRC_DROP_VC:
	case SDRC_NO_REPLY:
	case SDRC_ERROR:
		return (rc);

	case SDRC_NOT_IMPLEMENTED:
		goto trans_err_not_supported;

	default:
		break;
	}

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

	if (xa->smb_msrcnt < n_setup ||
	    xa->smb_mprcnt < n_param ||
	    xa->smb_mdrcnt < n_data) {
		goto trans_err_too_small;
	}

	/* neato, blast it over there */

	n_setup = (n_setup + 1) / 2;		/* Convert to setup words */
	param_pad = 1;				/* always one */
	param_off = param_pad + 32 + 21 + (n_setup << 1) + 2;
	data_pad = (param_off + n_param) & 1;	/* Pad to short */
	/* Param off from hdr start */
	data_off = param_off + n_param + data_pad;
	total_bytes = param_pad + n_param + data_pad + n_data;

	rc = smbsr_encode_result(sr, 10+n_setup, total_bytes,
	    "bww2.wwwwwwb.Cw#.C#.C",
	    10 + 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);
	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);

trans_err_too_small:
	rc = NERR_BufTooSmall;
	goto trans_err;

trans_err_not_supported:
	rc = ERROR_NOT_SUPPORTED;
	goto trans_err;

trans_err:
	pos = MBC_LENGTH(&sr->reply) + 23;
	rc = smbsr_encode_result(sr, 10, 4, "bww2.wwwwwwb.www",
	    10,		/* wct */
	    4, 0,	/* tpscnt tdscnt */
	    4, pos, 0,	/* pscnt psoff psdisp */
	    0, 0, 0,	/* dscnt dsoff dsdisp */
	    0,		/* suwcnt */
	    4,		/* bcc */
	    rc,
	    0);		/* converter word? */
	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}

static smb_sdrc_t
smb_trans2_dispatch(struct smb_request *sr, struct smb_xa *xa)
{
	int		rc, pos;
	int		total_bytes, n_setup, n_param, n_data;
	int		param_off, param_pad, data_off, data_pad;
	uint16_t	opcode;
	uint16_t  nt_unknown_secret = 0x0100;
	char *fmt;

	n_setup = (xa->smb_msrcnt < 200) ? xa->smb_msrcnt : 200;
	n_setup++;
	n_setup = n_setup & ~0x0001;
	n_param = (xa->smb_mprcnt < smb_maxbufsize)
	    ? xa->smb_mprcnt : smb_maxbufsize;
	n_param++;
	n_param = n_param & ~0x0001;
	rc = smb_maxbufsize - (SMBHEADERSIZE + 28 + n_setup + n_param);
	n_data =  (xa->smb_mdrcnt < rc) ? xa->smb_mdrcnt : rc;
	MBC_INIT(&xa->rep_setup_mb, n_setup * 2);
	MBC_INIT(&xa->rep_param_mb, n_param);
	MBC_INIT(&xa->rep_data_mb, n_data);

	if (smb_mbc_decodef(&xa->req_setup_mb, "w", &opcode) != 0)
		goto trans_err_not_supported;

	/*
	 * Save this for /proc to read later.
	 */
	xa->smb_func = opcode;

	/* for now, only respond to the */
	switch (opcode) {
	case TRANS2_OPEN2:
		rc = smb_com_trans2_open2(sr, xa);
		break;

	case TRANS2_CREATE_DIRECTORY:
		rc = smb_com_trans2_create_directory(sr, xa);
		break;

	case TRANS2_FIND_FIRST2:
		/*
		 * Should have enough room to send the response
		 * data back to client.
		 */
		if (n_data == 0) {
			smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
			    ERRDOS, ERROR_BAD_LENGTH);
			return (SDRC_ERROR);
		}
		rc = smb_com_trans2_find_first2(sr, xa);
		break;

	case TRANS2_FIND_NEXT2:
		/*
		 * Should have enough room to send the response
		 * data back to client.
		 */
		if (n_data == 0) {
			smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
			    ERRDOS, ERROR_BAD_LENGTH);
			return (SDRC_ERROR);
		}
		rc = smb_com_trans2_find_next2(sr, xa);
		break;

	case TRANS2_QUERY_FS_INFORMATION:
		/*
		 * Should have enough room to send the response
		 * data back to client.
		 */
		if (n_data == 0) {
			smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
			    ERRDOS, ERROR_BAD_LENGTH);
			return (SDRC_ERROR);
		}
		rc = smb_com_trans2_query_fs_information(sr, xa);
		break;

	case TRANS2_QUERY_PATH_INFORMATION:
		/*
		 * Should have enough room to send the response
		 * data back to client.
		 */
		if (n_data == 0) {
			smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
			    ERRDOS, ERROR_BAD_LENGTH);
			return (SDRC_ERROR);
		}
		rc = smb_com_trans2_query_path_information(sr, xa);
		break;

	case TRANS2_QUERY_FILE_INFORMATION:
		/*
		 * Should have enough room to send the response
		 * data back to client.
		 */
		if (n_data == 0) {
			smbsr_error(sr, NT_STATUS_INFO_LENGTH_MISMATCH,
			    ERRDOS, ERROR_BAD_LENGTH);
			return (SDRC_ERROR);
		}
		rc = smb_com_trans2_query_file_information(sr, xa);
		break;

	case TRANS2_SET_PATH_INFORMATION:
		rc = smb_com_trans2_set_path_information(sr, xa);
		break;

	case TRANS2_SET_FILE_INFORMATION:
		rc = smb_com_trans2_set_file_information(sr, xa);
		break;
	default:
		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
		goto trans_err_not_supported;
	}

	switch (rc) {
	case SDRC_SUCCESS:
		break;

	case SDRC_DROP_VC:
	case SDRC_NO_REPLY:
	case SDRC_ERROR:
		return (rc);

	case SDRC_NOT_IMPLEMENTED:
		goto trans_err_not_supported;

	default:
		break;
	}

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

	if (xa->smb_msrcnt < n_setup ||
	    xa->smb_mprcnt < n_param ||
	    xa->smb_mdrcnt < n_data) {
		goto trans_err_too_small;
	}

	/* neato, blast it over there */

	n_setup = (n_setup + 1) / 2;		/* Conver to setup words */
	param_pad = 1;				/* must be one */
	param_off = param_pad + 32 + 21 + (n_setup << 1) + 2;

	/*
	 * Including the nt_unknown_secret value persuades netmon to
	 * display the correct data format for QueryPathInfo and
	 * QueryFileInfo.
	 */
	if (opcode == TRANS2_QUERY_FILE_INFORMATION ||
	    opcode == TRANS2_QUERY_PATH_INFORMATION) {
		data_pad = sizeof (uint16_t);
		data_off = param_off + n_param + data_pad;
		fmt = "bww2.wwwwwwb.Cw#.CwC";
		nt_unknown_secret = 0x0100;
	}
	else
	{
		data_pad = (param_off + n_param) & 1; /* Pad to short */
		/* Param off from hdr start */
		data_off = param_off + n_param + data_pad;
		fmt = "bww2.wwwwwwb.Cw#.C#.C";
		/*LINTED E_ASSIGN_NARROW_CONV*/
		nt_unknown_secret = data_pad;
	}

	total_bytes = param_pad + n_param + data_pad + n_data;

	rc = smbsr_encode_result(sr, 10+n_setup, total_bytes,
	    fmt,
	    10 + n_setup,		/* wct */
	    n_param,			/* Total Parameter Bytes */
	    n_data /* + data_pad */,	/* Total Data Bytes */
	    n_param,			/* Total Parameter Bytes this buffer */
	    param_off,			/* Param offset from header start */
	    0,				/* Param displacement */
	    n_data /* + data_pad */,	/* 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,
	    nt_unknown_secret,
	    &xa->rep_data_mb);
	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);

trans_err_too_small:
	rc = NERR_BufTooSmall;
	goto trans_err;

trans_err_not_supported:
	rc = ERROR_NOT_SUPPORTED;
	goto trans_err;

trans_err:
	pos = MBC_LENGTH(&sr->reply) + 23;
	rc = smbsr_encode_result(sr, 10, 4, "bww2.wwwwwwb.www",
	    10,		/* wct */
	    4, 0,	/* tpscnt tdscnt */
	    4, pos, 0,	/* pscnt psoff psdisp */
	    0, 0, 0,	/* dscnt dsoff dsdisp */
	    0,		/* suwcnt */
	    4,		/* bcc */
	    rc,
	    0);		/* converter word? */
	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}

smb_xa_t *
smb_xa_create(
    smb_session_t	*session,
    smb_request_t	*sr,
    uint32_t		total_parameter_count,
    uint32_t		total_data_count,
    uint32_t		max_parameter_count,
    uint32_t		max_data_count,
    uint32_t		max_setup_count,
    uint32_t		setup_word_count)
{
	smb_xa_t	*xa, *nxa;
	smb_llist_t	*xlist;

	xa = MEM_ZALLOC("xa", sizeof (smb_xa_t));
	xa->xa_refcnt = 1;
	xa->smb_com = sr->smb_com;
	xa->smb_flg = sr->smb_flg;
	xa->smb_flg2 = sr->smb_flg2;
	xa->smb_tid = sr->smb_tid;
	xa->smb_pid = sr->smb_pid;
	xa->smb_uid = sr->smb_uid;
	xa->xa_smb_mid = sr->smb_mid;
	xa->reply_seqnum = sr->reply_seqnum;
	xa->smb_tpscnt = total_parameter_count;
	xa->smb_tdscnt = total_data_count;
	xa->smb_mprcnt = max_parameter_count;
	xa->smb_mdrcnt = max_data_count;
	xa->smb_msrcnt = max_setup_count;
	xa->smb_suwcnt = setup_word_count;
	xa->xa_session = session;
	xa->xa_magic = SMB_XA_MAGIC;

	/*
	 * The new xa structure is checked against the current list to see
	 * if it exists already.
	 */
	xlist = &session->s_xa_list;
	smb_llist_enter(xlist, RW_WRITER);
	nxa = smb_llist_head(xlist);
	while (nxa) {
		ASSERT(nxa->xa_magic == SMB_XA_MAGIC);
		if (nxa->xa_smb_mid == xa->xa_smb_mid &&
		    nxa->smb_pid == xa->smb_pid &&
		    !SMB_XA_CLOSED(nxa) &&
		    !(nxa->xa_flags & SMB_XA_FLAG_COMPLETE)) {
			smb_llist_exit(xlist);
			MEM_FREE("xa", xa);
			return (NULL);
		}
		nxa = smb_llist_next(xlist, nxa);
	}
	smb_llist_insert_tail(xlist, xa);
	smb_llist_exit(xlist);
	return (xa);
}

void
smb_xa_delete(smb_xa_t *xa)
{
	ASSERT(xa->xa_refcnt == 0);
	ASSERT(SMB_XA_CLOSED(xa));

	if (xa->xa_smb_trans_name)
		MEM_FREE("smb", xa->xa_smb_trans_name);

	if (xa->rep_setup_mb.chain != NULL)
		m_freem(xa->rep_setup_mb.chain);
	if (xa->rep_param_mb.chain != NULL)
		m_freem(xa->rep_param_mb.chain);
	if (xa->rep_data_mb.chain != NULL)
		m_freem(xa->rep_data_mb.chain);

	xa->xa_magic = (uint32_t)~SMB_XA_MAGIC;
	MEM_FREE("xa", xa);
}

smb_xa_t *
smb_xa_hold(smb_xa_t *xa)
{
	mutex_enter(&xa->xa_mutex);
	xa->xa_refcnt++;
	ASSERT(xa->xa_refcnt);
	mutex_exit(&xa->xa_mutex);
	return (xa);
}

void
smb_xa_rele(smb_session_t *session, smb_xa_t *xa)
{
	mutex_enter(&xa->xa_mutex);
	ASSERT(xa->xa_refcnt);
	xa->xa_refcnt--;
	if (SMB_XA_CLOSED(xa) && (xa->xa_refcnt == 0)) {
		mutex_exit(&xa->xa_mutex);
		smb_llist_enter(&session->s_xa_list, RW_WRITER);
		smb_llist_remove(&session->s_xa_list, xa);
		smb_llist_exit(&session->s_xa_list);
		smb_xa_delete(xa);
		return;
	}
	mutex_exit(&xa->xa_mutex);
}

int
smb_xa_open(smb_xa_t *xa)
{
	int rc;

	mutex_enter(&xa->xa_mutex);

	ASSERT((xa->xa_flags & SMB_XA_FLAG_OPEN) == 0);

	if ((xa->xa_flags & SMB_XA_FLAG_CLOSE) == 0) {
		xa->xa_flags |= SMB_XA_FLAG_OPEN;
		rc = 0;
	} else {
		rc = ERROR_INVALID_HANDLE;
	}

	mutex_exit(&xa->xa_mutex);

	return (rc);
}

void
smb_xa_close(smb_xa_t *xa)
{
	mutex_enter(&xa->xa_mutex);
	xa->xa_flags |= SMB_XA_FLAG_CLOSE;
	xa->xa_flags &= ~SMB_XA_FLAG_OPEN;

	if (xa->xa_refcnt == 0) {
		mutex_exit(&xa->xa_mutex);
		smb_llist_enter(&xa->xa_session->s_xa_list, RW_WRITER);
		smb_llist_remove(&xa->xa_session->s_xa_list, xa);
		smb_llist_exit(&xa->xa_session->s_xa_list);
		smb_xa_delete(xa);
		return;
	}

	mutex_exit(&xa->xa_mutex);
}

int
smb_xa_complete(smb_xa_t *xa)
{
	int rc;

	mutex_enter(&xa->xa_mutex);
	if (xa->xa_flags & (SMB_XA_FLAG_COMPLETE | SMB_XA_FLAG_CLOSE)) {
		rc = 0;
	} else {
		rc = 1;
		xa->xa_flags |= SMB_XA_FLAG_COMPLETE;
	}
	mutex_exit(&xa->xa_mutex);
	return (rc);
}

smb_xa_t *
smb_xa_find(
    smb_session_t	*session,
    uint16_t		pid,
    uint16_t		mid)
{
	smb_xa_t	*xa;
	smb_llist_t	*xlist;

	xlist = &session->s_xa_list;
	smb_llist_enter(xlist, RW_READER);
	xa = smb_llist_head(xlist);
	while (xa) {
		mutex_enter(&xa->xa_mutex);
		if (xa->xa_smb_mid == mid &&
		    xa->smb_pid == pid &&
		    !SMB_XA_CLOSED(xa) &&
		    !(xa->xa_flags & SMB_XA_FLAG_COMPLETE)) {
			xa->xa_refcnt++;
			ASSERT(xa->xa_refcnt);
			mutex_exit(&xa->xa_mutex);
			break;
		}
		mutex_exit(&xa->xa_mutex);
		xa = smb_llist_next(xlist, xa);
	}
	smb_llist_exit(xlist);
	return (xa);
}