#include <sys/param.h>
#include <sys/systm.h>
#include <sys/inttypes.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb.h>
#include <netsmb/smb2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb2_rq.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
#if 0
int
smbfs_smb2_locking(struct smbnode *np, int op, uint32_t pid,
offset_t start, uint64_t len, int largelock,
struct smb_cred *scrp, uint32_t timeout)
{
return (ENOTSUP);
}
#endif
int
smbfs_smb2_getpattr(
struct smbnode *np,
struct smbfattr *fap,
struct smb_cred *scrp)
{
smb_fh_t tmp_fh;
struct smb_share *ssp = np->n_mount->smi_share;
uint32_t rights = (STD_RIGHT_READ_CONTROL_ACCESS |
SA_RIGHT_FILE_READ_ATTRIBUTES);
int error;
bzero(&tmp_fh, sizeof (tmp_fh));
error = smbfs_smb_ntcreatex(np,
NULL, 0, 0,
rights, SMB_EFA_NORMAL,
NTCREATEX_SHARE_ACCESS_ALL,
NTCREATEX_DISP_OPEN,
0,
scrp, &tmp_fh,
NULL, fap);
if (error == 0) {
(void) smb_smb_close(ssp, &tmp_fh, scrp);
}
return (error);
}
static int
smbfs_smb2_query_info(struct smb_share *ssp, smb2fid_t *fid,
struct mdchain *info_mdp, uint32_t *iolen,
uint8_t type, uint8_t level, uint32_t addl_info,
struct smb_cred *scrp)
{
struct smb_rq *rqp = NULL;
struct mbchain *mbp;
struct mdchain *mdp;
uint32_t dlen = 0;
uint16_t doff = 0;
uint16_t ssize = 0;
int error;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_QUERY_INFO, scrp, &rqp);
if (error)
goto out;
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 41);
mb_put_uint8(mbp, type);
mb_put_uint8(mbp, level);
mb_put_uint32le(mbp, *iolen);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, addl_info);
mb_put_uint32le(mbp, 0);
mb_put_uint64le(mbp, fid->fid_persistent);
mb_put_uint64le(mbp, fid->fid_volatile);
error = smb2_rq_simple(rqp);
if (error) {
if (rqp->sr_error == NT_STATUS_INVALID_PARAMETER)
error = ENOTSUP;
goto out;
}
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &ssize);
if (ssize != 9) {
error = EBADRPC;
goto out;
}
md_get_uint16le(mdp, &doff);
md_get_uint32le(mdp, &dlen);
*iolen = dlen;
if (dlen != 0) {
mblk_t *m = NULL;
int skip = (int)doff - (SMB2_HDRLEN + 8);
if (skip < 0) {
error = EBADRPC;
goto out;
}
if (skip > 0) {
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
error = md_get_mbuf(mdp, dlen, &m);
if (error)
goto out;
md_initm(info_mdp, m);
}
out:
smb_rq_done(rqp);
return (error);
}
int
smbfs_smb2_qfileinfo(struct smb_share *ssp, smb2fid_t *fid,
struct smbfattr *fap, struct smb_cred *scrp)
{
struct mdchain info_mdc, *mdp = &info_mdc;
uint32_t iolen = 1024;
int error;
bzero(mdp, sizeof (*mdp));
error = smbfs_smb2_query_info(ssp, fid, mdp, &iolen,
SMB2_0_INFO_FILE, FileAllInformation, 0, scrp);
if (error)
goto out;
error = smbfs_decode_file_all_info(ssp, mdp, fap);
out:
md_done(mdp);
return (error);
}
static int
smbfs_smb2_query_fs_info(struct smb_share *ssp, struct mdchain *mdp,
uint8_t level, struct smb_cred *scrp)
{
smb2fid_t fid;
uint32_t iolen = 1024;
boolean_t opened = B_FALSE;
int error;
error = smb2_smb_ntcreate(
ssp, NULL,
NULL, NULL,
0,
SA_RIGHT_FILE_READ_ATTRIBUTES,
SMB_EFA_NORMAL,
NTCREATEX_SHARE_ACCESS_ALL,
NTCREATEX_DISP_OPEN,
0,
NTCREATEX_IMPERSONATION_IMPERSONATION,
scrp, &fid, NULL, NULL);
if (error != 0)
goto out;
opened = B_TRUE;
error = smbfs_smb2_query_info(ssp, &fid, mdp, &iolen,
SMB2_0_INFO_FILESYSTEM, level, 0, scrp);
out:
if (opened)
(void) smb2_smb_close(ssp, &fid, scrp);
return (error);
}
int
smbfs_smb2_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *info,
struct smb_cred *scrp)
{
struct mdchain info_mdc, *mdp = &info_mdc;
int error;
bzero(mdp, sizeof (*mdp));
error = smbfs_smb2_query_fs_info(ssp, mdp,
FileFsAttributeInformation, scrp);
if (error)
goto out;
error = smbfs_decode_fs_attr_info(ssp, mdp, info);
out:
md_done(mdp);
return (error);
}
int
smbfs_smb2_statfs(struct smb_share *ssp,
struct smb_fs_size_info *info,
struct smb_cred *scrp)
{
struct mdchain info_mdc, *mdp = &info_mdc;
int error;
bzero(mdp, sizeof (*mdp));
error = smbfs_smb2_query_fs_info(ssp, mdp,
FileFsFullSizeInformation, scrp);
if (error)
goto out;
md_get_uint64le(mdp, &info->total_units);
md_get_uint64le(mdp, &info->caller_avail);
md_get_uint64le(mdp, &info->actual_avail);
md_get_uint32le(mdp, &info->sect_per_unit);
error = md_get_uint32le(mdp, &info->bytes_per_sect);
out:
md_done(mdp);
return (error);
}
int
smbfs_smb2_flush(struct smb_share *ssp, smb2fid_t *fid,
struct smb_cred *scrp)
{
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_FLUSH, scrp, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 24);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint64le(mbp, fid->fid_persistent);
mb_put_uint64le(mbp, fid->fid_volatile);
rqp->sr_flags |= SMBR_NORECONNECT;
error = smb2_rq_simple(rqp);
smb_rq_done(rqp);
return (error);
}
static int
smbfs_smb2_set_info(struct smb_share *ssp, smb2fid_t *fid,
struct mbchain *info_mbp, uint8_t type, uint8_t level,
uint32_t addl_info, struct smb_cred *scrp)
{
struct smb_rq *rqp = NULL;
struct mbchain *mbp;
uint32_t *buffer_lenp;
int base, len;
int error;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_SET_INFO, scrp, &rqp);
if (error)
goto out;
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 33);
mb_put_uint8(mbp, type);
mb_put_uint8(mbp, level);
buffer_lenp = mb_reserve(mbp, sizeof (uint32_t));
mb_put_uint16le(mbp, SMB2_HDRLEN + 32);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, addl_info);
mb_put_uint64le(mbp, fid->fid_persistent);
mb_put_uint64le(mbp, fid->fid_volatile);
base = mbp->mb_count;
error = mb_put_mbchain(mbp, info_mbp);
if (error)
goto out;
len = mbp->mb_count - base;
*buffer_lenp = htolel(len);
if (error)
goto out;
error = smb2_rq_simple(rqp);
out:
smb_rq_done(rqp);
return (error);
}
int
smbfs_smb2_seteof(struct smb_share *ssp, smb2fid_t *fid,
uint64_t newsize, struct smb_cred *scrp)
{
struct mbchain data_mb, *mbp = &data_mb;
uint8_t level = FileEndOfFileInformation;
int error;
mb_init(mbp);
mb_put_uint64le(mbp, newsize);
error = smbfs_smb2_set_info(ssp, fid, mbp,
SMB2_0_INFO_FILE, level, 0, scrp);
mb_done(mbp);
return (error);
}
int
smbfs_smb2_setdisp(struct smb_share *ssp, smb2fid_t *fid,
uint8_t newdisp, struct smb_cred *scrp)
{
struct mbchain data_mb, *mbp = &data_mb;
uint8_t level = FileDispositionInformation;
int error;
mb_init(mbp);
mb_put_uint8(mbp, newdisp);
error = smbfs_smb2_set_info(ssp, fid, mbp,
SMB2_0_INFO_FILE, level, 0, scrp);
mb_done(mbp);
return (error);
}
int
smbfs_smb2_setfattr(struct smb_share *ssp, smb2fid_t *fid,
struct mbchain *mbp, struct smb_cred *scrp)
{
uint8_t level = FileBasicInformation;
int error;
error = smbfs_smb2_set_info(ssp, fid, mbp,
SMB2_0_INFO_FILE, level, 0, scrp);
return (error);
}
int
smbfs_smb2_rename(struct smbnode *np, struct smbnode *tdnp,
const char *tname, int tnlen, int overwrite,
smb2fid_t *fid, struct smb_cred *scrp)
{
struct smb_share *ssp = np->n_mount->smi_share;
struct mbchain data_mb, *mbp = &data_mb;
uint32_t *name_lenp;
uint8_t level = FileRenameInformation;
int base, len;
int error;
mb_init(mbp);
mb_put_uint32le(mbp, (overwrite & 1));
mb_put_uint32le(mbp, 0);
mb_put_uint64le(mbp, 0);
name_lenp = mb_reserve(mbp, 4);
base = mbp->mb_count;
if (tnlen > 0) {
error = smbfs_fullpath(mbp, SSTOVC(ssp),
tdnp, tname, tnlen, '\\');
if (error)
goto out;
}
len = mbp->mb_count - base;
*name_lenp = htolel(len);
error = smbfs_smb2_set_info(ssp, fid, mbp,
SMB2_0_INFO_FILE, level, 0, scrp);
out:
mb_done(mbp);
return (error);
}
#define SMBFS_QDIR_MAX_BUF (1<<16)
static int
smbfs_smb2_qdir(struct smbfs_fctx *ctx)
{
smb_fh_t *fhp = ctx->f_fhp;
smb_share_t *ssp = ctx->f_ssp;
smb_vc_t *vcp = SSTOVC(ssp);
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
uint16_t *name_lenp;
uint8_t level, flags;
uint16_t ssize = 0;
uint16_t obuf_off = 0;
uint32_t obuf_len = 0;
uint32_t obuf_req;
int error;
level = (uint8_t)ctx->f_infolevel;
flags = 0;
if (ctx->f_flags & SMBFS_RDD_FINDSINGLE)
flags |= SMB2_QDIR_FLAG_SINGLE;
if (ctx->f_flags & SMBFS_RDD_FINDFIRST)
ctx->f_rkey = 0;
else
flags |= SMB2_QDIR_FLAG_INDEX;
obuf_req = SMBFS_QDIR_MAX_BUF;
if (obuf_req > vcp->vc_sopt.sv2_maxtransact)
obuf_req = vcp->vc_sopt.sv2_maxtransact;
if (ctx->f_rq) {
smb_rq_done(ctx->f_rq);
ctx->f_rq = NULL;
}
error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB2_QUERY_DIRECTORY,
ctx->f_scred, &rqp);
if (error)
return (error);
ctx->f_rq = rqp;
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 33);
mb_put_uint8(mbp, level);
mb_put_uint8(mbp, flags);
mb_put_uint32le(mbp, ctx->f_rkey);
mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent);
mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile);
mb_put_uint16le(mbp, 96);
name_lenp = mb_reserve(mbp, sizeof (uint16_t));
mb_put_uint32le(mbp, obuf_req);
if (ctx->f_wclen > 0) {
int base, len;
base = mbp->mb_count;
error = smb_put_dmem(mbp, vcp,
ctx->f_wildcard, ctx->f_wclen,
SMB_CS_NONE, NULL);
if (error)
return (error);
len = mbp->mb_count - base;
*name_lenp = htoles(len);
} else {
mb_put_uint16le(mbp, 0);
*name_lenp = 0;
}
error = smb2_rq_simple(rqp);
if (error != 0)
goto out;
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &ssize);
if (ssize != 9) {
error = EBADRPC;
goto out;
}
md_get_uint16le(mdp, &obuf_off);
md_get_uint32le(mdp, &obuf_len);
if (obuf_len < 8) {
error = ENOENT;
goto out;
}
{
mblk_t *m = NULL;
int skip = (int)obuf_off - (SMB2_HDRLEN + 8);
if (skip < 0) {
error = EBADRPC;
goto out;
}
if (skip > 0) {
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
error = md_get_mbuf(mdp, obuf_len, &m);
if (error)
goto out;
md_done(&ctx->f_mdchain);
md_initm(&ctx->f_mdchain, m);
}
ctx->f_left = obuf_len;
ctx->f_eofs = 0;
return (0);
out:
if (error != 0) {
ctx->f_left = 0;
ctx->f_eofs = 0;
ctx->f_flags |= SMBFS_RDD_EOF;
}
return (error);
}
int
smbfs_smb2_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
const char *wildcard, int wclen, uint32_t attr)
{
smb_fh_t *fhp = NULL;
uint32_t rights =
STD_RIGHT_READ_CONTROL_ACCESS |
SA_RIGHT_FILE_READ_ATTRIBUTES |
SA_RIGHT_FILE_READ_DATA;
int error;
ctx->f_type = ft_SMB2;
ASSERT(ctx->f_dnp == dnp);
error = smb_fh_create(ctx->f_ssp, &fhp);
if (error != 0)
goto errout;
error = smbfs_smb_ntcreatex(dnp,
NULL, 0, 0,
rights, SMB_EFA_NORMAL,
NTCREATEX_SHARE_ACCESS_ALL,
NTCREATEX_DISP_OPEN,
0,
ctx->f_scred, fhp,
NULL, NULL);
if (error != 0)
goto errout;
fhp->fh_rights = rights;
smb_fh_opened(fhp);
ctx->f_fhp = fhp;
ctx->f_namesz = SMB_MAXFNAMELEN + 1;
ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
ctx->f_infolevel = FileFullDirectoryInformation;
ctx->f_attrmask = attr;
ctx->f_wildcard = wildcard;
ctx->f_wclen = wclen;
return (0);
errout:
if (fhp != NULL)
smb_fh_rele(fhp);
return (error);
}
int
smbfs_smb2_findclose(struct smbfs_fctx *ctx)
{
smb_fh_t *fhp = NULL;
if ((fhp = ctx->f_fhp) != NULL) {
ctx->f_fhp = NULL;
smb_fh_rele(fhp);
}
if (ctx->f_name)
kmem_free(ctx->f_name, ctx->f_namesz);
if (ctx->f_rq)
smb_rq_done(ctx->f_rq);
md_done(&ctx->f_mdchain);
return (0);
}
int
smbfs_smb2_findnext(struct smbfs_fctx *ctx, uint16_t limit)
{
int error;
if ((ctx->f_eofs + 8) > ctx->f_left) {
if (ctx->f_flags & SMBFS_RDD_EOF)
return (ENOENT);
ctx->f_limit = limit;
error = smbfs_smb2_qdir(ctx);
if (error)
return (error);
ctx->f_otws++;
}
error = smbfs_decode_dirent(ctx);
return (error);
}
int
smbfs_smb2_get_streaminfo(smbnode_t *np, struct mdchain *mdp,
struct smb_cred *scrp)
{
smb_share_t *ssp = np->n_mount->smi_share;
smb_fh_t *fhp = NULL;
uint32_t rights =
STD_RIGHT_READ_CONTROL_ACCESS |
SA_RIGHT_FILE_READ_ATTRIBUTES;
uint32_t iolen = INT16_MAX;
int error;
error = smb_fh_create(ssp, &fhp);
if (error != 0)
goto out;
error = smbfs_smb_ntcreatex(np,
NULL, 0, 0,
rights, SMB_EFA_NORMAL,
NTCREATEX_SHARE_ACCESS_ALL,
NTCREATEX_DISP_OPEN,
0,
scrp, fhp, NULL, NULL);
if (error != 0)
goto out;
smb_fh_opened(fhp);
error = smbfs_smb2_query_info(ssp, &fhp->fh_fid2, mdp, &iolen,
SMB2_0_INFO_FILE, FileStreamInformation, 0, scrp);
out:
if (fhp != NULL)
smb_fh_rele(fhp);
return (error);
}
int
smbfs_smb2_getsec(struct smb_share *ssp, smb2fid_t *fid,
uint32_t selector, mblk_t **res, uint32_t *reslen,
struct smb_cred *scrp)
{
struct mdchain info_mdc, *mdp = &info_mdc;
int error;
bzero(mdp, sizeof (*mdp));
error = smbfs_smb2_query_info(ssp, fid, mdp, reslen,
SMB2_0_INFO_SECURITY, 0, selector, scrp);
if (error)
goto out;
if (mdp->md_top == NULL) {
error = EBADRPC;
goto out;
}
*res = mdp->md_top;
mdp->md_top = NULL;
out:
md_done(mdp);
return (error);
}
int
smbfs_smb2_setsec(struct smb_share *ssp, smb2fid_t *fid,
uint32_t selector, mblk_t **mp, struct smb_cred *scrp)
{
struct mbchain info_mbp, *mbp = &info_mbp;
int error;
ASSERT(*mp != NULL);
mb_initm(mbp, *mp);
*mp = NULL;
error = smbfs_smb2_set_info(ssp, fid, mbp,
SMB2_0_INFO_SECURITY, 0, selector, scrp);
mb_done(mbp);
return (error);
}