Beispiel #1
0
/*
 * smb_trans2_find_get_maxdata
 *
 * Calculate the minimum response space required for the specified
 * information level.
 *
 * A non-zero return value provides the minimum space required.
 * A return value of zero indicates an unknown information level.
 */
static int
smb_trans2_find_get_maxdata(smb_request_t *sr, uint16_t infolev, uint16_t fflag)
{
	int maxdata;

	maxdata = smb_ascii_or_unicode_null_len(sr);

	switch (infolev) {
	case SMB_INFO_STANDARD :
		if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
			maxdata += sizeof (int32_t);
		maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1;
		break;

	case SMB_INFO_QUERY_EA_SIZE:
		if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
			maxdata += sizeof (int32_t);
		maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
		break;

	case SMB_FIND_FILE_DIRECTORY_INFO:
		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4;
		break;

	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4;
		break;

	case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 8;
		break;

	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24;
		break;

	case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24
		    + 2 + 8;
		break;

	case SMB_FIND_FILE_NAMES_INFO:
		maxdata += 4 + 4 + 4;
		break;

	case SMB_MAC_FIND_BOTH_HFS_INFO:
		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1 + 2 +
		    4 + 32 + 4 + 1 + 1 + 24 + 4;
		break;

	default:
		maxdata = 0;
		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
		    ERRDOS, ERROR_INVALID_LEVEL);
	}

	return (maxdata);
}
/*
 * 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);
}
Beispiel #3
0
/*
 * This is to respond to the nt_transact_ioctl to either respond with the
 * number of snapshots, or to respond with the list.  It needs to be sorted
 * before the reply.  If the the max data bytes to return is
 * SMB_VSS_COUNT_SIZE, then all that is requested is the count, otherwise
 * return the count and the list of @GMT tokens (one token for each
 * snapshot).
 */
uint32_t
smb_vss_enum_snapshots(smb_request_t *sr, smb_fsctl_t *fsctl)
{
	uint32_t count = 0;
	char *root_path;
	uint32_t status = NT_STATUS_SUCCESS;
	smb_node_t *tnode;
	smb_gmttoken_response_t snaps;

	ASSERT(sr->tid_tree);
	ASSERT(sr->tid_tree->t_snode);

	if (fsctl->MaxOutputResp < SMB_VSS_COUNT_SIZE)
		return (NT_STATUS_INVALID_PARAMETER);

	tnode = sr->tid_tree->t_snode;
	root_path  = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
	if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0)
		return (NT_STATUS_INVALID_PARAMETER);

	if (fsctl->MaxOutputResp == SMB_VSS_COUNT_SIZE) {
		count = smb_vss_get_count(sr->tid_tree, root_path);
		if (smb_mbc_encodef(fsctl->out_mbc, "lllw", count, 0,
		    (count * SMB_VSS_GMT_NET_SIZE(sr) +
		    smb_ascii_or_unicode_null_len(sr)), 0) != 0) {
			status = NT_STATUS_INVALID_PARAMETER;
		}
	} else {
		count = fsctl->MaxOutputResp / SMB_VSS_GMT_NET_SIZE(sr);

		smb_vss_get_snapshots(sr->tid_tree, root_path,
		    count, &snaps);

		status = smb_vss_encode_gmttokens(sr, fsctl, count, &snaps);

		smb_vss_get_snapshots_free(&snaps);
	}

	kmem_free(root_path, MAXPATHLEN);
	return (status);
}
/*
 * 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_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.
 *
 * Offset calculation:
 * 2 dwords + 2 quadwords => 4 + 4 + 8 + 8 => 24
 */
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_vattr.va_type == VDIR);
	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);

		if (done)
			next_offset = 0;
		else
			next_offset = 24 + stream_nlen +
			    smb_ascii_or_unicode_null_len(sr);

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

	/*
	 * Since last packet does not have a pad we need to check
	 * for the next stream before we encode the current one
	 */
	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;
			next_offset = 0;
			pad = 0;
		} else {
			next_offset = 24 + stream_nlen +
			    smb_ascii_or_unicode_null_len(sr);
			pad = smb_pad_align(next_offset, 8);
			next_offset += pad;
		}
		(void) 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);
	}
}