#include <sys/sdt.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/netbios.h>
static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
smb_sdrc_t
smb_pre_write(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off;
uint16_t count;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
param->rw_magic = SMB_RW_MAGIC;
rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
param->rw_count = (uint32_t)count;
param->rw_offset = (uint64_t)off;
param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
DTRACE_SMB_START(op__Write, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_write(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__Write, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_write(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
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 == 0) {
rc = smb_write_truncate(sr, param);
} else {
rc = smbsr_decode_data(sr, "D", ¶m->rw_vdb);
if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (SDRC_ERROR);
}
param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
rc = smb_common_write(sr, param);
}
if (rc != 0) {
if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
(uint16_t)param->rw_count, 0);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_pre_write_and_close(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off;
uint16_t count;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
param->rw_magic = SMB_RW_MAGIC;
if (sr->smb_wct == 12) {
rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
&count, &off, ¶m->rw_last_write);
} else {
rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
&count, &off, ¶m->rw_last_write);
}
param->rw_count = (uint32_t)count;
param->rw_offset = (uint64_t)off;
DTRACE_SMB_START(op__WriteAndClose, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_write_and_close(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__WriteAndClose, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_write_and_close(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
uint16_t count;
int rc = 0;
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 == 0) {
rc = smb_write_truncate(sr, param);
} else {
rc = smbsr_decode_data(sr, ".#B", param->rw_count,
¶m->rw_vdb);
if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (SDRC_ERROR);
}
param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
rc = smb_common_write(sr, param);
}
if (rc != 0) {
if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
smb_ofile_close(sr->fid_ofile, param->rw_last_write);
count = (uint16_t)param->rw_count;
rc = smbsr_encode_result(sr, 1, 0, "bww", 1, count, 0);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_pre_write_and_unlock(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off;
uint16_t count;
uint16_t remcnt;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
param->rw_magic = SMB_RW_MAGIC;
rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
param->rw_count = (uint32_t)count;
param->rw_offset = (uint64_t)off;
DTRACE_SMB_START(op__WriteAndUnlock, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_write_and_unlock(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__WriteAndUnlock, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_write_and_unlock(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
uint32_t lk_pid;
uint32_t status;
int rc = 0;
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);
if (param->rw_count == 0) {
rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
rc = smbsr_decode_data(sr, "D", ¶m->rw_vdb);
if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (SDRC_ERROR);
}
param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
if ((rc = smb_common_write(sr, param)) != 0) {
if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
lk_pid = sr->smb_pid & 0xFFFF;
status = smb_unlock_range(sr, param->rw_offset,
(uint64_t)param->rw_count, lk_pid);
if (status != NT_STATUS_SUCCESS) {
smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
ERRDOS, ERROR_NOT_LOCKED);
return (SDRC_ERROR);
}
rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
(uint16_t)param->rw_count, 0);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_pre_write_raw(smb_request_t *sr)
{
DTRACE_SMB_START(op__WriteRaw, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_write_raw(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__WriteRaw, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_write_raw(struct smb_request *sr)
{
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, ERRDOS,
ERROR_NOT_SUPPORTED);
return (SDRC_ERROR);
}
smb_sdrc_t
smb_pre_write_andx(smb_request_t *sr)
{
smb_rw_param_t *param;
uint32_t off_low;
uint32_t off_high;
uint16_t datalen_low;
uint16_t datalen_high;
uint16_t remcnt;
int rc;
param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
sr->arg.rw = param;
param->rw_magic = SMB_RW_MAGIC;
if (sr->smb_wct == 14) {
rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
&off_low, ¶m->rw_mode, &remcnt, &datalen_high,
&datalen_low, ¶m->rw_dsoff, &off_high);
if (param->rw_dsoff >= 63)
param->rw_dsoff -= 63;
param->rw_offset = ((uint64_t)off_high << 32) | off_low;
} else if (sr->smb_wct == 12) {
rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
&off_low, ¶m->rw_mode, &remcnt, &datalen_high,
&datalen_low, ¶m->rw_dsoff);
if (param->rw_dsoff >= 59)
param->rw_dsoff -= 59;
param->rw_offset = (uint64_t)off_low;
} else {
rc = -1;
}
param->rw_count = (uint32_t)datalen_low;
if ((sr->session->capabilities & CAP_LARGE_WRITEX) != 0 ||
(sr->smb_data.max_bytes > (sr->smb_data.chain_offset + 0xFFFF)))
param->rw_count |= ((uint32_t)datalen_high << 16);
DTRACE_SMB_START(op__WriteX, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_write_andx(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__WriteX, smb_request_t *, sr);
kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
}
smb_sdrc_t
smb_com_write_andx(smb_request_t *sr)
{
smb_rw_param_t *param = sr->arg.rw;
uint16_t count_high;
uint16_t count_low;
int rc;
ASSERT(param);
ASSERT(param->rw_magic == SMB_RW_MAGIC);
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 (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
STYPE_ISIPC(sr->tid_tree->t_res_type)) {
smbsr_error(sr, 0, ERRSRV, ERRaccess);
return (SDRC_ERROR);
}
rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
¶m->rw_vdb);
if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (SDRC_ERROR);
}
param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
if (param->rw_count != 0) {
if ((rc = smb_common_write(sr, param)) != 0) {
if (sr->smb_error.status !=
NT_STATUS_FILE_LOCK_CONFLICT)
smbsr_errno(sr, rc);
return (SDRC_ERROR);
}
}
count_low = param->rw_count & 0xFFFF;
count_high = (param->rw_count >> 16) & 0xFF;
rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
int
smb_common_write(smb_request_t *sr, smb_rw_param_t *param)
{
smb_ofile_t *ofile = sr->fid_ofile;
smb_node_t *node;
int stability = 0;
uint32_t lcount;
int rc = 0;
switch (sr->tid_tree->t_res_type & STYPE_MASK) {
case STYPE_DISKTREE:
case STYPE_PRINTQ:
node = ofile->f_node;
if (!smb_node_is_dir(node)) {
rc = smb_lock_range_access(sr, node, param->rw_offset,
param->rw_count, B_TRUE);
if (rc != NT_STATUS_SUCCESS) {
smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
ERRDOS, ERROR_LOCK_VIOLATION);
return (EACCES);
}
}
if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
(node->flags & NODE_FLAGS_WRITE_THROUGH)) {
stability = FSYNC;
}
rc = smb_fsop_write(sr, sr->user_cr, node, ofile,
¶m->rw_vdb.vdb_uio, &lcount, stability);
if (rc)
return (rc);
param->rw_count = lcount;
(void) smb_oplock_break_WRITE(node, ofile);
if (ofile->f_written == B_FALSE) {
ofile->f_written = B_TRUE;
smb_node_notify_modified(node);
}
break;
case STYPE_IPC:
param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
if ((rc = smb_opipe_write(sr, ¶m->rw_vdb.vdb_uio)) != 0)
param->rw_count = 0;
break;
default:
rc = EACCES;
break;
}
if (rc != 0)
return (rc);
mutex_enter(&ofile->f_mutex);
ofile->f_seek_pos = param->rw_offset + param->rw_count;
mutex_exit(&ofile->f_mutex);
return (rc);
}
static int
smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
{
smb_ofile_t *ofile = sr->fid_ofile;
smb_node_t *node = ofile->f_node;
smb_attr_t attr;
uint32_t status;
int rc;
if (STYPE_ISIPC(sr->tid_tree->t_res_type))
return (0);
mutex_enter(&node->n_mutex);
if (!smb_node_is_dir(node)) {
status = smb_lock_range_access(sr, node, param->rw_offset,
param->rw_count, B_TRUE);
if (status != NT_STATUS_SUCCESS) {
mutex_exit(&node->n_mutex);
smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
ERRDOS, ERROR_LOCK_VIOLATION);
return (EACCES);
}
}
mutex_exit(&node->n_mutex);
bzero(&attr, sizeof (smb_attr_t));
attr.sa_mask = SMB_AT_SIZE;
attr.sa_vattr.va_size = param->rw_offset;
rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr);
if (rc != 0)
return (rc);
mutex_enter(&ofile->f_mutex);
ofile->f_seek_pos = param->rw_offset + param->rw_count;
mutex_exit(&ofile->f_mutex);
return (0);
}