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 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 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 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 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 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 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 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 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 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 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 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 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