smb_dev.c revision 89306
1/*
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/sys/netsmb/smb_dev.c 89306 2002-01-13 11:58:06Z alfred $
33 */
34#include <sys/param.h>
35#include <sys/kernel.h>
36#include <sys/systm.h>
37#include <sys/conf.h>
38#include <sys/fcntl.h>
39#include <sys/ioccom.h>
40#include <sys/lock.h>
41#include <sys/malloc.h>
42#include <sys/file.h>		/* Must come after sys/malloc.h */
43#include <sys/mbuf.h>
44#include <sys/poll.h>
45#include <sys/proc.h>
46#include <sys/select.h>
47#include <sys/socket.h>
48#include <sys/socketvar.h>
49#include <sys/sysctl.h>
50#include <sys/uio.h>
51#include <sys/vnode.h>
52
53#include <net/if.h>
54
55#include <netsmb/smb.h>
56#include <netsmb/smb_conn.h>
57#include <netsmb/smb_subr.h>
58#include <netsmb/smb_dev.h>
59
60#define SMB_GETDEV(dev)		((struct smb_dev*)(dev)->si_drv1)
61#define	SMB_CHECKMINOR(dev)	do { \
62				    sdp = SMB_GETDEV(dev); \
63				    if (sdp == NULL) return ENXIO; \
64				} while(0)
65
66static d_open_t	 nsmb_dev_open;
67static d_close_t nsmb_dev_close;
68static d_read_t	 nsmb_dev_read;
69static d_write_t nsmb_dev_write;
70static d_ioctl_t nsmb_dev_ioctl;
71static d_poll_t	 nsmb_dev_poll;
72
73MODULE_DEPEND(netsmb, libiconv, 1, 1, 1);
74MODULE_VERSION(netsmb, NSMB_VERSION);
75
76static int smb_version = NSMB_VERSION;
77
78
79SYSCTL_DECL(_net_smb);
80SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
81
82static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
83
84
85/*
86int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio);
87*/
88
89static struct cdevsw nsmb_cdevsw = {
90	/* open */	nsmb_dev_open,
91	/* close */	nsmb_dev_close,
92	/* read */	nsmb_dev_read,
93	/* write */	nsmb_dev_write,
94	/* ioctl */ 	nsmb_dev_ioctl,
95	/* poll */	nsmb_dev_poll,
96	/* mmap */	nommap,
97	/* strategy */	nostrategy,
98	/* name */	NSMB_NAME,
99	/* maj */	NSMB_MAJOR,
100	/* dump */	nodump,
101	/* psize */	nopsize,
102	/* flags */	0,
103#ifndef FB_CURRENT
104	/* bmaj */	-1
105#endif
106};
107
108static eventhandler_tag nsmb_dev_tag;
109
110static void
111nsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev)
112{
113	int min;
114
115	if (*dev != NODEV)
116		return;
117	if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1)
118		return;
119	*dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min);
120}
121
122static int
123nsmb_dev_open(dev_t dev, int oflags, int devtype, struct thread *td)
124{
125	struct smb_dev *sdp;
126	struct proc *p = td->td_proc;
127	struct ucred *cred = p->p_ucred;
128	int s;
129
130	sdp = SMB_GETDEV(dev);
131	if (sdp && (sdp->sd_flags & NSMBFL_OPEN))
132		return EBUSY;
133	if (sdp == NULL) {
134		sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK);
135		dev->si_drv1 = (void*)sdp;
136	}
137	/*
138	 * XXX: this is just crazy - make a device for an already passed device...
139	 * someone should take care of it.
140	 */
141	if ((dev->si_flags & SI_NAMED) == 0)
142		make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700,
143		    NSMB_NAME"%d", dev2unit(dev));
144	bzero(sdp, sizeof(*sdp));
145/*
146	STAILQ_INIT(&sdp->sd_rqlist);
147	STAILQ_INIT(&sdp->sd_rplist);
148	bzero(&sdp->sd_pollinfo, sizeof(struct selinfo));
149*/
150	s = splimp();
151	sdp->sd_level = -1;
152	sdp->sd_flags |= NSMBFL_OPEN;
153	splx(s);
154	return 0;
155}
156
157static int
158nsmb_dev_close(dev_t dev, int flag, int fmt, struct thread *td)
159{
160	struct smb_dev *sdp;
161	struct smb_vc *vcp;
162	struct smb_share *ssp;
163	struct smb_cred scred;
164	int s;
165
166	SMB_CHECKMINOR(dev);
167	s = splimp();
168	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
169		splx(s);
170		return EBADF;
171	}
172	smb_makescred(&scred, td, NULL);
173	ssp = sdp->sd_share;
174	if (ssp != NULL)
175		smb_share_rele(ssp, &scred);
176	vcp = sdp->sd_vc;
177	if (vcp != NULL)
178		smb_vc_rele(vcp, &scred);
179/*
180	smb_flushq(&sdp->sd_rqlist);
181	smb_flushq(&sdp->sd_rplist);
182*/
183	dev->si_drv1 = NULL;
184	free(sdp, M_NSMBDEV);
185	destroy_dev(dev);
186	splx(s);
187	return 0;
188}
189
190
191static int
192nsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
193{
194	struct smb_dev *sdp;
195	struct smb_vc *vcp;
196	struct smb_share *ssp;
197	struct smb_cred scred;
198	int error = 0;
199
200	SMB_CHECKMINOR(dev);
201	if ((sdp->sd_flags & NSMBFL_OPEN) == 0)
202		return EBADF;
203
204	smb_makescred(&scred, td, NULL);
205	switch (cmd) {
206	    case SMBIOC_OPENSESSION:
207		if (sdp->sd_vc)
208			return EISCONN;
209		error = smb_usr_opensession((struct smbioc_ossn*)data,
210		    &scred, &vcp);
211		if (error)
212			break;
213		sdp->sd_vc = vcp;
214		smb_vc_unlock(vcp, 0, td);
215		sdp->sd_level = SMBL_VC;
216		break;
217	    case SMBIOC_OPENSHARE:
218		if (sdp->sd_share)
219			return EISCONN;
220		if (sdp->sd_vc == NULL)
221			return ENOTCONN;
222		error = smb_usr_openshare(sdp->sd_vc,
223		    (struct smbioc_oshare*)data, &scred, &ssp);
224		if (error)
225			break;
226		sdp->sd_share = ssp;
227		smb_share_unlock(ssp, 0, td);
228		sdp->sd_level = SMBL_SHARE;
229		break;
230	    case SMBIOC_REQUEST:
231		if (sdp->sd_share == NULL)
232			return ENOTCONN;
233		error = smb_usr_simplerequest(sdp->sd_share,
234		    (struct smbioc_rq*)data, &scred);
235		break;
236	    case SMBIOC_T2RQ:
237		if (sdp->sd_share == NULL)
238			return ENOTCONN;
239		error = smb_usr_t2request(sdp->sd_share,
240		    (struct smbioc_t2rq*)data, &scred);
241		break;
242	    case SMBIOC_SETFLAGS: {
243		struct smbioc_flags *fl = (struct smbioc_flags*)data;
244		int on;
245
246		if (fl->ioc_level == SMBL_VC) {
247			if (fl->ioc_mask & SMBV_PERMANENT) {
248				on = fl->ioc_flags & SMBV_PERMANENT;
249				if ((vcp = sdp->sd_vc) == NULL)
250					return ENOTCONN;
251				error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred);
252				if (error)
253					break;
254				if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
255					vcp->obj.co_flags |= SMBV_PERMANENT;
256					smb_vc_ref(vcp);
257				} else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
258					vcp->obj.co_flags &= ~SMBV_PERMANENT;
259					smb_vc_rele(vcp, &scred);
260				}
261				smb_vc_put(vcp, &scred);
262			} else
263				error = EINVAL;
264		} else if (fl->ioc_level == SMBL_SHARE) {
265			if (fl->ioc_mask & SMBS_PERMANENT) {
266				on = fl->ioc_flags & SMBS_PERMANENT;
267				if ((ssp = sdp->sd_share) == NULL)
268					return ENOTCONN;
269				error = smb_share_get(ssp, LK_EXCLUSIVE, &scred);
270				if (error)
271					break;
272				if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
273					ssp->obj.co_flags |= SMBS_PERMANENT;
274					smb_share_ref(ssp);
275				} else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
276					ssp->obj.co_flags &= ~SMBS_PERMANENT;
277					smb_share_rele(ssp, &scred);
278				}
279				smb_share_put(ssp, &scred);
280			} else
281				error = EINVAL;
282			break;
283		} else
284			error = EINVAL;
285		break;
286	    }
287	    case SMBIOC_LOOKUP:
288		if (sdp->sd_vc || sdp->sd_share)
289			return EISCONN;
290		vcp = NULL;
291		ssp = NULL;
292		error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp);
293		if (error)
294			break;
295		if (vcp) {
296			sdp->sd_vc = vcp;
297			smb_vc_unlock(vcp, 0, td);
298			sdp->sd_level = SMBL_VC;
299		}
300		if (ssp) {
301			sdp->sd_share = ssp;
302			smb_share_unlock(ssp, 0, td);
303			sdp->sd_level = SMBL_SHARE;
304		}
305		break;
306	    case SMBIOC_READ: case SMBIOC_WRITE: {
307		struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
308		struct uio auio;
309		struct iovec iov;
310
311		if ((ssp = sdp->sd_share) == NULL)
312			return ENOTCONN;
313		iov.iov_base = rwrq->ioc_base;
314		iov.iov_len = rwrq->ioc_cnt;
315		auio.uio_iov = &iov;
316		auio.uio_iovcnt = 1;
317		auio.uio_offset = rwrq->ioc_offset;
318		auio.uio_resid = rwrq->ioc_cnt;
319		auio.uio_segflg = UIO_USERSPACE;
320		auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
321		auio.uio_td = td;
322		if (cmd == SMBIOC_READ)
323			error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred);
324		else
325			error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred);
326		rwrq->ioc_cnt -= auio.uio_resid;
327		break;
328	    }
329	    default:
330		error = ENODEV;
331	}
332	return error;
333}
334
335static int
336nsmb_dev_read(dev_t dev, struct uio *uio, int flag)
337{
338	return EACCES;
339}
340
341static int
342nsmb_dev_write(dev_t dev, struct uio *uio, int flag)
343{
344	return EACCES;
345}
346
347static int
348nsmb_dev_poll(dev_t dev, int events, struct thread *td)
349{
350	return ENODEV;
351}
352
353static int
354nsmb_dev_load(module_t mod, int cmd, void *arg)
355{
356	int error = 0;
357
358	switch (cmd) {
359	    case MOD_LOAD:
360		error = smb_sm_init();
361		if (error)
362			break;
363		error = smb_iod_init();
364		if (error) {
365			smb_sm_done();
366			break;
367		}
368		cdevsw_add(&nsmb_cdevsw);
369		nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000);
370		printf("netsmb_dev: loaded\n");
371		break;
372	    case MOD_UNLOAD:
373		smb_iod_done();
374		error = smb_sm_done();
375		error = 0;
376		EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag);
377		cdevsw_remove(&nsmb_cdevsw);
378		printf("netsmb_dev: unloaded\n");
379		break;
380	    default:
381		error = EINVAL;
382		break;
383	}
384	return error;
385}
386
387DEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
388
389/*
390 * Convert a file descriptor to appropriate smb_share pointer
391 */
392static struct file*
393nsmb_getfp(struct filedesc* fdp, int fd, int flag)
394{
395	struct file* fp;
396
397	FILEDESC_LOCK(fdp);
398	if (((u_int)fd) >= fdp->fd_nfiles ||
399	    (fp = fdp->fd_ofiles[fd]) == NULL ||
400	    (fp->f_flag & flag) == 0) {
401		FILEDESC_UNLOCK(fdp);
402		return (NULL);
403	}
404	fhold(fp);
405	FILEDESC_UNLOCK(fdp);
406	return (fp);
407}
408
409int
410smb_dev2share(int fd, int mode, struct smb_cred *scred,
411	struct smb_share **sspp)
412{
413	struct file *fp;
414	struct vnode *vp;
415	struct smb_dev *sdp;
416	struct smb_share *ssp;
417	dev_t dev;
418	int error;
419
420	fp = nsmb_getfp(scred->scr_td->td_proc->p_fd, fd, FREAD | FWRITE);
421	if (fp == NULL)
422		return EBADF;
423	vp = (struct vnode*)fp->f_data;
424	if (vp == NULL) {
425		fdrop(fp, curthread);
426		return EBADF;
427	}
428	dev = vn_todev(vp);
429	if (dev == NODEV) {
430		fdrop(fp, curthread);
431		return EBADF;
432	}
433	SMB_CHECKMINOR(dev);
434	ssp = sdp->sd_share;
435	if (ssp == NULL) {
436		fdrop(fp, curthread);
437		return ENOTCONN;
438	}
439	error = smb_share_get(ssp, LK_EXCLUSIVE, scred);
440	if (error == 0)
441		*sspp = ssp;
442	fdrop(fp, curthread);
443	return error;
444}
445
446