#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#define SMB_CORE_READ_MAX 4432
#define SMB_READX_MAX 0x10000
int smb_common_read(smb_request_t *, smb_rw_param_t *);
smb_sdrc_t
smb_pre_read(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off_low;
uint16_t count;
uint16_t remcnt;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid,
&count, &off_low, &remcnt);
param->rw_offset = (uint64_t)off_low;
param->rw_count = (uint32_t)count;
param->rw_mincnt = 0;
DTRACE_SMB_START(op__Read, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_read(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__Read, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_read(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
uint16_t count;
int rc;
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
if (param->rw_count > SMB_CORE_READ_MAX)
param->rw_count = SMB_CORE_READ_MAX;
if ((rc = smb_common_read(sr, param)) != 0) {
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
count = (uint16_t)param->rw_count;
rc = smbsr_encode_result(sr, 5, VAR_BCC, "bw8.wbwC",
5, count, VAR_BCC, 0x01, count, &sr->raw_data);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_pre_lock_and_read(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off_low;
uint16_t count;
uint16_t remcnt;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid,
&count, &off_low, &remcnt);
param->rw_offset = (uint64_t)off_low;
param->rw_count = (uint32_t)count;
param->rw_mincnt = 0;
DTRACE_SMB_START(op__LockAndRead, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_lock_and_read(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__LockAndRead, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_lock_and_read(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
DWORD status;
uint32_t lk_pid;
uint16_t count;
int rc;
if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
return (SDRC_ERROR);
}
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
lk_pid = sr->smb_pid & 0xFFFF;
status = smb_lock_range(sr, param->rw_offset, (uint64_t)param->rw_count,
lk_pid, SMB_LOCK_TYPE_READWRITE, 0);
if (status != NT_STATUS_SUCCESS) {
smb_lock_range_error(sr, status);
return (SDRC_ERROR);
}
if (param->rw_count > SMB_CORE_READ_MAX)
param->rw_count = SMB_CORE_READ_MAX;
if ((rc = smb_common_read(sr, param)) != 0) {
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
count = (uint16_t)param->rw_count;
rc = smbsr_encode_result(sr, 5, VAR_BCC, "bw8.wbwC",
5, count, VAR_BCC, 0x1, count, &sr->raw_data);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_pre_read_raw(smb_request_t *sr)
{
DTRACE_SMB_START(op__ReadRaw, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_read_raw(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__ReadRaw, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_read_raw(smb_request_t *sr)
{
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, ERRDOS,
ERROR_NOT_SUPPORTED);
return (SDRC_ERROR);
}
smb_sdrc_t
smb_pre_read_andx(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off_low;
uint32_t off_high;
uint32_t maxcnt_high;
uint16_t maxcnt_low;
uint16_t mincnt;
uint16_t remcnt;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
if (sr->smb_wct == 12) {
rc = smbsr_decode_vwv(sr, "b3.wlwwlwl", ¶m->rw_andx,
&sr->smb_fid, &off_low, &maxcnt_low, &mincnt, &maxcnt_high,
&remcnt, &off_high);
param->rw_offset = ((uint64_t)off_high << 32) |
(uint64_t)off_low;
param->rw_count = (uint32_t)maxcnt_low;
if ((sr->session->capabilities & CAP_LARGE_READX) &&
(maxcnt_high < 0xFF))
param->rw_count |= maxcnt_high << 16;
} else {
rc = smbsr_decode_vwv(sr, "b3.wlwwlw", ¶m->rw_andx,
&sr->smb_fid, &off_low, &maxcnt_low, &mincnt, &maxcnt_high,
&remcnt);
param->rw_offset = (uint64_t)off_low;
param->rw_count = (uint32_t)maxcnt_low;
}
param->rw_mincnt = 0;
DTRACE_SMB_START(op__ReadX, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_read_andx(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__ReadX, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_read_andx(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
uint16_t datalen_high;
uint16_t datalen_low;
uint16_t data_offset;
uint16_t offset2;
int rc;
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
if (param->rw_count >= SMB_READX_MAX)
param->rw_count = 0;
if ((rc = smb_common_read(sr, param)) != 0) {
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
datalen_low = param->rw_count & 0xFFFF;
datalen_high = (param->rw_count >> 16) & 0xFF;
data_offset = (sr->andx_prev_wct == 0) ? 0 : sr->andx_prev_wct + 1;
if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
data_offset += 60;
offset2 = (param->rw_andx == 0xFF) ? 0 : param->rw_count + 60;
rc = smbsr_encode_result(sr, 12, VAR_BCC, "bb1.ww4.www8.wbC",
12,
param->rw_andx,
offset2,
0,
datalen_low,
data_offset,
datalen_high,
VAR_BCC,
0x00,
&sr->raw_data);
} else {
data_offset += 59;
offset2 = (param->rw_andx == 0xFF) ? 0 : param->rw_count + 59;
rc = smbsr_encode_result(sr, 12, VAR_BCC, "bb1.ww4.www8.wC",
12,
param->rw_andx,
offset2,
-1,
datalen_low,
data_offset,
datalen_high,
VAR_BCC,
&sr->raw_data);
}
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
int
smb_common_read(smb_request_t *sr, smb_rw_param_t *param)
{
smb_ofile_t *ofile = sr->fid_ofile;
smb_node_t *node;
smb_vdb_t *vdb = ¶m->rw_vdb;
struct mbuf *top;
int rc;
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 = param->rw_count;
vdb->vdb_uio.uio_loffset = (offset_t)param->rw_offset;
vdb->vdb_uio.uio_segflg = UIO_SYSSPACE;
vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
switch (sr->tid_tree->t_res_type & STYPE_MASK) {
case STYPE_DISKTREE:
node = ofile->f_node;
if (!smb_node_is_dir(node)) {
rc = smb_lock_range_access(sr, node, param->rw_offset,
param->rw_count, B_FALSE);
if (rc != NT_STATUS_SUCCESS) {
rc = ERANGE;
break;
}
}
if ((ofile->f_flags & SMB_OFLAGS_EXECONLY) &&
!(sr->smb_flg2 & SMB_FLAGS2_READ_IF_EXECUTE)) {
rc = EACCES;
break;
}
sr->raw_data.max_bytes = vdb->vdb_uio.uio_resid;
top = smb_mbuf_allocate(&vdb->vdb_uio);
rc = smb_fsop_read(sr, sr->user_cr, node, ofile,
&vdb->vdb_uio, 0);
sr->raw_data.max_bytes -= vdb->vdb_uio.uio_resid;
smb_mbuf_trim(top, sr->raw_data.max_bytes);
MBC_ATTACH_MBUF(&sr->raw_data, top);
break;
case STYPE_IPC:
sr->raw_data.max_bytes = vdb->vdb_uio.uio_resid;
top = smb_mbuf_allocate(&vdb->vdb_uio);
rc = smb_opipe_read(sr, &vdb->vdb_uio);
sr->raw_data.max_bytes -= vdb->vdb_uio.uio_resid;
smb_mbuf_trim(top, sr->raw_data.max_bytes);
MBC_ATTACH_MBUF(&sr->raw_data, top);
break;
default:
rc = EACCES;
break;
}
param->rw_count -= vdb->vdb_uio.uio_resid;
if (rc != 0)
return (rc);
if (param->rw_mincnt != 0 && param->rw_count < param->rw_mincnt) {
param->rw_count = 0;
}
param->rw_offset += param->rw_count;
mutex_enter(&sr->fid_ofile->f_mutex);
ofile->f_seek_pos = param->rw_offset;
mutex_exit(&sr->fid_ofile->f_mutex);
return (rc);
}