1139823Simp/*-
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 *
1475374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2275374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475374Sbp * SUCH DAMAGE.
2575374Sbp */
26116189Sobrien
27116189Sobrien#include <sys/cdefs.h>
28116189Sobrien__FBSDID("$FreeBSD$");
29116189Sobrien
3075374Sbp#include <sys/param.h>
3175374Sbp#include <sys/kernel.h>
32250236Sdavide#include <sys/capability.h>
33129880Sphk#include <sys/module.h>
3475374Sbp#include <sys/systm.h>
3576166Smarkm#include <sys/conf.h>
3676166Smarkm#include <sys/fcntl.h>
3775374Sbp#include <sys/ioccom.h>
3876166Smarkm#include <sys/lock.h>
3975374Sbp#include <sys/malloc.h>
4076166Smarkm#include <sys/file.h>		/* Must come after sys/malloc.h */
41108524Salfred#include <sys/filedesc.h>
4275374Sbp#include <sys/mbuf.h>
4376166Smarkm#include <sys/poll.h>
4475374Sbp#include <sys/proc.h>
4576166Smarkm#include <sys/select.h>
4675374Sbp#include <sys/socket.h>
4775374Sbp#include <sys/socketvar.h>
4875374Sbp#include <sys/sysctl.h>
4976166Smarkm#include <sys/uio.h>
5075374Sbp#include <sys/vnode.h>
5175374Sbp
5275374Sbp#include <net/if.h>
5375374Sbp
5475374Sbp#include <netsmb/smb.h>
5575374Sbp#include <netsmb/smb_conn.h>
5675374Sbp#include <netsmb/smb_subr.h>
5775374Sbp#include <netsmb/smb_dev.h>
5875374Sbp
59250236Sdavidestatic struct cdev *nsmb_dev;
6075374Sbp
6175374Sbpstatic d_open_t	 nsmb_dev_open;
6275374Sbpstatic d_ioctl_t nsmb_dev_ioctl;
6375374Sbp
64120492SfjoeMODULE_DEPEND(netsmb, libiconv, 1, 1, 2);
6575374SbpMODULE_VERSION(netsmb, NSMB_VERSION);
6675374Sbp
6775374Sbpstatic int smb_version = NSMB_VERSION;
68250236Sdavidestruct sx smb_lock;
6975374Sbp
7075374Sbp
7175374SbpSYSCTL_DECL(_net_smb);
7275374SbpSYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
7375374Sbp
7475374Sbpstatic MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
7575374Sbp
7675374Sbpstatic struct cdevsw nsmb_cdevsw = {
77126080Sphk	.d_version =	D_VERSION,
78111815Sphk	.d_open =	nsmb_dev_open,
79111815Sphk	.d_ioctl =	nsmb_dev_ioctl,
80125706Stjr	.d_name =	NSMB_NAME
8175374Sbp};
8275374Sbp
83250236Sdavidestatic int
84250236Sdavidensmb_dev_init(void)
85250236Sdavide{
8675374Sbp
87250236Sdavide	nsmb_dev = make_dev(&nsmb_cdevsw, 0, UID_ROOT, GID_OPERATOR,
88250236Sdavide	    0600, "nsmb");
89250236Sdavide	if (nsmb_dev == NULL)
90250236Sdavide		return (ENOMEM);
91250236Sdavide	return (0);
92250236Sdavide}
93250236Sdavide
94250236Sdavidestatic void
95250236Sdavidensmb_dev_destroy(void)
9675374Sbp{
9775374Sbp
98250236Sdavide	MPASS(nsmb_dev != NULL);
99250236Sdavide	destroy_dev(nsmb_dev);
100250236Sdavide	nsmb_dev = NULL;
101250236Sdavide}
102184592Srwatson
103250236Sdavidestatic struct smb_dev *
104250236Sdavidesmbdev_alloc(struct cdev *dev)
105250236Sdavide{
106250236Sdavide	struct smb_dev *sdp;
107250236Sdavide
108250236Sdavide	sdp = malloc(sizeof(struct smb_dev), M_NSMBDEV, M_WAITOK | M_ZERO);
109250236Sdavide	sdp->dev = dev;
110250236Sdavide	sdp->sd_level = -1;
111250236Sdavide	sdp->sd_flags |= NSMBFL_OPEN;
112250236Sdavide	sdp->refcount = 1;
113250236Sdavide	return (sdp);
114250236Sdavide}
115250236Sdavide
116250236Sdavidevoid
117250236Sdavidesdp_dtor(void *arg)
118250236Sdavide{
119250236Sdavide	struct smb_dev *dev;
120250236Sdavide
121250236Sdavide	dev = (struct smb_dev *)arg;
122250236Sdavide	SMB_LOCK();
123250236Sdavide	sdp_trydestroy(dev);
124250236Sdavide	SMB_UNLOCK();
12575374Sbp}
12675374Sbp
12775374Sbpstatic int
128130585Sphknsmb_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
12975374Sbp{
13075374Sbp	struct smb_dev *sdp;
131250236Sdavide	int error;
13275374Sbp
133250236Sdavide	sdp = smbdev_alloc(dev);
134250236Sdavide	error = devfs_set_cdevpriv(sdp, sdp_dtor);
135250236Sdavide	if (error) {
136250236Sdavide		free(sdp, M_NSMBDEV);
137250236Sdavide		return (error);
13875374Sbp	}
139250236Sdavide	return (0);
14075374Sbp}
14175374Sbp
142250236Sdavidevoid
143250236Sdavidesdp_trydestroy(struct smb_dev *sdp)
14475374Sbp{
14575374Sbp	struct smb_vc *vcp;
14675374Sbp	struct smb_share *ssp;
147242386Sdavide	struct smb_cred *scred;
14875374Sbp
149250236Sdavide	SMB_LOCKASSERT();
150250236Sdavide	if (!sdp)
151250236Sdavide		panic("No smb_dev upon device close");
152250236Sdavide	MPASS(sdp->refcount > 0);
153250236Sdavide	sdp->refcount--;
154250236Sdavide	if (sdp->refcount)
155250236Sdavide		return;
156242386Sdavide	scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK);
157250236Sdavide	smb_makescred(scred, curthread, NULL);
15875374Sbp	ssp = sdp->sd_share;
159250237Sdavide	if (ssp != NULL) {
160250237Sdavide		smb_share_lock(ssp);
161242386Sdavide		smb_share_rele(ssp, scred);
162250237Sdavide	}
16375374Sbp	vcp = sdp->sd_vc;
164250237Sdavide	if (vcp != NULL) {
165250237Sdavide		smb_vc_lock(vcp);
166242386Sdavide		smb_vc_rele(vcp, scred);
167250237Sdavide	}
168250236Sdavide	free(scred, M_NSMBDEV);
16975374Sbp	free(sdp, M_NSMBDEV);
170250236Sdavide	return;
17175374Sbp}
17275374Sbp
17375374Sbp
17475374Sbpstatic int
175130585Sphknsmb_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
17675374Sbp{
17775374Sbp	struct smb_dev *sdp;
17875374Sbp	struct smb_vc *vcp;
17975374Sbp	struct smb_share *ssp;
180242386Sdavide	struct smb_cred *scred;
18175374Sbp	int error = 0;
18275374Sbp
183250236Sdavide	error = devfs_get_cdevpriv((void **)&sdp);
184250236Sdavide	if (error)
185250236Sdavide		return (error);
186242386Sdavide	scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK);
187250236Sdavide	SMB_LOCK();
188242386Sdavide	smb_makescred(scred, td, NULL);
18975374Sbp	switch (cmd) {
19075374Sbp	    case SMBIOC_OPENSESSION:
191242386Sdavide		if (sdp->sd_vc) {
192242386Sdavide			error = EISCONN;
193242386Sdavide			goto out;
194242386Sdavide		}
19575374Sbp		error = smb_usr_opensession((struct smbioc_ossn*)data,
196242386Sdavide		    scred, &vcp);
19775374Sbp		if (error)
19875374Sbp			break;
19975374Sbp		sdp->sd_vc = vcp;
200250237Sdavide		smb_vc_unlock(vcp);
20175374Sbp		sdp->sd_level = SMBL_VC;
20275374Sbp		break;
20375374Sbp	    case SMBIOC_OPENSHARE:
204242386Sdavide		if (sdp->sd_share) {
205242386Sdavide			error = EISCONN;
206242386Sdavide			goto out;
207242386Sdavide		}
208242386Sdavide		if (sdp->sd_vc == NULL) {
209242386Sdavide			error = ENOTCONN;
210242386Sdavide			goto out;
211242386Sdavide		}
21275374Sbp		error = smb_usr_openshare(sdp->sd_vc,
213242386Sdavide		    (struct smbioc_oshare*)data, scred, &ssp);
21475374Sbp		if (error)
21575374Sbp			break;
21675374Sbp		sdp->sd_share = ssp;
217250237Sdavide		smb_share_unlock(ssp);
21875374Sbp		sdp->sd_level = SMBL_SHARE;
21975374Sbp		break;
22075374Sbp	    case SMBIOC_REQUEST:
221242386Sdavide		if (sdp->sd_share == NULL) {
222242386Sdavide			error = ENOTCONN;
223242386Sdavide			goto out;
224242386Sdavide		}
22575374Sbp		error = smb_usr_simplerequest(sdp->sd_share,
226242386Sdavide		    (struct smbioc_rq*)data, scred);
22775374Sbp		break;
22875374Sbp	    case SMBIOC_T2RQ:
229242386Sdavide		if (sdp->sd_share == NULL) {
230242386Sdavide			error = ENOTCONN;
231242386Sdavide			goto out;
232242386Sdavide		}
23375374Sbp		error = smb_usr_t2request(sdp->sd_share,
234242386Sdavide		    (struct smbioc_t2rq*)data, scred);
23575374Sbp		break;
23675374Sbp	    case SMBIOC_SETFLAGS: {
23775374Sbp		struct smbioc_flags *fl = (struct smbioc_flags*)data;
23875374Sbp		int on;
23975374Sbp
24075374Sbp		if (fl->ioc_level == SMBL_VC) {
24175374Sbp			if (fl->ioc_mask & SMBV_PERMANENT) {
24275374Sbp				on = fl->ioc_flags & SMBV_PERMANENT;
243242386Sdavide				if ((vcp = sdp->sd_vc) == NULL) {
244242386Sdavide					error = ENOTCONN;
245242386Sdavide					goto out;
246242386Sdavide				}
247250237Sdavide				error = smb_vc_get(vcp, scred);
24875374Sbp				if (error)
24975374Sbp					break;
25075374Sbp				if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
25175374Sbp					vcp->obj.co_flags |= SMBV_PERMANENT;
25287192Sbp					smb_vc_ref(vcp);
25375374Sbp				} else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
25475374Sbp					vcp->obj.co_flags &= ~SMBV_PERMANENT;
255242386Sdavide					smb_vc_rele(vcp, scred);
25675374Sbp				}
257242386Sdavide				smb_vc_put(vcp, scred);
25875374Sbp			} else
25975374Sbp				error = EINVAL;
26075374Sbp		} else if (fl->ioc_level == SMBL_SHARE) {
26175374Sbp			if (fl->ioc_mask & SMBS_PERMANENT) {
26275374Sbp				on = fl->ioc_flags & SMBS_PERMANENT;
263242386Sdavide				if ((ssp = sdp->sd_share) == NULL) {
264242386Sdavide					error = ENOTCONN;
265242386Sdavide					goto out;
266242386Sdavide				}
267250237Sdavide				error = smb_share_get(ssp, scred);
26875374Sbp				if (error)
26975374Sbp					break;
27075374Sbp				if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
27175374Sbp					ssp->obj.co_flags |= SMBS_PERMANENT;
27287192Sbp					smb_share_ref(ssp);
27375374Sbp				} else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
27475374Sbp					ssp->obj.co_flags &= ~SMBS_PERMANENT;
275242386Sdavide					smb_share_rele(ssp, scred);
27675374Sbp				}
277242386Sdavide				smb_share_put(ssp, scred);
27875374Sbp			} else
27975374Sbp				error = EINVAL;
28075374Sbp			break;
28175374Sbp		} else
28275374Sbp			error = EINVAL;
28375374Sbp		break;
28475374Sbp	    }
28575374Sbp	    case SMBIOC_LOOKUP:
286242386Sdavide		if (sdp->sd_vc || sdp->sd_share) {
287242386Sdavide			error = EISCONN;
288242386Sdavide			goto out;
289242386Sdavide		}
29075374Sbp		vcp = NULL;
29175374Sbp		ssp = NULL;
292242386Sdavide		error = smb_usr_lookup((struct smbioc_lookup*)data, scred, &vcp, &ssp);
29375374Sbp		if (error)
29475374Sbp			break;
29575374Sbp		if (vcp) {
29675374Sbp			sdp->sd_vc = vcp;
297250237Sdavide			smb_vc_unlock(vcp);
29875374Sbp			sdp->sd_level = SMBL_VC;
29975374Sbp		}
30075374Sbp		if (ssp) {
30175374Sbp			sdp->sd_share = ssp;
302250237Sdavide			smb_share_unlock(ssp);
30375374Sbp			sdp->sd_level = SMBL_SHARE;
30475374Sbp		}
30575374Sbp		break;
30675374Sbp	    case SMBIOC_READ: case SMBIOC_WRITE: {
30775374Sbp		struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
30875374Sbp		struct uio auio;
30975374Sbp		struct iovec iov;
31075374Sbp
311242386Sdavide		if ((ssp = sdp->sd_share) == NULL) {
312242386Sdavide			error = ENOTCONN;
313242386Sdavide			goto out;
314242386Sdavide	 	}
31575374Sbp		iov.iov_base = rwrq->ioc_base;
31675374Sbp		iov.iov_len = rwrq->ioc_cnt;
31775374Sbp		auio.uio_iov = &iov;
31875374Sbp		auio.uio_iovcnt = 1;
31975374Sbp		auio.uio_offset = rwrq->ioc_offset;
32075374Sbp		auio.uio_resid = rwrq->ioc_cnt;
32175374Sbp		auio.uio_segflg = UIO_USERSPACE;
32275374Sbp		auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
32387192Sbp		auio.uio_td = td;
32475374Sbp		if (cmd == SMBIOC_READ)
325242386Sdavide			error = smb_read(ssp, rwrq->ioc_fh, &auio, scred);
32675374Sbp		else
327242386Sdavide			error = smb_write(ssp, rwrq->ioc_fh, &auio, scred);
32875374Sbp		rwrq->ioc_cnt -= auio.uio_resid;
32975374Sbp		break;
33075374Sbp	    }
33175374Sbp	    default:
33275374Sbp		error = ENODEV;
33375374Sbp	}
334242386Sdavideout:
335242386Sdavide	free(scred, M_NSMBDEV);
336250236Sdavide	SMB_UNLOCK();
33775374Sbp	return error;
33875374Sbp}
33975374Sbp
34075374Sbpstatic int
34175374Sbpnsmb_dev_load(module_t mod, int cmd, void *arg)
34275374Sbp{
34375374Sbp	int error = 0;
34475374Sbp
34575374Sbp	switch (cmd) {
34675374Sbp	    case MOD_LOAD:
34775374Sbp		error = smb_sm_init();
34875374Sbp		if (error)
34975374Sbp			break;
35075374Sbp		error = smb_iod_init();
35175374Sbp		if (error) {
35275374Sbp			smb_sm_done();
35375374Sbp			break;
35475374Sbp		}
355250236Sdavide		error = nsmb_dev_init();
356250236Sdavide		if (error)
357250236Sdavide			break;
358250236Sdavide		sx_init(&smb_lock, "samba device lock");
35975374Sbp		break;
36075374Sbp	    case MOD_UNLOAD:
36175374Sbp		smb_iod_done();
36275374Sbp		error = smb_sm_done();
363152676Sbp		if (error)
364152676Sbp			break;
365250236Sdavide		nsmb_dev_destroy();
366250236Sdavide		sx_destroy(&smb_lock);
36775374Sbp		break;
36875374Sbp	    default:
36975374Sbp		error = EINVAL;
37075374Sbp		break;
37175374Sbp	}
37275374Sbp	return error;
37375374Sbp}
37475374Sbp
37575374SbpDEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
37675374Sbp
37775374Sbpint
37875374Sbpsmb_dev2share(int fd, int mode, struct smb_cred *scred,
379250236Sdavide	struct smb_share **sspp, struct smb_dev **ssdp)
38075374Sbp{
381255219Spjd	cap_rights_t rights;
382250236Sdavide	struct file *fp, *fptmp;
38375374Sbp	struct smb_dev *sdp;
38475374Sbp	struct smb_share *ssp;
385250236Sdavide	struct thread *td;
38675374Sbp	int error;
38775374Sbp
388250236Sdavide	td = curthread;
389255219Spjd	error = fget(td, fd, cap_rights_init(&rights, CAP_READ), &fp);
390250236Sdavide	if (error)
391250236Sdavide		return (error);
392250236Sdavide	fptmp = td->td_fpop;
393250236Sdavide	td->td_fpop = fp;
394250236Sdavide	error = devfs_get_cdevpriv((void **)&sdp);
395250236Sdavide	td->td_fpop = fptmp;
396250236Sdavide	fdrop(fp, td);
397250236Sdavide	if (error || sdp == NULL)
398250236Sdavide		return (error);
399250236Sdavide	SMB_LOCK();
400250236Sdavide	*ssdp = sdp;
40175374Sbp	ssp = sdp->sd_share;
40289306Salfred	if (ssp == NULL) {
403250236Sdavide		SMB_UNLOCK();
404250236Sdavide		return (ENOTCONN);
40589306Salfred	}
406250237Sdavide	error = smb_share_get(ssp, scred);
407250236Sdavide	if (error == 0) {
408250236Sdavide		sdp->refcount++;
40989306Salfred		*sspp = ssp;
410250236Sdavide	}
411250236Sdavide	SMB_UNLOCK();
41289306Salfred	return error;
41375374Sbp}
41475374Sbp
415