Ejemplo n.º 1
0
/*
 * smb_stream_fits
 *
 * Check if the named stream entry can fit in the response buffer.
 *
 * Required space =
 *	offset (size of current entry)
 *	+ SMB_STREAM_ENCODE_FIXED_SIZE
 *      + length of encoded stream name
 *	+ length of null terminator
 *	+ alignment padding
 */
static boolean_t
smb_stream_fits(smb_request_t *sr, smb_xa_t *xa, char *name, uint32_t offset)
{
	uint32_t len, pad;

	len = SMB_STREAM_ENCODE_FIXED_SZ +
	    smb_ascii_or_unicode_strlen(sr, name) +
	    smb_ascii_or_unicode_null_len(sr);
	pad = smb_pad_align(len, 8);
	len += pad;

	return (MBC_ROOM_FOR(&xa->rep_data_mb, offset + len) != 0);
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
/*
 * smb_mbc_vdecodef
 *
 * This function reads the contents of the mbc chain passed in under the list
 * of arguments passed in.
 *
 * The format string provides a description of the parameters passed in as well
 * as an action to be taken by smb_mbc_vdecodef().
 *
 *	%	Pointer to an SMB request structure (smb_request_t *). There
 *		should be only one of these in the string.
 *
 *	C	Pointer to an mbuf chain. Copy to that mbuf chain the number of
 *		bytes specified (number preceding C).
 *
 *	m	Pointer to an mbuf. Copy to that mbuf the number of bytes
 *		specified (number preceding m).
 *
 *	M	Read the 32 bit value at the current location of the mbuf chain
 *		and check if it matches the signature of an SMB request (SMBX).
 *
 *	b	Pointer to a buffer. Copy to that buffer the number of bytes
 *		specified (number preceding b).
 *
 *	c	Same as 'b'.
 *
 *	w	Pointer to a word (16bit value). Copy the next 16bit value into
 *		that location.
 *
 *	l	Pointer to a long (32bit value). Copy the next 32bit value into
 *		that location.
 *
 *	q	Pointer to a quad (64bit value). Copy the next 64bit value into
 *		that location.
 *
 *	Q	Same as above with a call to qswap().
 *
 *	B	Pointer to a vardata_block structure. That structure is used to
 *		retrieve data from the mbuf chain (an iovec type structure is
 *		embedded in a vardata_block).
 *
 *	D	Pointer to a vardata_block structure. That structure is used to
 *		retrieve data from the mbuf chain, however, two fields of the
 *		vardata_block structure (tag and len) are first initialized
 *		using the mbuf chain itself.
 *
 *	V	Same as 'D'.
 *
 *	L
 *
 *	A
 *
 *	P	Same as 'A'
 *
 *	S	Same as 'A'
 *
 *	u	Pointer to a string pointer. Allocate memory and retrieve the
 *		string at the current location in the mbuf chain. Store the
 *		address to the buffer allocated at the address specified by
 *		the pointer. In addition if an sr was passed and it indicates
 *		that the string is an unicode string, convert it.
 *
 *	s	Same as 'u' without convertion.
 *
 *	U	Same as 'u'. The string to retrieve is unicode.
 *
 *	y	Pointer to a 32bit value. Read the dos time at the current mbuf
 *		chain location, convert it to unix time and store it at the
 *		location indicated by the pointer.
 *
 *	Y	Same as 'y' bt the dos time coded in the mbuf chain is inverted.
 *
 *	.	Skip the number of bytes indicated by the number preceding '.'.
 *
 *	,	Same as '.' but take in account it is an unicode string.
 */
int
smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap)
{
	uint8_t		c;
	uint8_t		cval;
	uint8_t		*cvalp;
	uint8_t		**cvalpp;
	uint16_t	wval;
	uint16_t	*wvalp;
	uint32_t	*lvalp;
	uint64_t	*llvalp;
	smb_vdb_t	*vdp;
	smb_request_t	*sr = NULL;
	uint32_t	lval;
	int		unicode = 0;
	int		repc;

	while ((c = *fmt++) != 0) {
		repc = 1;

		if ('0' <= c && c <= '9') {
			repc = 0;
			do {
				repc = repc * 10 + c - '0';
				c = *fmt++;
			} while ('0' <= c && c <= '9');
		} else if (c == '#') {
			repc = va_arg(ap, int);
			c = *fmt++;
		}

		switch (c) {
		case '%':
			sr = va_arg(ap, struct smb_request *);
			unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE;
			break;

		case 'C':	/* Mbuf_chain */
			if (mbc_marshal_get_mbuf_chain(mbc, repc,
			    va_arg(ap, mbuf_chain_t *)) != 0)
				return (-1);
			break;

		case 'm':	/* struct_mbuf */
			if (mbc_marshal_get_mbufs(mbc, repc,
			    va_arg(ap, mbuf_t **)) != 0)
				return (-1);
			break;

		case 'M':
			if (mbc_marshal_get_long(mbc, &lval) != 0)
				/* Data will never be available */
				return (-1);

			if (lval != 0x424D53FF) /* 0xFF S M B */
				return (-1);
			break;

		case 'b':
		case 'c':
			cvalp = va_arg(ap, uint8_t *);
			if (MBC_ROOM_FOR(mbc, repc) == 0)
				/* Data will never be available */
				return (-1);

			while (repc-- > 0)
				*cvalp++ = mbc_marshal_fetch_byte(mbc);
			break;

		case 'w':
			wvalp = va_arg(ap, uint16_t *);
			while (repc-- > 0)
				if (mbc_marshal_get_short(mbc, wvalp++) != 0)
					return (-1);
			break;

		case 'l':
			lvalp = va_arg(ap, uint32_t *);
			while (repc-- > 0)
				if (mbc_marshal_get_long(mbc, lvalp++) != 0)
					return (-1);
			break;

		case 'q':
			llvalp = va_arg(ap, uint64_t *);
			while (repc-- > 0)
				if (mbc_marshal_get_long_long(
				    mbc, llvalp++) != 0)
					return (-1);
			break;

		case 'Q':
			llvalp = va_arg(ap, uint64_t *);
			while (repc-- > 0)
				if (mbc_marshal_get_odd_long_long(
				    mbc, llvalp++) != 0)
					return (-1);
			break;

		case 'B':
			vdp = va_arg(ap, struct vardata_block *);
			vdp->vdb_tag = 0;
			vdp->vdb_len = repc;
			vdp->vdb_uio.uio_iov = &vdp->vdb_iovec[0];
			vdp->vdb_uio.uio_iovcnt = MAX_IOVEC;
			vdp->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
			vdp->vdb_uio.uio_resid = repc;
			if (mbc_marshal_get_uio(mbc, &vdp->vdb_uio) != 0)
				return (-1);
			break;

		case 'D':
		case 'V':
			vdp = va_arg(ap, struct vardata_block *);
			if (mbc_marshal_get_char(mbc, &vdp->vdb_tag) != 0)
				return (-1);
			if (mbc_marshal_get_short(mbc, &wval) != 0)
				return (-1);
			vdp->vdb_len = (uint32_t)wval;
			vdp->vdb_uio.uio_iov = &vdp->vdb_iovec[0];
			vdp->vdb_uio.uio_iovcnt = MAX_IOVEC;
			vdp->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
			vdp->vdb_uio.uio_resid = vdp->vdb_len;
			if (vdp->vdb_len != 0) {
				if (mbc_marshal_get_uio(mbc,
				    &vdp->vdb_uio) != 0)
					return (-1);
			}
			break;

		case 'L':
			if (mbc_marshal_get_char(mbc, &cval) != 0)
				return (-1);
			if (cval != 2)
				return (-1);
			goto ascii_conversion;

		case 'A':
		case 'S':
			if (mbc_marshal_get_char(mbc, &cval) != 0)
				return (-1);
			if (((c == 'A' || c == 'S') && cval != 4) ||
			    (c == 'L' && cval != 2))
				return (-1);
			/* FALLTHROUGH */

		case 'u': /* Convert from unicode if flags are set */
			if (unicode)
				goto unicode_translation;
			/* FALLTHROUGH */

		case 's':
ascii_conversion:
			ASSERT(sr != NULL);
			cvalpp = va_arg(ap, uint8_t **);
			if (repc <= 1)
				repc = 0;
			if (mbc_marshal_get_ascii_string(sr,
			    mbc, cvalpp, repc) != 0)
				return (-1);
			break;

		case 'U': /* Convert from unicode */
unicode_translation:
			ASSERT(sr != 0);
			cvalpp = va_arg(ap, uint8_t **);
			if (repc <= 1)
				repc = 0;
			if (mbc->chain_offset & 1)
				mbc->chain_offset++;
			if (mbc_marshal_get_unicode_string(sr,
			    mbc, cvalpp, repc) != 0)
				return (-1);
			break;

		case 'Y': /* dos time to unix time tt/dd */
			lvalp = va_arg(ap, uint32_t *);
			while (repc-- > 0) {
				short	d, t;

				if (mbc_marshal_get_short(mbc,
				    (uint16_t *)&t) != 0)
					return (-1);
				if (mbc_marshal_get_short(mbc,
				    (uint16_t *)&d) != 0)
					return (-1);
				*lvalp++ = smb_time_dos_to_unix(d, t);
			}
			break;

		case 'y': /* dos time to unix time dd/tt */
			lvalp = va_arg(ap, uint32_t *);
			while (repc-- > 0) {
				short	d, t;

				if (mbc_marshal_get_short(mbc,
				    (uint16_t *)&d) != 0)
					return (-1);
				if (mbc_marshal_get_short(mbc,
				    (uint16_t *)&t) != 0)
					return (-1);
				*lvalp++ = smb_time_dos_to_unix(d, t);
			}
			break;

		case ',':
			if (unicode)
				repc *= 2;
			/* FALLTHROUGH */

		case '.':
			if (mbc_marshal_get_skip(mbc, repc) != 0)
				return (-1);
			break;

		default:
			ASSERT(0);
			return (-1);
		}
	}
Ejemplo n.º 4
0
/*
 * smb_encode_stream_info
 *
 * This function encodes the streams information.
 * The following rules about how have been derived from observed NT
 * behaviour.
 *
 * If the target is a file:
 * 1. If there are no named streams, the response should still contain
 *    an entry for the unnamed stream.
 * 2. If there are named streams, the response should contain an entry
 *    for the unnamed stream followed by the entries for the named
 *    streams.
 *
 * If the target is a directory:
 * 1. If there are no streams, the response is complete. Directories
 *    do not report the unnamed stream.
 * 2. If there are streams, the response should contain entries for
 *    those streams but there should not be an entry for the unnamed
 *    stream.
 *
 * Note that the stream name lengths exclude the null terminator but
 * the field lengths (i.e. next offset calculations) need to include
 * the null terminator and be padded to a multiple of 8 bytes. The
 * last entry does not seem to need any padding.
 *
 * If an error is encountered when trying to read the stream entries
 * (smb_odir_read_streaminfo) it is treated as if there are no [more]
 * entries. The entries that have been read so far are returned and
 * no error is reported.
 *
 * If the response buffer is not large enough to return all of the
 * named stream entries, the entries that do fit are returned and
 * a warning code is set (NT_STATUS_BUFFER_OVERFLOW). The next_offset
 * value in the last returned entry must be 0.
 */
static void
smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
{
	char *stream_name;
	uint32_t next_offset;
	uint32_t stream_nlen;
	uint32_t pad;
	u_offset_t datasz, allocsz;
	boolean_t is_dir;
	smb_streaminfo_t *sinfo, *sinfo_next;
	int rc = 0;
	boolean_t done = B_FALSE;
	boolean_t eos = B_FALSE;
	uint16_t odid;
	smb_odir_t *od = NULL;

	smb_node_t *fnode = qinfo->qi_node;
	smb_attr_t *attr = &qinfo->qi_attr;

	ASSERT(fnode);
	if (SMB_IS_STREAM(fnode)) {
		fnode = fnode->n_unode;
		ASSERT(fnode);
	}
	ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
	ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);

	sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
	sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
	is_dir = ((attr->sa_dosattr & FILE_ATTRIBUTE_DIRECTORY) != 0);
	datasz = attr->sa_vattr.va_size;
	allocsz = attr->sa_allocsz;

	odid = smb_odir_openat(sr, fnode);
	if (odid != 0)
		od = smb_tree_lookup_odir(sr->tid_tree, odid);
	if (od != NULL)
		rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);

	if ((od == NULL) || (rc != 0) || (eos))
		done = B_TRUE;

	/* If not a directory, encode an entry for the unnamed stream. */
	if (!is_dir) {
		stream_name = "::$DATA";
		stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
		next_offset = SMB_STREAM_ENCODE_FIXED_SZ + stream_nlen +
		    smb_ascii_or_unicode_null_len(sr);

		/* Can unnamed stream fit in response buffer? */
		if (MBC_ROOM_FOR(&xa->rep_data_mb, next_offset) == 0) {
			done = B_TRUE;
			smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
			    ERRDOS, ERROR_MORE_DATA);
		} else {
			/* Can first named stream fit in rsp buffer? */
			if (!done && !smb_stream_fits(sr, xa, sinfo->si_name,
			    next_offset)) {
				done = B_TRUE;
				smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
				    ERRDOS, ERROR_MORE_DATA);
			}

			if (done)
				next_offset = 0;

			(void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu", sr,
			    next_offset, stream_nlen, datasz, allocsz,
			    stream_name);
		}
	}

	/*
	 * If there is no next entry, or there is not enough space in
	 * the response buffer for the next entry, the next_offset and
	 * padding are 0.
	 */
	while (!done) {
		stream_nlen = smb_ascii_or_unicode_strlen(sr, sinfo->si_name);
		sinfo_next->si_name[0] = 0;

		rc = smb_odir_read_streaminfo(sr, od, sinfo_next, &eos);
		if ((rc != 0) || (eos)) {
			done = B_TRUE;
		} else {
			next_offset = SMB_STREAM_ENCODE_FIXED_SZ +
			    stream_nlen +
			    smb_ascii_or_unicode_null_len(sr);
			pad = smb_pad_align(next_offset, 8);
			next_offset += pad;

			/* Can next named stream fit in response buffer? */
			if (!smb_stream_fits(sr, xa, sinfo_next->si_name,
			    next_offset)) {
				done = B_TRUE;
				smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
				    ERRDOS, ERROR_MORE_DATA);
			}
		}

		if (done) {
			next_offset = 0;
			pad = 0;
		}

		rc = smb_mbc_encodef(&xa->rep_data_mb, "%llqqu#.",
		    sr, next_offset, stream_nlen,
		    sinfo->si_size, sinfo->si_alloc_size,
		    sinfo->si_name, pad);

		(void) memcpy(sinfo, sinfo_next, sizeof (smb_streaminfo_t));
	}

	kmem_free(sinfo, sizeof (smb_streaminfo_t));
	kmem_free(sinfo_next, sizeof (smb_streaminfo_t));
	if (od) {
		smb_odir_close(od);
		smb_odir_release(od);
	}
}
/*
 * 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);
}