#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
#include <sys/policy.h>
#include <sys/zone.h>
#include <sys/pathname.h>
#include <sys/mount.h>
#include <sys/sdt.h>
#include <fs/fs_subr.h>
#include <sys/modctl.h>
#include <sys/devops.h>
#include <sys/thread.h>
#include <sys/socket.h>
#include <sys/zone.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/mchain.h>
#include <netsmb/smb.h>
#include <netsmb/smb2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#include <netsmb/smb_pass.h>
#ifndef _KERNEL
#include <libfknsmb.h>
#define _init(v) nsmb_drv_init(v)
#define _fini(v) nsmb_drv_fini(v)
#endif
#define NSMB_MIN_MINOR 1
#define NSMB_MAX_MINOR L_MAXMIN32
const uint32_t nsmb_version = NSMB_VERSION;
dev_t nsmb_dev_tcp = NODEV;
dev_t nsmb_dev_tcp6 = NODEV;
static void *statep;
static major_t nsmb_major;
static minor_t last_minor = NSMB_MIN_MINOR;
static kmutex_t dev_lck;
static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
#ifdef _KERNEL
static dev_info_t *nsmb_dip;
zone_key_t nsmb_zone_key;
extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
static struct cb_ops nsmb_cbops = {
nsmb_open,
nsmb_close,
nodev,
nodev,
nodev,
nodev,
nodev,
nsmb_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_MP,
CB_REV,
nodev,
nodev
};
static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
void *arg, void **result);
static struct dev_ops nsmb_ops = {
DEVO_REV,
0,
nsmb_getinfo,
nulldev,
nulldev,
nsmb_attach,
nsmb_detach,
nodev,
&nsmb_cbops,
NULL,
NULL,
ddi_quiesce_not_needed,
};
static struct modldrv nsmb_modldrv = {
&mod_driverops,
"SMBFS network driver",
&nsmb_ops
};
static struct modlinkage nsmb_modlinkage = {
MODREV_1,
(void *)&nsmb_modldrv,
NULL
};
#endif
int
_init(void)
{
#ifdef _KERNEL
int error;
#endif
(void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
(void) smb_sm_init();
smb_pkey_init();
#ifdef _KERNEL
zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
nsmb_zone_destroy);
if ((error = mod_install((&nsmb_modlinkage))) != 0) {
(void) zone_key_delete(nsmb_zone_key);
smb_pkey_fini();
smb_sm_done();
mutex_destroy(&dev_lck);
ddi_soft_state_fini(&statep);
return (error);
}
#else
streams_msg_init();
nsmb_major = 1;
nsmb_dev_tcp = AF_INET;
nsmb_dev_tcp6 = AF_INET6;
#endif
return (0);
}
int
_fini(void)
{
int status;
if ((status = smb_sm_idle()) != 0)
return (status);
if ((status = smb_pkey_idle()) != 0)
return (status);
#ifdef _KERNEL
if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
return (status);
}
(void) zone_key_delete(nsmb_zone_key);
#endif
smb_pkey_fini();
smb_sm_done();
mutex_destroy(&dev_lck);
ddi_soft_state_fini(&statep);
return (status);
}
#ifdef _KERNEL
int
_info(struct modinfo *modinfop)
{
return (mod_info(&nsmb_modlinkage, modinfop));
}
static int
nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
int ret = DDI_SUCCESS;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = nsmb_dip;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = NULL;
break;
default:
ret = DDI_FAILURE;
}
return (ret);
}
static int
nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
major_t tmaj;
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (ddi_get_instance(dip) > 0)
return (DDI_FAILURE);
if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
0) == DDI_FAILURE) {
cmn_err(CE_WARN, "nsmb_attach: create minor");
return (DDI_FAILURE);
}
nsmb_major = ddi_name_to_major(NSMB_NAME);
tmaj = ddi_name_to_major("tcp");
if (tmaj == DDI_MAJOR_T_NONE)
cmn_err(CE_NOTE, "no tcp major?");
else
nsmb_dev_tcp = makedevice(tmaj, 0);
tmaj = ddi_name_to_major("tcp6");
if (tmaj == DDI_MAJOR_T_NONE)
cmn_err(CE_NOTE, "no tcp6 major?");
else
nsmb_dev_tcp6 = makedevice(tmaj, 0);
nsmb_dip = dip;
ddi_report_dev(dip);
return (DDI_SUCCESS);
}
static int
nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
if (ddi_get_instance(dip) > 0)
return (DDI_FAILURE);
nsmb_dip = NULL;
ddi_remove_minor_node(dip, NULL);
return (DDI_SUCCESS);
}
#else
int
nsmb_drv_ioctl(dev32_t dev32, int cmd, intptr_t arg, int flags)
{
dev_t dev = expldev(dev32);
cred_t *cr = CRED();
int err;
err = nsmb_ioctl(dev, cmd, arg, flags, cr, NULL);
return (err);
}
int
nsmb_drv_open(dev32_t *dev32p, int flags, int otyp)
{
dev_t dev = expldev(*dev32p);
int err;
err = nsmb_open(&dev, flags, otyp, CRED());
if (err == 0) {
VERIFY(cmpldev(dev32p, dev) != 0);
}
return (err);
}
int
nsmb_drv_close(dev32_t dev32, int flags, int otyp)
{
dev_t dev = expldev(dev32);
int err;
err = nsmb_close(dev, flags, otyp, CRED());
return (err);
}
void
nsmb_drv_load(void)
{
}
#endif
static int
nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags,
cred_t *cr, int *rvalp)
{
smb_dev_t *sdp;
int err;
sdp = ddi_get_soft_state(statep, getminor(dev));
if (sdp == NULL) {
return (EBADF);
}
if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
return (EBADF);
}
if (sdp->zoneid != getzoneid())
return (EIO);
err = smb_usr_ioctl(sdp, cmd, arg, flags, cr);
return (err);
}
static int
nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
{
smb_dev_t *sdp;
minor_t m;
mutex_enter(&dev_lck);
for (m = last_minor + 1; m != last_minor; m++) {
if (m > NSMB_MAX_MINOR)
m = NSMB_MIN_MINOR;
if (ddi_get_soft_state(statep, m) == NULL) {
last_minor = m;
goto found;
}
}
mutex_exit(&dev_lck);
return (ENXIO);
found:
if (ddi_soft_state_zalloc(statep, m) == DDI_FAILURE) {
mutex_exit(&dev_lck);
return (ENXIO);
}
if ((sdp = ddi_get_soft_state(statep, m)) == NULL) {
mutex_exit(&dev_lck);
return (ENXIO);
}
*dev = makedevice(nsmb_major, m);
mutex_exit(&dev_lck);
sdp->sd_flags |= NSMBFL_OPEN;
sdp->zoneid = crgetzoneid(cr);
mutex_init(&sdp->sd_lock, NULL, MUTEX_DRIVER, NULL);
return (0);
}
static int
nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
{
minor_t inst = getminor(dev);
smb_dev_t *sdp;
int err;
sdp = ddi_get_soft_state(statep, inst);
if (sdp != NULL)
err = nsmb_close2(sdp, cr);
else
err = ENXIO;
mutex_enter(&dev_lck);
ddi_soft_state_free(statep, inst);
mutex_exit(&dev_lck);
return (err);
}
static int
nsmb_close2(smb_dev_t *sdp, cred_t *cr)
{
struct smb_vc *vcp;
struct smb_share *ssp;
struct smb_fh *fhp;
fhp = sdp->sd_fh;
if (fhp != NULL)
smb_fh_rele(fhp);
ssp = sdp->sd_share;
if (ssp != NULL)
smb_share_rele(ssp);
vcp = sdp->sd_vc;
if (vcp != NULL) {
if (sdp->sd_flags & NSMBFL_IOD)
smb_iod_disconnect(vcp);
smb_vc_rele(vcp);
}
mutex_destroy(&sdp->sd_lock);
return (0);
}
int
smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags)
{
#ifdef _KERNEL
file_t *fp = NULL;
vnode_t *vp;
#endif
smb_dev_t *from_sdp;
dev_t dev;
int32_t ufd;
int err;
if (sdp->sd_vc != NULL)
return (EISCONN);
if (ddi_copyin((void *) arg, &ufd, sizeof (ufd), flags))
return (EFAULT);
#ifdef _KERNEL
if ((fp = getf(ufd)) == NULL)
return (EBADF);
vp = fp->f_vnode;
dev = vp->v_rdev;
#else
dev = expldev((dev32_t)ufd);
#endif
if (dev == 0 || dev == NODEV ||
getmajor(dev) != nsmb_major) {
err = EINVAL;
goto out;
}
from_sdp = ddi_get_soft_state(statep, getminor(dev));
if (from_sdp == NULL) {
err = EINVAL;
goto out;
}
if ((sdp->sd_vc = from_sdp->sd_vc) != NULL)
smb_vc_hold(sdp->sd_vc);
if ((sdp->sd_share = from_sdp->sd_share) != NULL)
smb_share_hold(sdp->sd_share);
sdp->sd_level = from_sdp->sd_level;
err = 0;
out:
#ifdef _KERNEL
if (fp)
releasef(ufd);
#endif
return (err);
}
int
smb_dev2share(int fd, struct smb_share **sspp)
{
#ifdef _KERNEL
file_t *fp = NULL;
vnode_t *vp;
#endif
smb_dev_t *sdp;
smb_share_t *ssp;
dev_t dev;
int err;
#ifdef _KERNEL
if ((fp = getf(fd)) == NULL)
return (EBADF);
vp = fp->f_vnode;
dev = vp->v_rdev;
#else
dev = expldev((dev32_t)fd);
#endif
if (dev == 0 || dev == NODEV ||
getmajor(dev) != nsmb_major) {
err = EINVAL;
goto out;
}
sdp = ddi_get_soft_state(statep, getminor(dev));
if (sdp == NULL) {
err = EINVAL;
goto out;
}
ssp = sdp->sd_share;
if (ssp == NULL) {
err = ENOTCONN;
goto out;
}
*sspp = ssp;
smb_share_hold(ssp);
err = 0;
out:
#ifdef _KERNEL
if (fp)
releasef(fd);
#endif
return (err);
}