#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
static int smb_set_by_path(smb_request_t *, smb_xa_t *, uint16_t);
static uint32_t smb_set_fileinfo(smb_request_t *, smb_setinfo_t *, int);
static uint32_t smb_set_information(smb_request_t *, smb_setinfo_t *);
static uint32_t smb_set_information2(smb_request_t *, smb_setinfo_t *);
static uint32_t smb_set_standard_info(smb_request_t *, smb_setinfo_t *);
static uint32_t smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *);
smb_sdrc_t
smb_com_trans2_set_file_information(smb_request_t *sr, smb_xa_t *xa)
{
uint16_t infolev;
if (smb_mbc_decodef(&xa->req_param_mb, "ww",
&sr->smb_fid, &infolev) != 0)
return (SDRC_ERROR);
if (smb_set_by_fid(sr, xa, infolev) != 0)
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_com_trans2_set_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_set_by_path(sr, xa, infolev) != 0)
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_pre_set_information(smb_request_t *sr)
{
DTRACE_SMB_START(op__SetInformation, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_set_information(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__SetInformation, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_set_information(smb_request_t *sr)
{
uint16_t infolev = SMB_SET_INFORMATION;
smb_fqi_t *fqi = &sr->arg.dirop.fqi;
if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (SDRC_ERROR);
}
if (smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path) != 0)
return (SDRC_ERROR);
if (smb_set_by_path(sr, NULL, infolev) != 0)
return (SDRC_ERROR);
if (smbsr_encode_empty_result(sr) != 0)
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_pre_set_information2(smb_request_t *sr)
{
DTRACE_SMB_START(op__SetInformation2, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_set_information2(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__SetInformation2, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_set_information2(smb_request_t *sr)
{
uint16_t infolev = SMB_SET_INFORMATION2;
if (smbsr_decode_vwv(sr, "w", &sr->smb_fid) != 0)
return (SDRC_ERROR);
if (smb_set_by_fid(sr, NULL, infolev) != 0)
return (SDRC_ERROR);
if (smbsr_encode_empty_result(sr) != 0)
return (SDRC_ERROR);
return (SDRC_SUCCESS);
}
static int
smb_set_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
{
smb_setinfo_t sinfo;
uint32_t status;
int rc = 0;
if (SMB_TREE_IS_READONLY(sr)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
if (STYPE_ISIPC(sr->tid_tree->t_res_type))
return (0);
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (-1);
}
if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
smbsr_release_file(sr);
return (0);
}
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
bzero(&sinfo, sizeof (sinfo));
sinfo.si_node = sr->fid_ofile->f_node;
if (xa != NULL)
sinfo.si_data = xa->req_data_mb;
status = smb_set_fileinfo(sr, &sinfo, infolev);
if (status != 0) {
smbsr_error(sr, status, 0, 0);
rc = -1;
}
smbsr_release_file(sr);
return (rc);
}
static int
smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
{
int rc;
uint32_t status;
smb_setinfo_t sinfo;
smb_node_t *node, *dnode;
char *name;
smb_pathname_t *pn;
if (SMB_TREE_IS_READONLY(sr)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
if (STYPE_ISIPC(sr->tid_tree->t_res_type))
return (0);
pn = &sr->arg.dirop.fqi.fq_path;
smb_pathname_init(sr, pn, pn->pn_path);
if (!smb_pathname_validate(sr, pn))
return (-1);
name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path,
sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dnode, name);
if (rc == 0) {
rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
sr->tid_tree->t_snode, dnode, name, &node);
smb_node_release(dnode);
}
kmem_free(name, MAXNAMELEN);
if (rc != 0) {
smbsr_errno(sr, rc);
return (-1);
}
bzero(&sinfo, sizeof (sinfo));
sinfo.si_node = node;
if (xa != NULL)
sinfo.si_data = xa->req_data_mb;
status = smb_set_fileinfo(sr, &sinfo, infolev);
if (status != 0) {
smbsr_error(sr, status, 0, 0);
rc = -1;
}
smb_node_release(node);
return (rc);
}
static uint32_t
smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo, int infolev)
{
uint32_t status;
switch (infolev) {
case SMB_SET_INFORMATION:
status = smb_set_information(sr, sinfo);
break;
case SMB_SET_INFORMATION2:
status = smb_set_information2(sr, sinfo);
break;
case SMB_INFO_STANDARD:
status = smb_set_standard_info(sr, sinfo);
break;
case SMB_INFO_SET_EAS:
status = NT_STATUS_EAS_NOT_SUPPORTED;
break;
case SMB_SET_FILE_BASIC_INFO:
case SMB_FILE_BASIC_INFORMATION:
status = smb_set_basic_info(sr, sinfo);
break;
case SMB_SET_FILE_DISPOSITION_INFO:
case SMB_FILE_DISPOSITION_INFORMATION:
status = smb_set_disposition_info(sr, sinfo);
break;
case SMB_SET_FILE_END_OF_FILE_INFO:
case SMB_FILE_END_OF_FILE_INFORMATION:
status = smb_set_eof_info(sr, sinfo);
break;
case SMB_SET_FILE_ALLOCATION_INFO:
case SMB_FILE_ALLOCATION_INFORMATION:
status = smb_set_alloc_info(sr, sinfo);
break;
case SMB_FILE_RENAME_INFORMATION:
status = smb_set_rename_info(sr, sinfo);
break;
case SMB_FILE_LINK_INFORMATION:
status = NT_STATUS_NOT_SUPPORTED;
break;
default:
status = NT_STATUS_INVALID_INFO_CLASS;
break;
}
return (status);
}
static uint32_t
smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
{
smb_attr_t attr;
smb_node_t *node = sinfo->si_node;
uint32_t status = 0;
uint32_t mtime;
uint16_t attributes;
int rc;
if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0)
return (NT_STATUS_INFO_LENGTH_MISMATCH);
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
(!smb_node_is_dir(node))) {
return (NT_STATUS_INVALID_PARAMETER);
}
bzero(&attr, sizeof (smb_attr_t));
if (attributes != FILE_ATTRIBUTE_NORMAL) {
attr.sa_dosattr = attributes;
attr.sa_mask |= SMB_AT_DOSATTR;
}
if (mtime != 0 && mtime != UINT_MAX) {
attr.sa_vattr.va_mtime.tv_sec =
smb_time_local_to_gmt(sr, mtime);
attr.sa_mask |= SMB_AT_MTIME;
}
rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
if (rc != 0)
status = smb_errno2status(rc);
return (status);
}
static uint32_t
smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
{
smb_attr_t attr;
uint32_t crtime, atime, mtime;
uint32_t status = 0;
int rc;
if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0)
return (NT_STATUS_INFO_LENGTH_MISMATCH);
bzero(&attr, sizeof (smb_attr_t));
if (mtime != 0 && mtime != UINT_MAX) {
attr.sa_vattr.va_mtime.tv_sec =
smb_time_local_to_gmt(sr, mtime);
attr.sa_mask |= SMB_AT_MTIME;
}
if (crtime != 0 && crtime != UINT_MAX) {
attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
attr.sa_mask |= SMB_AT_CRTIME;
}
if (atime != 0 && atime != UINT_MAX) {
attr.sa_vattr.va_atime.tv_sec =
smb_time_local_to_gmt(sr, atime);
attr.sa_mask |= SMB_AT_ATIME;
}
rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr,
sr->fid_ofile, &attr);
if (rc != 0)
status = smb_errno2status(rc);
return (status);
}
static uint32_t
smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
{
smb_attr_t attr;
smb_node_t *node = sinfo->si_node;
uint32_t crtime, atime, mtime;
uint32_t status = 0;
int rc;
if (smb_mbc_decodef(&sinfo->si_data, "yyy",
&crtime, &atime, &mtime) != 0)
return (NT_STATUS_INFO_LENGTH_MISMATCH);
bzero(&attr, sizeof (smb_attr_t));
if (mtime != 0 && mtime != (uint32_t)-1) {
attr.sa_vattr.va_mtime.tv_sec =
smb_time_local_to_gmt(sr, mtime);
attr.sa_mask |= SMB_AT_MTIME;
}
if (crtime != 0 && crtime != (uint32_t)-1) {
attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
attr.sa_mask |= SMB_AT_CRTIME;
}
if (atime != 0 && atime != (uint32_t)-1) {
attr.sa_vattr.va_atime.tv_sec =
smb_time_local_to_gmt(sr, atime);
attr.sa_mask |= SMB_AT_ATIME;
}
rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
if (rc != 0)
status = smb_errno2status(rc);
return (status);
}
static uint32_t
smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *sinfo)
{
smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
char *fname;
char *path;
uint8_t flags;
uint32_t rootdir, namelen;
uint32_t status = 0;
int rc;
rc = smb_mbc_decodef(&sinfo->si_data, "b...ll",
&flags, &rootdir, &namelen);
if (rc == 0) {
rc = smb_mbc_decodef(&sinfo->si_data, "%#U",
sr, namelen, &fname);
}
if (rc != 0)
return (NT_STATUS_INFO_LENGTH_MISMATCH);
if ((rootdir != 0) || (namelen == 0) || (namelen >= MAXNAMELEN)) {
return (NT_STATUS_INVALID_PARAMETER);
}
if (strchr(fname, '\\') != NULL) {
return (NT_STATUS_NOT_SUPPORTED);
}
path = smb_srm_zalloc(sr, SMB_MAXPATHLEN);
if (src_fqi->fq_path.pn_pname) {
(void) snprintf(path, SMB_MAXPATHLEN, "%s\\%s",
src_fqi->fq_path.pn_pname, fname);
} else {
rc = smb_node_getshrpath(sinfo->si_node->n_dnode,
sr->tid_tree, path, SMB_MAXPATHLEN);
if (rc != 0) {
status = smb_errno2status(rc);
return (status);
}
(void) strlcat(path, "\\", SMB_MAXPATHLEN);
(void) strlcat(path, fname, SMB_MAXPATHLEN);
}
dst_fqi->fq_dnode = sinfo->si_node->n_dnode;
(void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
status = smb_setinfo_rename(sr, sinfo->si_node, path, flags);
return (status);
}