smb_dev.c revision 76166
175374Sbp/*
275374Sbp * Copyright (c) 2000-2001 Boris Popov
375374Sbp * All rights reserved.
475374Sbp *
575374Sbp * Redistribution and use in source and binary forms, with or without
675374Sbp * modification, are permitted provided that the following conditions
775374Sbp * are met:
875374Sbp * 1. Redistributions of source code must retain the above copyright
975374Sbp *    notice, this list of conditions and the following disclaimer.
1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright
1175374Sbp *    notice, this list of conditions and the following disclaimer in the
1275374Sbp *    documentation and/or other materials provided with the distribution.
1375374Sbp * 3. All advertising materials mentioning features or use of this software
1475374Sbp *    must display the following acknowledgement:
1575374Sbp *    This product includes software developed by Boris Popov.
1675374Sbp * 4. Neither the name of the author nor the names of any co-contributors
1775374Sbp *    may be used to endorse or promote products derived from this software
1875374Sbp *    without specific prior written permission.
1975374Sbp *
2075374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2175374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2275374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2375374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2475374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2575374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2675374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2775374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2875374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2975374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3075374Sbp * SUCH DAMAGE.
3175374Sbp *
3275374Sbp * $FreeBSD: head/sys/netsmb/smb_dev.c 76166 2001-05-01 08:13:21Z markm $
3375374Sbp */
3475374Sbp#include <sys/param.h>
3575374Sbp#include <sys/kernel.h>
3675374Sbp#include <sys/systm.h>
3776166Smarkm#include <sys/conf.h>
3876166Smarkm#include <sys/fcntl.h>
3975374Sbp#include <sys/ioccom.h>
4076166Smarkm#include <sys/lock.h>
4175374Sbp#include <sys/malloc.h>
4276166Smarkm#include <sys/file.h>		/* Must come after sys/malloc.h */
4375374Sbp#include <sys/mbuf.h>
4476166Smarkm#include <sys/poll.h>
4575374Sbp#include <sys/proc.h>
4676166Smarkm#include <sys/select.h>
4775374Sbp#include <sys/socket.h>
4875374Sbp#include <sys/socketvar.h>
4975374Sbp#include <sys/sysctl.h>
5076166Smarkm#include <sys/uio.h>
5175374Sbp#include <sys/vnode.h>
5275374Sbp
5375374Sbp#include <net/if.h>
5475374Sbp
5575374Sbp#include <netsmb/smb.h>
5675374Sbp#include <netsmb/smb_conn.h>
5775374Sbp#include <netsmb/smb_subr.h>
5875374Sbp#include <netsmb/smb_dev.h>
5975374Sbp
6075374Sbp#define SMB_GETDEV(dev)		((struct smb_dev*)(dev)->si_drv1)
6175374Sbp#define	SMB_CHECKMINOR(dev)	do { \
6275374Sbp				    sdp = SMB_GETDEV(dev); \
6375374Sbp				    if (sdp == NULL) return ENXIO; \
6475374Sbp				} while(0)
6575374Sbp
6675374Sbpstatic d_open_t	 nsmb_dev_open;
6775374Sbpstatic d_close_t nsmb_dev_close;
6875374Sbpstatic d_read_t	 nsmb_dev_read;
6975374Sbpstatic d_write_t nsmb_dev_write;
7075374Sbpstatic d_ioctl_t nsmb_dev_ioctl;
7175374Sbpstatic d_poll_t	 nsmb_dev_poll;
7275374Sbp
7375374SbpMODULE_DEPEND(netsmb, libiconv, 1, 1, 1);
7475374SbpMODULE_VERSION(netsmb, NSMB_VERSION);
7575374Sbp
7675374Sbpstatic int smb_version = NSMB_VERSION;
7775374Sbp
7875374Sbp
7975374SbpSYSCTL_DECL(_net_smb);
8075374SbpSYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
8175374Sbp
8275374Sbpstatic MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
8375374Sbp
8475374Sbp
8575374Sbp/*
8675374Sbpint smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio);
8775374Sbp*/
8875374Sbp
8975374Sbpstatic struct cdevsw nsmb_cdevsw = {
9075374Sbp	/* open */	nsmb_dev_open,
9175374Sbp	/* close */	nsmb_dev_close,
9275374Sbp	/* read */	nsmb_dev_read,
9375374Sbp	/* write */	nsmb_dev_write,
9475374Sbp	/* ioctl */ 	nsmb_dev_ioctl,
9575374Sbp	/* poll */	nsmb_dev_poll,
9675374Sbp	/* mmap */	nommap,
9775374Sbp	/* strategy */	nostrategy,
9875374Sbp	/* name */	NSMB_NAME,
9975374Sbp	/* maj */	NSMB_MAJOR,
10075374Sbp	/* dump */	nodump,
10175374Sbp	/* psize */	nopsize,
10275374Sbp	/* flags */	0,
10375374Sbp#ifndef FB_CURRENT
10475374Sbp	/* bmaj */	-1
10575374Sbp#endif
10675374Sbp};
10775374Sbp
10875374Sbpstatic eventhandler_tag nsmb_dev_tag;
10975374Sbp
11075374Sbpstatic void
11175374Sbpnsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev)
11275374Sbp{
11375374Sbp	int min;
11475374Sbp
11575374Sbp	if (*dev != NODEV)
11675374Sbp		return;
11775374Sbp	if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1)
11875374Sbp		return;
11975374Sbp	*dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min);
12075374Sbp}
12175374Sbp
12275374Sbpstatic int
12375374Sbpnsmb_dev_open(dev_t dev, int oflags, int devtype, struct proc *p)
12475374Sbp{
12575374Sbp	struct smb_dev *sdp;
12675374Sbp	struct ucred *cred = p->p_ucred;
12775374Sbp	int s;
12875374Sbp
12975374Sbp	sdp = SMB_GETDEV(dev);
13075374Sbp	if (sdp && (sdp->sd_flags & NSMBFL_OPEN))
13175374Sbp		return EBUSY;
13275374Sbp	if (sdp == NULL) {
13375374Sbp		sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK);
13475374Sbp		dev->si_drv1 = (void*)sdp;
13575374Sbp	}
13675374Sbp	/*
13775374Sbp	 * XXX: this is just crazy - make a device for an already passed device...
13875374Sbp	 * someone should take care of it.
13975374Sbp	 */
14075374Sbp	if ((dev->si_flags & SI_NAMED) == 0)
14175374Sbp		make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700,
14275374Sbp		    NSMB_NAME"%d", dev2unit(dev));
14375374Sbp	bzero(sdp, sizeof(*sdp));
14475374Sbp/*
14575374Sbp	STAILQ_INIT(&sdp->sd_rqlist);
14675374Sbp	STAILQ_INIT(&sdp->sd_rplist);
14775374Sbp	bzero(&sdp->sd_pollinfo, sizeof(struct selinfo));
14875374Sbp*/
14975374Sbp	s = splimp();
15075374Sbp	sdp->sd_level = -1;
15175374Sbp	sdp->sd_flags |= NSMBFL_OPEN;
15275374Sbp	splx(s);
15375374Sbp	return 0;
15475374Sbp}
15575374Sbp
15675374Sbpstatic int
15775374Sbpnsmb_dev_close(dev_t dev, int flag, int fmt, struct proc *p)
15875374Sbp{
15975374Sbp	struct smb_dev *sdp;
16075374Sbp	struct smb_vc *vcp;
16175374Sbp	struct smb_share *ssp;
16275374Sbp	struct smb_cred scred;
16375374Sbp	int s;
16475374Sbp
16575374Sbp	SMB_CHECKMINOR(dev);
16675374Sbp	s = splimp();
16775374Sbp	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
16875374Sbp		splx(s);
16975374Sbp		return EBADF;
17075374Sbp	}
17175374Sbp	smb_makescred(&scred, p, NULL);
17275374Sbp	ssp = sdp->sd_share;
17375374Sbp	if (ssp != NULL)
17475374Sbp		smb_share_rele(ssp, &scred);
17575374Sbp	vcp = sdp->sd_vc;
17675374Sbp	if (vcp != NULL)
17775374Sbp		smb_vc_rele(vcp, &scred);
17875374Sbp/*
17975374Sbp	smb_flushq(&sdp->sd_rqlist);
18075374Sbp	smb_flushq(&sdp->sd_rplist);
18175374Sbp*/
18275374Sbp#if __FreeBSD_version > 400001
18375374Sbp	dev->si_drv1 = NULL;
18475374Sbp	free(sdp, M_NSMBDEV);
18575374Sbp	destroy_dev(dev);
18675374Sbp#else
18775374Sbp	sdp->sd_flags &= ~NSMBFL_OPEN;
18875374Sbp#endif
18975374Sbp	splx(s);
19075374Sbp	return 0;
19175374Sbp}
19275374Sbp
19375374Sbp
19475374Sbpstatic int
19575374Sbpnsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
19675374Sbp{
19775374Sbp	struct smb_dev *sdp;
19875374Sbp	struct smb_vc *vcp;
19975374Sbp	struct smb_share *ssp;
20075374Sbp	struct smb_cred scred;
20175374Sbp	int error = 0;
20275374Sbp
20375374Sbp	SMB_CHECKMINOR(dev);
20475374Sbp	if ((sdp->sd_flags & NSMBFL_OPEN) == 0)
20575374Sbp		return EBADF;
20675374Sbp
20775374Sbp	smb_makescred(&scred, p, NULL);
20875374Sbp	switch (cmd) {
20975374Sbp	    case SMBIOC_OPENSESSION:
21075374Sbp		if (sdp->sd_vc)
21175374Sbp			return EISCONN;
21275374Sbp		error = smb_usr_opensession((struct smbioc_ossn*)data,
21375374Sbp		    &scred, &vcp);
21475374Sbp		if (error)
21575374Sbp			break;
21675374Sbp		sdp->sd_vc = vcp;
21775374Sbp		smb_vc_unlock(vcp, 0, p);
21875374Sbp		sdp->sd_level = SMBL_VC;
21975374Sbp		break;
22075374Sbp	    case SMBIOC_OPENSHARE:
22175374Sbp		if (sdp->sd_share)
22275374Sbp			return EISCONN;
22375374Sbp		if (sdp->sd_vc == NULL)
22475374Sbp			return ENOTCONN;
22575374Sbp		error = smb_usr_openshare(sdp->sd_vc,
22675374Sbp		    (struct smbioc_oshare*)data, &scred, &ssp);
22775374Sbp		if (error)
22875374Sbp			break;
22975374Sbp		sdp->sd_share = ssp;
23075374Sbp		smb_share_unlock(ssp, 0, p);
23175374Sbp		sdp->sd_level = SMBL_SHARE;
23275374Sbp		break;
23375374Sbp	    case SMBIOC_REQUEST:
23475374Sbp		if (sdp->sd_share == NULL)
23575374Sbp			return ENOTCONN;
23675374Sbp		error = smb_usr_simplerequest(sdp->sd_share,
23775374Sbp		    (struct smbioc_rq*)data, &scred);
23875374Sbp		break;
23975374Sbp	    case SMBIOC_T2RQ:
24075374Sbp		if (sdp->sd_share == NULL)
24175374Sbp			return ENOTCONN;
24275374Sbp		error = smb_usr_t2request(sdp->sd_share,
24375374Sbp		    (struct smbioc_t2rq*)data, &scred);
24475374Sbp		break;
24575374Sbp	    case SMBIOC_SETFLAGS: {
24675374Sbp		struct smbioc_flags *fl = (struct smbioc_flags*)data;
24775374Sbp		int on;
24875374Sbp
24975374Sbp		if (fl->ioc_level == SMBL_VC) {
25075374Sbp			if (fl->ioc_mask & SMBV_PERMANENT) {
25175374Sbp				on = fl->ioc_flags & SMBV_PERMANENT;
25275374Sbp				if ((vcp = sdp->sd_vc) == NULL)
25375374Sbp					return ENOTCONN;
25475374Sbp				error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred);
25575374Sbp				if (error)
25675374Sbp					break;
25775374Sbp				if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
25875374Sbp					vcp->obj.co_flags |= SMBV_PERMANENT;
25975374Sbp					smb_vc_ref(vcp, p);
26075374Sbp				} else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
26175374Sbp					vcp->obj.co_flags &= ~SMBV_PERMANENT;
26275374Sbp					smb_vc_rele(vcp, &scred);
26375374Sbp				}
26475374Sbp				smb_vc_put(vcp, &scred);
26575374Sbp			} else
26675374Sbp				error = EINVAL;
26775374Sbp		} else if (fl->ioc_level == SMBL_SHARE) {
26875374Sbp			if (fl->ioc_mask & SMBS_PERMANENT) {
26975374Sbp				on = fl->ioc_flags & SMBS_PERMANENT;
27075374Sbp				if ((ssp = sdp->sd_share) == NULL)
27175374Sbp					return ENOTCONN;
27275374Sbp				error = smb_share_get(ssp, LK_EXCLUSIVE, &scred);
27375374Sbp				if (error)
27475374Sbp					break;
27575374Sbp				if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
27675374Sbp					ssp->obj.co_flags |= SMBS_PERMANENT;
27775374Sbp					smb_share_ref(ssp, p);
27875374Sbp				} else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
27975374Sbp					ssp->obj.co_flags &= ~SMBS_PERMANENT;
28075374Sbp					smb_share_rele(ssp, &scred);
28175374Sbp				}
28275374Sbp				smb_share_put(ssp, &scred);
28375374Sbp			} else
28475374Sbp				error = EINVAL;
28575374Sbp			break;
28675374Sbp		} else
28775374Sbp			error = EINVAL;
28875374Sbp		break;
28975374Sbp	    }
29075374Sbp	    case SMBIOC_LOOKUP:
29175374Sbp		if (sdp->sd_vc || sdp->sd_share)
29275374Sbp			return EISCONN;
29375374Sbp		vcp = NULL;
29475374Sbp		ssp = NULL;
29575374Sbp		error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp);
29675374Sbp		if (error)
29775374Sbp			break;
29875374Sbp		if (vcp) {
29975374Sbp			sdp->sd_vc = vcp;
30075374Sbp			smb_vc_unlock(vcp, 0, p);
30175374Sbp			sdp->sd_level = SMBL_VC;
30275374Sbp		}
30375374Sbp		if (ssp) {
30475374Sbp			sdp->sd_share = ssp;
30575374Sbp			smb_share_unlock(ssp, 0, p);
30675374Sbp			sdp->sd_level = SMBL_SHARE;
30775374Sbp		}
30875374Sbp		break;
30975374Sbp	    case SMBIOC_READ: case SMBIOC_WRITE: {
31075374Sbp		struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
31175374Sbp		struct uio auio;
31275374Sbp		struct iovec iov;
31375374Sbp
31475374Sbp		if ((ssp = sdp->sd_share) == NULL)
31575374Sbp			return ENOTCONN;
31675374Sbp		iov.iov_base = rwrq->ioc_base;
31775374Sbp		iov.iov_len = rwrq->ioc_cnt;
31875374Sbp		auio.uio_iov = &iov;
31975374Sbp		auio.uio_iovcnt = 1;
32075374Sbp		auio.uio_offset = rwrq->ioc_offset;
32175374Sbp		auio.uio_resid = rwrq->ioc_cnt;
32275374Sbp		auio.uio_segflg = UIO_USERSPACE;
32375374Sbp		auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
32475374Sbp		auio.uio_procp = p;
32575374Sbp		if (cmd == SMBIOC_READ)
32675374Sbp			error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred);
32775374Sbp		else
32875374Sbp			error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred);
32975374Sbp		rwrq->ioc_cnt -= auio.uio_resid;
33075374Sbp		break;
33175374Sbp	    }
33275374Sbp	    default:
33375374Sbp		error = ENODEV;
33475374Sbp	}
33575374Sbp	return error;
33675374Sbp}
33775374Sbp
33875374Sbpstatic int
33975374Sbpnsmb_dev_read(dev_t dev, struct uio *uio, int flag)
34075374Sbp{
34175374Sbp	return EACCES;
34275374Sbp}
34375374Sbp
34475374Sbpstatic int
34575374Sbpnsmb_dev_write(dev_t dev, struct uio *uio, int flag)
34675374Sbp{
34775374Sbp	return EACCES;
34875374Sbp}
34975374Sbp
35075374Sbpstatic int
35175374Sbpnsmb_dev_poll(dev_t dev, int events, struct proc *p)
35275374Sbp{
35375374Sbp	return ENODEV;
35475374Sbp}
35575374Sbp
35675374Sbpstatic int
35775374Sbpnsmb_dev_load(module_t mod, int cmd, void *arg)
35875374Sbp{
35975374Sbp	int error = 0;
36075374Sbp
36175374Sbp	switch (cmd) {
36275374Sbp	    case MOD_LOAD:
36375374Sbp		error = smb_sm_init();
36475374Sbp		if (error)
36575374Sbp			break;
36675374Sbp		error = smb_iod_init();
36775374Sbp		if (error) {
36875374Sbp			smb_sm_done();
36975374Sbp			break;
37075374Sbp		}
37175374Sbp#if __FreeBSD_version > 400001
37275374Sbp		cdevsw_add(&nsmb_cdevsw);
37375374Sbp#endif
37475374Sbp#if __FreeBSD_version > 500000
37575374Sbp		nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000);
37675374Sbp#endif
37775374Sbp		printf("netsmb_dev: loaded\n");
37875374Sbp		break;
37975374Sbp	    case MOD_UNLOAD:
38075374Sbp		smb_iod_done();
38175374Sbp		error = smb_sm_done();
38275374Sbp		error = 0;
38375374Sbp#if __FreeBSD_version > 500000
38475374Sbp		EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag);
38575374Sbp#endif
38675374Sbp#if __FreeBSD_version > 400001
38775374Sbp		cdevsw_remove(&nsmb_cdevsw);
38875374Sbp#endif
38975374Sbp		printf("netsmb_dev: unloaded\n");
39075374Sbp		break;
39175374Sbp	    default:
39275374Sbp		error = EINVAL;
39375374Sbp		break;
39475374Sbp	}
39575374Sbp	return error;
39675374Sbp}
39775374Sbp
39875374Sbp#if __FreeBSD_version > 400000
39975374SbpDEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
40075374Sbp#else
40175374SbpCDEV_MODULE(dev_netsmb, NSMB_MAJOR, nsmb_cdevsw, nsmb_dev_load, 0);
40275374Sbp#endif
40375374Sbp
40475374Sbp
40575374Sbp/*
40675374Sbp * Convert a file descriptor to appropriate smb_share pointer
40775374Sbp */
40875374Sbpstatic struct file*
40975374Sbpnsmb_getfp(struct filedesc* fdp, int fd, int flag)
41075374Sbp{
41175374Sbp	struct file* fp;
41275374Sbp
41375374Sbp	if (((u_int)fd) >= fdp->fd_nfiles ||
41475374Sbp	    (fp = fdp->fd_ofiles[fd]) == NULL ||
41575374Sbp	    (fp->f_flag & flag) == 0)
41675374Sbp		return (NULL);
41775374Sbp	return (fp);
41875374Sbp}
41975374Sbp
42075374Sbpint
42175374Sbpsmb_dev2share(int fd, int mode, struct smb_cred *scred,
42275374Sbp	struct smb_share **sspp)
42375374Sbp{
42475374Sbp	struct file *fp;
42575374Sbp	struct vnode *vp;
42675374Sbp	struct smb_dev *sdp;
42775374Sbp	struct smb_share *ssp;
42875374Sbp	dev_t dev;
42975374Sbp	int error;
43075374Sbp
43175374Sbp	if ((fp = nsmb_getfp(scred->scr_p->p_fd, fd, FREAD | FWRITE)) == NULL)
43275374Sbp		return EBADF;
43375374Sbp	vp = (struct vnode*)fp->f_data;
43475374Sbp	if (vp == NULL)
43575374Sbp		return EBADF;
43675374Sbp	dev = vn_todev(vp);
43775374Sbp	if (dev == NODEV)
43875374Sbp		return EBADF;
43975374Sbp	SMB_CHECKMINOR(dev);
44075374Sbp	ssp = sdp->sd_share;
44175374Sbp	if (ssp == NULL)
44275374Sbp		return ENOTCONN;
44375374Sbp	error = smb_share_get(ssp, LK_EXCLUSIVE, scred);
44475374Sbp	if (error)
44575374Sbp		return error;
44675374Sbp	*sspp = ssp;
44775374Sbp	return 0;
44875374Sbp}
44975374Sbp
450