#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smbinfo.h>
static int smb_trans2_set_fs_ctrl_info(smb_request_t *, smb_xa_t *);
smb_sdrc_t
smb_pre_query_information_disk(smb_request_t *sr)
{
DTRACE_SMB_START(op__QueryInformationDisk, smb_request_t *, sr);
return (SDRC_SUCCESS);
}
void
smb_post_query_information_disk(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__QueryInformationDisk, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_query_information_disk(smb_request_t *sr)
{
int rc;
fsblkcnt64_t total_blocks, free_blocks;
unsigned long block_size, unit_size;
unsigned short blocks_per_unit, bytes_per_block;
unsigned short total_units, free_units;
smb_fssize_t fssize;
if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
return (SDRC_ERROR);
}
if (smb_fssize(sr, &fssize) != 0)
return (SDRC_ERROR);
unit_size = fssize.fs_sectors_per_unit;
block_size = fssize.fs_bytes_per_sector;
total_blocks = fssize.fs_caller_units;
free_blocks = fssize.fs_caller_avail;
while (block_size > 512) {
block_size >>= 1;
unit_size <<= 1;
}
while (total_blocks >= 0xFFFF) {
total_blocks >>= 1;
free_blocks >>= 1;
if ((unit_size <<= 1) > 0xFFFF) {
unit_size >>= 1;
total_blocks = 0xFFFF;
free_blocks <<= 1;
break;
}
}
total_units = (total_blocks >= 0xFFFF) ?
0xFFFF : (unsigned short)total_blocks;
free_units = (free_blocks >= 0xFFFF) ?
0xFFFF : (unsigned short)free_blocks;
bytes_per_block = (unsigned short)block_size;
blocks_per_unit = (unsigned short)unit_size;
rc = smbsr_encode_result(sr, 5, 0, "bwwww2.w",
5,
total_units,
blocks_per_unit,
bytes_per_block,
free_units,
0);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
smb_sdrc_t
smb_com_trans2_query_fs_information(smb_request_t *sr, smb_xa_t *xa)
{
uint32_t flags;
char *encode_str, *tmpbuf;
uint64_t max_int;
uint16_t infolev;
int rc, length, buflen;
smb_tree_t *tree;
smb_node_t *snode;
char *fsname = "NTFS";
fsid_t fsid;
smb_fssize_t fssize;
smb_msgbuf_t mb;
tree = sr->tid_tree;
if (!STYPE_ISDSK(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, "w", &infolev) != 0)
return (SDRC_ERROR);
snode = tree->t_snode;
fsid = SMB_NODE_FSID(snode);
switch (infolev) {
case SMB_INFO_ALLOCATION:
if (smb_fssize(sr, &fssize) != 0)
return (SDRC_ERROR);
max_int = (uint64_t)UINT_MAX;
if (fssize.fs_caller_units > max_int)
fssize.fs_caller_units = max_int;
if (fssize.fs_caller_avail > max_int)
fssize.fs_caller_avail = max_int;
(void) smb_mbc_encodef(&xa->rep_data_mb, "llllw",
0,
fssize.fs_sectors_per_unit,
fssize.fs_caller_units,
fssize.fs_caller_avail,
fssize.fs_bytes_per_sector);
break;
case SMB_INFO_VOLUME:
if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
(sr->session->native_os == NATIVE_OS_WIN95)) {
length = smb_wcequiv_strlen(tree->t_volume);
buflen = length + sizeof (smb_wchar_t);
tmpbuf = smb_srm_zalloc(sr, buflen);
smb_msgbuf_init(&mb, (uint8_t *)tmpbuf, buflen,
SMB_MSGBUF_UNICODE);
rc = smb_msgbuf_encode(&mb, "U", tree->t_volume);
if (rc >= 0) {
rc = smb_mbc_encodef(&xa->rep_data_mb,
"%lb#c", sr, fsid.val[0],
length, length, tmpbuf);
}
smb_msgbuf_term(&mb);
} else {
length = strlen(tree->t_volume);
rc = smb_mbc_encodef(&xa->rep_data_mb, "%lbs", sr,
fsid.val[0], length, tree->t_volume);
}
if (rc < 0)
return (SDRC_ERROR);
break;
case SMB_QUERY_FS_VOLUME_INFO:
case SMB_FILE_FS_VOLUME_INFORMATION:
if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
(sr->session->native_os == NATIVE_OS_WIN95)) {
length = smb_wcequiv_strlen(tree->t_volume);
encode_str = "%qllb.U";
} else {
length = strlen(tree->t_volume);
encode_str = "%qllb.s";
}
(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
0ll,
fsid.val[0],
length,
0,
tree->t_volume);
break;
case SMB_QUERY_FS_SIZE_INFO:
case SMB_FILE_FS_SIZE_INFORMATION:
if (smb_fssize(sr, &fssize) != 0)
return (SDRC_ERROR);
(void) smb_mbc_encodef(&xa->rep_data_mb, "qqll",
fssize.fs_caller_units,
fssize.fs_caller_avail,
fssize.fs_sectors_per_unit,
fssize.fs_bytes_per_sector);
break;
case SMB_QUERY_FS_DEVICE_INFO:
case SMB_FILE_FS_DEVICE_INFORMATION:
(void) smb_mbc_encodef(&xa->rep_data_mb, "ll",
FILE_DEVICE_FILE_SYSTEM,
FILE_DEVICE_IS_MOUNTED);
break;
case SMB_QUERY_FS_ATTRIBUTE_INFO:
case SMB_FILE_FS_ATTRIBUTE_INFORMATION:
if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
(sr->session->native_os == NATIVE_OS_WINNT) ||
(sr->session->native_os == NATIVE_OS_WIN2000) ||
(sr->session->native_os == NATIVE_OS_WIN95) ||
(sr->session->native_os == NATIVE_OS_MACOS)) {
length = smb_wcequiv_strlen(fsname);
encode_str = "%lllU";
sr->smb_flg2 |= SMB_FLAGS2_UNICODE;
} else {
length = strlen(fsname);
encode_str = "%llls";
}
flags = FILE_CASE_PRESERVED_NAMES;
if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK)
flags |= FILE_UNICODE_ON_DISK;
if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS)
flags |= FILE_PERSISTENT_ACLS;
if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0)
flags |= FILE_CASE_SENSITIVE_SEARCH;
if (tree->t_flags & SMB_TREE_STREAMS)
flags |= FILE_NAMED_STREAMS;
if (tree->t_flags & SMB_TREE_QUOTA)
flags |= FILE_VOLUME_QUOTAS;
if (tree->t_flags & SMB_TREE_SPARSE)
flags |= FILE_SUPPORTS_SPARSE_FILES;
(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
flags,
MAXNAMELEN,
length,
fsname);
break;
case SMB_FILE_FS_CONTROL_INFORMATION:
if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
ERRDOS, ERROR_NOT_SUPPORTED);
return (SDRC_ERROR);
}
(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqqqll",
0,
0,
0,
SMB_QUOTA_UNLIMITED,
SMB_QUOTA_UNLIMITED,
FILE_VC_QUOTA_ENFORCE,
0);
break;
case SMB_FILE_FS_FULLSIZE_INFORMATION:
if (smb_fssize(sr, &fssize) != 0)
return (SDRC_ERROR);
(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqll",
fssize.fs_caller_units,
fssize.fs_caller_avail,
fssize.fs_volume_avail,
fssize.fs_sectors_per_unit,
fssize.fs_bytes_per_sector);
break;
case SMB_FILE_FS_LABEL_INFORMATION:
case SMB_FILE_FS_OBJECTID_INFORMATION:
case SMB_FILE_FS_DRIVERPATH_INFORMATION:
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
ERRDOS, ERROR_NOT_SUPPORTED);
return (SDRC_ERROR);
default:
smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
ERRDOS, ERROR_INVALID_LEVEL);
return (SDRC_ERROR);
}
return (SDRC_SUCCESS);
}
int
smb_fssize(smb_request_t *sr, smb_fssize_t *fssize)
{
smb_node_t *node;
struct statvfs64 df;
uid_t uid;
smb_quota_t quota;
int spu;
int rc;
bzero(fssize, sizeof (smb_fssize_t));
node = sr->tid_tree->t_snode;
if ((rc = smb_fsop_statfs(sr->user_cr, node, &df)) != 0)
return (rc);
if (df.f_frsize < DEV_BSIZE)
df.f_frsize = DEV_BSIZE;
if (df.f_bsize < df.f_frsize)
df.f_bsize = df.f_frsize;
spu = df.f_bsize / df.f_frsize;
fssize->fs_bytes_per_sector = (uint16_t)df.f_frsize;
fssize->fs_sectors_per_unit = spu;
if (df.f_bavail > df.f_blocks)
df.f_bavail = 0;
fssize->fs_volume_units = df.f_blocks / spu;
fssize->fs_volume_avail = df.f_bavail / spu;
fssize->fs_caller_units = df.f_blocks / spu;
fssize->fs_caller_avail = df.f_bavail / spu;
if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA))
return (0);
uid = crgetuid(sr->uid_user->u_cred);
if (smb_quota_query_user_quota(sr, uid, "a) != NT_STATUS_SUCCESS)
return (0);
if ((quota.q_limit != SMB_QUOTA_UNLIMITED) && (quota.q_limit != 0)) {
fssize->fs_caller_units = quota.q_limit / df.f_bsize;
if (quota.q_limit <= quota.q_used)
fssize->fs_caller_avail = 0;
else
fssize->fs_caller_avail =
(quota.q_limit - quota.q_used) / df.f_bsize;
}
return (0);
}
smb_sdrc_t
smb_com_trans2_set_fs_information(smb_request_t *sr, smb_xa_t *xa)
{
smb_tree_t *tree;
uint16_t infolev;
tree = sr->tid_tree;
if (!STYPE_ISDSK(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, "ww",
&sr->smb_fid, &infolev) != 0)
return (SDRC_ERROR);
switch (infolev) {
case SMB_FILE_FS_CONTROL_INFORMATION:
if (smb_trans2_set_fs_ctrl_info(sr, xa) != 0)
return (SDRC_ERROR);
break;
case SMB_FILE_FS_VOLUME_INFORMATION:
case SMB_FILE_FS_LABEL_INFORMATION:
case SMB_FILE_FS_SIZE_INFORMATION:
case SMB_FILE_FS_DEVICE_INFORMATION:
case SMB_FILE_FS_ATTRIBUTE_INFORMATION:
case SMB_FILE_FS_FULLSIZE_INFORMATION:
case SMB_FILE_FS_OBJECTID_INFORMATION:
case SMB_FILE_FS_DRIVERPATH_INFORMATION:
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
ERRDOS, ERROR_NOT_SUPPORTED);
return (SDRC_ERROR);
default:
smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
ERRDOS, ERROR_INVALID_LEVEL);
return (SDRC_ERROR);
}
return (SDRC_SUCCESS);
}
static int
smb_trans2_set_fs_ctrl_info(smb_request_t *sr, smb_xa_t *xa)
{
int rc;
uint64_t fstart, fthresh, fstop, qthresh, qlimit;
uint32_t qctrl, qpad;
if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
ERRDOS, ERROR_NOT_SUPPORTED);
return (-1);
}
if (!smb_user_is_admin(sr->uid_user)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
rc = smb_mbc_decodef(&xa->req_data_mb, "qqqqqll", &fstart,
&fthresh, &fstop, &qthresh, &qlimit, &qctrl, &qpad);
if ((rc != 0) || (fstart != 0) || (fthresh != 0) || (fstop != 0)) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
ERRDOS, ERROR_INVALID_PARAMETER);
return (-1);
}
if ((qctrl != FILE_VC_QUOTA_ENFORCE) ||
(qlimit != SMB_QUOTA_UNLIMITED) ||
(qthresh != SMB_QUOTA_UNLIMITED)) {
smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
ERRDOS, ERROR_NOT_SUPPORTED);
return (-1);
}
return (0);
}