1/* $NetBSD: smb_dev.c,v 1.49 2016/07/18 21:03:01 pgoyette Exp $ */
2
3/*
4 * Copyright (c) 2000-2001 Boris Popov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * FreeBSD: src/sys/netsmb/smb_dev.c,v 1.4 2001/12/02 08:47:29 bp Exp
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: smb_dev.c,v 1.49 2016/07/18 21:03:01 pgoyette Exp $");
39
40#include <sys/param.h>
41#include <sys/kernel.h>
42#include <sys/systm.h>
43#include <sys/conf.h>
44#include <sys/fcntl.h>
45#include <sys/filedesc.h>
46#include <sys/ioccom.h>
47#include <sys/lock.h>
48#include <sys/malloc.h>
49#include <sys/file.h> /* Must come after sys/malloc.h */
50#include <sys/module.h>
51#include <sys/mbuf.h>
52#include <sys/poll.h>
53#include <sys/proc.h>
54#include <sys/select.h>
55#include <sys/socket.h>
56#include <sys/socketvar.h>
57#include <sys/sysctl.h>
58#include <sys/uio.h>
59#include <sys/vnode.h>
60
61#include <miscfs/specfs/specdev.h> /* XXX */
62
63#include <net/if.h>
64
65#include <netsmb/smb.h>
66#include <netsmb/smb_conn.h>
67#include <netsmb/smb_subr.h>
68#include <netsmb/smb_dev.h>
69#include <netsmb/smb_rq.h>
70
71#include "ioconf.h"
72
73static struct smb_dev **smb_devtbl; /* indexed by minor */
74#define SMB_GETDEV(dev) (smb_devtbl[minor(dev)])
75#define NSMB_DEFNUM 4
76
77
78MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
79
80
81/*
82int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio);
83*/
84
85dev_type_open(nsmb_dev_open);
86dev_type_close(nsmb_dev_close);
87dev_type_ioctl(nsmb_dev_ioctl);
88
89const struct cdevsw nsmb_cdevsw = {
90 .d_open = nsmb_dev_open,
91 .d_close = nsmb_dev_close,
92 .d_read = noread,
93 .d_write = nowrite,
94 .d_ioctl = nsmb_dev_ioctl,
95 .d_stop = nostop,
96 .d_tty = notty,
97 .d_poll = nopoll,
98 .d_mmap = nommap,
99 .d_kqfilter = nokqfilter,
100 .d_discard = nodiscard,
101 .d_flag = D_OTHER,
102};
103
104
105static bool nsmb_inited = false;
106
107void
108nsmbattach(int num)
109{
110
111 if (nsmb_inited)
112 return;
113 nsmb_inited = true;
114
115 if (num <= 0) {
116#ifdef DIAGNOSTIC
117 panic("nsmbattach: count <= 0");
118#endif
119 return;
120 }
121
122 if (num == 1)
123 num = NSMB_DEFNUM;
124
125 smb_devtbl = malloc(num * sizeof(void *), M_NSMBDEV, M_WAITOK|M_ZERO);
126
127 if (smb_sm_init()) {
128#ifdef DIAGNOSTIC
129 panic("netsmbattach: smb_sm_init failed");
130#endif
131 return;
132 }
133
134 if (smb_iod_init()) {
135#ifdef DIAGNOSTIC
136 panic("netsmbattach: smb_iod_init failed");
137#endif
138 smb_sm_done();
139 return;
140 }
141 smb_rqpool_init();
142}
143
144static void
145nsmbdetach(void)
146{
147
148 smb_iod_done();
149 smb_sm_done();
150 smb_rqpool_fini();
151 free(smb_devtbl, M_NSMBDEV);
152 nsmb_inited = false;
153}
154
155int
156nsmb_dev_open(dev_t dev, int oflags, int devtype,
157 struct lwp *l)
158{
159 struct smb_dev *sdp;
160 int s;
161
162 sdp = SMB_GETDEV(dev);
163 if (sdp && (sdp->sd_flags & NSMBFL_OPEN))
164 return EBUSY;
165 if (sdp == NULL) {
166 sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK);
167 smb_devtbl[minor(dev)] = (void*)sdp;
168 }
169
170
171 memset(sdp, 0, sizeof(*sdp));
172/*
173 STAILQ_INIT(&sdp->sd_rqlist);
174 STAILQ_INIT(&sdp->sd_rplist);
175 selinit(&sdp->sd_pollinfo);
176*/
177 s = splnet();
178 sdp->sd_level = -1;
179 sdp->sd_flags |= NSMBFL_OPEN;
180 splx(s);
181 return 0;
182}
183
184int
185nsmb_dev_close(dev_t dev, int flag, int fmt, struct lwp *l)
186{
187 struct smb_dev *sdp;
188 struct smb_vc *vcp;
189 struct smb_share *ssp;
190 struct smb_cred scred;
191 int s;
192
193 sdp = SMB_GETDEV(dev);
194 if (!sdp)
195 return (ENXIO);
196
197 s = splnet();
198 if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
199 splx(s);
200 return EBADF;
201 }
202 smb_makescred(&scred, l, NULL);
203 ssp = sdp->sd_share;
204 if (ssp != NULL) {
205 smb_share_lock(ssp);
206 smb_share_rele(ssp, &scred);
207 }
208 vcp = sdp->sd_vc;
209 if (vcp != NULL) {
210 smb_vc_lock(vcp);
211 smb_vc_rele(vcp, &scred);
212 }
213/*
214 smb_flushq(&sdp->sd_rqlist);
215 smb_flushq(&sdp->sd_rplist);
216 seldestroy(&sdp->sd_pollinfo);
217*/
218 smb_devtbl[minor(dev)] = NULL;
219 free(sdp, M_NSMBDEV);
220 splx(s);
221 return 0;
222}
223
224
225int
226nsmb_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag,
227 struct lwp *l)
228{
229 struct smb_dev *sdp;
230 struct smb_vc *vcp;
231 struct smb_share *ssp;
232 struct smb_cred scred;
233 int error = 0;
234
235 sdp = SMB_GETDEV(dev);
236 if (!sdp)
237 return (ENXIO);
238 if ((sdp->sd_flags & NSMBFL_OPEN) == 0)
239 return EBADF;
240
241 smb_makescred(&scred, l, NULL);
242 switch (cmd) {
243 case SMBIOC_OPENSESSION:
244 if (sdp->sd_vc)
245 return EISCONN;
246 error = smb_usr_opensession((struct smbioc_ossn*)data,
247 &scred, &vcp);
248 if (error)
249 break;
250 sdp->sd_vc = vcp;
251 smb_vc_unlock(vcp);
252 sdp->sd_level = SMBL_VC;
253 break;
254 case SMBIOC_OPENSHARE:
255 if (sdp->sd_share)
256 return EISCONN;
257 if (sdp->sd_vc == NULL)
258 return ENOTCONN;
259 error = smb_usr_openshare(sdp->sd_vc,
260 (struct smbioc_oshare*)data, &scred, &ssp);
261 if (error)
262 break;
263 sdp->sd_share = ssp;
264 smb_share_unlock(ssp);
265 sdp->sd_level = SMBL_SHARE;
266 break;
267 case SMBIOC_REQUEST:
268 if (sdp->sd_share == NULL)
269 return ENOTCONN;
270 error = smb_usr_simplerequest(sdp->sd_share,
271 (struct smbioc_rq*)data, &scred);
272 break;
273 case SMBIOC_T2RQ:
274 if (sdp->sd_share == NULL)
275 return ENOTCONN;
276 error = smb_usr_t2request(sdp->sd_share,
277 (struct smbioc_t2rq*)data, &scred);
278 break;
279 case SMBIOC_SETFLAGS: {
280 struct smbioc_flags *fl = (struct smbioc_flags*)data;
281 int on;
282
283 if (fl->ioc_level == SMBL_VC) {
284 if (fl->ioc_mask & SMBV_PERMANENT) {
285 on = fl->ioc_flags & SMBV_PERMANENT;
286 if ((vcp = sdp->sd_vc) == NULL)
287 return ENOTCONN;
288 error = smb_vc_get(vcp, &scred);
289 if (error)
290 break;
291 if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
292 vcp->obj.co_flags |= SMBV_PERMANENT;
293 smb_vc_ref(vcp);
294 } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
295 vcp->obj.co_flags &= ~SMBV_PERMANENT;
296 smb_vc_rele(vcp, &scred);
297 }
298 smb_vc_put(vcp, &scred);
299 } else
300 error = EINVAL;
301 } else if (fl->ioc_level == SMBL_SHARE) {
302 if (fl->ioc_mask & SMBS_PERMANENT) {
303 on = fl->ioc_flags & SMBS_PERMANENT;
304 if ((ssp = sdp->sd_share) == NULL)
305 return ENOTCONN;
306 error = smb_share_get(ssp, &scred);
307 if (error)
308 break;
309 if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
310 ssp->obj.co_flags |= SMBS_PERMANENT;
311 smb_share_ref(ssp);
312 } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
313 ssp->obj.co_flags &= ~SMBS_PERMANENT;
314 smb_share_rele(ssp, &scred);
315 }
316 smb_share_put(ssp, &scred);
317 } else
318 error = EINVAL;
319 break;
320 } else
321 error = EINVAL;
322 break;
323 }
324 case SMBIOC_LOOKUP:
325 if (sdp->sd_vc || sdp->sd_share)
326 return EISCONN;
327 vcp = NULL;
328 ssp = NULL;
329 error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp);
330 if (error)
331 break;
332 if (vcp) {
333 sdp->sd_vc = vcp;
334 smb_vc_unlock(vcp);
335 sdp->sd_level = SMBL_VC;
336 }
337 if (ssp) {
338 sdp->sd_share = ssp;
339 smb_share_unlock(ssp);
340 sdp->sd_level = SMBL_SHARE;
341 }
342 break;
343 case SMBIOC_READ: case SMBIOC_WRITE: {
344 struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
345 struct uio auio;
346 struct iovec iov;
347
348 if ((ssp = sdp->sd_share) == NULL)
349 return ENOTCONN;
350 iov.iov_base = rwrq->ioc_base;
351 iov.iov_len = rwrq->ioc_cnt;
352 auio.uio_iov = &iov;
353 auio.uio_iovcnt = 1;
354 auio.uio_offset = rwrq->ioc_offset;
355 auio.uio_resid = rwrq->ioc_cnt;
356 auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
357 auio.uio_vmspace = l->l_proc->p_vmspace;
358 if (cmd == SMBIOC_READ)
359 error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred);
360 else
361 error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred);
362 rwrq->ioc_cnt -= auio.uio_resid;
363 break;
364 }
365 default:
366 error = ENODEV;
367 }
368 return error;
369}
370
371MODULE(MODULE_CLASS_DRIVER, nsmb, NULL);
372static int
373nsmb_modcmd(modcmd_t cmd, void *arg)
374{
375#ifdef _MODULE
376 devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
377#endif
378 int error = 0;
379
380 switch (cmd) {
381 case MODULE_CMD_INIT:
382 nsmbattach(1);
383#ifdef _MODULE
384 error =
385 devsw_attach("nsmb", NULL, &bmajor, &nsmb_cdevsw, &cmajor);
386 if (error) {
387 nsmbdetach();
388 }
389#endif
390
391 break;
392 case MODULE_CMD_FINI:
393#ifdef _MODULE
394 error = devsw_detach(NULL, &nsmb_cdevsw);
395 if (error)
396 break;
397#endif
398 nsmbdetach();
399 break;
400 default:
401 error = ENOTTY;
402 break;
403 }
404
405 return error;
406}
407
408/*
409 * Convert a file descriptor to appropriate smb_share pointer
410 */
411int
412smb_dev2share(int fd, int mode, struct smb_cred *scred,
413 struct smb_share **sspp)
414{
415 file_t *fp;
416 struct vnode *vp;
417 struct smb_dev *sdp;
418 struct smb_share *ssp;
419 dev_t dev;
420 int error;
421
422 if ((fp = fd_getfile(fd)) == NULL)
423 return (EBADF);
424
425 vp = fp->f_vnode;
426 if (fp->f_type != DTYPE_VNODE
427 || (fp->f_flag & (FREAD|FWRITE)) == 0
428 || vp->v_type != VCHR
429 || vp->v_rdev == NODEV) {
430 fd_putfile(fd);
431 return (EBADF);
432 }
433
434 dev = vp->v_rdev;
435
436 fd_putfile(fd);
437
438 sdp = SMB_GETDEV(dev);
439 if (!sdp)
440 return (ENXIO);
441 ssp = sdp->sd_share;
442 if (ssp == NULL)
443 return ENOTCONN;
444
445 error = smb_share_get(ssp, scred);
446 if (error)
447 return error;
448
449 *sspp = ssp;
450 return 0;
451}
452