smb_dev.c revision 280258
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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sys/netsmb/smb_dev.c 280258 2015-03-19 13:37:36Z rwatson $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/capsicum.h>
33#include <sys/module.h>
34#include <sys/systm.h>
35#include <sys/conf.h>
36#include <sys/fcntl.h>
37#include <sys/ioccom.h>
38#include <sys/lock.h>
39#include <sys/malloc.h>
40#include <sys/file.h>		/* Must come after sys/malloc.h */
41#include <sys/filedesc.h>
42#include <sys/mbuf.h>
43#include <sys/poll.h>
44#include <sys/proc.h>
45#include <sys/select.h>
46#include <sys/socket.h>
47#include <sys/socketvar.h>
48#include <sys/sysctl.h>
49#include <sys/uio.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
59static struct cdev *nsmb_dev;
60
61static d_open_t	 nsmb_dev_open;
62static d_ioctl_t nsmb_dev_ioctl;
63
64MODULE_DEPEND(netsmb, libiconv, 1, 1, 2);
65MODULE_VERSION(netsmb, NSMB_VERSION);
66
67static int smb_version = NSMB_VERSION;
68struct sx smb_lock;
69
70
71SYSCTL_DECL(_net_smb);
72SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
73
74static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
75
76static struct cdevsw nsmb_cdevsw = {
77	.d_version =	D_VERSION,
78	.d_open =	nsmb_dev_open,
79	.d_ioctl =	nsmb_dev_ioctl,
80	.d_name =	NSMB_NAME
81};
82
83static int
84nsmb_dev_init(void)
85{
86
87	nsmb_dev = make_dev(&nsmb_cdevsw, 0, UID_ROOT, GID_OPERATOR,
88	    0600, "nsmb");
89	if (nsmb_dev == NULL)
90		return (ENOMEM);
91	return (0);
92}
93
94static void
95nsmb_dev_destroy(void)
96{
97
98	MPASS(nsmb_dev != NULL);
99	destroy_dev(nsmb_dev);
100	nsmb_dev = NULL;
101}
102
103static struct smb_dev *
104smbdev_alloc(struct cdev *dev)
105{
106	struct smb_dev *sdp;
107
108	sdp = malloc(sizeof(struct smb_dev), M_NSMBDEV, M_WAITOK | M_ZERO);
109	sdp->dev = dev;
110	sdp->sd_level = -1;
111	sdp->sd_flags |= NSMBFL_OPEN;
112	sdp->refcount = 1;
113	return (sdp);
114}
115
116void
117sdp_dtor(void *arg)
118{
119	struct smb_dev *dev;
120
121	dev = (struct smb_dev *)arg;
122	SMB_LOCK();
123	sdp_trydestroy(dev);
124	SMB_UNLOCK();
125}
126
127static int
128nsmb_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
129{
130	struct smb_dev *sdp;
131	int error;
132
133	sdp = smbdev_alloc(dev);
134	error = devfs_set_cdevpriv(sdp, sdp_dtor);
135	if (error) {
136		free(sdp, M_NSMBDEV);
137		return (error);
138	}
139	return (0);
140}
141
142void
143sdp_trydestroy(struct smb_dev *sdp)
144{
145	struct smb_vc *vcp;
146	struct smb_share *ssp;
147	struct smb_cred *scred;
148
149	SMB_LOCKASSERT();
150	if (!sdp)
151		panic("No smb_dev upon device close");
152	MPASS(sdp->refcount > 0);
153	sdp->refcount--;
154	if (sdp->refcount)
155		return;
156	scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK);
157	smb_makescred(scred, curthread, NULL);
158	ssp = sdp->sd_share;
159	if (ssp != NULL) {
160		smb_share_lock(ssp);
161		smb_share_rele(ssp, scred);
162	}
163	vcp = sdp->sd_vc;
164	if (vcp != NULL) {
165		smb_vc_lock(vcp);
166		smb_vc_rele(vcp, scred);
167	}
168	free(scred, M_NSMBDEV);
169	free(sdp, M_NSMBDEV);
170	return;
171}
172
173
174static int
175nsmb_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
176{
177	struct smb_dev *sdp;
178	struct smb_vc *vcp;
179	struct smb_share *ssp;
180	struct smb_cred *scred;
181	int error = 0;
182
183	error = devfs_get_cdevpriv((void **)&sdp);
184	if (error)
185		return (error);
186	scred = malloc(sizeof(struct smb_cred), M_NSMBDEV, M_WAITOK);
187	SMB_LOCK();
188	smb_makescred(scred, td, NULL);
189	switch (cmd) {
190	    case SMBIOC_OPENSESSION:
191		if (sdp->sd_vc) {
192			error = EISCONN;
193			goto out;
194		}
195		error = smb_usr_opensession((struct smbioc_ossn*)data,
196		    scred, &vcp);
197		if (error)
198			break;
199		sdp->sd_vc = vcp;
200		smb_vc_unlock(vcp);
201		sdp->sd_level = SMBL_VC;
202		break;
203	    case SMBIOC_OPENSHARE:
204		if (sdp->sd_share) {
205			error = EISCONN;
206			goto out;
207		}
208		if (sdp->sd_vc == NULL) {
209			error = ENOTCONN;
210			goto out;
211		}
212		error = smb_usr_openshare(sdp->sd_vc,
213		    (struct smbioc_oshare*)data, scred, &ssp);
214		if (error)
215			break;
216		sdp->sd_share = ssp;
217		smb_share_unlock(ssp);
218		sdp->sd_level = SMBL_SHARE;
219		break;
220	    case SMBIOC_REQUEST:
221		if (sdp->sd_share == NULL) {
222			error = ENOTCONN;
223			goto out;
224		}
225		error = smb_usr_simplerequest(sdp->sd_share,
226		    (struct smbioc_rq*)data, scred);
227		break;
228	    case SMBIOC_T2RQ:
229		if (sdp->sd_share == NULL) {
230			error = ENOTCONN;
231			goto out;
232		}
233		error = smb_usr_t2request(sdp->sd_share,
234		    (struct smbioc_t2rq*)data, scred);
235		break;
236	    case SMBIOC_SETFLAGS: {
237		struct smbioc_flags *fl = (struct smbioc_flags*)data;
238		int on;
239
240		if (fl->ioc_level == SMBL_VC) {
241			if (fl->ioc_mask & SMBV_PERMANENT) {
242				on = fl->ioc_flags & SMBV_PERMANENT;
243				if ((vcp = sdp->sd_vc) == NULL) {
244					error = ENOTCONN;
245					goto out;
246				}
247				error = smb_vc_get(vcp, scred);
248				if (error)
249					break;
250				if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
251					vcp->obj.co_flags |= SMBV_PERMANENT;
252					smb_vc_ref(vcp);
253				} else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
254					vcp->obj.co_flags &= ~SMBV_PERMANENT;
255					smb_vc_rele(vcp, scred);
256				}
257				smb_vc_put(vcp, scred);
258			} else
259				error = EINVAL;
260		} else if (fl->ioc_level == SMBL_SHARE) {
261			if (fl->ioc_mask & SMBS_PERMANENT) {
262				on = fl->ioc_flags & SMBS_PERMANENT;
263				if ((ssp = sdp->sd_share) == NULL) {
264					error = ENOTCONN;
265					goto out;
266				}
267				error = smb_share_get(ssp, scred);
268				if (error)
269					break;
270				if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
271					ssp->obj.co_flags |= SMBS_PERMANENT;
272					smb_share_ref(ssp);
273				} else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
274					ssp->obj.co_flags &= ~SMBS_PERMANENT;
275					smb_share_rele(ssp, scred);
276				}
277				smb_share_put(ssp, scred);
278			} else
279				error = EINVAL;
280			break;
281		} else
282			error = EINVAL;
283		break;
284	    }
285	    case SMBIOC_LOOKUP:
286		if (sdp->sd_vc || sdp->sd_share) {
287			error = EISCONN;
288			goto out;
289		}
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);
298			sdp->sd_level = SMBL_VC;
299		}
300		if (ssp) {
301			sdp->sd_share = ssp;
302			smb_share_unlock(ssp);
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			error = ENOTCONN;
313			goto out;
314	 	}
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_td = td;
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	}
334out:
335	free(scred, M_NSMBDEV);
336	SMB_UNLOCK();
337	return error;
338}
339
340static int
341nsmb_dev_load(module_t mod, int cmd, void *arg)
342{
343	int error = 0;
344
345	switch (cmd) {
346	    case MOD_LOAD:
347		error = smb_sm_init();
348		if (error)
349			break;
350		error = smb_iod_init();
351		if (error) {
352			smb_sm_done();
353			break;
354		}
355		error = nsmb_dev_init();
356		if (error)
357			break;
358		sx_init(&smb_lock, "samba device lock");
359		break;
360	    case MOD_UNLOAD:
361		smb_iod_done();
362		error = smb_sm_done();
363		if (error)
364			break;
365		nsmb_dev_destroy();
366		sx_destroy(&smb_lock);
367		break;
368	    default:
369		error = EINVAL;
370		break;
371	}
372	return error;
373}
374
375DEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
376
377int
378smb_dev2share(int fd, int mode, struct smb_cred *scred,
379	struct smb_share **sspp, struct smb_dev **ssdp)
380{
381	cap_rights_t rights;
382	struct file *fp, *fptmp;
383	struct smb_dev *sdp;
384	struct smb_share *ssp;
385	struct thread *td;
386	int error;
387
388	td = curthread;
389	error = fget(td, fd, cap_rights_init(&rights, CAP_READ), &fp);
390	if (error)
391		return (error);
392	fptmp = td->td_fpop;
393	td->td_fpop = fp;
394	error = devfs_get_cdevpriv((void **)&sdp);
395	td->td_fpop = fptmp;
396	fdrop(fp, td);
397	if (error || sdp == NULL)
398		return (error);
399	SMB_LOCK();
400	*ssdp = sdp;
401	ssp = sdp->sd_share;
402	if (ssp == NULL) {
403		SMB_UNLOCK();
404		return (ENOTCONN);
405	}
406	error = smb_share_get(ssp, scred);
407	if (error == 0) {
408		sdp->refcount++;
409		*sspp = ssp;
410	}
411	SMB_UNLOCK();
412	return error;
413}
414
415