#include <smbsrv/smb2_kproto.h>
#include <smb/winioctl.h>
smb_sdrc_t
smb2_ioctl(smb_request_t *sr)
{
smb2fid_t smb2fid;
smb_fsctl_t fsctl;
mbuf_chain_t in_mbc;
uint32_t InputOffset;
uint32_t MaxInputResp;
uint32_t OutputOffset;
uint32_t Flags;
uint32_t status = 0;
uint16_t StructSize;
uint16_t DeviceType;
int rc = 0;
bzero(&in_mbc, sizeof (in_mbc));
rc = smb_mbc_decodef(
&sr->smb_data, "w..lqqlllllll4.",
&StructSize,
&fsctl.CtlCode,
&smb2fid.persistent,
&smb2fid.temporal,
&InputOffset,
&fsctl.InputCount,
&MaxInputResp,
&OutputOffset,
&fsctl.OutputCount,
&fsctl.MaxOutputResp,
&Flags);
if (rc || StructSize != 57)
return (SDRC_ERROR);
if (fsctl.InputCount) {
if (InputOffset < (SMB2_HDR_SIZE + 56))
return (SDRC_ERROR);
if (fsctl.InputCount > smb2_max_trans)
return (SDRC_ERROR);
rc = MBC_SHADOW_CHAIN(&in_mbc, &sr->smb_data,
sr->smb2_cmd_hdr + InputOffset, fsctl.InputCount);
if (rc) {
return (SDRC_ERROR);
}
}
fsctl.in_mbc = &in_mbc;
if (fsctl.MaxOutputResp > smb2_max_trans)
fsctl.MaxOutputResp = smb2_max_trans;
sr->raw_data.max_bytes = fsctl.MaxOutputResp;
fsctl.out_mbc = &sr->raw_data;
if (Flags != SMB2_0_IOCTL_IS_FSCTL) {
status = NT_STATUS_NOT_SUPPORTED;
} else switch (fsctl.CtlCode) {
case FSCTL_DFS_GET_REFERRALS:
case FSCTL_DFS_GET_REFERRALS_EX:
case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
case FSCTL_VALIDATE_NEGOTIATE_INFO:
case FSCTL_PIPE_WAIT:
if (smb2fid.temporal != ~0LL ||
smb2fid.persistent != ~0LL) {
status = NT_STATUS_INVALID_PARAMETER;
}
break;
default:
status = smb2sr_lookup_fid(sr, &smb2fid);
if (status != 0) {
status = NT_STATUS_FILE_CLOSED;
}
break;
}
DTRACE_SMB2_START(op__Ioctl, smb_request_t *, sr);
if (status)
goto errout;
DeviceType = fsctl.CtlCode >> 16;
switch (DeviceType) {
case FILE_DEVICE_DFS:
status = smb_dfs_fsctl(sr, &fsctl);
break;
case FILE_DEVICE_FILE_SYSTEM:
status = smb2_fsctl_fs(sr, &fsctl);
break;
case FILE_DEVICE_NAMED_PIPE:
status = smb_opipe_fsctl(sr, &fsctl);
break;
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
status = smb2_fsctl_netfs(sr, &fsctl);
break;
default:
status = NT_STATUS_NOT_SUPPORTED;
break;
}
errout:
sr->smb2_status = status;
DTRACE_SMB2_DONE(op__Ioctl, smb_request_t *, sr);
if (status != 0) {
if ((NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR) &&
(fsctl.CtlCode != FSCTL_SRV_COPYCHUNK) &&
(fsctl.CtlCode != FSCTL_SRV_COPYCHUNK_WRITE)) {
smb2sr_put_error(sr, status);
return (SDRC_SUCCESS);
}
}
fsctl.InputCount = 0;
InputOffset = SMB2_HDR_SIZE + 48;
fsctl.OutputCount = MBC_LENGTH(&sr->raw_data);
OutputOffset = (fsctl.OutputCount) ? InputOffset : 0;
StructSize = 49;
rc = smb_mbc_encodef(
&sr->reply, "w..lqqlllll4.#C",
StructSize,
fsctl.CtlCode,
smb2fid.persistent,
smb2fid.temporal,
InputOffset,
fsctl.InputCount,
OutputOffset,
fsctl.OutputCount,
0,
fsctl.OutputCount,
&sr->raw_data);
if (rc)
sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
return (SDRC_SUCCESS);
}