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 33 /* 34 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 35 * Use is subject to license terms. 36 * 37 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 38 */ 39 40 #include <sys/types.h> 41 #include <sys/param.h> 42 #include <sys/errno.h> 43 #include <sys/sysmacros.h> 44 #include <sys/uio.h> 45 #include <sys/buf.h> 46 #include <sys/modctl.h> 47 #include <sys/open.h> 48 #include <sys/file.h> 49 #include <sys/kmem.h> 50 #include <sys/conf.h> 51 #include <sys/cmn_err.h> 52 #include <sys/stat.h> 53 #include <sys/ddi.h> 54 #include <sys/sunddi.h> 55 #include <sys/sunldi.h> 56 #include <sys/policy.h> 57 #include <sys/zone.h> 58 #include <sys/pathname.h> 59 #include <sys/mount.h> 60 #include <sys/sdt.h> 61 #include <fs/fs_subr.h> 62 #include <sys/modctl.h> 63 #include <sys/devops.h> 64 #include <sys/thread.h> 65 #include <sys/types.h> 66 #include <sys/zone.h> 67 68 #include <netsmb/smb_osdep.h> 69 #include <netsmb/mchain.h> /* for "htoles()" */ 70 71 #include <netsmb/smb.h> 72 #include <netsmb/smb_conn.h> 73 #include <netsmb/smb_subr.h> 74 #include <netsmb/smb_dev.h> 75 #include <netsmb/smb_pass.h> 76 77 #ifndef _KERNEL 78 #include <libfknsmb.h> 79 80 #define _init(v) nsmb_drv_init(v) 81 #define _fini(v) nsmb_drv_fini(v) 82 83 #endif /* _KERNEL */ 84 85 #define NSMB_MIN_MINOR 1 86 #define NSMB_MAX_MINOR L_MAXMIN32 87 88 /* for version checks */ 89 const uint32_t nsmb_version = NSMB_VERSION; 90 91 static void *statep; 92 static major_t nsmb_major; 93 static minor_t last_minor = NSMB_MIN_MINOR; 94 static kmutex_t dev_lck; 95 96 /* 97 * cb_ops device operations. 98 */ 99 static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp); 100 static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp); 101 static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 102 cred_t *credp, int *rvalp); 103 static int nsmb_close2(smb_dev_t *sdp, cred_t *cr); 104 105 #ifdef _KERNEL 106 107 static dev_info_t *nsmb_dip; 108 109 /* Zone support */ 110 zone_key_t nsmb_zone_key; 111 extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data); 112 extern void nsmb_zone_destroy(zoneid_t zoneid, void *data); 113 114 /* smbfs cb_ops */ 115 static struct cb_ops nsmb_cbops = { 116 nsmb_open, /* open */ 117 nsmb_close, /* close */ 118 nodev, /* strategy */ 119 nodev, /* print */ 120 nodev, /* dump */ 121 nodev, /* read */ 122 nodev, /* write */ 123 nsmb_ioctl, /* ioctl */ 124 nodev, /* devmap */ 125 nodev, /* mmap */ 126 nodev, /* segmap */ 127 nochpoll, /* poll */ 128 ddi_prop_op, /* prop_op */ 129 NULL, /* stream */ 130 D_MP, /* cb_flag */ 131 CB_REV, /* rev */ 132 nodev, /* int (*cb_aread)() */ 133 nodev /* int (*cb_awrite)() */ 134 }; 135 136 /* 137 * Device options 138 */ 139 static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 140 static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 141 static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, 142 void *arg, void **result); 143 144 static struct dev_ops nsmb_ops = { 145 DEVO_REV, /* devo_rev, */ 146 0, /* refcnt */ 147 nsmb_getinfo, /* info */ 148 nulldev, /* identify */ 149 nulldev, /* probe */ 150 nsmb_attach, /* attach */ 151 nsmb_detach, /* detach */ 152 nodev, /* reset */ 153 &nsmb_cbops, /* driver ops - devctl interfaces */ 154 NULL, /* bus operations */ 155 NULL, /* power */ 156 ddi_quiesce_not_needed, /* quiesce */ 157 }; 158 159 /* 160 * Module linkage information. 161 */ 162 163 static struct modldrv nsmb_modldrv = { 164 &mod_driverops, /* Driver module */ 165 "SMBFS network driver", 166 &nsmb_ops /* Driver ops */ 167 }; 168 169 static struct modlinkage nsmb_modlinkage = { 170 MODREV_1, 171 (void *)&nsmb_modldrv, 172 NULL 173 }; 174 175 #endif /* _KERNEL */ 176 177 int 178 _init(void) 179 { 180 #ifdef _KERNEL 181 int error; 182 #endif /* _KERNEL */ 183 184 (void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1); 185 186 /* Can initialize some mutexes also. */ 187 mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL); 188 189 /* Connection data structures. */ 190 (void) smb_sm_init(); 191 192 /* Initialize password Key chain DB. */ 193 smb_pkey_init(); 194 195 /* Time conversion stuff. */ 196 smb_time_init(); 197 198 /* Initialize crypto mechanisms. */ 199 smb_crypto_mech_init(); 200 201 #ifdef _KERNEL 202 zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown, 203 nsmb_zone_destroy); 204 205 /* 206 * Install the module. Do this after other init, 207 * to prevent entrances before we're ready. 208 */ 209 if ((error = mod_install((&nsmb_modlinkage))) != 0) { 210 211 /* Same as 2nd half of _fini */ 212 (void) zone_key_delete(nsmb_zone_key); 213 smb_pkey_fini(); 214 smb_sm_done(); 215 mutex_destroy(&dev_lck); 216 ddi_soft_state_fini(&statep); 217 218 return (error); 219 } 220 #else /* _KERNEL */ 221 streams_msg_init(); 222 /* No attach, so need to set major. */ 223 nsmb_major = 1; 224 #endif /* _KERNEL */ 225 226 return (0); 227 } 228 229 int 230 _fini(void) 231 { 232 int status; 233 234 /* 235 * Prevent unload if we have active VCs 236 * or stored passwords 237 */ 238 if ((status = smb_sm_idle()) != 0) 239 return (status); 240 if ((status = smb_pkey_idle()) != 0) 241 return (status); 242 243 #ifdef _KERNEL 244 /* 245 * Remove the module. Do this before destroying things, 246 * to prevent new entrances while we're destorying. 247 */ 248 if ((status = mod_remove(&nsmb_modlinkage)) != 0) { 249 return (status); 250 } 251 252 (void) zone_key_delete(nsmb_zone_key); 253 #endif /* _KERNEL */ 254 255 /* Time conversion stuff. */ 256 smb_time_fini(); 257 258 /* Destroy password Key chain DB. */ 259 smb_pkey_fini(); 260 261 smb_sm_done(); 262 263 mutex_destroy(&dev_lck); 264 ddi_soft_state_fini(&statep); 265 266 return (status); 267 } 268 269 #ifdef _KERNEL 270 271 int 272 _info(struct modinfo *modinfop) 273 { 274 return (mod_info(&nsmb_modlinkage, modinfop)); 275 } 276 277 /*ARGSUSED*/ 278 static int 279 nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 280 { 281 int ret = DDI_SUCCESS; 282 283 switch (cmd) { 284 case DDI_INFO_DEVT2DEVINFO: 285 *result = nsmb_dip; 286 break; 287 case DDI_INFO_DEVT2INSTANCE: 288 *result = NULL; 289 break; 290 default: 291 ret = DDI_FAILURE; 292 } 293 return (ret); 294 } 295 296 static int 297 nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 298 { 299 300 if (cmd != DDI_ATTACH) 301 return (DDI_FAILURE); 302 303 /* 304 * We only support only one "instance". Note that 305 * "instances" are different from minor units. 306 * We get one (unique) minor unit per open. 307 */ 308 if (ddi_get_instance(dip) > 0) 309 return (DDI_FAILURE); 310 311 if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO, 312 NULL) == DDI_FAILURE) { 313 cmn_err(CE_WARN, "nsmb_attach: create minor"); 314 return (DDI_FAILURE); 315 } 316 317 /* 318 * We need the major number a couple places, 319 * i.e. in smb_dev2share() 320 */ 321 nsmb_major = ddi_name_to_major(NSMB_NAME); 322 323 nsmb_dip = dip; 324 ddi_report_dev(dip); 325 return (DDI_SUCCESS); 326 } 327 328 /*ARGSUSED*/ 329 static int 330 nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 331 { 332 333 if (cmd != DDI_DETACH) 334 return (DDI_FAILURE); 335 if (ddi_get_instance(dip) > 0) 336 return (DDI_FAILURE); 337 338 nsmb_dip = NULL; 339 ddi_remove_minor_node(dip, NULL); 340 341 return (DDI_SUCCESS); 342 } 343 344 #else /* _KERNEL */ 345 346 /* 347 * Wrappers for libfknsmb: ioctl, open, close, load 348 */ 349 350 /*ARGSUSED*/ 351 int 352 nsmb_drv_ioctl(dev32_t dev32, int cmd, intptr_t arg, int flags) 353 { 354 dev_t dev = expldev(dev32); 355 cred_t *cr = CRED(); 356 int err; 357 358 err = nsmb_ioctl(dev, cmd, arg, flags, cr, NULL); 359 return (err); 360 } 361 362 /*ARGSUSED*/ 363 int 364 nsmb_drv_open(dev32_t *dev32p, int flags, int otyp) 365 { 366 dev_t dev = expldev(*dev32p); 367 int err; 368 369 err = nsmb_open(&dev, flags, otyp, CRED()); 370 if (err == 0) { 371 /* 372 * We have NSMB_MAX_MINOR == L_MAXMIN32 373 * therefore cmpldev never fails. 374 */ 375 VERIFY(cmpldev(dev32p, dev) != 0); 376 } 377 return (err); 378 } 379 380 /*ARGSUSED*/ 381 int 382 nsmb_drv_close(dev32_t dev32, int flags, int otyp) 383 { 384 dev_t dev = expldev(dev32); 385 int err; 386 387 err = nsmb_close(dev, flags, otyp, CRED()); 388 return (err); 389 } 390 391 /* 392 * This function intentionally does nothing. It's used only to 393 * force libfknsmb to load at program start so one can set 394 * breakpoints etc. without debugger "force load" tricks. 395 */ 396 void 397 nsmb_drv_load(void) 398 { 399 } 400 401 #endif /* _KERNEL */ 402 403 /*ARGSUSED*/ 404 static int 405 nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */ 406 cred_t *cr, int *rvalp) 407 { 408 smb_dev_t *sdp; 409 int err; 410 411 sdp = ddi_get_soft_state(statep, getminor(dev)); 412 if (sdp == NULL) { 413 return (EBADF); 414 } 415 if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { 416 return (EBADF); 417 } 418 419 /* 420 * Dont give access if the zone id is not as the same as we 421 * set in the nsmb_open or dont belong to the global zone. 422 * Check if the user belongs to this zone.. 423 */ 424 if (sdp->zoneid != getzoneid()) 425 return (EIO); 426 427 /* 428 * We have a zone_shutdown call back that kills all the VCs 429 * in a zone that's shutting down. That action will cause 430 * all of these ioctls to fail on such VCs, so no need to 431 * check the zone status here on every ioctl call. 432 */ 433 434 /* 435 * Serialize ioctl calls. The smb_usr_... functions 436 * don't expect concurrent calls on a given sdp. 437 */ 438 mutex_enter(&sdp->sd_lock); 439 if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) { 440 mutex_exit(&sdp->sd_lock); 441 return (EBUSY); 442 } 443 sdp->sd_flags |= NSMBFL_IOCTL; 444 mutex_exit(&sdp->sd_lock); 445 446 err = 0; 447 switch (cmd) { 448 case SMBIOC_GETVERS: 449 (void) ddi_copyout(&nsmb_version, (void *)arg, 450 sizeof (nsmb_version), flags); 451 break; 452 453 case SMBIOC_FLAGS2: 454 err = smb_usr_get_flags2(sdp, arg, flags); 455 break; 456 457 case SMBIOC_GETSSNKEY: 458 err = smb_usr_get_ssnkey(sdp, arg, flags); 459 break; 460 461 case SMBIOC_DUP_DEV: 462 err = smb_usr_dup_dev(sdp, arg, flags); 463 break; 464 465 case SMBIOC_REQUEST: 466 err = smb_usr_simplerq(sdp, arg, flags, cr); 467 break; 468 469 case SMBIOC_T2RQ: 470 err = smb_usr_t2request(sdp, arg, flags, cr); 471 break; 472 473 case SMBIOC_READ: 474 case SMBIOC_WRITE: 475 err = smb_usr_rw(sdp, cmd, arg, flags, cr); 476 break; 477 478 case SMBIOC_NTCREATE: 479 err = smb_usr_ntcreate(sdp, arg, flags, cr); 480 break; 481 482 case SMBIOC_PRINTJOB: 483 err = smb_usr_printjob(sdp, arg, flags, cr); 484 break; 485 486 case SMBIOC_CLOSEFH: 487 err = smb_usr_closefh(sdp, cr); 488 break; 489 490 case SMBIOC_SSN_CREATE: 491 case SMBIOC_SSN_FIND: 492 err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr); 493 break; 494 495 case SMBIOC_SSN_KILL: 496 case SMBIOC_SSN_RELE: 497 err = smb_usr_drop_ssn(sdp, cmd); 498 break; 499 500 case SMBIOC_TREE_CONNECT: 501 case SMBIOC_TREE_FIND: 502 err = smb_usr_get_tree(sdp, cmd, arg, flags, cr); 503 break; 504 505 case SMBIOC_TREE_KILL: 506 case SMBIOC_TREE_RELE: 507 err = smb_usr_drop_tree(sdp, cmd); 508 break; 509 510 case SMBIOC_IOD_WORK: 511 err = smb_usr_iod_work(sdp, arg, flags, cr); 512 break; 513 514 case SMBIOC_IOD_IDLE: 515 case SMBIOC_IOD_RCFAIL: 516 err = smb_usr_iod_ioctl(sdp, cmd, arg, flags); 517 break; 518 519 case SMBIOC_PK_ADD: 520 case SMBIOC_PK_DEL: 521 case SMBIOC_PK_CHK: 522 case SMBIOC_PK_DEL_OWNER: 523 case SMBIOC_PK_DEL_EVERYONE: 524 err = smb_pkey_ioctl(cmd, arg, flags, cr); 525 break; 526 527 default: 528 err = ENOTTY; 529 break; 530 } 531 532 mutex_enter(&sdp->sd_lock); 533 sdp->sd_flags &= ~NSMBFL_IOCTL; 534 mutex_exit(&sdp->sd_lock); 535 536 return (err); 537 } 538 539 /* 540 * This does "clone" open, meaning it automatically 541 * assigns an available minor unit for each open. 542 */ 543 /*ARGSUSED*/ 544 static int 545 nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr) 546 { 547 smb_dev_t *sdp; 548 minor_t m; 549 550 mutex_enter(&dev_lck); 551 552 for (m = last_minor + 1; m != last_minor; m++) { 553 if (m > NSMB_MAX_MINOR) 554 m = NSMB_MIN_MINOR; 555 556 if (ddi_get_soft_state(statep, m) == NULL) { 557 last_minor = m; 558 goto found; 559 } 560 } 561 562 /* No available minor units. */ 563 mutex_exit(&dev_lck); 564 return (ENXIO); 565 566 found: 567 /* NB: dev_lck still held */ 568 if (ddi_soft_state_zalloc(statep, m) == DDI_FAILURE) { 569 mutex_exit(&dev_lck); 570 return (ENXIO); 571 } 572 if ((sdp = ddi_get_soft_state(statep, m)) == NULL) { 573 mutex_exit(&dev_lck); 574 return (ENXIO); 575 } 576 *dev = makedevice(nsmb_major, m); 577 mutex_exit(&dev_lck); 578 579 sdp->sd_smbfid = -1; 580 sdp->sd_flags |= NSMBFL_OPEN; 581 sdp->zoneid = crgetzoneid(cr); 582 mutex_init(&sdp->sd_lock, NULL, MUTEX_DRIVER, NULL); 583 584 return (0); 585 } 586 587 /*ARGSUSED*/ 588 static int 589 nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr) 590 { 591 minor_t inst = getminor(dev); 592 smb_dev_t *sdp; 593 int err; 594 595 /* 596 * 1. Check the validity of the minor number. 597 * 2. Release any shares/vc associated with the connection. 598 * 3. Can close the minor number. 599 * 4. Deallocate any resources allocated in open() call. 600 */ 601 602 sdp = ddi_get_soft_state(statep, inst); 603 if (sdp != NULL) 604 err = nsmb_close2(sdp, cr); 605 else 606 err = ENXIO; 607 608 /* 609 * Free the instance 610 */ 611 mutex_enter(&dev_lck); 612 ddi_soft_state_free(statep, inst); 613 mutex_exit(&dev_lck); 614 return (err); 615 } 616 617 static int 618 nsmb_close2(smb_dev_t *sdp, cred_t *cr) 619 { 620 struct smb_vc *vcp; 621 struct smb_share *ssp; 622 623 if (sdp->sd_smbfid != -1) 624 (void) smb_usr_closefh(sdp, cr); 625 626 ssp = sdp->sd_share; 627 if (ssp != NULL) 628 smb_share_rele(ssp); 629 630 vcp = sdp->sd_vc; 631 if (vcp != NULL) { 632 /* 633 * If this dev minor was opened by smbiod, 634 * mark this VC as "dead" because it now 635 * will have no IOD to service it. 636 */ 637 if (sdp->sd_flags & NSMBFL_IOD) 638 smb_iod_disconnect(vcp); 639 smb_vc_rele(vcp); 640 } 641 mutex_destroy(&sdp->sd_lock); 642 643 return (0); 644 } 645 646 /* 647 * Helper for SMBIOC_DUP_DEV 648 * Duplicate state from the FD @arg ("from") onto 649 * the FD for this device instance. 650 */ 651 int 652 smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags) 653 { 654 #ifdef _KERNEL 655 file_t *fp = NULL; 656 vnode_t *vp; 657 #endif /* _KERNEL */ 658 smb_dev_t *from_sdp; 659 dev_t dev; 660 int32_t ufd; 661 int err; 662 663 /* Should be no VC */ 664 if (sdp->sd_vc != NULL) 665 return (EISCONN); 666 667 /* 668 * Get from_sdp (what we will duplicate) 669 */ 670 if (ddi_copyin((void *) arg, &ufd, sizeof (ufd), flags)) 671 return (EFAULT); 672 #ifdef _KERNEL 673 if ((fp = getf(ufd)) == NULL) 674 return (EBADF); 675 /* rele fp below */ 676 vp = fp->f_vnode; 677 dev = vp->v_rdev; 678 #else /* _KERNEL */ 679 /* 680 * No getf(ufd) -- ufd is really a dev32_t 681 */ 682 dev = expldev((dev32_t)ufd); 683 #endif /* _KERNEL */ 684 if (dev == 0 || dev == NODEV || 685 getmajor(dev) != nsmb_major) { 686 err = EINVAL; 687 goto out; 688 } 689 690 from_sdp = ddi_get_soft_state(statep, getminor(dev)); 691 if (from_sdp == NULL) { 692 err = EINVAL; 693 goto out; 694 } 695 696 /* 697 * Duplicate VC and share references onto this FD. 698 */ 699 if ((sdp->sd_vc = from_sdp->sd_vc) != NULL) 700 smb_vc_hold(sdp->sd_vc); 701 if ((sdp->sd_share = from_sdp->sd_share) != NULL) 702 smb_share_hold(sdp->sd_share); 703 sdp->sd_level = from_sdp->sd_level; 704 err = 0; 705 706 out: 707 #ifdef _KERNEL 708 if (fp) 709 releasef(ufd); 710 #endif /* _KERNEL */ 711 return (err); 712 } 713 714 715 /* 716 * Helper used by smbfs_mount 717 */ 718 int 719 smb_dev2share(int fd, struct smb_share **sspp) 720 { 721 #ifdef _KERNEL 722 file_t *fp = NULL; 723 vnode_t *vp; 724 #endif /* _KERNEL */ 725 smb_dev_t *sdp; 726 smb_share_t *ssp; 727 dev_t dev; 728 int err; 729 730 #ifdef _KERNEL 731 if ((fp = getf(fd)) == NULL) 732 return (EBADF); 733 /* rele fp below */ 734 vp = fp->f_vnode; 735 dev = vp->v_rdev; 736 #else /* _KERNEL */ 737 /* 738 * No getf(ufd) -- fd is really a dev32_t 739 */ 740 dev = expldev((dev32_t)fd); 741 #endif /* _KERNEL */ 742 if (dev == 0 || dev == NODEV || 743 getmajor(dev) != nsmb_major) { 744 err = EINVAL; 745 goto out; 746 } 747 748 sdp = ddi_get_soft_state(statep, getminor(dev)); 749 if (sdp == NULL) { 750 err = EINVAL; 751 goto out; 752 } 753 754 ssp = sdp->sd_share; 755 if (ssp == NULL) { 756 err = ENOTCONN; 757 goto out; 758 } 759 760 /* 761 * Our caller gains a ref. to this share. 762 */ 763 *sspp = ssp; 764 smb_share_hold(ssp); 765 err = 0; 766 767 out: 768 #ifdef _KERNEL 769 if (fp) 770 releasef(fd); 771 #endif /* _KERNEL */ 772 return (err); 773 } 774