Example #1
0
/*
 * prep_smb_transact
 *
 * Prepare the SMB_COM_TRANSACTION request.
 */
static int
prep_smb_transact(smb_msgbuf_t *mb, unsigned short fid, char *out,
    unsigned short out_len, unsigned short in_max, unsigned unicode)
{
	int data_off;
	int rc;
	unsigned short bcc;

	/*
	 * The byte count seems to include the pad
	 * byte to word align the filename and two
	 * spurious pad bytes between the filename
	 * and the transaction data.
	 */
	bcc = out_len + 3;
	bcc += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN;

	data_off  = 32;		/* sizeof SMB header up to smb_wct */
	data_off += 1;		/* sizeof smb_wct */
	data_off += 16*2;	/* sizeof word parameters */
	data_off += 2;		/* sizeof smb_bcc */
	data_off += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN;
	data_off += 3;
	/* this is where data starts */

	rc = smb_msgbuf_encode(mb,
	    "(wct)b"
	    "(tpscnt)w (tdscnt)w (mprcnt)w (mdrcnt)w (msrcnt)b"
	    "(rsvd). (flags)w (timeo)l  (rsvd1)2."
	    "(pscnt)w (psoff)w (dscnt)w (dsoff)w (suwcnt)b"
	    "(rsvd2). (pipop)w (fid)w (bcc)w (fname)u",
	    16,				/* smb_wct */
	    0,				/* total parm bytes */
	    out_len,			/* total data bytes */
	    0,				/* max parm bytes to ret */
	    in_max,			/* max data bytes to ret */
	    0,				/* max setup words to ret */
	    0,				/* transact flags */
	    0,				/* transact timeout */
	    0,				/* parameter bytes */
	    data_off,			/* parameter offset */
	    out_len,			/* data bytes */
	    data_off,			/* data offset */
	    2,				/* total setup words */
	    0x0026,			/* OP=TransactNmPipe */
	    fid,			/* FID */
	    bcc,			/* byte count */
	    TX_FILENAME);		/* file name */

	/*
	 * Transaction data - padded.
	 */
	rc = smb_msgbuf_encode(mb, "..#c", out_len, out);
	return (rc);
}
Example #2
0
/*
 * smbrdr_hdr_setup
 *
 * Build an SMB header based on the information in the given handle.
 * The SMB header is described in section 3.2 of the CIFS spec.
 * As this is a canned function, no error checking is performed here.
 * The return value from smb_msgbuf_encode is simply returned to the caller.
 */
static int
smbrdr_hdr_setup(smbrdr_handle_t *srh)
{
	static unsigned short my_pid = 0;

	if (!my_pid)
		my_pid = getpid();

	return (smb_msgbuf_encode(&srh->srh_mbuf, "Mb4.bw12.wwww",
	    srh->srh_cmd,
	    srh->srh_session->smb_flags,
	    srh->srh_session->smb_flags2,
	    (srh->srh_tree) ? srh->srh_tree->tid : 0,
	    my_pid,
	    (srh->srh_user) ? srh->srh_user->uid : 0,
	    0 /* mid */));
}
Example #3
0
/*
 * smb_trans2_mbc_encode
 *
 * This function encodes the mbc for one directory entry.
 *
 * The function returns -1 when the max data requested by client
 * is reached. If the entry is valid and successful encoded, 0
 * will be returned; otherwise, 1 will be returned.
 *
 * We always null terminate the filename. The space for the null
 * is included in the maxdata calculation and is therefore included
 * in the next_entry_offset. namelen is the unterminated length of
 * the filename. For levels except STANDARD and EA_SIZE, if the
 * filename is ascii the name length returned to the client should
 * include the null terminator. Otherwise the length returned to
 * the client should not include the terminator.
 *
 * Returns: 0 - data successfully encoded
 *          1 - client request's maxdata limit reached
 *	   -1 - error
 */
static int
smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
    smb_fileinfo_t *fileinfo, smb_find_args_t *args)
{
	int		namelen, shortlen;
	uint32_t	next_entry_offset;
	uint32_t	dsize32, asize32;
	uint32_t	mb_flags = 0;
	uint32_t	resume_key;
	char		buf83[26];
	smb_msgbuf_t	mb;

	namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
	if (namelen == -1)
		return (-1);

	/*
	 * If ascii the filename length returned to the client should
	 * include the null terminator for levels except STANDARD and
	 * EASIZE.
	 */
	if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
		if ((args->fa_infolev != SMB_INFO_STANDARD) &&
		    (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
			namelen += 1;
	}

	next_entry_offset = args->fa_maxdata + namelen;

	if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0)
		return (1);

	mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
	dsize32 = (fileinfo->fi_size > UINT_MAX) ?
	    UINT_MAX : (uint32_t)fileinfo->fi_size;
	asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ?
	    UINT_MAX : (uint32_t)fileinfo->fi_alloc_size;

	resume_key = fileinfo->fi_cookie;
	if (smbd_use_resume_keys == 0)
		resume_key = 0;

	/*
	 * This switch handles all the "information levels" (formats)
	 * that we support.  Note that all formats have the file name
	 * placed after some fixed-size data, and the code to write
	 * the file name is factored out at the end of this switch.
	 */
	switch (args->fa_infolev) {
	case SMB_INFO_STANDARD:
		if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
			(void) smb_mbc_encodef(&xa->rep_data_mb, "l",
			    resume_key);

		(void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwb", sr,
		    smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
		    dsize32,
		    asize32,
		    fileinfo->fi_dosattr,
		    namelen);
		break;

	case SMB_INFO_QUERY_EA_SIZE:
		if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
			(void) smb_mbc_encodef(&xa->rep_data_mb, "l",
			    resume_key);

		(void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr,
		    smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
		    dsize32,
		    asize32,
		    fileinfo->fi_dosattr,
		    0L,		/* EA Size */
		    namelen);
		break;

	case SMB_FIND_FILE_DIRECTORY_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqll", sr,
		    next_entry_offset,
		    resume_key,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen);
		break;

	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll", sr,
		    next_entry_offset,
		    resume_key,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L);
		break;

	case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.q", sr,
		    next_entry_offset,
		    resume_key,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    fileinfo->fi_nodeid);
		break;

	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
		bzero(buf83, sizeof (buf83));
		smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
		    mb_flags);
		if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) {
			smb_msgbuf_term(&mb);
			return (-1);
		}
		shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);

		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c",
		    sr,
		    next_entry_offset,
		    resume_key,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    shortlen,
		    buf83);

		smb_msgbuf_term(&mb);
		break;

	case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
		bzero(buf83, sizeof (buf83));
		smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
		    mb_flags);
		if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) {
			smb_msgbuf_term(&mb);
			return (-1);
		}
		shortlen = smb_ascii_or_unicode_strlen(sr,
		    fileinfo->fi_shortname);

		(void) smb_mbc_encodef(&xa->rep_data_mb,
		    "%llTTTTqqlllb.24c2.q",
		    sr,
		    next_entry_offset,
		    resume_key,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    shortlen,
		    buf83,
		    fileinfo->fi_nodeid);

		smb_msgbuf_term(&mb);
		break;

	case SMB_FIND_FILE_NAMES_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%lll", sr,
		    next_entry_offset,
		    resume_key,
		    namelen);
		break;

	default:
		/* invalid info. level */
		return (-1);
	}

	/*
	 * At this point we have written all the fixed-size data
	 * for the specified info. level, and we're about to put
	 * the file name string in the message.  We may later
	 * need the offset in the trans2 data where this string
	 * is placed, so save the message position now.  Note:
	 * We also need to account for the alignment padding
	 * that may precede the unicode string.
	 */
	args->fa_lno = xa->rep_data_mb.chain_offset;
	if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 &&
	    (args->fa_lno & 1) != 0)
		args->fa_lno++;

	(void) smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
	    fileinfo->fi_name);

	return (0);
}
Example #4
0
smb_sdrc_t
smb_com_negotiate(smb_request_t *sr)
{
	smb_session_t 		*session = sr->session;
	smb_arg_negotiate_t	*negprot = sr->sr_negprot;
	uint16_t		secmode;
	uint32_t		sesskey;
	char			*nbdomain;
	uint8_t			*wcbuf;
	int			wclen;
	smb_msgbuf_t		mb;
	int			rc;

	if (session->s_state != SMB_SESSION_STATE_ESTABLISHED) {
		/* The protocol has already been negotiated. */
		smbsr_error(sr, 0, ERRSRV, ERRerror);
		return (SDRC_ERROR);
	}

	/*
	 * Special case for negotiating SMB2 from SMB1.  The client
	 * includes the  "SMB 2..." dialects in the SMB1 negotiate,
	 * and if SMB2 is enabled, we choose one of those and then
	 * send an SMB2 reply to that SMB1 request.  Yes, it's very
	 * strange, but this SMB1 request can have an SMB2 reply!
	 * To accomplish this, we let the SMB2 code send the reply
	 * and return the special code SDRC_NO_REPLY to the SMB1
	 * dispatch logic so it will NOT send an SMB1 reply.
	 * (Or possibly send an SMB1 error reply.)
	 */
	if (negprot->ni_dialect >= DIALECT_SMB2002) {
		rc = smb1_negotiate_smb2(sr);
		ASSERT(rc == SDRC_NO_REPLY ||
		    rc == SDRC_DROP_VC || rc == SDRC_ERROR);
		return (rc);
	}

	session->secmode = NEGOTIATE_ENCRYPT_PASSWORDS |
	    NEGOTIATE_USER_SECURITY;
	secmode = session->secmode;
	sesskey = session->sesskey;

	negprot->ni_servertime.tv_sec = gethrestime_sec();
	negprot->ni_servertime.tv_nsec = 0;
	negprot->ni_tzcorrection = sr->sr_gmtoff / 60;
	negprot->ni_maxmpxcount = sr->sr_cfg->skc_maxworkers;
	negprot->ni_keylen = SMB_CHALLENGE_SZ;
	bcopy(&session->challenge_key, negprot->ni_key, SMB_CHALLENGE_SZ);
	nbdomain = sr->sr_cfg->skc_nbdomain;

	/*
	 * UNICODE support is required for long share names,
	 * long file names and streams.  Note: CAP_RAW_MODE
	 * is not supported because it does nothing to help
	 * modern clients and causes nasty complications.
	 */
	negprot->ni_capabilities = CAP_LARGE_FILES
	    | CAP_UNICODE
	    | CAP_NT_SMBS
	    | CAP_STATUS32
	    | CAP_NT_FIND
	    | CAP_LEVEL_II_OPLOCKS
	    | CAP_LOCK_AND_READ
	    | CAP_RPC_REMOTE_APIS
	    | CAP_LARGE_READX
	    | CAP_LARGE_WRITEX
	    | CAP_DFS;

	if (smb_cap_passthru)
		negprot->ni_capabilities |= CAP_INFOLEVEL_PASSTHRU;
	else
		cmn_err(CE_NOTE, "smbsrv: cap passthru is %s",
		    (negprot->ni_capabilities & CAP_INFOLEVEL_PASSTHRU) ?
		    "enabled" : "disabled");

	switch (negprot->ni_dialect) {
	case PC_NETWORK_PROGRAM_1_0:	/* core */
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
		    sizeof (smb_dos_tcp_rcvbuf), CRED());
		rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
		    negprot->ni_index, 0);
		break;

	case Windows_for_Workgroups_3_1a:
	case PCLAN1_0:
	case MICROSOFT_NETWORKS_1_03:
	case MICROSOFT_NETWORKS_3_0:
	case LANMAN1_0:
	case LM1_2X002:
	case DOS_LM1_2X002:
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
		    sizeof (smb_dos_tcp_rcvbuf), CRED());
		sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK;
		rc = smbsr_encode_result(sr, 13, VAR_BCC,
		    "bwwwwwwlYww2.w#c",
		    13,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    SMB_DOS_MAXBUF,		/* max buffer size */
		    1,				/* max MPX */
		    1,				/* max VCs */
		    0,				/* read/write raw */
		    sesskey,			/* session key */
		    negprot->ni_servertime.tv_sec, /* server date/time */
		    negprot->ni_tzcorrection,
		    (uint16_t)negprot->ni_keylen, /* encryption key length */
						/* reserved field handled 2. */
		    VAR_BCC,
		    (int)negprot->ni_keylen,
		    negprot->ni_key);		/* encryption key */
		break;

	case DOS_LANMAN2_1:
	case LANMAN2_1:
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
		    sizeof (smb_dos_tcp_rcvbuf), CRED());
		sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK;
		rc = smbsr_encode_result(sr, 13, VAR_BCC,
		    "bwwwwwwlYww2.w#cs",
		    13,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    SMB_DOS_MAXBUF,		/* max buffer size */
		    1,				/* max MPX */
		    1,				/* max VCs */
		    0,				/* read/write raw */
		    sesskey,			/* session key */
		    negprot->ni_servertime.tv_sec, /* server date/time */
		    negprot->ni_tzcorrection,
		    (uint16_t)negprot->ni_keylen, /* encryption key length */
						/* reserved field handled 2. */
		    VAR_BCC,
		    (int)negprot->ni_keylen,
		    negprot->ni_key,		/* encryption key */
		    nbdomain);
		break;

	case NT_LM_0_12:
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_nt_tcp_rcvbuf,
		    sizeof (smb_nt_tcp_rcvbuf), CRED());

		/*
		 * Allow SMB signatures if using encrypted passwords
		 */
		if ((secmode & NEGOTIATE_ENCRYPT_PASSWORDS) &&
		    sr->sr_cfg->skc_signing_enable) {
			secmode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
			if (sr->sr_cfg->skc_signing_required)
				secmode |=
				    NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;

			session->secmode = secmode;
		}

		/*
		 * Does the client want Extended Security?
		 * (and if we have it enabled)
		 * If so, handle as if a different dialect.
		 */
		if ((sr->smb_flg2 & SMB_FLAGS2_EXT_SEC) && smb_cap_ext_sec)
			goto NT_LM_0_12_ext_sec;
		sr->smb_flg2 &= ~SMB_FLAGS2_EXT_SEC;

		/*
		 * nbdomain is not expected to be aligned.
		 * Use temporary buffer to avoid alignment padding
		 */
		wclen = smb_wcequiv_strlen(nbdomain) + sizeof (smb_wchar_t);
		wcbuf = smb_srm_zalloc(sr, wclen);
		smb_msgbuf_init(&mb, wcbuf, wclen, SMB_MSGBUF_UNICODE);
		if (smb_msgbuf_encode(&mb, "U", nbdomain) < 0) {
			smb_msgbuf_term(&mb);
			smbsr_error(sr, 0, ERRSRV, ERRerror);
			return (SDRC_ERROR);
		}

		rc = smbsr_encode_result(sr, 17, VAR_BCC,
		    "bwbwwllllTwbw#c#c",
		    17,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    negprot->ni_maxmpxcount,	/* max MPX */
		    1,				/* max VCs */
		    (DWORD)smb_maxbufsize,	/* max buffer size */
		    0xFFFF,			/* max raw size */
		    sesskey,			/* session key */
		    negprot->ni_capabilities,
		    &negprot->ni_servertime,	/* system time */
		    negprot->ni_tzcorrection,
		    negprot->ni_keylen,		/* encryption key length */
		    VAR_BCC,
		    (int)negprot->ni_keylen,
		    negprot->ni_key,		/* encryption key */
		    wclen,
		    wcbuf);			/* nbdomain (unicode) */

		smb_msgbuf_term(&mb);
		break;

NT_LM_0_12_ext_sec:
		/*
		 * This is the "Extended Security" variant of
		 * dialect NT_LM_0_12.
		 */
		negprot->ni_capabilities |= CAP_EXTENDED_SECURITY;

		rc = smbsr_encode_result(sr, 17, VAR_BCC,
		    "bwbwwllllTwbw#c#c",
		    17,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    negprot->ni_maxmpxcount,	/* max MPX */
		    1,				/* max VCs */
		    (DWORD)smb_maxbufsize,	/* max buffer size */
		    0xFFFF,			/* max raw size */
		    sesskey,			/* session key */
		    negprot->ni_capabilities,
		    &negprot->ni_servertime,	/* system time */
		    negprot->ni_tzcorrection,
		    0,		/* encryption key length (MBZ) */
		    VAR_BCC,
		    UUID_LEN,
		    sr->sr_cfg->skc_machine_uuid,
		    sr->sr_cfg->skc_negtok_len,
		    sr->sr_cfg->skc_negtok);
		break;


	default:
		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, -1, 0);
		break;
	}

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

	/*
	 * Save the agreed dialect. Note that the state is also
	 * used to detect and reject attempts to re-negotiate.
	 */
	session->dialect = negprot->ni_dialect;
	session->s_state = SMB_SESSION_STATE_NEGOTIATED;

	/* Allow normal SMB1 requests now. */
	session->newrq_func = smb1sr_newrq;

	return (SDRC_SUCCESS);
}
Example #5
0
/*
 * This function builds a response for a NetShareEnum RAP request.
 * List of shares is scanned twice. In the first round the total number
 * of shares which their OEM name is shorter than 13 chars (esi->es_ntotal)
 * and also the number of shares that fit in the given buffer are calculated.
 * In the second round the shares data are encoded in the buffer.
 *
 * The data associated with each share has two parts, a fixed size part and
 * a variable size part which is share's comment. The outline of the response
 * buffer is so that fixed part for all the shares will appear first and follows
 * with the comments for all those shares and that's why the data cannot be
 * encoded in one round without unnecessarily complicating the code.
 */
void
smb_kshare_enum(smb_server_t *sv, smb_enumshare_info_t *esi)
{
	smb_avl_t *share_avl;
	smb_avl_cursor_t cursor;
	smb_kshare_t *shr;
	int remained;
	uint16_t infolen = 0;
	uint16_t cmntlen = 0;
	uint16_t sharelen;
	uint16_t clen;
	uint32_t cmnt_offs;
	smb_msgbuf_t info_mb;
	smb_msgbuf_t cmnt_mb;
	boolean_t autohome_added = B_FALSE;

	if (!smb_export_isready(sv)) {
		esi->es_ntotal = esi->es_nsent = 0;
		esi->es_datasize = 0;
		return;
	}

	esi->es_ntotal = esi->es_nsent = 0;
	remained = esi->es_bufsize;
	share_avl = &sv->sv_export.e_share_avl;

	/* Do the necessary calculations in the first round */
	smb_avl_iterinit(share_avl, &cursor);

	while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
		if (shr->shr_oemname == NULL) {
			smb_avl_release(share_avl, shr);
			continue;
		}

		if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
			if (esi->es_posix_uid == shr->shr_uid) {
				autohome_added = B_TRUE;
			} else {
				smb_avl_release(share_avl, shr);
				continue;
			}
		}

		esi->es_ntotal++;

		if (remained <= 0) {
			smb_avl_release(share_avl, shr);
			continue;
		}

		clen = strlen(shr->shr_cmnt) + 1;
		sharelen = SHARE_INFO_1_SIZE + clen;

		if (sharelen <= remained) {
			infolen += SHARE_INFO_1_SIZE;
			cmntlen += clen;
		}

		remained -= sharelen;
		smb_avl_release(share_avl, shr);
	}

	esi->es_datasize = infolen + cmntlen;

	smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
	smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
	cmnt_offs = infolen;

	/* Encode the data in the second round */
	smb_avl_iterinit(share_avl, &cursor);
	autohome_added = B_FALSE;

	while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
		if (shr->shr_oemname == NULL) {
			smb_avl_release(share_avl, shr);
			continue;
		}

		if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
			if (esi->es_posix_uid == shr->shr_uid) {
				autohome_added = B_TRUE;
			} else {
				smb_avl_release(share_avl, shr);
				continue;
			}
		}

		if (smb_msgbuf_encode(&info_mb, "13c.wl",
		    shr->shr_oemname, shr->shr_type, cmnt_offs) < 0) {
			smb_avl_release(share_avl, shr);
			break;
		}

		if (smb_msgbuf_encode(&cmnt_mb, "s", shr->shr_cmnt) < 0) {
			smb_avl_release(share_avl, shr);
			break;
		}

		cmnt_offs += strlen(shr->shr_cmnt) + 1;
		esi->es_nsent++;

		smb_avl_release(share_avl, shr);
	}

	smb_msgbuf_term(&info_mb);
	smb_msgbuf_term(&cmnt_mb);
}
/*
 * smb_trans2_mbc_encode
 *
 * This function encodes the mbc for one directory entry.
 *
 * The function returns -1 when the max data requested by client
 * is reached. If the entry is valid and successful encoded, 0
 * will be returned; otherwise, 1 will be returned.
 *
 * We always null terminate the filename. The space for the null
 * is included in the maxdata calculation and is therefore included
 * in the next_entry_offset. namelen is the unterminated length of
 * the filename. For levels except STANDARD and EA_SIZE, if the
 * filename is ascii the name length returned to the client should
 * include the null terminator. Otherwise the length returned to
 * the client should not include the terminator.
 *
 * Returns: 0 - data successfully encoded
 *          1 - client request's maxdata limit reached
 *	   -1 - error
 */
static int
smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
    smb_fileinfo_t *fileinfo, smb_find_args_t *args)
{
	int		namelen, shortlen, buflen;
	uint32_t	next_entry_offset;
	uint32_t	dsize32, asize32;
	uint32_t	mb_flags = 0;
	char		buf83[26];
	char		*tmpbuf;
	smb_msgbuf_t	mb;

	namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
	if (namelen == -1)
		return (-1);

	next_entry_offset = args->fa_maxdata + namelen;

	if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0)
		return (1);

	/*
	 * If ascii the filename length returned to the client should
	 * include the null terminator for levels except STANDARD and
	 * EASIZE.
	 */
	if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
		if ((args->fa_infolev != SMB_INFO_STANDARD) &&
		    (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
			namelen += 1;
	}

	mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
	dsize32 = (fileinfo->fi_size > UINT_MAX) ?
	    UINT_MAX : (uint32_t)fileinfo->fi_size;
	asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ?
	    UINT_MAX : (uint32_t)fileinfo->fi_alloc_size;

	switch (args->fa_infolev) {
	case SMB_INFO_STANDARD:
		if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
			(void) smb_mbc_encodef(&xa->rep_data_mb, "l",
			    fileinfo->fi_cookie);

		(void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwbu", sr,
		    smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
		    dsize32,
		    asize32,
		    fileinfo->fi_dosattr,
		    namelen,
		    fileinfo->fi_name);
		break;

	case SMB_INFO_QUERY_EA_SIZE:
		if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
			(void) smb_mbc_encodef(&xa->rep_data_mb, "l",
			    fileinfo->fi_cookie);

		/*
		 * Unicode filename should NOT be aligned. Encode ('u')
		 * into a temporary buffer, then encode buffer as a
		 * byte stream ('#c').
		 * Regardless of whether unicode or ascii, a single
		 * termination byte is used.
		 */
		buflen = namelen + sizeof (smb_wchar_t);
		tmpbuf = kmem_zalloc(buflen, KM_SLEEP);
		smb_msgbuf_init(&mb, (uint8_t *)tmpbuf, buflen, mb_flags);
		if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_name) < 0) {
			smb_msgbuf_term(&mb);
			kmem_free(tmpbuf, buflen);
			return (-1);
		}
		tmpbuf[namelen] = '\0';

		(void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb#c", sr,
		    smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
		    smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
		    dsize32,
		    asize32,
		    fileinfo->fi_dosattr,
		    0L,		/* EA Size */
		    namelen,
		    namelen + 1,
		    tmpbuf);

		smb_msgbuf_term(&mb);
		kmem_free(tmpbuf, buflen);
		break;

	case SMB_FIND_FILE_DIRECTORY_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqllu", sr,
		    next_entry_offset,
		    fileinfo->fi_cookie,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    fileinfo->fi_name);
		break;

	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllu", sr,
		    next_entry_offset,
		    fileinfo->fi_cookie,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    fileinfo->fi_name);
		break;

	case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.qu", sr,
		    next_entry_offset,
		    fileinfo->fi_cookie,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    fileinfo->fi_nodeid,
		    fileinfo->fi_name);
		break;

	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
		bzero(buf83, sizeof (buf83));
		smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
		    mb_flags);
		if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) {
			smb_msgbuf_term(&mb);
			return (-1);
		}
		shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);

		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24cu",
		    sr,
		    next_entry_offset,
		    fileinfo->fi_cookie,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    shortlen,
		    buf83,
		    fileinfo->fi_name);

		smb_msgbuf_term(&mb);
		break;

	case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
		bzero(buf83, sizeof (buf83));
		smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
		    mb_flags);
		if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) {
			smb_msgbuf_term(&mb);
			return (-1);
		}
		shortlen = smb_ascii_or_unicode_strlen(sr,
		    fileinfo->fi_shortname);

		(void) smb_mbc_encodef(&xa->rep_data_mb,
		    "%llTTTTqqlllb.24c2.qu",
		    sr,
		    next_entry_offset,
		    fileinfo->fi_cookie,
		    &fileinfo->fi_crtime,
		    &fileinfo->fi_atime,
		    &fileinfo->fi_mtime,
		    &fileinfo->fi_ctime,
		    fileinfo->fi_size,
		    fileinfo->fi_alloc_size,
		    fileinfo->fi_dosattr,
		    namelen,
		    0L,
		    shortlen,
		    buf83,
		    fileinfo->fi_nodeid,
		    fileinfo->fi_name);

		smb_msgbuf_term(&mb);
		break;

	case SMB_FIND_FILE_NAMES_INFO:
		(void) smb_mbc_encodef(&xa->rep_data_mb, "%lllu", sr,
		    next_entry_offset,
		    fileinfo->fi_cookie,
		    namelen,
		    fileinfo->fi_name);
		break;
	}

	return (0);
}