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 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/kmem.h> 44 #include <sys/systm.h> 45 #include <sys/policy.h> 46 #include <sys/conf.h> 47 #include <sys/proc.h> 48 #include <sys/fcntl.h> 49 #include <sys/file.h> 50 #include <sys/socket.h> 51 #include <sys/sunddi.h> 52 #include <sys/cmn_err.h> 53 54 #include <netsmb/smb_osdep.h> 55 56 #include <smb/winioctl.h> 57 #include <netsmb/smb.h> 58 #include <netsmb/smb_conn.h> 59 #include <netsmb/smb_rq.h> 60 #include <netsmb/smb_subr.h> 61 #include <netsmb/smb_dev.h> 62 63 static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg); 64 65 /* 66 * Ioctl function for SMBIOC_GETSSNKEY 67 * Size copied out is SMBIOC_HASH_SZ. 68 * 69 * The RPC library needs this for encrypting things 70 * like "set password" requests. This is called 71 * with an active RPC binding, so the connection 72 * will already be active (but this checks). 73 */ 74 int 75 smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags) 76 { 77 struct smb_vc *vcp = NULL; 78 79 /* This ioctl requires an active session. */ 80 if ((vcp = sdp->sd_vc) == NULL) 81 return (ENOTCONN); 82 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) 83 return (ENOTCONN); 84 85 /* 86 * Return the session key. 87 */ 88 if (vcp->vc_ssnkey == NULL || 89 vcp->vc_ssnkeylen < SMBIOC_HASH_SZ) 90 return (EINVAL); 91 if (ddi_copyout(vcp->vc_ssnkey, (void *)arg, 92 SMBIOC_HASH_SZ, flags)) 93 return (EFAULT); 94 95 return (0); 96 } 97 98 /* 99 * Ioctl function for SMBIOC_XACTNP (transact named pipe) 100 */ 101 int 102 smb_usr_xnp(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 103 { 104 struct smb_cred scred; 105 struct smb_share *ssp; 106 struct smb_fh *fhp; 107 smbioc_xnp_t *ioc = NULL; 108 struct mbchain send_mb; 109 struct mdchain recv_md; 110 uint32_t rdlen; 111 int err, mbseg; 112 113 /* This ioctl requires a file handle. */ 114 if ((fhp = sdp->sd_fh) == NULL) 115 return (EINVAL); 116 ssp = FHTOSS(fhp); 117 118 /* After reconnect, force close+reopen */ 119 if (fhp->fh_vcgenid != ssp->ss_vcgenid) 120 return (ESTALE); 121 122 bzero(&send_mb, sizeof (send_mb)); 123 bzero(&recv_md, sizeof (recv_md)); 124 125 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 126 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 127 err = EFAULT; 128 goto out; 129 } 130 131 /* 132 * Copyin the send data, into an mbchain, 133 * save output buffer size. 134 */ 135 mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; 136 err = smb_cpdatain(&send_mb, ioc->ioc_tdlen, ioc->ioc_tdata, mbseg); 137 if (err) 138 goto out; 139 rdlen = ioc->ioc_rdlen; 140 141 /* 142 * Run the SMB2 ioctl or SMB1 trans2 143 */ 144 smb_credinit(&scred, cr); 145 if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { 146 err = smb2_smb_ioctl(ssp, &fhp->fh_fid2, 147 &send_mb, &recv_md, &rdlen, 148 FSCTL_PIPE_TRANSCEIVE, &scred); 149 } else { 150 err = smb_t2_xnp(ssp, fhp->fh_fid1, 151 &send_mb, &recv_md, &rdlen, 152 &ioc->ioc_more, &scred); 153 } 154 smb_credrele(&scred); 155 156 /* Copyout returned data. */ 157 if (err == 0 && recv_md.md_top != NULL) { 158 /* User's buffer large enough for copyout? */ 159 size_t len = m_fixhdr(recv_md.md_top); 160 if (len > ioc->ioc_rdlen) { 161 err = EMSGSIZE; 162 goto out; 163 } 164 err = md_get_mem(&recv_md, ioc->ioc_rdata, len, mbseg); 165 if (err) 166 goto out; 167 } else 168 ioc->ioc_rdlen = 0; 169 170 /* Tell caller received length */ 171 if (rdlen <= ioc->ioc_rdlen) { 172 /* Normal case */ 173 ioc->ioc_rdlen = rdlen; 174 } else { 175 /* Buffer overlow. Leave ioc_rdlen */ 176 ioc->ioc_more = 1; 177 } 178 179 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); 180 181 out: 182 kmem_free(ioc, sizeof (*ioc)); 183 184 return (err); 185 } 186 187 /* helper for _t2request */ 188 static int 189 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg) 190 { 191 int error; 192 193 if (len == 0) 194 return (0); 195 error = mb_init(mbp); 196 if (error) 197 return (error); 198 return (mb_put_mem(mbp, data, len, mbseg)); 199 } 200 201 /* 202 * Helper for nsmb_ioctl cases 203 * SMBIOC_READ, SMBIOC_WRITE 204 */ 205 int 206 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 207 { 208 struct smb_cred scred; 209 struct smb_share *ssp; 210 struct smb_fh *fhp; 211 smbioc_rw_t *ioc = NULL; 212 struct iovec aiov[1]; 213 struct uio auio; 214 int err; 215 uio_rw_t rw; 216 217 /* This ioctl requires a file handle. */ 218 if ((fhp = sdp->sd_fh) == NULL) 219 return (EINVAL); 220 ssp = FHTOSS(fhp); 221 222 /* After reconnect, force close+reopen */ 223 if (fhp->fh_vcgenid != ssp->ss_vcgenid) 224 return (ESTALE); 225 226 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 227 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 228 err = EFAULT; 229 goto out; 230 } 231 232 switch (cmd) { 233 case SMBIOC_READ: 234 rw = UIO_READ; 235 break; 236 case SMBIOC_WRITE: 237 rw = UIO_WRITE; 238 break; 239 default: 240 err = ENODEV; 241 goto out; 242 } 243 244 aiov[0].iov_base = ioc->ioc_base; 245 aiov[0].iov_len = (size_t)ioc->ioc_cnt; 246 247 auio.uio_iov = aiov; 248 auio.uio_iovcnt = 1; 249 auio.uio_loffset = ioc->ioc_offset; 250 auio.uio_segflg = (flags & FKIOCTL) ? 251 UIO_SYSSPACE : UIO_USERSPACE; 252 auio.uio_fmode = 0; 253 auio.uio_resid = (size_t)ioc->ioc_cnt; 254 255 smb_credinit(&scred, cr); 256 err = smb_rwuio(fhp, rw, &auio, &scred, 0); 257 smb_credrele(&scred); 258 259 /* 260 * On return ioc_cnt holds the 261 * number of bytes transferred. 262 */ 263 ioc->ioc_cnt -= auio.uio_resid; 264 265 (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); 266 267 out: 268 kmem_free(ioc, sizeof (*ioc)); 269 270 return (err); 271 } 272 273 /* 274 * Helper for nsmb_ioctl case 275 * SMBIOC_NTCREATE 276 */ 277 int 278 smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 279 { 280 struct smb_cred scred; 281 struct mbchain name_mb; 282 struct smb_share *ssp; 283 struct smb_fh *fhp = NULL; 284 smbioc_ntcreate_t *ioc = NULL; 285 int err, nmlen; 286 287 mb_init(&name_mb); 288 289 /* This ioctl requires a share. */ 290 if ((ssp = sdp->sd_share) == NULL) 291 return (ENOTCONN); 292 293 /* Must not already have a file handle. */ 294 if (sdp->sd_fh != NULL) 295 return (EINVAL); 296 297 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 298 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 299 err = EFAULT; 300 goto out; 301 } 302 303 /* Build name_mb */ 304 ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0'; 305 nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1); 306 err = smb_put_dmem(&name_mb, SSTOVC(ssp), 307 ioc->ioc_name, nmlen, 308 SMB_CS_NONE, NULL); 309 if (err != 0) 310 goto out; 311 312 err = smb_fh_create(ssp, &fhp); 313 if (err != 0) 314 goto out; 315 316 /* 317 * Do the OtW open, save the FID. 318 */ 319 smb_credinit(&scred, cr); 320 err = smb_smb_ntcreate(ssp, &name_mb, 321 0, /* create flags */ 322 ioc->ioc_req_acc, 323 ioc->ioc_efattr, 324 ioc->ioc_share_acc, 325 ioc->ioc_open_disp, 326 ioc->ioc_creat_opts, 327 NTCREATEX_IMPERSONATION_IMPERSONATION, 328 &scred, 329 fhp, 330 NULL, 331 NULL); 332 smb_credrele(&scred); 333 if (err != 0) 334 goto out; 335 336 fhp->fh_rights = ioc->ioc_req_acc; 337 smb_fh_opened(fhp); 338 sdp->sd_fh = fhp; 339 fhp = NULL; 340 341 out: 342 if (fhp != NULL) 343 smb_fh_rele(fhp); 344 kmem_free(ioc, sizeof (*ioc)); 345 mb_done(&name_mb); 346 347 return (err); 348 } 349 350 /* 351 * Helper for nsmb_ioctl case 352 * SMBIOC_PRINTJOB 353 */ 354 int 355 smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) 356 { 357 static const char invalid_chars[] = SMB_FILENAME_INVALID_CHARS; 358 struct smb_cred scred; 359 struct mbchain name_mb; 360 struct smb_share *ssp; 361 struct smb_fh *fhp = NULL; 362 smbioc_printjob_t *ioc = NULL; 363 int err, cklen, nmlen; 364 uint32_t access = SA_RIGHT_FILE_WRITE_DATA | 365 SA_RIGHT_FILE_READ_ATTRIBUTES; 366 367 mb_init(&name_mb); 368 369 /* This ioctl requires a share. */ 370 if ((ssp = sdp->sd_share) == NULL) 371 return (ENOTCONN); 372 373 /* The share must be a print queue. */ 374 if (ssp->ss_type != STYPE_PRINTQ) 375 return (EINVAL); 376 377 /* Must not already have a file handle. */ 378 if (sdp->sd_fh != NULL) 379 return (EINVAL); 380 381 smb_credinit(&scred, cr); 382 ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); 383 if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { 384 err = EFAULT; 385 goto out; 386 } 387 388 /* 389 * Use the print job title as the file name to open, but 390 * check for invalid characters first. See the notes in 391 * libsmbfs/smb/print.c about job name sanitizing. 392 */ 393 ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0'; 394 nmlen = strnlen(ioc->ioc_title, SMBIOC_MAX_NAME-1); 395 cklen = strcspn(ioc->ioc_title, invalid_chars); 396 if (cklen < nmlen) { 397 err = EINVAL; 398 goto out; 399 } 400 401 /* Build name_mb */ 402 err = smb_put_dmem(&name_mb, SSTOVC(ssp), 403 ioc->ioc_title, nmlen, 404 SMB_CS_NONE, NULL); 405 if (err != 0) 406 goto out; 407 408 err = smb_fh_create(ssp, &fhp); 409 if (err != 0) 410 goto out; 411 412 /* 413 * Do the OtW open, save the FID. 414 */ 415 smb_credinit(&scred, cr); 416 if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { 417 err = smb2_smb_ntcreate(ssp, &name_mb, 418 NULL, NULL, /* cctx in, out */ 419 0, /* create flags */ 420 access, 421 SMB_EFA_NORMAL, 422 NTCREATEX_SHARE_ACCESS_NONE, 423 NTCREATEX_DISP_CREATE, 424 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, 425 NTCREATEX_IMPERSONATION_IMPERSONATION, 426 &scred, 427 &fhp->fh_fid2, 428 NULL, 429 NULL); 430 } else { 431 err = smb_smb_open_prjob(ssp, ioc->ioc_title, 432 ioc->ioc_setuplen, ioc->ioc_prmode, 433 &scred, &fhp->fh_fid1); 434 } 435 smb_credrele(&scred); 436 if (err != 0) 437 goto out; 438 439 fhp->fh_rights = access; 440 smb_fh_opened(fhp); 441 sdp->sd_fh = fhp; 442 fhp = NULL; 443 444 out: 445 if (fhp != NULL) 446 smb_fh_rele(fhp); 447 kmem_free(ioc, sizeof (*ioc)); 448 mb_done(&name_mb); 449 450 return (err); 451 } 452 453 /* 454 * Helper for nsmb_ioctl case 455 * SMBIOC_CLOSEFH 456 */ 457 /*ARGSUSED*/ 458 int 459 smb_usr_closefh(smb_dev_t *sdp, cred_t *cr) 460 { 461 struct smb_fh *fhp; 462 463 /* This ioctl requires a file handle. */ 464 if ((fhp = sdp->sd_fh) == NULL) 465 return (EINVAL); 466 sdp->sd_fh = NULL; 467 468 smb_fh_close(fhp); 469 smb_fh_rele(fhp); 470 471 return (0); 472 } 473 474 /* 475 * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE 476 * Find or create a session (a.k.a. "VC" in here) 477 */ 478 int 479 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 480 { 481 struct smb_cred scred; 482 smbioc_ossn_t *ossn = NULL; 483 struct smb_vc *vcp = NULL; 484 int error = 0; 485 uid_t realuid; 486 487 /* Should be no VC */ 488 if (sdp->sd_vc != NULL) 489 return (EISCONN); 490 491 smb_credinit(&scred, cr); 492 ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP); 493 if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) { 494 error = EFAULT; 495 goto out; 496 } 497 498 /* 499 * Only superuser can specify a UID or GID. 500 */ 501 realuid = crgetruid(cr); 502 if (ossn->ssn_owner == SMBM_ANY_OWNER) 503 ossn->ssn_owner = realuid; 504 else { 505 /* 506 * Do we have the privilege to create with the 507 * specified uid? (does uid == cr->cr_uid, etc.) 508 */ 509 if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) { 510 error = EPERM; 511 goto out; 512 } 513 /* ossn->ssn_owner is OK */ 514 } 515 516 /* 517 * Make sure the strings are null terminated. 518 */ 519 ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0'; 520 ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0'; 521 ossn->ssn_id.id_user[ SMBIOC_MAX_NAME-1] = '\0'; 522 523 if (cmd == SMBIOC_SSN_CREATE) 524 ossn->ssn_vopt |= SMBVOPT_CREATE; 525 else /* FIND */ 526 ossn->ssn_vopt &= ~SMBVOPT_CREATE; 527 528 error = smb_vc_findcreate(ossn, &scred, &vcp); 529 if (error) 530 goto out; 531 ASSERT(vcp != NULL); 532 533 /* 534 * We have a VC, held, but not locked. 535 * If we're creating, mark this instance as 536 * an open from IOD so close can do cleanup. 537 * 538 * XXX: Would be nice to have a back pointer 539 * from the VC to this (IOD) sdp instance. 540 */ 541 if (cmd == SMBIOC_SSN_CREATE) { 542 if (vcp->iod_thr != NULL) { 543 error = EEXIST; 544 goto out; 545 } 546 sdp->sd_flags |= NSMBFL_IOD; 547 } else { 548 /* 549 * Wait for it to finish connecting 550 * (or reconnect) if necessary. 551 */ 552 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { 553 error = smb_iod_reconnect(vcp); 554 if (error != 0) 555 goto out; 556 } 557 } 558 559 /* 560 * The VC has a hold from _findvc 561 * which we keep until _SSN_RELE 562 * or nsmb_close(). 563 */ 564 sdp->sd_level = SMBL_VC; 565 sdp->sd_vc = vcp; 566 vcp = NULL; 567 (void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags); 568 569 out: 570 if (vcp) { 571 /* Error path: rele hold from _findcreate */ 572 smb_vc_rele(vcp); 573 } 574 kmem_free(ossn, sizeof (*ossn)); 575 smb_credrele(&scred); 576 577 return (error); 578 } 579 580 /* 581 * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL 582 * Release or kill the current session. 583 */ 584 int 585 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd) 586 { 587 struct smb_vc *vcp = NULL; 588 589 /* Must have a VC. */ 590 if ((vcp = sdp->sd_vc) == NULL) 591 return (ENOTCONN); 592 593 /* If we have a share ref, drop it too. */ 594 if (sdp->sd_share) { 595 smb_share_rele(sdp->sd_share); 596 sdp->sd_share = NULL; 597 sdp->sd_level = SMBL_VC; 598 } 599 600 if (cmd == SMBIOC_SSN_KILL) 601 smb_vc_kill(vcp); 602 603 /* Drop the VC ref. */ 604 smb_vc_rele(vcp); 605 sdp->sd_vc = NULL; 606 sdp->sd_level = 0; 607 608 return (0); 609 } 610 611 /* 612 * Find or create a tree (connected share) 613 */ 614 int 615 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 616 { 617 struct smb_cred scred; 618 smbioc_tcon_t *tcon = NULL; 619 struct smb_vc *vcp = NULL; 620 struct smb_share *ssp = NULL; 621 int error = 0; 622 623 /* Must have a VC. */ 624 if ((vcp = sdp->sd_vc) == NULL) 625 return (ENOTCONN); 626 /* Should not have a share. */ 627 if (sdp->sd_share != NULL) 628 return (EISCONN); 629 630 smb_credinit(&scred, cr); 631 tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP); 632 if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) { 633 error = EFAULT; 634 goto out; 635 } 636 637 /* 638 * Make sure the strings are null terminated. 639 */ 640 tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0'; 641 tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0'; 642 643 if (cmd == SMBIOC_TREE_CONNECT) 644 tcon->tc_opt |= SMBSOPT_CREATE; 645 else /* FIND */ 646 tcon->tc_opt &= ~SMBSOPT_CREATE; 647 648 error = smb_share_findcreate(tcon, vcp, &ssp, &scred); 649 if (error) 650 goto out; 651 ASSERT(ssp != NULL); 652 653 /* 654 * We have a share, held, but not locked. 655 * If we're creating, do tree connect now, 656 * otherwise let that wait for a request. 657 */ 658 if (cmd == SMBIOC_TREE_CONNECT) { 659 error = smb_share_tcon(ssp, &scred); 660 if (error) 661 goto out; 662 } 663 664 /* 665 * Give caller the real share type from 666 * the tree connect response, so they can 667 * see if they got the requested type. 668 */ 669 tcon->tc_sh.sh_type = ssp->ss_type; 670 671 /* 672 * The share has a hold from _tcon 673 * which we keep until nsmb_close() 674 * or the SMBIOC_TDIS below. 675 */ 676 sdp->sd_level = SMBL_SHARE; 677 sdp->sd_share = ssp; 678 ssp = NULL; 679 (void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags); 680 681 out: 682 if (ssp) { 683 /* Error path: rele hold from _findcreate */ 684 smb_share_rele(ssp); 685 } 686 /* 687 * This structure may contain a 688 * cleartext password, so zap it. 689 */ 690 bzero(tcon, sizeof (*tcon)); 691 kmem_free(tcon, sizeof (*tcon)); 692 smb_credrele(&scred); 693 694 return (error); 695 } 696 697 /* 698 * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL 699 * Release or kill the current tree 700 */ 701 int 702 smb_usr_drop_tree(smb_dev_t *sdp, int cmd) 703 { 704 struct smb_share *ssp = NULL; 705 706 /* Must have a VC and a share. */ 707 if (sdp->sd_vc == NULL) 708 return (ENOTCONN); 709 if ((ssp = sdp->sd_share) == NULL) 710 return (ENOTCONN); 711 712 if (cmd == SMBIOC_TREE_KILL) 713 smb_share_kill(ssp); 714 715 /* Drop the share ref. */ 716 smb_share_rele(sdp->sd_share); 717 sdp->sd_share = NULL; 718 sdp->sd_level = SMBL_VC; 719 720 return (0); 721 } 722 723 /* 724 * Ioctl handler for all SMBIOC_IOD_... 725 */ 726 int 727 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 728 { 729 struct smb_vc *vcp; 730 int err = 0; 731 732 /* Must be the IOD. */ 733 if ((sdp->sd_flags & NSMBFL_IOD) == 0) 734 return (EINVAL); 735 /* Must have a VC and no share. */ 736 if ((vcp = sdp->sd_vc) == NULL) 737 return (EINVAL); 738 if (sdp->sd_share != NULL) 739 return (EINVAL); 740 741 /* 742 * Is there already an IOD for this VC? 743 * (Should never happen.) 744 */ 745 SMB_VC_LOCK(vcp); 746 if (vcp->iod_thr == NULL) 747 vcp->iod_thr = curthread; 748 else 749 err = EEXIST; 750 SMB_VC_UNLOCK(vcp); 751 if (err) 752 return (err); 753 754 /* 755 * Copy the "work" state, etc. into the VC, 756 * and back to the caller on the way out. 757 * Clear the "out only" part. 758 */ 759 if (ddi_copyin((void *)arg, &vcp->vc_work, 760 sizeof (smbioc_ssn_work_t), flags)) { 761 err = EFAULT; 762 goto out; 763 } 764 vcp->vc_work.wk_out_state = 0; 765 766 switch (cmd) { 767 768 case SMBIOC_IOD_CONNECT: 769 err = nsmb_iod_connect(vcp, cr); 770 break; 771 772 case SMBIOC_IOD_NEGOTIATE: 773 err = nsmb_iod_negotiate(vcp, cr); 774 break; 775 776 case SMBIOC_IOD_SSNSETUP: 777 err = nsmb_iod_ssnsetup(vcp, cr); 778 break; 779 780 case SMBIOC_IOD_WORK: 781 err = smb_iod_vc_work(vcp, flags, cr); 782 break; 783 784 case SMBIOC_IOD_IDLE: 785 err = smb_iod_vc_idle(vcp); 786 break; 787 788 case SMBIOC_IOD_RCFAIL: 789 err = smb_iod_vc_rcfail(vcp); 790 break; 791 792 default: 793 err = ENOTTY; 794 break; 795 } 796 797 out: 798 vcp->vc_work.wk_out_state = vcp->vc_state; 799 (void) ddi_copyout(&vcp->vc_work, (void *)arg, 800 sizeof (smbioc_ssn_work_t), flags); 801 802 /* 803 * The IOD thread is leaving the driver. Clear iod_thr, 804 * and wake up anybody waiting for us to quit. 805 */ 806 SMB_VC_LOCK(vcp); 807 vcp->iod_thr = NULL; 808 cv_broadcast(&vcp->vc_statechg); 809 SMB_VC_UNLOCK(vcp); 810 811 return (err); 812 } 813 814 int 815 smb_usr_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) 816 { 817 int err; 818 819 /* 820 * Serialize ioctl calls. The smb_usr_... functions 821 * don't expect concurrent calls on a given sdp. 822 */ 823 mutex_enter(&sdp->sd_lock); 824 if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) { 825 mutex_exit(&sdp->sd_lock); 826 return (EBUSY); 827 } 828 sdp->sd_flags |= NSMBFL_IOCTL; 829 mutex_exit(&sdp->sd_lock); 830 831 err = 0; 832 switch (cmd) { 833 case SMBIOC_GETVERS: 834 (void) ddi_copyout(&nsmb_version, (void *)arg, 835 sizeof (nsmb_version), flags); 836 break; 837 838 case SMBIOC_GETSSNKEY: 839 err = smb_usr_get_ssnkey(sdp, arg, flags); 840 break; 841 842 case SMBIOC_DUP_DEV: 843 err = smb_usr_dup_dev(sdp, arg, flags); 844 break; 845 846 case SMBIOC_XACTNP: 847 err = smb_usr_xnp(sdp, arg, flags, cr); 848 break; 849 850 case SMBIOC_READ: 851 case SMBIOC_WRITE: 852 err = smb_usr_rw(sdp, cmd, arg, flags, cr); 853 break; 854 855 case SMBIOC_NTCREATE: 856 err = smb_usr_ntcreate(sdp, arg, flags, cr); 857 break; 858 859 case SMBIOC_PRINTJOB: 860 err = smb_usr_printjob(sdp, arg, flags, cr); 861 break; 862 863 case SMBIOC_CLOSEFH: 864 err = smb_usr_closefh(sdp, cr); 865 break; 866 867 case SMBIOC_SSN_CREATE: 868 case SMBIOC_SSN_FIND: 869 err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr); 870 break; 871 872 case SMBIOC_SSN_KILL: 873 case SMBIOC_SSN_RELE: 874 err = smb_usr_drop_ssn(sdp, cmd); 875 break; 876 877 case SMBIOC_TREE_CONNECT: 878 case SMBIOC_TREE_FIND: 879 err = smb_usr_get_tree(sdp, cmd, arg, flags, cr); 880 break; 881 882 case SMBIOC_TREE_KILL: 883 case SMBIOC_TREE_RELE: 884 err = smb_usr_drop_tree(sdp, cmd); 885 break; 886 887 case SMBIOC_IOD_CONNECT: 888 case SMBIOC_IOD_NEGOTIATE: 889 case SMBIOC_IOD_SSNSETUP: 890 case SMBIOC_IOD_WORK: 891 case SMBIOC_IOD_IDLE: 892 case SMBIOC_IOD_RCFAIL: 893 err = smb_usr_iod_ioctl(sdp, cmd, arg, flags, cr); 894 break; 895 896 case SMBIOC_PK_ADD: 897 case SMBIOC_PK_DEL: 898 case SMBIOC_PK_CHK: 899 case SMBIOC_PK_DEL_OWNER: 900 case SMBIOC_PK_DEL_EVERYONE: 901 err = smb_pkey_ioctl(cmd, arg, flags, cr); 902 break; 903 904 default: 905 err = ENOTTY; 906 break; 907 } 908 909 mutex_enter(&sdp->sd_lock); 910 sdp->sd_flags &= ~NSMBFL_IOCTL; 911 mutex_exit(&sdp->sd_lock); 912 913 return (err); 914 } 915