/*
 * smb_com_trans2_query_file_information
 */
smb_sdrc_t
smb_com_trans2_query_file_information(struct smb_request *sr, struct smb_xa *xa)
{
	uint16_t infolev;

	if (smb_mbc_decodef(&xa->req_param_mb, "ww",
	    &sr->smb_fid, &infolev) != 0)
		return (SDRC_ERROR);

	if (smb_query_by_fid(sr, xa, infolev) != 0)
		return (SDRC_ERROR);

	return (SDRC_SUCCESS);
}
Exemple #2
0
smb_sdrc_t
smb_com_echo(struct smb_request *sr)
{
	unsigned short necho;
	unsigned short nbytes;
	unsigned short i;
	struct mbuf_chain reply;
	char *data;

	if (smbsr_decode_vwv(sr, "w", &necho) != 0)
		return (SDRC_ERROR);

	nbytes = sr->smb_bcc;
	data = smb_srm_zalloc(sr, nbytes);

	if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data))
		return (SDRC_ERROR);

	for (i = 1; i <= necho; ++i) {
		MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes);

		(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, "bww#c", 1, i,
		    nbytes, nbytes, data);

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

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

	return (SDRC_NO_REPLY);
}
int
smb2_decode_header(smb_request_t *sr)
{
	uint64_t ssnid;
	uint32_t pid, tid;
	uint16_t hdr_len;
	int rc;

	rc = smb_mbc_decodef(
	    &sr->command, "Nwww..wwllqllq16c",
	    &hdr_len,			/* w */
	    &sr->smb2_credit_charge,	/* w */
	    &sr->smb2_chan_seq,		/* w */
	    /* reserved			  .. */
	    &sr->smb2_cmd_code,		/* w */
	    &sr->smb2_credit_request,	/* w */
	    &sr->smb2_hdr_flags,	/* l */
	    &sr->smb2_next_command,	/* l */
	    &sr->smb2_messageid,	/* q */
	    &pid,			/* l */
	    &tid,			/* l */
	    &ssnid,			/* q */
	    sr->smb2_sig);		/* 16c */
	if (rc)
		return (rc);

	if (hdr_len != SMB2_HDR_SIZE)
		return (-1);

	sr->smb_uid = (uint16_t)ssnid;	/* XXX wide UIDs */

	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
		sr->smb2_async_id = pid |
		    ((uint64_t)sr->smb_tid) << 32;
	} else {
		sr->smb_pid = pid;
		sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
	}

	return (rc);
}
/*
 * smb_com_trans2_query_path_information
 */
smb_sdrc_t
smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa)
{
	uint16_t infolev;
	char *path;

	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
		smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST,
		    ERRDOS, ERROR_INVALID_FUNCTION);
		return (SDRC_ERROR);
	}

	if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u",
	    sr, &infolev, &path) != 0)
		return (SDRC_ERROR);

	if (smb_query_by_path(sr, xa, infolev, path) != 0)
		return (SDRC_ERROR);

	return (SDRC_SUCCESS);
}
/*
 * smb_com_trans2_query_path_information
 */
smb_sdrc_t
smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa)
{
	uint16_t	infolev;
	smb_fqi_t	*fqi = &sr->arg.dirop.fqi;

	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
		smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST,
		    ERRDOS, ERROR_INVALID_FUNCTION);
		return (SDRC_ERROR);
	}

	if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u",
	    sr, &infolev, &fqi->fq_path.pn_path) != 0)
		return (SDRC_ERROR);

	if (smb_query_by_path(sr, xa, infolev) != 0)
		return (SDRC_ERROR);

	return (SDRC_SUCCESS);
}
/*
 * smb_nt_transact_notify_change
 *
 * Handle and SMB NT transact NOTIFY CHANGE request.
 * Basically, wait until "something has changed", and either
 * return information about what changed, or return a special
 * error telling the client "many things changed".
 *
 * The implementation uses a per-node list of waiting notify
 * requests like this one, each with a blocked worker thead.
 * Later, FEM and/or smbsrv events wake these threads, which
 * then send the reply to the client.
 */
smb_sdrc_t
smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa)
{
	uint32_t		CompletionFilter;
	unsigned char		WatchTree;
	uint32_t		status;
	hrtime_t		t1, t2;

	if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
	    &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) {
		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
		return (SDRC_ERROR);
	}
	CompletionFilter &= FILE_NOTIFY_VALID_MASK;
	if (WatchTree)
		CompletionFilter |= NODE_FLAGS_WATCH_TREE;

	smbsr_lookup_file(sr);

	t1 = gethrtime();
	status = smb_notify_common(sr, &xa->rep_data_mb, CompletionFilter);
	t2 = gethrtime();

	/*
	 * We don't want to include the (indefinite) wait time of the
	 * smb_notify_common() call in the SMB1 transact latency.
	 * The easiest way to do that, without adding special case
	 * logic to the common SMB1 dispatch handler is to adjust the
	 * start time of this request to effectively subtract out the
	 * time we were blocked in smb_notify_common().
	 */
	sr->sr_time_start += (t2 - t1);

	if (status != 0)
		smbsr_error(sr, status, 0, 0);

	return (SDRC_SUCCESS);
}
/*
 * smb_com_trans2_find_first2
 *
 *  Client Request                Value
 *  ============================  ==================================
 *
 *  UCHAR  WordCount              15
 *  UCHAR  TotalDataCount         Total size of extended attribute list
 *  UCHAR  SetupCount             1
 *  UCHAR  Setup[0]               TRANS2_FIND_FIRST2
 *
 *  Parameter Block Encoding      Description
 *  ============================  ==================================
 *  USHORT SearchAttributes;
 *  USHORT SearchCount;           Maximum number of entries to return
 *  USHORT Flags;                 Additional information:
 *                                Bit 0 - close search after this request
 *                                Bit 1 - close search if end of search
 *                                reached
 *                                Bit 2 - return resume keys for each
 *                                entry found
 *                                Bit 3 - continue search from previous
 *                                ending place
 *                                Bit 4 - find with backup intent
 *  USHORT InformationLevel;      See below
 *  ULONG SearchStorageType;
 *  STRING FileName;              Pattern for the search
 *  UCHAR Data[ TotalDataCount ]  FEAList if InformationLevel is
 *                                QUERY_EAS_FROM_LIST
 *
 *  Response Parameter Block      Description
 *  ============================  ==================================
 *
 *  USHORT Sid;                   Search handle
 *  USHORT SearchCount;           Number of entries returned
 *  USHORT EndOfSearch;           Was last entry returned?
 *  USHORT EaErrorOffset;         Offset into EA list if EA error
 *  USHORT LastNameOffset;        Offset into data to file name of last
 *                                entry, if server needs it to resume
 *                                search; else 0
 *  UCHAR Data[ TotalDataCount ]  Level dependent info about the matches
 *                                found in the search
 */
smb_sdrc_t
smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
{
	int		count;
	uint16_t	sattr, odid;
	smb_pathname_t	*pn;
	smb_odir_t	*od;
	smb_find_args_t	args;
	uint32_t	odir_flags = 0;

	bzero(&args, sizeof (smb_find_args_t));

	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
		    ERRDOS, ERROR_ACCESS_DENIED);
		return (SDRC_ERROR);
	}

	pn = &sr->arg.dirop.fqi.fq_path;

	if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr,
	    &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev,
	    &pn->pn_path) != 0) {
		return (SDRC_ERROR);
	}

	smb_pathname_init(sr, pn, pn->pn_path);
	if (!smb_pathname_validate(sr, pn))
		return (-1);

	if (smb_is_stream_name(pn->pn_path)) {
		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
		    ERRDOS, ERROR_INVALID_NAME);
		return (SDRC_ERROR);
	}

	if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) {
		sr->user_cr = smb_user_getprivcred(sr->uid_user);
		odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT;
	}

	args.fa_maxdata =
	    smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
	if (args.fa_maxdata == 0)
		return (SDRC_ERROR);

	odid = smb_odir_open(sr, pn->pn_path, sattr, odir_flags);
	if (odid == 0) {
		if (sr->smb_error.status == NT_STATUS_OBJECT_PATH_NOT_FOUND) {
			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
			    ERRDOS, ERROR_FILE_NOT_FOUND);
		}
		return (SDRC_ERROR);
	}

	od = smb_tree_lookup_odir(sr->tid_tree, odid);
	if (od == NULL)
		return (SDRC_ERROR);

	count = smb_trans2_find_entries(sr, xa, od, &args);

	if (count == -1) {
		smb_odir_close(od);
		smb_odir_release(od);
		return (SDRC_ERROR);
	}

	if (count == 0) {
		smb_odir_close(od);
		smb_odir_release(od);
		smbsr_errno(sr, ENOENT);
		return (SDRC_ERROR);
	}

	if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
	    (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
		smb_odir_close(od);
	} /* else leave odir open for trans2_find_next2 */

	smb_odir_release(od);

	(void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww",
	    odid,	/* Search ID */
	    count,	/* Search Count */
	    args.fa_eos, /* End Of Search */
	    0,		/* EA Error Offset */
	    args.fa_lno); /* Last Name Offset */

	return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa)
{
	struct open_param *op = &sr->arg.open;
	uint32_t	creation_time;
	uint32_t	alloc_size;
	uint16_t	flags;
	uint16_t	file_attr;
	int		rc;

	bzero(op, sizeof (sr->arg.open));

	rc = smb_mbc_decodef(&xa->req_param_mb, "%wwwwlwl10.u",
	    sr, &flags, &op->omode, &op->fqi.fq_sattr, &file_attr,
	    &creation_time, &op->ofun, &alloc_size, &op->fqi.fq_path.pn_path);
	if (rc != 0)
		return (SDRC_ERROR);

	if ((creation_time != 0) && (creation_time != UINT_MAX))
		op->crtime.tv_sec = smb_time_local_to_gmt(sr, creation_time);
	op->crtime.tv_nsec = 0;

	op->dattr = file_attr;
	op->dsize = alloc_size;
	op->create_options = FILE_NON_DIRECTORY_FILE;

	op->desired_access = smb_omode_to_amask(op->omode);
	op->share_access = smb_denymode_to_sharemode(op->omode,
	    op->fqi.fq_path.pn_path);

	op->create_disposition = smb_ofun_to_crdisposition(op->ofun);
	if (op->create_disposition > FILE_MAXIMUM_DISPOSITION)
		op->create_disposition = FILE_CREATE;

	if (op->omode & SMB_DA_WRITE_THROUGH)
		op->create_options |= FILE_WRITE_THROUGH;

	if (sr->smb_flg & SMB_FLAGS_OPLOCK) {
		if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY)
			op->op_oplock_level = SMB_OPLOCK_BATCH;
		else
			op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
	} else {
		op->op_oplock_level = SMB_OPLOCK_NONE;
	}

	if (smb_common_open(sr) != NT_STATUS_SUCCESS)
		return (SDRC_ERROR);

	if (op->op_oplock_level != SMB_OPLOCK_NONE)
		op->action_taken |= SMB_OACT_LOCK;
	else
		op->action_taken &= ~SMB_OACT_LOCK;

	file_attr = op->dattr & FILE_ATTRIBUTE_MASK;

	if (!STYPE_ISDSK(sr->tid_tree->t_res_type))
		op->dsize = 0;

	(void) smb_mbc_encodef(&xa->rep_param_mb, "wwllwwwwlwl",
	    sr->smb_fid,
	    file_attr,
	    (uint32_t)0,	/* creation time */
	    (uint32_t)op->dsize,
	    op->omode,
	    op->ftype,
	    op->devstate,
	    op->action_taken,
	    op->fileid,
	    (uint16_t)0,	/* EA error offset */
	    (uint32_t)0);	/* EA list length */

	return (SDRC_SUCCESS);
}
smb_sdrc_t
smb2_session_setup(smb_request_t *sr)
{
	smb_arg_sessionsetup_t	*sinfo;
	uint16_t StructureSize;
	uint8_t  Flags;
	uint8_t  SecurityMode;
	uint32_t Capabilities;	/* ignored - see above */
	uint32_t Channel;
	uint16_t SecBufOffset;
	uint16_t SecBufLength;
	uint64_t PrevSessionId;
	uint16_t SessionFlags;
	uint32_t status;
	int skip;
	int rc = 0;

	sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t));
	sr->sr_ssetup = sinfo;

	rc = smb_mbc_decodef(
	    &sr->smb_data, "wbbllwwq",
	    &StructureSize,	/* w */
	    &Flags,		/* b */
	    &SecurityMode,	/* b */
	    &Capabilities,	/* l */
	    &Channel,		/* l */
	    &SecBufOffset,	/* w */
	    &SecBufLength,	/* w */
	    &PrevSessionId);	/* q */
	if (rc)
		return (SDRC_ERROR);

	/*
	 * We're normally positioned at the security buffer now,
	 * but there could be some padding before it.
	 */
	skip = (SecBufOffset + sr->smb2_cmd_hdr) -
	    sr->smb_data.chain_offset;
	if (skip < 0)
		return (SDRC_ERROR);
	if (skip > 0)
		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);

	/*
	 * Get the security buffer
	 */
	sinfo->ssi_iseclen = SecBufLength;
	sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen);
	rc = smb_mbc_decodef(&sr->smb_data, "#c",
	    sinfo->ssi_iseclen, sinfo->ssi_isecblob);
	if (rc)
		return (SDRC_ERROR);

	/*
	 * The real auth. work happens in here.
	 */
	status = smb_authenticate_ext(sr);

	SecBufOffset = SMB2_HDR_SIZE + 8;
	SecBufLength = sinfo->ssi_oseclen;
	SessionFlags = 0;

	switch (status) {

	case NT_STATUS_SUCCESS:	/* Authenticated */
		if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST)
			SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST;
		if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON)
			SessionFlags |= SMB2_SESSION_FLAG_IS_NULL;
		smb2_ss_adjust_credits(sr);
		break;

	/*
	 * This is not really an error, but tells the client
	 * it should send another session setup request.
	 * Not smb2_put_error because we send a payload.
	 */
	case NT_STATUS_MORE_PROCESSING_REQUIRED:
		sr->smb2_status = status;
		break;

	default:
		SecBufLength = 0;
		sr->smb2_status = status;
		break;
	}

	/*
	 * SMB2 Session Setup reply
	 */

	rc = smb_mbc_encodef(
	    &sr->reply,
	    "wwww#c",
	    9,	/* StructSize */	/* w */
	    SessionFlags,		/* w */
	    SecBufOffset,		/* w */
	    SecBufLength,		/* w */
	    SecBufLength,		/* # */
	    sinfo->ssi_osecblob);	/* c */
	if (rc)
		return (SDRC_ERROR);

	return (SDRC_SUCCESS);
}
Exemple #10
0
/*
 * SMB Negotiate gets special handling.  This is called directly by
 * the reader thread (see smbsr_newrq_initial) with what _should_ be
 * an SMB1 Negotiate.  Only the "\ffSMB" header has been checked
 * when this is called, so this needs to check the SMB command,
 * if it's Negotiate execute it, then send the reply, etc.
 *
 * Since this is called directly from the reader thread, we
 * know this is the only thread currently using this session.
 * This has to duplicate some of what smb1sr_work does as a
 * result of bypassing the normal dispatch mechanism.
 *
 * The caller always frees this request.
 */
int
smb1_newrq_negotiate(smb_request_t *sr)
{
	smb_sdrc_t	sdrc;
	uint16_t	pid_hi, pid_lo;

	/*
	 * Decode the header
	 */
	if (smb_mbc_decodef(&sr->command, SMB_HEADER_ED_FMT,
	    &sr->smb_com,
	    &sr->smb_rcls,
	    &sr->smb_reh,
	    &sr->smb_err,
	    &sr->smb_flg,
	    &sr->smb_flg2,
	    &pid_hi,
	    sr->smb_sig,
	    &sr->smb_tid,
	    &pid_lo,
	    &sr->smb_uid,
	    &sr->smb_mid) != 0)
		return (-1);
	if (sr->smb_com != SMB_COM_NEGOTIATE)
		return (-1);

	sr->smb_pid = (pid_hi << 16) | pid_lo;

	/*
	 * Reserve space for the reply header.
	 */
	(void) smb_mbc_encodef(&sr->reply, "#.", SMB_HEADER_LEN);
	sr->first_smb_com = sr->smb_com;

	if (smb_mbc_decodef(&sr->command, "b", &sr->smb_wct) != 0)
		return (-1);
	(void) MBC_SHADOW_CHAIN(&sr->smb_vwv, &sr->command,
	    sr->command.chain_offset, sr->smb_wct * 2);

	if (smb_mbc_decodef(&sr->command, "#.w", sr->smb_wct*2, &sr->smb_bcc))
		return (-1);
	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
	    sr->command.chain_offset, sr->smb_bcc);

	sr->command.chain_offset += sr->smb_bcc;
	if (sr->command.chain_offset > sr->command.max_bytes)
		return (-1);

	/* Store pointers for later */
	sr->cur_reply_offset = sr->reply.chain_offset;

	sdrc = smb_pre_negotiate(sr);
	if (sdrc == SDRC_SUCCESS)
		sdrc = smb_com_negotiate(sr);
	smb_post_negotiate(sr);

	if (sdrc != SDRC_NO_REPLY)
		smbsr_send_reply(sr);
	if (sdrc == SDRC_DROP_VC)
		return (-1);

	return (0);
}
Exemple #11
0
/*
 * See [MS-DFSC] for details about this command
 */
uint32_t
smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl)
{
	dfs_info_t *referrals;
	dfs_referral_response_t refrsp;
	dfs_reftype_t reftype;
	char *path;
	uint16_t maxver;
	uint32_t status;
	int rc;

	/*
	 * The caller checks this, because the error reporting method
	 * varies across SMB versions.
	 */
	ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type));

	/*
	 * XXX Instead of decoding the referral request and encoding
	 * the response here (in-kernel) we could pass the given
	 * request buffer in our door call, and let that return the
	 * response buffer ready to stuff into out_mbc.  That would
	 * allow all this decoding/encoding to happen at user-level.
	 * (and most of this file would go away. :-)
	 */
	switch (fsctl->CtlCode) {
	case FSCTL_DFS_GET_REFERRALS:
		/*
		 * Input data is (w) MaxReferralLevel, (U) path
		 */
		rc = smb_mbc_decodef(fsctl->in_mbc, "%wu",
		    sr, &maxver, &path);
		if (rc != 0)
			return (NT_STATUS_INVALID_PARAMETER);
		break;

	case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */
	default:
		return (NT_STATUS_NOT_SUPPORTED);
	}

	reftype = smb_dfs_get_reftype((const char *)path);
	switch (reftype) {
	case DFS_REFERRAL_INVALID:
		/* Need to check the error for this case */
		return (NT_STATUS_INVALID_PARAMETER);

	case DFS_REFERRAL_DOMAIN:
	case DFS_REFERRAL_DC:
		/* MS-DFSC: this error is returned by non-DC root */
		return (NT_STATUS_INVALID_PARAMETER);

	case DFS_REFERRAL_SYSVOL:
		/* MS-DFSC: this error is returned by non-DC root */
		return (NT_STATUS_NO_SUCH_DEVICE);

	default:
		break;
	}

	status = smb_dfs_referrals_get(sr, path, reftype, &refrsp);
	if (status != NT_STATUS_SUCCESS)
		return (status);

	referrals = &refrsp.rp_referrals;
	smb_dfs_encode_hdr(fsctl->out_mbc, referrals);

	/*
	 * Server may respond with any referral version at or below
	 * the maximum specified in the request.
	 */
	switch (maxver) {
	case DFS_REFERRAL_V1:
		status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals);
		break;

	case DFS_REFERRAL_V2:
		status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals);
		break;

	case DFS_REFERRAL_V3:
		status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals,
		    DFS_REFERRAL_V3);
		break;

	case DFS_REFERRAL_V4:
	default:
		status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals,
		    DFS_REFERRAL_V4);
		break;
	}

	smb_dfs_referrals_free(&refrsp);

	return (status);
}
Exemple #12
0
smb_sdrc_t
smb2_read(smb_request_t *sr)
{
	smb_ofile_t *of = NULL;
	smb_vdb_t *vdb = NULL;
	struct mbuf *m = NULL;
	uint16_t StructSize;
	uint8_t Padding;
	uint8_t DataOff;
	uint32_t Length;
	uint64_t Offset;
	smb2fid_t smb2fid;
	uint32_t MinCount;
	uint32_t Channel;
	uint32_t Remaining;
	uint16_t ChanInfoOffset;
	uint16_t ChanInfoLength;
	uint32_t XferCount;
	uint32_t status;
	int rc = 0;

	/*
	 * SMB2 Read request
	 */
	rc = smb_mbc_decodef(
	    &sr->smb_data,
	    "wb.lqqqlllww",
	    &StructSize,		/* w */
	    &Padding,			/* b. */
	    &Length,			/* l */
	    &Offset,			/* q */
	    &smb2fid.persistent,	/* q */
	    &smb2fid.temporal,		/* q */
	    &MinCount,			/* l */
	    &Channel,			/* l */
	    &Remaining,			/* l */
	    &ChanInfoOffset,		/* w */
	    &ChanInfoLength);		/* w */
	if (rc)
		return (SDRC_ERROR);
	if (StructSize != 49)
		return (SDRC_ERROR);

	status = smb2sr_lookup_fid(sr, &smb2fid);
	if (status) {
		smb2sr_put_error(sr, status);
		return (SDRC_SUCCESS);
	}
	of = sr->fid_ofile;

	if (Length > smb2_max_rwsize) {
		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
		return (SDRC_SUCCESS);
	}
	if (MinCount > Length)
		MinCount = Length;

	/* This is automatically free'd. */
	vdb = smb_srm_zalloc(sr, sizeof (*vdb));
	vdb->vdb_tag = 0;
	vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0];
	vdb->vdb_uio.uio_iovcnt = MAX_IOVEC;
	vdb->vdb_uio.uio_resid = Length;
	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
	vdb->vdb_uio.uio_segflg = UIO_SYSSPACE;
	vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;

	sr->raw_data.max_bytes = Length;
	m = smb_mbuf_allocate(&vdb->vdb_uio);

	switch (of->f_tree->t_res_type & STYPE_MASK) {
	case STYPE_DISKTREE:
		if (!smb_node_is_dir(of->f_node)) {
			/* Check for conflicting locks. */
			rc = smb_lock_range_access(sr, of->f_node,
			    Offset, Length, B_FALSE);
			if (rc) {
				rc = ERANGE;
				break;
			}
		}
		rc = smb_fsop_read(sr, of->f_cr, of->f_node, &vdb->vdb_uio);
		break;
	case STYPE_IPC:
		rc = smb_opipe_read(sr, &vdb->vdb_uio);
		break;
	default:
	case STYPE_PRINTQ:
		rc = EACCES;
		break;
	}

	/* How much data we moved. */
	XferCount = Length - vdb->vdb_uio.uio_resid;

	sr->raw_data.max_bytes = XferCount;
	smb_mbuf_trim(m, XferCount);
	MBC_ATTACH_MBUF(&sr->raw_data, m);

	/*
	 * Checking the error return _after_ dealing with
	 * the returned data so that if m was allocated,
	 * it will be free'd via sr->raw_data cleanup.
	 */
	if (rc) {
		smb2sr_put_errno(sr, rc);
		return (SDRC_SUCCESS);
	}

	/*
	 * SMB2 Read reply
	 */
	DataOff = SMB2_HDR_SIZE + 16;
	rc = smb_mbc_encodef(
	    &sr->reply,
	    "wb.lllC",
	    17,	/* StructSize */	/* w */
	    DataOff,			/* b. */
	    XferCount,			/* l */
	    0, /* DataRemaining */	/* l */
	    0, /* reserved */		/* l */
	    &sr->raw_data);		/* C */
	if (rc)
		return (SDRC_ERROR);

	mutex_enter(&of->f_mutex);
	of->f_seek_pos = Offset + XferCount;
	mutex_exit(&of->f_mutex);

	return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_com_locking_andx(smb_request_t *sr)
{
	unsigned short	i;
	unsigned char	lock_type;	/* See lock_type table above */
	unsigned char	oplock_level;	/* The new oplock level */
	uint32_t	timeout;	/* Milliseconds to wait for lock */
	unsigned short	unlock_num;	/* # unlock range structs */
	unsigned short	lock_num;	/* # lock range structs */
	uint32_t	save_pid;	/* Process Id of owner */
	uint32_t	offset32, length32;
	uint64_t	offset64;
	uint64_t	length64;
	DWORD		result;
	int 		rc;
	uint32_t	ltype;
	smb_ofile_t	*ofile;
	uint16_t	tmp_pid;	/* locking uses 16-bit pids */
	uint8_t		brk;

	rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type,
	    &oplock_level, &timeout, &unlock_num, &lock_num);
	if (rc != 0)
		return (SDRC_ERROR);

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

	if (lock_type & LOCKING_ANDX_SHARED_LOCK)
		ltype = SMB_LOCK_TYPE_READONLY;
	else
		ltype = SMB_LOCK_TYPE_READWRITE;

	save_pid = sr->smb_pid;	/* Save the original pid */

	if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
		if (oplock_level == 0)
			brk = SMB_OPLOCK_BREAK_TO_NONE;
		else
			brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
		smb_oplock_ack(ofile->f_node, ofile, brk);
		if (unlock_num == 0 && lock_num == 0)
			return (SDRC_NO_REPLY);
	}

	/*
	 * No support for changing locktype (although we could probably
	 * implement this)
	 */
	if (lock_type & LOCKING_ANDX_CHANGE_LOCK_TYPE) {
		smbsr_error(sr, 0, ERRDOS,
		    ERROR_ATOMIC_LOCKS_NOT_SUPPORTED);
		return (SDRC_ERROR);
	}

	/*
	 * No support for cancel lock (smbtorture expects this)
	 */
	if (lock_type & LOCKING_ANDX_CANCEL_LOCK) {
		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
		    ERRDOS, ERROR_INVALID_PARAMETER);
		return (SDRC_ERROR);
	}

	if (lock_type & LOCKING_ANDX_LARGE_FILES) {
		/*
		 * negotiated protocol should be NT LM 0.12 or later
		 */
		if (sr->session->dialect < NT_LM_0_12) {
			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
			    ERRDOS, ERROR_INVALID_PARAMETER);
			return (SDRC_ERROR);
		}

		for (i = 0; i < unlock_num; i++) {
			rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
			    &tmp_pid, &offset64, &length64);
			if (rc) {
				/*
				 * This is the error returned by Windows 2000
				 * even when STATUS32 has been negotiated.
				 */
				smbsr_error(sr, 0, ERRSRV, ERRerror);
				return (SDRC_ERROR);
			}
			sr->smb_pid = tmp_pid;	/* NB: 16-bit */

			result = smb_unlock_range(sr, sr->fid_ofile->f_node,
			    offset64, length64);
			if (result != NT_STATUS_SUCCESS) {
				smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
				    ERRDOS, ERROR_NOT_LOCKED);
				return (SDRC_ERROR);
			}
		}

		for (i = 0; i < lock_num; i++) {
			rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
			    &tmp_pid, &offset64, &length64);
			if (rc) {
				smbsr_error(sr, 0, ERRSRV, ERRerror);
				return (SDRC_ERROR);
			}
			sr->smb_pid = tmp_pid;	/* NB: 16-bit */

			result = smb_lock_range(sr, offset64, length64, timeout,
			    ltype);
			if (result != NT_STATUS_SUCCESS) {
				smb_lock_range_error(sr, result);
				return (SDRC_ERROR);
			}
		}
	} else {
		for (i = 0; i < unlock_num; i++) {
			rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid,
			    &offset32, &length32);
			if (rc) {
				smbsr_error(sr, 0, ERRSRV, ERRerror);
				return (SDRC_ERROR);
			}
			sr->smb_pid = tmp_pid;	/* NB: 16-bit */

			result = smb_unlock_range(sr, sr->fid_ofile->f_node,
			    (uint64_t)offset32, (uint64_t)length32);
			if (result != NT_STATUS_SUCCESS) {
				smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
				    ERRDOS, ERROR_NOT_LOCKED);
				return (SDRC_ERROR);
			}
		}

		for (i = 0; i < lock_num; i++) {
			rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid,
			    &offset32, &length32);
			if (rc) {
				smbsr_error(sr, 0, ERRSRV, ERRerror);
				return (SDRC_ERROR);
			}
			sr->smb_pid = tmp_pid;	/* NB: 16-bit */

			result = smb_lock_range(sr, (uint64_t)offset32,
			    (uint64_t)length32, timeout, ltype);
			if (result != NT_STATUS_SUCCESS) {
				smb_lock_range_error(sr, result);
				return (SDRC_ERROR);
			}
		}
	}

	sr->smb_pid = save_pid;
	if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0))
		return (SDRC_ERROR);
	return (SDRC_SUCCESS);
}
/*
 * smb_com_trans2_find_next2
 *
 *  Client Request                     Value
 *  ================================== =================================
 *
 *  WordCount                          15
 *  SetupCount                         1
 *  Setup[0]                           TRANS2_FIND_NEXT2
 *
 *  Parameter Block Encoding           Description
 *  ================================== =================================
 *
 *  USHORT Sid;                        Search handle
 *  USHORT SearchCount;                Maximum number of entries to
 *                                      return
 *  USHORT InformationLevel;           Levels described in
 *                                      TRANS2_FIND_FIRST2 request
 *  ULONG ResumeKey;                   Value returned by previous find2
 *                                      call
 *  USHORT Flags;                      Additional information: bit set-
 *                                      0 - close search after this
 *                                      request
 *                                      1 - close search if end of search
 *                                      reached
 *                                      2 - return resume keys for each
 *                                      entry found
 *                                      3 - resume/continue from previous
 *                                      ending place
 *                                      4 - find with backup intent
 *  STRING FileName;                   Resume file name
 *
 * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2
 * call.  If Bit3 of Flags is set, then FileName may be the NULL string,
 * since the search is continued from the previous TRANS2_FIND request.
 * Otherwise, FileName must not be more than 256 characters long.
 *
 *  Response Field                     Description
 *  ================================== =================================
 *
 *  USHORT SearchCount;                Number of entries returned
 *  USHORT EndOfSearch;                Was last entry returned?
 *  USHORT EaErrorOffset;              Offset into EA list if EA error
 *  USHORT LastNameOffset;             Offset into data to file name of
 *                                      last entry, if server needs it to
 *                                      resume search; else 0
 *  UCHAR Data[TotalDataCount]         Level dependent info about the
 *                                      matches found in the search
 *
 *
 * The last parameter in the request is a filename, which is a
 * null-terminated unicode string.
 *
 * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr,
 *    &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname)
 *
 * The filename parameter is not currently decoded because we
 * expect a 2-byte null but Mac OS 10 clients send a 1-byte null,
 * which leads to a decode error.
 * Thus, we do not support resume by filename.  We treat a request
 * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST.
 */
smb_sdrc_t
smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
{
	int			count;
	uint16_t		odid;
	uint32_t		cookie;
	smb_odir_t		*od;
	smb_find_args_t		args;
	boolean_t		eos;
	smb_odir_resume_t	odir_resume;

	bzero(&args, sizeof (smb_find_args_t));

	if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlw", sr, &odid,
	    &args.fa_maxcount, &args.fa_infolev, &cookie, &args.fa_fflag)
	    != 0) {
		return (SDRC_ERROR);
	}

	/* continuation by filename not supported */
	if ((args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) || (cookie == 0)) {
		odir_resume.or_type = SMB_ODIR_RESUME_IDX;
		odir_resume.or_idx = 0;
	} else {
		odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
		odir_resume.or_cookie = cookie;
	}

	if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
		sr->user_cr = smb_user_getprivcred(sr->uid_user);

	args.fa_maxdata =
	    smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
	if (args.fa_maxdata == 0)
		return (SDRC_ERROR);

	od = smb_tree_lookup_odir(sr->tid_tree, odid);
	if (od == NULL) {
		smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
		    ERRDOS, ERROR_INVALID_HANDLE);
		return (SDRC_ERROR);
	}
	smb_odir_resume_at(od, &odir_resume);
	count = smb_trans2_find_entries(sr, xa, od, &args, &eos);

	if (count == -1) {
		smb_odir_close(od);
		smb_odir_release(od);
		return (SDRC_ERROR);
	}

	if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
	    (eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
		smb_odir_close(od);
	} /* else leave odir open for trans2_find_next2 */

	smb_odir_release(od);
	(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
	    count, (eos) ? 1 : 0, 0, 0);

	return (SDRC_SUCCESS);
}
/*
 * smb_com_trans2_find_first2
 *
 *  Client Request                Value
 *  ============================  ==================================
 *
 *  UCHAR  WordCount              15
 *  UCHAR  TotalDataCount         Total size of extended attribute list
 *  UCHAR  SetupCount             1
 *  UCHAR  Setup[0]               TRANS2_FIND_FIRST2
 *
 *  Parameter Block Encoding      Description
 *  ============================  ==================================
 *  USHORT SearchAttributes;
 *  USHORT SearchCount;           Maximum number of entries to return
 *  USHORT Flags;                 Additional information:
 *                                Bit 0 - close search after this request
 *                                Bit 1 - close search if end of search
 *                                reached
 *                                Bit 2 - return resume keys for each
 *                                entry found
 *                                Bit 3 - continue search from previous
 *                                ending place
 *                                Bit 4 - find with backup intent
 *  USHORT InformationLevel;      See below
 *  ULONG SearchStorageType;
 *  STRING FileName;              Pattern for the search
 *  UCHAR Data[ TotalDataCount ]  FEAList if InformationLevel is
 *                                QUERY_EAS_FROM_LIST
 *
 *  Response Parameter Block      Description
 *  ============================  ==================================
 *
 *  USHORT Sid;                   Search handle
 *  USHORT SearchCount;           Number of entries returned
 *  USHORT EndOfSearch;           Was last entry returned?
 *  USHORT EaErrorOffset;         Offset into EA list if EA error
 *  USHORT LastNameOffset;        Offset into data to file name of last
 *                                entry, if server needs it to resume
 *                                search; else 0
 *  UCHAR Data[ TotalDataCount ]  Level dependent info about the matches
 *                                found in the search
 */
smb_sdrc_t
smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
{
	int		count;
	uint16_t	sattr, odid;
	char		*path;
	smb_odir_t	*od;
	smb_find_args_t	args;
	boolean_t	eos;
	uint32_t	odir_flags = 0;

	bzero(&args, sizeof (smb_find_args_t));

	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
		    ERRDOS, ERROR_ACCESS_DENIED);
		return (SDRC_ERROR);
	}

	if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr,
	    &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev, &path) != 0) {
		return (SDRC_ERROR);
	}

	if (smb_is_stream_name(path)) {
		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
		    ERRDOS, ERROR_INVALID_NAME);
		return (SDRC_ERROR);
	}

	if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) {
		sr->user_cr = smb_user_getprivcred(sr->uid_user);
		odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT;
	}

	args.fa_maxdata =
	    smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
	if (args.fa_maxdata == 0)
		return (SDRC_ERROR);

	if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
		(void) smb_convert_wildcards(path);

	odid = smb_odir_open(sr, path, sattr, odir_flags);
	if (odid == 0)
		return (SDRC_ERROR);

	od = smb_tree_lookup_odir(sr->tid_tree, odid);
	if (od == NULL)
		return (SDRC_ERROR);
	count = smb_trans2_find_entries(sr, xa, od, &args, &eos);

	if (count == -1) {
		smb_odir_close(od);
		smb_odir_release(od);
		return (SDRC_ERROR);
	}

	if (count == 0) {
		smb_odir_close(od);
		smb_odir_release(od);
		smbsr_errno(sr, ENOENT);
		return (SDRC_ERROR);
	}

	if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
	    (eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
		smb_odir_close(od);
	} /* else leave odir open for trans2_find_next2 */

	smb_odir_release(od);

	(void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww",
	    odid, count, (eos) ? 1 : 0, 0, 0);

	return (SDRC_SUCCESS);
}
/*
 * smb_com_trans2_find_next2
 *
 *  Client Request                     Value
 *  ================================== =================================
 *
 *  WordCount                          15
 *  SetupCount                         1
 *  Setup[0]                           TRANS2_FIND_NEXT2
 *
 *  Parameter Block Encoding           Description
 *  ================================== =================================
 *
 *  USHORT Sid;                        Search handle
 *  USHORT SearchCount;                Maximum number of entries to
 *                                      return
 *  USHORT InformationLevel;           Levels described in
 *                                      TRANS2_FIND_FIRST2 request
 *  ULONG ResumeKey;                   Value returned by previous find2
 *                                      call
 *  USHORT Flags;                      Additional information: bit set-
 *                                      0 - close search after this
 *                                      request
 *                                      1 - close search if end of search
 *                                      reached
 *                                      2 - return resume keys for each
 *                                      entry found
 *                                      3 - resume/continue from previous
 *                                      ending place
 *                                      4 - find with backup intent
 *  STRING FileName;                   Resume file name
 *
 * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2
 * call.  If Bit3 of Flags is set, then FileName may be the NULL string,
 * since the search is continued from the previous TRANS2_FIND request.
 * Otherwise, FileName must not be more than 256 characters long.
 *
 *  Response Field                     Description
 *  ================================== =================================
 *
 *  USHORT SearchCount;                Number of entries returned
 *  USHORT EndOfSearch;                Was last entry returned?
 *  USHORT EaErrorOffset;              Offset into EA list if EA error
 *  USHORT LastNameOffset;             Offset into data to file name of
 *                                      last entry, if server needs it to
 *                                      resume search; else 0
 *  UCHAR Data[TotalDataCount]         Level dependent info about the
 *                                      matches found in the search
 *
 *
 * The last parameter in the request is a filename, which is a
 * null-terminated unicode string.
 *
 * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr,
 *    &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname)
 *
 * The filename parameter is not currently decoded because we
 * expect a 2-byte null but Mac OS 10 clients send a 1-byte null,
 * which leads to a decode error.
 * Thus, we do not support resume by filename.  We treat a request
 * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST.
 */
smb_sdrc_t
smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
{
	int			count;
	uint16_t		odid;
	smb_odir_t		*od;
	smb_find_args_t		args;
	smb_odir_resume_t	odir_resume;

	bzero(&args, sizeof (args));
	bzero(&odir_resume, sizeof (odir_resume));

	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
		    ERRDOS, ERROR_ACCESS_DENIED);
		return (SDRC_ERROR);
	}

	if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr,
	    &odid, &args.fa_maxcount, &args.fa_infolev,
	    &odir_resume.or_cookie, &args.fa_fflag,
	    &odir_resume.or_fname) != 0) {
		return (SDRC_ERROR);
	}

	if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
		sr->user_cr = smb_user_getprivcred(sr->uid_user);

	args.fa_maxdata =
	    smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
	if (args.fa_maxdata == 0)
		return (SDRC_ERROR);

	od = smb_tree_lookup_odir(sr->tid_tree, odid);
	if (od == NULL) {
		smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
		    ERRDOS, ERROR_INVALID_HANDLE);
		return (SDRC_ERROR);
	}

	/*
	 * Set the correct position in the directory.
	 *
	 * "Continue from last" is easy, but due to a history of
	 * buggy server implementations, most clients don't use
	 * that method.  The most widely used (and reliable) is
	 * resume by file name.  Unfortunately, that can't really
	 * be fully supported unless your file system stores all
	 * directory entries in some sorted order (like NTFS).
	 * We can partially support resume by name, where the only
	 * name we're ever asked to resume on is the same as the
	 * most recent we returned.  That's always what the client
	 * gives us as the resume name, so we can simply remember
	 * the last name/offset pair and use that to position on
	 * the following FindNext call.  In the unlikely event
	 * that the client asks to resume somewhere else, we'll
	 * use the numeric resume key, and hope the client gives
	 * correctly uses one of the resume keys we provided.
	 */
	if (args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) {
		odir_resume.or_type = SMB_ODIR_RESUME_CONT;
	} else {
		odir_resume.or_type = SMB_ODIR_RESUME_FNAME;
	}
	smb_odir_resume_at(od, &odir_resume);

	count = smb_trans2_find_entries(sr, xa, od, &args);
	if (count == -1) {
		smb_odir_close(od);
		smb_odir_release(od);
		return (SDRC_ERROR);
	}

	if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
	    (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
		smb_odir_close(od);
	} /* else leave odir open for trans2_find_next2 */

	smb_odir_release(od);

	(void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
	    count,	/* Search Count */
	    args.fa_eos, /* End Of Search */
	    0,		/* EA Error Offset */
	    args.fa_lno); /* Last Name Offset */

	return (SDRC_SUCCESS);
}
/*
 * smb_nt_transact_notify_change
 *
 * This function is responsible for processing NOTIFY CHANGE requests.
 * Requests are stored in a global queue. This queue is processed when
 * a monitored directory is changed or client cancels one of its already
 * sent requests.
 */
smb_sdrc_t
smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa)
{
	uint32_t		CompletionFilter;
	unsigned char		WatchTree;
	smb_node_t		*node;

	if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
	    &CompletionFilter, &sr->smb_fid, &WatchTree) != 0)
		return (SDRC_NOT_IMPLEMENTED);

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

	node = sr->fid_ofile->f_node;

	if (!smb_node_is_dir(node)) {
		/*
		 * Notify change requests are only valid on directories.
		 */
		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 0, 0);
		return (SDRC_ERROR);
	}

	mutex_enter(&sr->sr_mutex);
	switch (sr->sr_state) {
	case SMB_REQ_STATE_ACTIVE:
		node->waiting_event++;
		node->flags |= NODE_FLAGS_NOTIFY_CHANGE;
		if ((node->flags & NODE_FLAGS_CHANGED) == 0) {
			sr->sr_ncr.nc_node = node;
			sr->sr_ncr.nc_flags = CompletionFilter;
			if (WatchTree)
				sr->sr_ncr.nc_flags |= NODE_FLAGS_WATCH_TREE;

			sr->sr_keep = B_TRUE;
			sr->sr_state = SMB_REQ_STATE_WAITING_EVENT;

			smb_slist_insert_tail(&smb_ncr_list, sr);

			/*
			 * Monitor events system-wide.
			 *
			 * XXX: smb_node_ref() and smb_node_release()
			 * take &node->n_lock.  May need alternate forms
			 * of these routines if node->n_lock is taken
			 * around calls to smb_fem_fcn_install() and
			 * smb_fem_fcn_uninstall().
			 */

			smb_fem_fcn_install(node);

			mutex_exit(&sr->sr_mutex);
			return (SDRC_SR_KEPT);
		} else {
			/* node already changed, reply immediately */
			if (--node->waiting_event == 0)
				node->flags &=
				    ~(NODE_FLAGS_NOTIFY_CHANGE |
				    NODE_FLAGS_CHANGED);
			mutex_exit(&sr->sr_mutex);
			return (SDRC_SUCCESS);
		}

	case SMB_REQ_STATE_CANCELED:
		mutex_exit(&sr->sr_mutex);
		smbsr_error(sr, NT_STATUS_CANCELLED, 0, 0);
		return (SDRC_ERROR);

	default:
		ASSERT(0);
		mutex_exit(&sr->sr_mutex);
		return (SDRC_SUCCESS);
	}
}
/*
 * smb_nt_transact_create
 *
 * This command is used to create or open a file or directory, when EAs
 * or an SD must be applied to the file. The request parameter block
 * encoding, data block encoding and output parameter block encoding are
 * described in CIFS section 4.2.2.
 *
 * The format of the command is SmbNtTransact but it is basically the same
 * as SmbNtCreateAndx with the option to supply extended attributes or a
 * security descriptor. For information not defined in CIFS section 4.2.2
 * see section 4.2.1 (NT_CREATE_ANDX).
 */
smb_sdrc_t
smb_pre_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
{
	struct open_param *op = &sr->arg.open;
	uint8_t SecurityFlags;
	uint32_t EaLength;
	uint32_t ImpersonationLevel;
	uint32_t NameLength;
	uint32_t sd_len;
	uint32_t status;
	smb_sd_t sd;
	int rc;

	bzero(op, sizeof (sr->arg.open));

	rc = smb_mbc_decodef(&xa->req_param_mb, "%lllqllllllllb",
	    sr,
	    &op->nt_flags,
	    &op->rootdirfid,
	    &op->desired_access,
	    &op->dsize,
	    &op->dattr,
	    &op->share_access,
	    &op->create_disposition,
	    &op->create_options,
	    &sd_len,
	    &EaLength,
	    &NameLength,
	    &ImpersonationLevel,
	    &SecurityFlags);

	if (rc == 0) {
		if (NameLength == 0) {
			op->fqi.fq_path.pn_path = "\\";
		} else if (NameLength >= MAXPATHLEN) {
			smbsr_error(sr, NT_STATUS_OBJECT_PATH_NOT_FOUND,
			    ERRDOS, ERROR_PATH_NOT_FOUND);
			rc = -1;
		} else {
			rc = smb_mbc_decodef(&xa->req_param_mb, "%#u",
			    sr, NameLength, &op->fqi.fq_path.pn_path);
		}
	}

	op->op_oplock_level = SMB_OPLOCK_NONE;
	if (op->nt_flags & NT_CREATE_FLAG_REQUEST_OPLOCK) {
		if (op->nt_flags & NT_CREATE_FLAG_REQUEST_OPBATCH)
			op->op_oplock_level = SMB_OPLOCK_BATCH;
		else
			op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
	}

	if (sd_len) {
		status = smb_decode_sd(xa, &sd);
		if (status != NT_STATUS_SUCCESS) {
			smbsr_error(sr, status, 0, 0);
			return (SDRC_ERROR);
		}
		op->sd = kmem_alloc(sizeof (smb_sd_t), KM_SLEEP);
		*op->sd = sd;
	} else {
		op->sd = NULL;
	}

	DTRACE_SMB_2(op__NtTransactCreate__start, smb_request_t *, sr,
	    struct open_param *, op);

	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
Exemple #19
0
smb_sdrc_t
smb_com_echo(struct smb_request *sr)
{
	unsigned short necho;
	unsigned short nbytes;
	unsigned short i;
	struct mbuf_chain reply;
	char *data;
	uint16_t	pid_hi, pid_lo;

	pid_hi = sr->smb_pid >> 16;
	pid_lo = (uint16_t)sr->smb_pid;

	if (smbsr_decode_vwv(sr, "w", &necho) != 0)
		return (SDRC_ERROR);

	/*
	 * Don't let the client fool us into doing
	 * more work than is "reasonable".
	 */
	if (necho > smb_max_echo)
		necho = smb_max_echo;

	nbytes = sr->smb_bcc;
	data = smb_srm_zalloc(sr, nbytes);

	if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data))
		return (SDRC_ERROR);

	for (i = 1; i <= necho; ++i) {

		/*
		 * According to [MS-CIFS] 3.3.5.32 echo is
		 * subject to cancellation.
		 */
		if (sr->sr_state != SMB_REQ_STATE_ACTIVE)
			break;

		MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes);

		(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,
		    pid_hi,
		    sr->smb_sig,
		    sr->smb_tid,
		    pid_lo,
		    sr->smb_uid,
		    sr->smb_mid);

		(void) smb_mbc_encodef(&reply, "bww#c", 1, i,
		    nbytes, nbytes, data);

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

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

		delay(MSEC_TO_TICK(100));
	}

	return (SDRC_NO_REPLY);
}
Exemple #20
0
smb_sdrc_t
smb2_close(smb_request_t *sr)
{
	smb_attr_t attr;
	smb_ofile_t *of;
	uint16_t StructSize;
	uint16_t Flags;
	uint32_t reserved;
	smb2fid_t smb2fid;
	uint32_t status;
	int rc = 0;

	/*
	 * SMB2 Close request
	 */
	rc = smb_mbc_decodef(
	    &sr->smb_data, "wwlqq",
	    &StructSize,		/* w */
	    &Flags,			/* w */
	    &reserved,			/* l */
	    &smb2fid.persistent,	/* q */
	    &smb2fid.temporal);		/* q */
	if (rc)
		return (SDRC_ERROR);
	if (StructSize != 24)
		return (SDRC_ERROR);

	status = smb2sr_lookup_fid(sr, &smb2fid);
	if (status) {
		smb2sr_put_error(sr, status);
		return (SDRC_SUCCESS);
	}
	of = sr->fid_ofile;

	bzero(&attr, sizeof (attr));
	if (Flags & SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) {
		attr.sa_mask = SMB_AT_ALL;
		status = smb2_ofile_getattr(sr, of, &attr);
		if (status) {
			/*
			 * We could not stat the open file.
			 * Let's not fail the close call,
			 * but just turn off the flag.
			 */
			Flags = 0;
		}
	}

	smb_ofile_close(of, 0);

	/*
	 * SMB2 Close reply
	 */
	(void) smb_mbc_encodef(
	    &sr->reply,
	    "wwlTTTTqql",
	    60,	/* StructSize */	/* w */
	    Flags,			/* w */
	    0, /* reserved */		/* l */
	    &attr.sa_crtime,		/* T */
	    &attr.sa_vattr.va_atime,	/* T */
	    &attr.sa_vattr.va_mtime,	/* T */
	    &attr.sa_vattr.va_ctime,	/* T */
	    attr.sa_allocsz,		/* q */
	    attr.sa_vattr.va_size,	/* q */
	    attr.sa_dosattr);		/* l */

	return (SDRC_SUCCESS);
}