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 * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
33 */
34
35 /*
36 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
37 * Use is subject to license terms.
38 */
39
40 #include <sys/param.h>
41 #include <sys/kmem.h>
42 #include <sys/systm.h>
43 #include <sys/policy.h>
44 #include <sys/conf.h>
45 #include <sys/proc.h>
46 #include <sys/fcntl.h>
47 #include <sys/file.h>
48 #include <sys/socket.h>
49 #include <sys/sunddi.h>
50 #include <sys/cmn_err.h>
51
52 #include <netsmb/smb_osdep.h>
53
54 #include <netsmb/smb.h>
55 #include <netsmb/smb_conn.h>
56 #include <netsmb/smb_rq.h>
57 #include <netsmb/smb_subr.h>
58 #include <netsmb/smb_dev.h>
59
60 static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
61
62 /*
63 * Ioctl function for SMBIOC_FLAGS2
64 */
65 int
smb_usr_get_flags2(smb_dev_t * sdp,intptr_t arg,int flags)66 smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags)
67 {
68 struct smb_vc *vcp = NULL;
69
70 /* This ioctl requires a session. */
71 if ((vcp = sdp->sd_vc) == NULL)
72 return (ENOTCONN);
73
74 /*
75 * Return the flags2 value.
76 */
77 if (ddi_copyout(&vcp->vc_hflags2, (void *)arg,
78 sizeof (u_int16_t), flags))
79 return (EFAULT);
80
81 return (0);
82 }
83
84 /*
85 * Ioctl function for SMBIOC_GETSSNKEY
86 * Size copied out is SMBIOC_HASH_SZ.
87 *
88 * The RPC library needs this for encrypting things
89 * like "set password" requests. This is called
90 * with an active RPC binding, so the connection
91 * will already be active (but this checks).
92 */
93 int
smb_usr_get_ssnkey(smb_dev_t * sdp,intptr_t arg,int flags)94 smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags)
95 {
96 struct smb_vc *vcp = NULL;
97
98 /* This ioctl requires an active session. */
99 if ((vcp = sdp->sd_vc) == NULL)
100 return (ENOTCONN);
101 if (vcp->vc_state != SMBIOD_ST_VCACTIVE)
102 return (ENOTCONN);
103
104 /*
105 * Return the session key.
106 */
107 if (ddi_copyout(vcp->vc_ssn_key, (void *)arg,
108 SMBIOC_HASH_SZ, flags))
109 return (EFAULT);
110
111 return (0);
112 }
113
114 /*
115 * Ioctl function for SMBIOC_REQUEST
116 */
117 int
smb_usr_simplerq(smb_dev_t * sdp,intptr_t arg,int flags,cred_t * cr)118 smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
119 {
120 struct smb_cred scred;
121 struct smb_share *ssp;
122 smbioc_rq_t *ioc = NULL;
123 struct smb_rq *rqp = NULL;
124 struct mbchain *mbp;
125 struct mdchain *mdp;
126 uint32_t rsz;
127 int err, mbseg;
128
129 /* This ioctl requires a share. */
130 if ((ssp = sdp->sd_share) == NULL)
131 return (ENOTCONN);
132
133 smb_credinit(&scred, cr);
134 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
135 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
136 err = EFAULT;
137 goto out;
138 }
139
140 /* See ddi_copyin, ddi_copyout */
141 mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
142
143 /*
144 * Lots of SMB commands could be safe, but
145 * these are the only ones used by libsmbfs.
146 */
147 switch (ioc->ioc_cmd) {
148 /* These are OK */
149 case SMB_COM_CLOSE:
150 case SMB_COM_FLUSH:
151 case SMB_COM_NT_CREATE_ANDX:
152 case SMB_COM_OPEN_PRINT_FILE:
153 case SMB_COM_CLOSE_PRINT_FILE:
154 break;
155
156 default:
157 err = EPERM;
158 goto out;
159 }
160
161 err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp);
162 if (err)
163 goto out;
164
165 mbp = &rqp->sr_rq;
166 err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg);
167
168 err = smb_rq_simple(rqp);
169 if (err == 0) {
170 /*
171 * This may have been an open, so save the
172 * generation ID of the share, which we
173 * check before trying read or write.
174 */
175 sdp->sd_vcgenid = ssp->ss_vcgenid;
176
177 /*
178 * Have reply data. to copyout.
179 * SMB header already parsed.
180 */
181 mdp = &rqp->sr_rp;
182 rsz = msgdsize(mdp->md_top) - SMB_HDRLEN;
183 if (ioc->ioc_rbufsz < rsz) {
184 err = EOVERFLOW;
185 goto out;
186 }
187 ioc->ioc_rbufsz = rsz;
188 err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg);
189 if (err)
190 goto out;
191
192 }
193
194 ioc->ioc_errclass = rqp->sr_errclass;
195 ioc->ioc_serror = rqp->sr_serror;
196 ioc->ioc_error = rqp->sr_error;
197 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
198
199 out:
200 if (rqp != NULL)
201 smb_rq_done(rqp); /* free rqp */
202 if (ioc != NULL)
203 kmem_free(ioc, sizeof (*ioc));
204 smb_credrele(&scred);
205
206 return (err);
207
208 }
209
210 /*
211 * Ioctl function for SMBIOC_T2RQ
212 */
213 int
smb_usr_t2request(smb_dev_t * sdp,intptr_t arg,int flags,cred_t * cr)214 smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
215 {
216 struct smb_cred scred;
217 struct smb_share *ssp;
218 smbioc_t2rq_t *ioc = NULL;
219 struct smb_t2rq *t2p = NULL;
220 struct mdchain *mdp;
221 int err, len, mbseg;
222
223 /* This ioctl requires a share. */
224 if ((ssp = sdp->sd_share) == NULL)
225 return (ENOTCONN);
226
227 smb_credinit(&scred, cr);
228 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
229 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
230 err = EFAULT;
231 goto out;
232 }
233
234 /* See ddi_copyin, ddi_copyout */
235 mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
236
237 if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) {
238 err = EINVAL;
239 goto out;
240 }
241
242 t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP);
243 err = smb_t2_init(t2p, SSTOCP(ssp),
244 ioc->ioc_setup, ioc->ioc_setupcnt, &scred);
245 if (err)
246 goto out;
247 len = t2p->t2_setupcount = ioc->ioc_setupcnt;
248 if (len > 1)
249 t2p->t2_setupdata = ioc->ioc_setup;
250
251 /* This ioc member is a fixed-size array. */
252 if (ioc->ioc_name[0]) {
253 /* Get the name length - carefully! */
254 ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0';
255 t2p->t_name_len = strlen(ioc->ioc_name);
256 t2p->t_name = ioc->ioc_name;
257 }
258 t2p->t2_maxscount = 0;
259 t2p->t2_maxpcount = ioc->ioc_rparamcnt;
260 t2p->t2_maxdcount = ioc->ioc_rdatacnt;
261
262 /* Transmit parameters */
263 err = smb_cpdatain(&t2p->t2_tparam,
264 ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg);
265 if (err)
266 goto out;
267
268 /* Transmit data */
269 err = smb_cpdatain(&t2p->t2_tdata,
270 ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg);
271 if (err)
272 goto out;
273
274 err = smb_t2_request(t2p);
275
276 /* Copyout returned parameters. */
277 mdp = &t2p->t2_rparam;
278 if (err == 0 && mdp->md_top != NULL) {
279 /* User's buffer large enough? */
280 len = m_fixhdr(mdp->md_top);
281 if (len > ioc->ioc_rparamcnt) {
282 err = EMSGSIZE;
283 goto out;
284 }
285 ioc->ioc_rparamcnt = (ushort_t)len;
286 err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg);
287 if (err)
288 goto out;
289 } else
290 ioc->ioc_rparamcnt = 0;
291
292 /* Copyout returned data. */
293 mdp = &t2p->t2_rdata;
294 if (err == 0 && mdp->md_top != NULL) {
295 /* User's buffer large enough? */
296 len = m_fixhdr(mdp->md_top);
297 if (len > ioc->ioc_rdatacnt) {
298 err = EMSGSIZE;
299 goto out;
300 }
301 ioc->ioc_rdatacnt = (ushort_t)len;
302 err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg);
303 if (err)
304 goto out;
305 } else
306 ioc->ioc_rdatacnt = 0;
307
308 ioc->ioc_errclass = t2p->t2_sr_errclass;
309 ioc->ioc_serror = t2p->t2_sr_serror;
310 ioc->ioc_error = t2p->t2_sr_error;
311 ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2;
312
313 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
314
315
316 out:
317 if (t2p != NULL) {
318 /* Note: t2p->t_name no longer allocated */
319 smb_t2_done(t2p);
320 kmem_free(t2p, sizeof (*t2p));
321 }
322 if (ioc != NULL)
323 kmem_free(ioc, sizeof (*ioc));
324 smb_credrele(&scred);
325
326 return (err);
327 }
328
329 /* helper for _t2request */
330 static int
smb_cpdatain(struct mbchain * mbp,int len,char * data,int mbseg)331 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
332 {
333 int error;
334
335 if (len == 0)
336 return (0);
337 error = mb_init(mbp);
338 if (error)
339 return (error);
340 return (mb_put_mem(mbp, data, len, mbseg));
341 }
342
343 /*
344 * Helper for nsmb_ioctl cases
345 * SMBIOC_READ, SMBIOC_WRITE
346 */
347 int
smb_usr_rw(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)348 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
349 {
350 struct smb_cred scred;
351 struct smb_share *ssp;
352 smbioc_rw_t *ioc = NULL;
353 struct iovec aiov[1];
354 struct uio auio;
355 u_int16_t fh;
356 int err;
357 uio_rw_t rw;
358
359 /* This ioctl requires a share. */
360 if ((ssp = sdp->sd_share) == NULL)
361 return (ENOTCONN);
362
363 /* After reconnect, force close+reopen */
364 if (sdp->sd_vcgenid != ssp->ss_vcgenid)
365 return (ESTALE);
366
367 smb_credinit(&scred, cr);
368 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
369 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
370 err = EFAULT;
371 goto out;
372 }
373
374 switch (cmd) {
375 case SMBIOC_READ:
376 rw = UIO_READ;
377 break;
378 case SMBIOC_WRITE:
379 rw = UIO_WRITE;
380 break;
381 default:
382 err = ENODEV;
383 goto out;
384 }
385
386 fh = ioc->ioc_fh;
387
388 aiov[0].iov_base = ioc->ioc_base;
389 aiov[0].iov_len = (size_t)ioc->ioc_cnt;
390
391 auio.uio_iov = aiov;
392 auio.uio_iovcnt = 1;
393 auio.uio_loffset = ioc->ioc_offset;
394 auio.uio_segflg = (flags & FKIOCTL) ?
395 UIO_SYSSPACE : UIO_USERSPACE;
396 auio.uio_fmode = 0;
397 auio.uio_resid = (size_t)ioc->ioc_cnt;
398
399 err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0);
400
401 /*
402 * On return ioc_cnt holds the
403 * number of bytes transferred.
404 */
405 ioc->ioc_cnt -= auio.uio_resid;
406
407 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
408
409 out:
410 if (ioc != NULL)
411 kmem_free(ioc, sizeof (*ioc));
412 smb_credrele(&scred);
413
414 return (err);
415 }
416
417 /*
418 * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
419 * Find or create a session (a.k.a. "VC" in here)
420 */
421 int
smb_usr_get_ssn(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)422 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
423 {
424 struct smb_cred scred;
425 smbioc_ossn_t *ossn = NULL;
426 struct smb_vc *vcp = NULL;
427 int error = 0;
428 uid_t realuid;
429
430 /* Should be no VC */
431 if (sdp->sd_vc != NULL)
432 return (EISCONN);
433
434 smb_credinit(&scred, cr);
435 ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
436 if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
437 error = EFAULT;
438 goto out;
439 }
440
441 /*
442 * Only superuser can specify a UID or GID.
443 */
444 realuid = crgetruid(cr);
445 if (ossn->ssn_owner == SMBM_ANY_OWNER)
446 ossn->ssn_owner = realuid;
447 else {
448 /*
449 * Do we have the privilege to create with the
450 * specified uid? (does uid == cr->cr_uid, etc.)
451 */
452 if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
453 error = EPERM;
454 goto out;
455 }
456 /* ossn->ssn_owner is OK */
457 }
458
459 /*
460 * Make sure the strings are null terminated.
461 */
462 ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
463 ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
464 ossn->ssn_id.id_user[ SMBIOC_MAX_NAME-1] = '\0';
465
466 if (cmd == SMBIOC_SSN_CREATE)
467 ossn->ssn_vopt |= SMBVOPT_CREATE;
468 else /* FIND */
469 ossn->ssn_vopt &= ~SMBVOPT_CREATE;
470
471 error = smb_vc_findcreate(ossn, &scred, &vcp);
472 if (error)
473 goto out;
474 ASSERT(vcp != NULL);
475
476 /*
477 * We have a VC, held, but not locked.
478 * If we're creating, mark this instance as
479 * an open from IOD so close can do cleanup.
480 *
481 * XXX: Would be nice to have a back pointer
482 * from the VC to this (IOD) sdp instance.
483 */
484 if (cmd == SMBIOC_SSN_CREATE) {
485 if (vcp->iod_thr != NULL) {
486 error = EEXIST;
487 goto out;
488 }
489 sdp->sd_flags |= NSMBFL_IOD;
490 } else {
491 /*
492 * Wait for it to finish connecting
493 * (or reconnect) if necessary.
494 */
495 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
496 error = smb_iod_reconnect(vcp);
497 if (error != 0)
498 goto out;
499 }
500 }
501
502 /*
503 * The VC has a hold from _findvc
504 * which we keep until _SSN_RELE
505 * or nsmb_close().
506 */
507 sdp->sd_level = SMBL_VC;
508 sdp->sd_vc = vcp;
509 vcp = NULL;
510 (void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
511
512 out:
513 if (vcp) {
514 /* Error path: rele hold from _findcreate */
515 smb_vc_rele(vcp);
516 }
517 if (ossn != NULL)
518 kmem_free(ossn, sizeof (*ossn));
519 smb_credrele(&scred);
520
521 return (error);
522 }
523
524 /*
525 * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
526 * Release or kill the current session.
527 */
528 int
smb_usr_drop_ssn(smb_dev_t * sdp,int cmd)529 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
530 {
531 struct smb_vc *vcp = NULL;
532
533 /* Must have a VC. */
534 if ((vcp = sdp->sd_vc) == NULL)
535 return (ENOTCONN);
536
537 /* If we have a share ref, drop it too. */
538 if (sdp->sd_share) {
539 smb_share_rele(sdp->sd_share);
540 sdp->sd_share = NULL;
541 sdp->sd_level = SMBL_VC;
542 }
543
544 if (cmd == SMBIOC_SSN_KILL)
545 smb_vc_kill(vcp);
546
547 /* Drop the VC ref. */
548 smb_vc_rele(vcp);
549 sdp->sd_vc = NULL;
550 sdp->sd_level = 0;
551
552 return (0);
553 }
554
555 /*
556 * Find or create a tree (connected share)
557 */
558 int
smb_usr_get_tree(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)559 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
560 {
561 struct smb_cred scred;
562 smbioc_tcon_t *tcon = NULL;
563 struct smb_vc *vcp = NULL;
564 struct smb_share *ssp = NULL;
565 int error = 0;
566
567 /* Must have a VC. */
568 if ((vcp = sdp->sd_vc) == NULL)
569 return (ENOTCONN);
570 /* Should not have a share. */
571 if (sdp->sd_share != NULL)
572 return (EISCONN);
573
574 smb_credinit(&scred, cr);
575 tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
576 if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
577 error = EFAULT;
578 goto out;
579 }
580
581 /*
582 * Make sure the strings are null terminated.
583 */
584 tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
585 tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
586 tcon->tc_sh.sh_type_req[SMBIOC_STYPE_LEN-1] = '\0';
587 bzero(tcon->tc_sh.sh_type_ret, SMBIOC_STYPE_LEN);
588
589 if (cmd == SMBIOC_TREE_CONNECT)
590 tcon->tc_opt |= SMBSOPT_CREATE;
591 else /* FIND */
592 tcon->tc_opt &= ~SMBSOPT_CREATE;
593
594 error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
595 if (error)
596 goto out;
597 ASSERT(ssp != NULL);
598
599 /*
600 * We have a share, held, but not locked.
601 * If we're creating, do tree connect now,
602 * otherwise let that wait for a request.
603 */
604 if (cmd == SMBIOC_TREE_CONNECT) {
605 error = smb_share_tcon(ssp, &scred);
606 if (error)
607 goto out;
608 }
609
610 /*
611 * Give caller the real share type from
612 * the tree connect response, so they can
613 * see if they got the requested type.
614 */
615 (void) memcpy(tcon->tc_sh.sh_type_ret,
616 ssp->ss_type_ret, SMBIOC_STYPE_LEN);
617
618 /*
619 * The share has a hold from _tcon
620 * which we keep until nsmb_close()
621 * or the SMBIOC_TDIS below.
622 */
623 sdp->sd_level = SMBL_SHARE;
624 sdp->sd_share = ssp;
625 ssp = NULL;
626 (void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
627
628 out:
629 if (ssp) {
630 /* Error path: rele hold from _findcreate */
631 smb_share_rele(ssp);
632 }
633 if (tcon) {
634 /*
635 * This structure may contain a
636 * cleartext password, so zap it.
637 */
638 bzero(tcon, sizeof (*tcon));
639 kmem_free(tcon, sizeof (*tcon));
640 }
641 smb_credrele(&scred);
642
643 return (error);
644 }
645
646 /*
647 * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
648 * Release or kill the current tree
649 */
650 int
smb_usr_drop_tree(smb_dev_t * sdp,int cmd)651 smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
652 {
653 struct smb_share *ssp = NULL;
654
655 /* Must have a VC and a share. */
656 if (sdp->sd_vc == NULL)
657 return (ENOTCONN);
658 if ((ssp = sdp->sd_share) == NULL)
659 return (ENOTCONN);
660
661 if (cmd == SMBIOC_TREE_KILL)
662 smb_share_kill(ssp);
663
664 /* Drop the share ref. */
665 smb_share_rele(sdp->sd_share);
666 sdp->sd_share = NULL;
667 sdp->sd_level = SMBL_VC;
668
669 return (0);
670 }
671
672
673 /*
674 * Ioctl function: SMBIOC_IOD_WORK
675 *
676 * Become the reader (IOD) thread, until either the connection is
677 * reset by the server, or until the connection is idle longer than
678 * some max time. (max idle time not yet implemented)
679 */
680 int
smb_usr_iod_work(smb_dev_t * sdp,intptr_t arg,int flags,cred_t * cr)681 smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
682 {
683 struct smb_vc *vcp = NULL;
684 int err = 0;
685
686 /* Must have a valid session. */
687 if ((vcp = sdp->sd_vc) == NULL)
688 return (EINVAL);
689 if (vcp->vc_flags & SMBV_GONE)
690 return (EINVAL);
691
692 /*
693 * Is there already an IOD for this VC?
694 * (Should never happen.)
695 */
696 SMB_VC_LOCK(vcp);
697 if (vcp->iod_thr == NULL)
698 vcp->iod_thr = curthread;
699 else
700 err = EEXIST;
701 SMB_VC_UNLOCK(vcp);
702 if (err)
703 return (err);
704
705 /*
706 * Copy the "work" state, etc. into the VC
707 * The MAC key is copied separately.
708 */
709 if (ddi_copyin((void *)arg, &vcp->vc_work,
710 sizeof (smbioc_ssn_work_t), flags)) {
711 err = EFAULT;
712 goto out;
713 }
714 if (vcp->vc_u_maclen) {
715 vcp->vc_mackeylen = vcp->vc_u_maclen;
716 vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP);
717 if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey,
718 vcp->vc_mackeylen, flags)) {
719 err = EFAULT;
720 goto out;
721 }
722 }
723
724 err = smb_iod_vc_work(vcp, cr);
725
726 /* Caller wants state here. */
727 vcp->vc_work.wk_out_state = vcp->vc_state;
728
729 (void) ddi_copyout(&vcp->vc_work, (void *)arg,
730 sizeof (smbioc_ssn_work_t), flags);
731
732 out:
733 if (vcp->vc_mackey) {
734 kmem_free(vcp->vc_mackey, vcp->vc_mackeylen);
735 vcp->vc_mackey = NULL;
736 vcp->vc_mackeylen = 0;
737 }
738
739 /*
740 * The IOD thread is leaving the driver. Clear iod_thr,
741 * and wake up anybody waiting for us to quit.
742 */
743 SMB_VC_LOCK(vcp);
744 vcp->iod_thr = NULL;
745 cv_broadcast(&vcp->vc_statechg);
746 SMB_VC_UNLOCK(vcp);
747
748 return (err);
749 }
750
751 /*
752 * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL
753 *
754 * Wait for user-level requests to be enqueued on this session,
755 * and then return to the user-space helper, which will then
756 * initiate a reconnect, etc.
757 */
758 int
smb_usr_iod_ioctl(smb_dev_t * sdp,int cmd,intptr_t arg,int flags)759 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags)
760 {
761 struct smb_vc *vcp = NULL;
762 int err = 0;
763
764 /* Must have a valid session. */
765 if ((vcp = sdp->sd_vc) == NULL)
766 return (EINVAL);
767 if (vcp->vc_flags & SMBV_GONE)
768 return (EINVAL);
769
770 /*
771 * Is there already an IOD for this VC?
772 * (Should never happen.)
773 */
774 SMB_VC_LOCK(vcp);
775 if (vcp->iod_thr == NULL)
776 vcp->iod_thr = curthread;
777 else
778 err = EEXIST;
779 SMB_VC_UNLOCK(vcp);
780 if (err)
781 return (err);
782
783 /* nothing to copyin */
784
785 switch (cmd) {
786 case SMBIOC_IOD_IDLE:
787 err = smb_iod_vc_idle(vcp);
788 break;
789
790 case SMBIOC_IOD_RCFAIL:
791 err = smb_iod_vc_rcfail(vcp);
792 break;
793
794 default:
795 err = ENOTTY;
796 goto out;
797 }
798
799 /* Both of these ioctls copy out the new state. */
800 (void) ddi_copyout(&vcp->vc_state, (void *)arg,
801 sizeof (int), flags);
802
803 out:
804 /*
805 * The IOD thread is leaving the driver. Clear iod_thr,
806 * and wake up anybody waiting for us to quit.
807 */
808 SMB_VC_LOCK(vcp);
809 vcp->iod_thr = NULL;
810 cv_broadcast(&vcp->vc_statechg);
811 SMB_VC_UNLOCK(vcp);
812
813 return (err);
814 }
815