xref: /freebsd/sys/netsmb/smb_dev.c (revision 1a2cdef4962b47be5057809ce730a733b7f3c27c)
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$
33  */
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/systm.h>
37 #include <sys/ioccom.h>
38 #include <sys/malloc.h>
39 #include <sys/uio.h>
40 #include <sys/conf.h>
41 #include <sys/mbuf.h>
42 #include <sys/proc.h>
43 #include <sys/fcntl.h>
44 #include <sys/file.h>
45 #include <sys/socket.h>
46 #include <sys/select.h>
47 #include <sys/poll.h>
48 #include <sys/socketvar.h>
49 #include <sys/sysctl.h>
50 #include <sys/vnode.h>
51 
52 #include <net/if.h>
53 
54 #include <netsmb/smb.h>
55 #include <netsmb/smb_conn.h>
56 #include <netsmb/smb_subr.h>
57 #include <netsmb/smb_dev.h>
58 
59 #define SMB_GETDEV(dev)		((struct smb_dev*)(dev)->si_drv1)
60 #define	SMB_CHECKMINOR(dev)	do { \
61 				    sdp = SMB_GETDEV(dev); \
62 				    if (sdp == NULL) return ENXIO; \
63 				} while(0)
64 
65 static d_open_t	 nsmb_dev_open;
66 static d_close_t nsmb_dev_close;
67 static d_read_t	 nsmb_dev_read;
68 static d_write_t nsmb_dev_write;
69 static d_ioctl_t nsmb_dev_ioctl;
70 static d_poll_t	 nsmb_dev_poll;
71 
72 MODULE_DEPEND(netsmb, libiconv, 1, 1, 1);
73 MODULE_VERSION(netsmb, NSMB_VERSION);
74 
75 static int smb_version = NSMB_VERSION;
76 
77 
78 SYSCTL_DECL(_net_smb);
79 SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
80 
81 static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
82 
83 
84 /*
85 int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio);
86 */
87 
88 static struct cdevsw nsmb_cdevsw = {
89 	/* open */	nsmb_dev_open,
90 	/* close */	nsmb_dev_close,
91 	/* read */	nsmb_dev_read,
92 	/* write */	nsmb_dev_write,
93 	/* ioctl */ 	nsmb_dev_ioctl,
94 	/* poll */	nsmb_dev_poll,
95 	/* mmap */	nommap,
96 	/* strategy */	nostrategy,
97 	/* name */	NSMB_NAME,
98 	/* maj */	NSMB_MAJOR,
99 	/* dump */	nodump,
100 	/* psize */	nopsize,
101 	/* flags */	0,
102 #ifndef FB_CURRENT
103 	/* bmaj */	-1
104 #endif
105 };
106 
107 static eventhandler_tag nsmb_dev_tag;
108 
109 static void
110 nsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev)
111 {
112 	int min;
113 
114 	if (*dev != NODEV)
115 		return;
116 	if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1)
117 		return;
118 	*dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min);
119 }
120 
121 static int
122 nsmb_dev_open(dev_t dev, int oflags, int devtype, struct proc *p)
123 {
124 	struct smb_dev *sdp;
125 	struct ucred *cred = p->p_ucred;
126 	int s;
127 
128 	sdp = SMB_GETDEV(dev);
129 	if (sdp && (sdp->sd_flags & NSMBFL_OPEN))
130 		return EBUSY;
131 	if (sdp == NULL) {
132 		sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK);
133 		dev->si_drv1 = (void*)sdp;
134 	}
135 	/*
136 	 * XXX: this is just crazy - make a device for an already passed device...
137 	 * someone should take care of it.
138 	 */
139 	if ((dev->si_flags & SI_NAMED) == 0)
140 		make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700,
141 		    NSMB_NAME"%d", dev2unit(dev));
142 	bzero(sdp, sizeof(*sdp));
143 /*
144 	STAILQ_INIT(&sdp->sd_rqlist);
145 	STAILQ_INIT(&sdp->sd_rplist);
146 	bzero(&sdp->sd_pollinfo, sizeof(struct selinfo));
147 */
148 	s = splimp();
149 	sdp->sd_level = -1;
150 	sdp->sd_flags |= NSMBFL_OPEN;
151 	splx(s);
152 	return 0;
153 }
154 
155 static int
156 nsmb_dev_close(dev_t dev, int flag, int fmt, struct proc *p)
157 {
158 	struct smb_dev *sdp;
159 	struct smb_vc *vcp;
160 	struct smb_share *ssp;
161 	struct smb_cred scred;
162 	int s;
163 
164 	SMB_CHECKMINOR(dev);
165 	s = splimp();
166 	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
167 		splx(s);
168 		return EBADF;
169 	}
170 	smb_makescred(&scred, p, NULL);
171 	ssp = sdp->sd_share;
172 	if (ssp != NULL)
173 		smb_share_rele(ssp, &scred);
174 	vcp = sdp->sd_vc;
175 	if (vcp != NULL)
176 		smb_vc_rele(vcp, &scred);
177 /*
178 	smb_flushq(&sdp->sd_rqlist);
179 	smb_flushq(&sdp->sd_rplist);
180 */
181 #if __FreeBSD_version > 400001
182 	dev->si_drv1 = NULL;
183 	free(sdp, M_NSMBDEV);
184 	destroy_dev(dev);
185 #else
186 	sdp->sd_flags &= ~NSMBFL_OPEN;
187 #endif
188 	splx(s);
189 	return 0;
190 }
191 
192 
193 static int
194 nsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
195 {
196 	struct smb_dev *sdp;
197 	struct smb_vc *vcp;
198 	struct smb_share *ssp;
199 	struct smb_cred scred;
200 	int error = 0;
201 
202 	SMB_CHECKMINOR(dev);
203 	if ((sdp->sd_flags & NSMBFL_OPEN) == 0)
204 		return EBADF;
205 
206 	smb_makescred(&scred, p, NULL);
207 	switch (cmd) {
208 	    case SMBIOC_OPENSESSION:
209 		if (sdp->sd_vc)
210 			return EISCONN;
211 		error = smb_usr_opensession((struct smbioc_ossn*)data,
212 		    &scred, &vcp);
213 		if (error)
214 			break;
215 		sdp->sd_vc = vcp;
216 		smb_vc_unlock(vcp, 0, p);
217 		sdp->sd_level = SMBL_VC;
218 		break;
219 	    case SMBIOC_OPENSHARE:
220 		if (sdp->sd_share)
221 			return EISCONN;
222 		if (sdp->sd_vc == NULL)
223 			return ENOTCONN;
224 		error = smb_usr_openshare(sdp->sd_vc,
225 		    (struct smbioc_oshare*)data, &scred, &ssp);
226 		if (error)
227 			break;
228 		sdp->sd_share = ssp;
229 		smb_share_unlock(ssp, 0, p);
230 		sdp->sd_level = SMBL_SHARE;
231 		break;
232 	    case SMBIOC_REQUEST:
233 		if (sdp->sd_share == NULL)
234 			return ENOTCONN;
235 		error = smb_usr_simplerequest(sdp->sd_share,
236 		    (struct smbioc_rq*)data, &scred);
237 		break;
238 	    case SMBIOC_T2RQ:
239 		if (sdp->sd_share == NULL)
240 			return ENOTCONN;
241 		error = smb_usr_t2request(sdp->sd_share,
242 		    (struct smbioc_t2rq*)data, &scred);
243 		break;
244 	    case SMBIOC_SETFLAGS: {
245 		struct smbioc_flags *fl = (struct smbioc_flags*)data;
246 		int on;
247 
248 		if (fl->ioc_level == SMBL_VC) {
249 			if (fl->ioc_mask & SMBV_PERMANENT) {
250 				on = fl->ioc_flags & SMBV_PERMANENT;
251 				if ((vcp = sdp->sd_vc) == NULL)
252 					return ENOTCONN;
253 				error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred);
254 				if (error)
255 					break;
256 				if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
257 					vcp->obj.co_flags |= SMBV_PERMANENT;
258 					smb_vc_ref(vcp, p);
259 				} else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
260 					vcp->obj.co_flags &= ~SMBV_PERMANENT;
261 					smb_vc_rele(vcp, &scred);
262 				}
263 				smb_vc_put(vcp, &scred);
264 			} else
265 				error = EINVAL;
266 		} else if (fl->ioc_level == SMBL_SHARE) {
267 			if (fl->ioc_mask & SMBS_PERMANENT) {
268 				on = fl->ioc_flags & SMBS_PERMANENT;
269 				if ((ssp = sdp->sd_share) == NULL)
270 					return ENOTCONN;
271 				error = smb_share_get(ssp, LK_EXCLUSIVE, &scred);
272 				if (error)
273 					break;
274 				if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
275 					ssp->obj.co_flags |= SMBS_PERMANENT;
276 					smb_share_ref(ssp, p);
277 				} else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
278 					ssp->obj.co_flags &= ~SMBS_PERMANENT;
279 					smb_share_rele(ssp, &scred);
280 				}
281 				smb_share_put(ssp, &scred);
282 			} else
283 				error = EINVAL;
284 			break;
285 		} else
286 			error = EINVAL;
287 		break;
288 	    }
289 	    case SMBIOC_LOOKUP:
290 		if (sdp->sd_vc || sdp->sd_share)
291 			return EISCONN;
292 		vcp = NULL;
293 		ssp = NULL;
294 		error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp);
295 		if (error)
296 			break;
297 		if (vcp) {
298 			sdp->sd_vc = vcp;
299 			smb_vc_unlock(vcp, 0, p);
300 			sdp->sd_level = SMBL_VC;
301 		}
302 		if (ssp) {
303 			sdp->sd_share = ssp;
304 			smb_share_unlock(ssp, 0, p);
305 			sdp->sd_level = SMBL_SHARE;
306 		}
307 		break;
308 	    case SMBIOC_READ: case SMBIOC_WRITE: {
309 		struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
310 		struct uio auio;
311 		struct iovec iov;
312 
313 		if ((ssp = sdp->sd_share) == NULL)
314 			return ENOTCONN;
315 		iov.iov_base = rwrq->ioc_base;
316 		iov.iov_len = rwrq->ioc_cnt;
317 		auio.uio_iov = &iov;
318 		auio.uio_iovcnt = 1;
319 		auio.uio_offset = rwrq->ioc_offset;
320 		auio.uio_resid = rwrq->ioc_cnt;
321 		auio.uio_segflg = UIO_USERSPACE;
322 		auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
323 		auio.uio_procp = p;
324 		if (cmd == SMBIOC_READ)
325 			error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred);
326 		else
327 			error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred);
328 		rwrq->ioc_cnt -= auio.uio_resid;
329 		break;
330 	    }
331 	    default:
332 		error = ENODEV;
333 	}
334 	return error;
335 }
336 
337 static int
338 nsmb_dev_read(dev_t dev, struct uio *uio, int flag)
339 {
340 	return EACCES;
341 }
342 
343 static int
344 nsmb_dev_write(dev_t dev, struct uio *uio, int flag)
345 {
346 	return EACCES;
347 }
348 
349 static int
350 nsmb_dev_poll(dev_t dev, int events, struct proc *p)
351 {
352 	return ENODEV;
353 }
354 
355 static int
356 nsmb_dev_load(module_t mod, int cmd, void *arg)
357 {
358 	int error = 0;
359 
360 	switch (cmd) {
361 	    case MOD_LOAD:
362 		error = smb_sm_init();
363 		if (error)
364 			break;
365 		error = smb_iod_init();
366 		if (error) {
367 			smb_sm_done();
368 			break;
369 		}
370 #if __FreeBSD_version > 400001
371 		cdevsw_add(&nsmb_cdevsw);
372 #endif
373 #if __FreeBSD_version > 500000
374 		nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000);
375 #endif
376 		printf("netsmb_dev: loaded\n");
377 		break;
378 	    case MOD_UNLOAD:
379 		smb_iod_done();
380 		error = smb_sm_done();
381 		error = 0;
382 #if __FreeBSD_version > 500000
383 		EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag);
384 #endif
385 #if __FreeBSD_version > 400001
386 		cdevsw_remove(&nsmb_cdevsw);
387 #endif
388 		printf("netsmb_dev: unloaded\n");
389 		break;
390 	    default:
391 		error = EINVAL;
392 		break;
393 	}
394 	return error;
395 }
396 
397 #if __FreeBSD_version > 400000
398 DEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
399 #else
400 CDEV_MODULE(dev_netsmb, NSMB_MAJOR, nsmb_cdevsw, nsmb_dev_load, 0);
401 #endif
402 
403 
404 /*
405  * Convert a file descriptor to appropriate smb_share pointer
406  */
407 static struct file*
408 nsmb_getfp(struct filedesc* fdp, int fd, int flag)
409 {
410 	struct file* fp;
411 
412 	if (((u_int)fd) >= fdp->fd_nfiles ||
413 	    (fp = fdp->fd_ofiles[fd]) == NULL ||
414 	    (fp->f_flag & flag) == 0)
415 		return (NULL);
416 	return (fp);
417 }
418 
419 int
420 smb_dev2share(int fd, int mode, struct smb_cred *scred,
421 	struct smb_share **sspp)
422 {
423 	struct file *fp;
424 	struct vnode *vp;
425 	struct smb_dev *sdp;
426 	struct smb_share *ssp;
427 	dev_t dev;
428 	int error;
429 
430 	if ((fp = nsmb_getfp(scred->scr_p->p_fd, fd, FREAD | FWRITE)) == NULL)
431 		return EBADF;
432 	vp = (struct vnode*)fp->f_data;
433 	if (vp == NULL)
434 		return EBADF;
435 	dev = vn_todev(vp);
436 	if (dev == NODEV)
437 		return EBADF;
438 	SMB_CHECKMINOR(dev);
439 	ssp = sdp->sd_share;
440 	if (ssp == NULL)
441 		return ENOTCONN;
442 	error = smb_share_get(ssp, LK_EXCLUSIVE, scred);
443 	if (error)
444 		return error;
445 	*sspp = ssp;
446 	return 0;
447 }
448 
449