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: smbfs_vfsops.c,v 1.73.64.1 2005/05/27 02:35:28 lindak Exp $ 33 */ 34 35 /* 36 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include <sys/systm.h> 43 #include <sys/cred.h> 44 #include <sys/vfs.h> 45 #include <sys/vnode.h> 46 #include <fs/fs_subr.h> 47 #include <sys/sysmacros.h> 48 #include <sys/kmem.h> 49 #include <sys/mkdev.h> 50 #include <sys/mount.h> 51 #include <sys/statvfs.h> 52 #include <sys/errno.h> 53 #include <sys/debug.h> 54 #include <sys/cmn_err.h> 55 #include <sys/modctl.h> 56 #include <sys/policy.h> 57 #include <sys/atomic.h> 58 #include <sys/zone.h> 59 #include <sys/vfs_opreg.h> 60 #include <sys/mntent.h> 61 #include <sys/priv.h> 62 #include <sys/tsol/label.h> 63 #include <sys/tsol/tndb.h> 64 #include <inet/ip.h> 65 66 #include <netsmb/smb_osdep.h> 67 #include <netsmb/smb.h> 68 #include <netsmb/smb_conn.h> 69 #include <netsmb/smb_subr.h> 70 #include <netsmb/smb_dev.h> 71 72 #include <smbfs/smbfs.h> 73 #include <smbfs/smbfs_node.h> 74 #include <smbfs/smbfs_subr.h> 75 76 /* 77 * Local functions definitions. 78 */ 79 int smbfsinit(int fstyp, char *name); 80 void smbfsfini(); 81 static int smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *); 82 83 /* 84 * SMBFS Mount options table for MS_OPTIONSTR 85 * Note: These are not all the options. 86 * Some options come in via MS_DATA. 87 * Others are generic (see vfs.c) 88 */ 89 static char *intr_cancel[] = { MNTOPT_NOINTR, NULL }; 90 static char *nointr_cancel[] = { MNTOPT_INTR, NULL }; 91 #ifdef NOT_YET 92 static char *force_dio_cancel[] = { MNTOPT_NOFORCEDIRECTIO, NULL }; 93 static char *noforce_dio_cancel[] = { MNTOPT_FORCEDIRECTIO, NULL }; 94 static char *largefiles_cancel[] = { MNTOPT_NOLARGEFILES, NULL }; 95 static char *nolargefiles_cancel[] = { MNTOPT_LARGEFILES, NULL }; 96 #endif 97 static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL }; 98 static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL }; 99 100 static mntopt_t mntopts[] = { 101 /* 102 * option name cancel option default arg flags 103 * ufs arg flag 104 */ 105 { MNTOPT_INTR, intr_cancel, NULL, MO_DEFAULT, 0 }, 106 { MNTOPT_NOINTR, nointr_cancel, NULL, 0, 0 }, 107 #ifdef NOT_YET 108 { MNTOPT_FORCEDIRECTIO, force_dio_cancel, NULL, 0, 0 }, 109 { MNTOPT_NOFORCEDIRECTIO, noforce_dio_cancel, NULL, 0, 0 }, 110 { MNTOPT_LARGEFILES, largefiles_cancel, NULL, MO_DEFAULT, 0 }, 111 { MNTOPT_NOLARGEFILES, nolargefiles_cancel, NULL, 0, 0 }, 112 #endif 113 { MNTOPT_XATTR, xattr_cancel, NULL, MO_DEFAULT, 0 }, 114 { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, 0 } 115 }; 116 117 static mntopts_t smbfs_mntopts = { 118 sizeof (mntopts) / sizeof (mntopt_t), 119 mntopts 120 }; 121 122 static vfsdef_t vfw = { 123 VFSDEF_VERSION, 124 "smbfs", /* type name string */ 125 smbfsinit, /* init routine */ 126 VSW_HASPROTO|VSW_NOTZONESAFE, /* flags */ 127 &smbfs_mntopts /* mount options table prototype */ 128 }; 129 130 static struct modlfs modlfs = { 131 &mod_fsops, 132 "SMBFS filesystem v" SMBFS_VER_STR, 133 &vfw 134 }; 135 136 static struct modlinkage modlinkage = { 137 MODREV_1, (void *)&modlfs, NULL 138 }; 139 140 /* 141 * Mutex to protect the following variables: 142 * smbfs_major 143 * smbfs_minor 144 */ 145 extern kmutex_t smbfs_minor_lock; 146 extern int smbfs_major; 147 extern int smbfs_minor; 148 149 /* 150 * Prevent unloads while we have mounts 151 */ 152 uint32_t smbfs_mountcount; 153 154 /* 155 * smbfs vfs operations. 156 */ 157 static int smbfs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *); 158 static int smbfs_unmount(vfs_t *, int, cred_t *); 159 static int smbfs_root(vfs_t *, vnode_t **); 160 static int smbfs_statvfs(vfs_t *, statvfs64_t *); 161 static int smbfs_sync(vfs_t *, short, cred_t *); 162 static void smbfs_freevfs(vfs_t *); 163 164 /* 165 * Module loading 166 */ 167 168 /* 169 * This routine is invoked automatically when the kernel module 170 * containing this routine is loaded. This allows module specific 171 * initialization to be done when the module is loaded. 172 */ 173 int 174 _init(void) 175 { 176 int status; 177 178 /* 179 * Check compiled-in version of "nsmb" 180 * that we're linked with. (paranoid) 181 */ 182 if (nsmb_version != NSMB_VERSION) { 183 cmn_err(CE_WARN, "_init: nsmb version mismatch"); 184 return (ENOTTY); 185 } 186 187 smbfs_mountcount = 0; 188 189 if ((status = smbfs_clntinit()) != 0) { 190 cmn_err(CE_WARN, "_init: smbfs_clntinit failed"); 191 return (status); 192 } 193 194 status = mod_install((struct modlinkage *)&modlinkage); 195 return (status); 196 } 197 198 /* 199 * Free kernel module resources that were allocated in _init 200 * and remove the linkage information into the kernel 201 */ 202 int 203 _fini(void) 204 { 205 int error; 206 207 /* 208 * If a forcedly unmounted instance is still hanging around, 209 * we cannot allow the module to be unloaded because that would 210 * cause panics once the VFS framework decides it's time to call 211 * into VFS_FREEVFS(). 212 */ 213 if (smbfs_mountcount) 214 return (EBUSY); 215 216 error = mod_remove(&modlinkage); 217 if (error) 218 return (error); 219 220 /* 221 * Free the allocated smbnodes, etc. 222 */ 223 smbfs_clntfini(); 224 225 /* 226 * Free the ops vectors 227 */ 228 smbfsfini(); 229 return (0); 230 } 231 232 /* 233 * Return information about the module 234 */ 235 int 236 _info(struct modinfo *modinfop) 237 { 238 return (mod_info((struct modlinkage *)&modlinkage, modinfop)); 239 } 240 241 /* 242 * Initialize the vfs structure 243 */ 244 245 int smbfsfstyp; 246 vfsops_t *smbfs_vfsops = NULL; 247 248 static const fs_operation_def_t smbfs_vfsops_template[] = { 249 { VFSNAME_MOUNT, { .vfs_mount = smbfs_mount } }, 250 { VFSNAME_UNMOUNT, { .vfs_unmount = smbfs_unmount } }, 251 { VFSNAME_ROOT, { .vfs_root = smbfs_root } }, 252 { VFSNAME_STATVFS, { .vfs_statvfs = smbfs_statvfs } }, 253 { VFSNAME_SYNC, { .vfs_sync = smbfs_sync } }, 254 { VFSNAME_VGET, { .error = fs_nosys } }, 255 { VFSNAME_MOUNTROOT, { .error = fs_nosys } }, 256 { VFSNAME_FREEVFS, { .vfs_freevfs = smbfs_freevfs } }, 257 { NULL, NULL } 258 }; 259 260 int 261 smbfsinit(int fstyp, char *name) 262 { 263 int error; 264 265 error = vfs_setfsops(fstyp, smbfs_vfsops_template, &smbfs_vfsops); 266 if (error != 0) { 267 zcmn_err(GLOBAL_ZONEID, CE_WARN, 268 "smbfsinit: bad vfs ops template"); 269 return (error); 270 } 271 272 error = vn_make_ops(name, smbfs_vnodeops_template, &smbfs_vnodeops); 273 if (error != 0) { 274 (void) vfs_freevfsops_by_type(fstyp); 275 zcmn_err(GLOBAL_ZONEID, CE_WARN, 276 "smbfsinit: bad vnode ops template"); 277 return (error); 278 } 279 280 smbfsfstyp = fstyp; 281 282 return (0); 283 } 284 285 void 286 smbfsfini() 287 { 288 if (smbfs_vfsops) { 289 (void) vfs_freevfsops_by_type(smbfsfstyp); 290 smbfs_vfsops = NULL; 291 } 292 if (smbfs_vnodeops) { 293 vn_freevnodeops(smbfs_vnodeops); 294 smbfs_vnodeops = NULL; 295 } 296 } 297 298 void 299 smbfs_free_smi(smbmntinfo_t *smi) 300 { 301 if (smi) { 302 smbfs_zonelist_remove(smi); 303 kmem_free(smi, sizeof (smbmntinfo_t)); 304 } 305 } 306 307 /* 308 * smbfs mount vfsop 309 * Set up mount info record and attach it to vfs struct. 310 */ 311 static int 312 smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) 313 { 314 char *data = uap->dataptr; 315 int error; 316 vnode_t *rtvp = NULL; /* root of this fs */ 317 smbmntinfo_t *smi = NULL; 318 dev_t smbfs_dev; 319 int version; 320 int devfd; 321 zone_t *zone = curproc->p_zone; 322 zone_t *mntzone = NULL; 323 smb_share_t *ssp = NULL; 324 smb_cred_t scred; 325 326 STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */ 327 328 if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 329 return (error); 330 331 if (mvp->v_type != VDIR) 332 return (ENOTDIR); 333 334 /* 335 * get arguments 336 * 337 * uap->datalen might be different from sizeof (args) 338 * in a compatible situation. 339 * 340 * XXX - todo: handle mount options string 341 */ 342 STRUCT_INIT(args, get_udatamodel()); 343 bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)); 344 if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen, 345 SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)))) 346 return (EFAULT); 347 348 /* 349 * Check mount program version 350 */ 351 version = STRUCT_FGET(args, version); 352 if (version != SMBFS_VERSION) { 353 cmn_err(CE_WARN, "mount version mismatch:" 354 " kernel=%d, mount=%d\n", 355 SMBFS_VERSION, version); 356 return (EINVAL); 357 } 358 359 if (uap->flags & MS_REMOUNT) { 360 cmn_err(CE_WARN, "MS_REMOUNT not implemented"); 361 return (ENOTSUP); 362 } 363 364 /* 365 * Check for busy 366 */ 367 mutex_enter(&mvp->v_lock); 368 if (!(uap->flags & MS_OVERLAY) && 369 (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 370 mutex_exit(&mvp->v_lock); 371 return (EBUSY); 372 } 373 mutex_exit(&mvp->v_lock); 374 375 /* 376 * Get the "share" from the netsmb driver (ssp). 377 * It is returned with a "ref" (hold) for us. 378 * Release this hold: at errout below, or in 379 * smbfs_freevfs(). 380 */ 381 devfd = STRUCT_FGET(args, devfd); 382 error = smb_dev2share(devfd, &ssp); 383 if (error) { 384 cmn_err(CE_WARN, "invalid device handle %d (%d)\n", 385 devfd, error); 386 return (error); 387 } 388 389 /* 390 * We don't have data structures to support multiple mounts of 391 * the same share object by the same owner, so don't allow it. 392 */ 393 if (ssp->ss_mount != NULL) { 394 smb_share_rele(ssp); 395 return (EBUSY); 396 } 397 398 smb_credinit(&scred, curproc, cr); 399 400 /* 401 * Use "goto errout" from here on. 402 * See: ssp, smi, rtvp, mntzone 403 */ 404 405 /* 406 * Determine the zone we're being mounted into. 407 */ 408 zone_hold(mntzone = zone); /* start with this assumption */ 409 if (getzoneid() == GLOBAL_ZONEID) { 410 zone_rele(mntzone); 411 mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); 412 ASSERT(mntzone != NULL); 413 if (mntzone != zone) { 414 error = EBUSY; 415 goto errout; 416 } 417 } 418 419 /* 420 * Stop the mount from going any further if the zone is going away. 421 */ 422 if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) { 423 error = EBUSY; 424 goto errout; 425 } 426 427 /* 428 * On a Trusted Extensions client, we may have to force read-only 429 * for read-down mounts. 430 */ 431 if (is_system_labeled()) { 432 void *addr; 433 int ipvers = 0; 434 struct smb_vc *vcp; 435 436 vcp = SSTOVC(ssp); 437 addr = smb_vc_getipaddr(vcp, &ipvers); 438 error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr); 439 440 if (error > 0) 441 goto errout; 442 443 if (error == -1) { 444 /* change mount to read-only to prevent write-down */ 445 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 446 } 447 } 448 449 /* 450 * Get root vnode. 451 */ 452 proceed: 453 454 /* 455 * Create a mount record and link it to the vfs struct. 456 * Compare with NFS: nfsrootvp() 457 */ 458 smi = kmem_zalloc(sizeof (smbmntinfo_t), KM_SLEEP); 459 460 smi->smi_share = ssp; 461 ssp->ss_mount = smi; 462 smi->smi_zone = mntzone; 463 smi->smi_flags = SMI_LLOCK; 464 465 /* 466 * Handle mount options. See also XATTR below. 467 * XXX: forcedirectio, largefiles (later) 468 */ 469 if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL)) 470 smi->smi_flags |= SMI_INT; 471 472 /* 473 * XXX If not root, get uid/gid from the covered vnode. 474 */ 475 smi->smi_args.dir_mode = STRUCT_FGET(args, dir_mode); 476 smi->smi_args.file_mode = STRUCT_FGET(args, file_mode); 477 smi->smi_args.uid = STRUCT_FGET(args, uid); 478 smi->smi_args.gid = STRUCT_FGET(args, gid); 479 480 /* 481 * Get attributes of the remote file system, 482 * i.e. ACL support, named streams, etc. 483 */ 484 error = smbfs_smb_qfsattr(ssp, &smi->smi_fsattr, &scred); 485 if (error) { 486 SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error); 487 } 488 489 /* 490 * We enable XATTR by default (via smbfs_mntopts) 491 * but if the share does not support named streams, 492 * force the NOXATTR option (also clears XATTR). 493 * Caller will set or clear VFS_XATTR after this. 494 */ 495 if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0) 496 vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0); 497 498 /* 499 * Assign a unique device id to the mount 500 */ 501 mutex_enter(&smbfs_minor_lock); 502 do { 503 smbfs_minor = (smbfs_minor + 1) & MAXMIN32; 504 smbfs_dev = makedevice(smbfs_major, smbfs_minor); 505 } while (vfs_devismounted(smbfs_dev)); 506 mutex_exit(&smbfs_minor_lock); 507 508 vfsp->vfs_dev = smbfs_dev; 509 vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp); 510 vfsp->vfs_data = (caddr_t)smi; 511 vfsp->vfs_fstype = smbfsfstyp; 512 vfsp->vfs_bsize = MAXBSIZE; 513 vfsp->vfs_bcount = 0; 514 515 smi->smi_vfsp = vfsp; 516 smbfs_zonelist_add(smi); 517 518 /* 519 * Create the root vnode, which we need in unmount 520 * for the call to smb_check_table(), etc. 521 */ 522 rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, 0, NULL); 523 if (!rtvp) { 524 cmn_err(CE_WARN, "smbfs_mount: make_node failed\n"); 525 return (ENOENT); 526 } 527 rtvp->v_type = VDIR; 528 rtvp->v_flag |= VROOT; 529 530 /* 531 * Could get attributes here, but that can wait 532 * until someone does a getattr call. 533 * 534 * NFS does other stuff here too: 535 * async worker threads 536 * init kstats 537 * 538 * End of code from NFS nfsrootvp() 539 */ 540 541 smb_credrele(&scred); 542 543 smi->smi_root = VTOSMB(rtvp); 544 545 atomic_inc_32(&smbfs_mountcount); 546 547 return (0); 548 549 errout: 550 551 ASSERT(rtvp == NULL); 552 553 vfsp->vfs_data = NULL; 554 if (smi) 555 smbfs_free_smi(smi); 556 557 if (mntzone != NULL) 558 zone_rele(mntzone); 559 560 if (ssp) 561 smb_share_rele(ssp); 562 563 smb_credrele(&scred); 564 565 /* args, if we allocated */ 566 567 return (error); 568 } 569 570 /* 571 * vfs operations 572 */ 573 static int 574 smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr) 575 { 576 smbmntinfo_t *smi; 577 smbnode_t *rtnp; 578 579 smi = VFTOSMI(vfsp); 580 581 if (secpolicy_fs_unmount(cr, vfsp) != 0) 582 return (EPERM); 583 584 if ((flag & MS_FORCE) == 0) { 585 #ifdef APPLE 586 smbfs_rflush(vfsp, cr); 587 #endif 588 589 /* 590 * If there are any active vnodes on this file system, 591 * (other than the root vnode) then the file system is 592 * busy and can't be umounted. 593 */ 594 if (smb_check_table(vfsp, smi->smi_root)) 595 return (EBUSY); 596 597 /* 598 * We normally hold a ref to the root vnode, so 599 * check for references beyond the one we expect: 600 * smbmntinfo_t -> smi_root 601 * Note that NFS does not hold the root vnode. 602 */ 603 if (smi->smi_root && 604 smi->smi_root->r_vnode->v_count > 1) 605 return (EBUSY); 606 } 607 608 /* 609 * common code for both forced and non-forced 610 * 611 * Setting VFS_UNMOUNTED prevents new operations. 612 * Operations already underway may continue, 613 * but not for long. 614 */ 615 vfsp->vfs_flag |= VFS_UNMOUNTED; 616 617 /* 618 * Shutdown any outstanding I/O requests on this share, 619 * and force a tree disconnect. The share object will 620 * continue to hang around until smb_share_rele(). 621 * This should also cause most active nodes to be 622 * released as their operations fail with EIO. 623 */ 624 smb_share_kill(smi->smi_share); 625 626 /* 627 * If we hold the root VP (and we normally do) 628 * then it's safe to release it now. 629 */ 630 if (smi->smi_root) { 631 rtnp = smi->smi_root; 632 smi->smi_root = NULL; 633 VN_RELE(rtnp->r_vnode); /* release root vnode */ 634 } 635 636 /* 637 * Remove all nodes from the node hash tables. 638 * This (indirectly) calls: smb_addfree, smbinactive, 639 * which will try to flush dirty pages, etc. so 640 * don't destroy the underlying share just yet. 641 * 642 * Also, with a forced unmount, some nodes may 643 * remain active, and those will get cleaned up 644 * after their last vn_rele. 645 */ 646 smbfs_destroy_table(vfsp); 647 648 /* 649 * Delete our kstats... 650 * 651 * Doing it here, rather than waiting until 652 * smbfs_freevfs so these are not visible 653 * after the unmount. 654 */ 655 if (smi->smi_io_kstats) { 656 kstat_delete(smi->smi_io_kstats); 657 smi->smi_io_kstats = NULL; 658 } 659 if (smi->smi_ro_kstats) { 660 kstat_delete(smi->smi_ro_kstats); 661 smi->smi_ro_kstats = NULL; 662 } 663 664 /* 665 * Note: the smb_share_rele() 666 * happens in smbfs_freevfs() 667 */ 668 669 return (0); 670 } 671 672 673 /* 674 * find root of smbfs 675 */ 676 static int 677 smbfs_root(vfs_t *vfsp, vnode_t **vpp) 678 { 679 smbmntinfo_t *smi; 680 vnode_t *vp; 681 682 smi = VFTOSMI(vfsp); 683 684 if (curproc->p_zone != smi->smi_zone) 685 return (EPERM); 686 687 if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) 688 return (EIO); 689 690 /* 691 * The root vp is created in mount and held 692 * until unmount, so this is paranoia. 693 */ 694 if (smi->smi_root == NULL) 695 return (EIO); 696 697 /* Just take a reference and return it. */ 698 vp = SMBTOV(smi->smi_root); 699 VN_HOLD(vp); 700 *vpp = vp; 701 702 return (0); 703 } 704 705 /* 706 * Get file system statistics. 707 */ 708 static int 709 smbfs_statvfs(vfs_t *vfsp, statvfs64_t *sbp) 710 { 711 int error; 712 smbmntinfo_t *smi = VFTOSMI(vfsp); 713 smb_share_t *ssp = smi->smi_share; 714 statvfs64_t stvfs; 715 hrtime_t now; 716 smb_cred_t scred; 717 718 if (curproc->p_zone != smi->smi_zone) 719 return (EPERM); 720 721 if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) 722 return (EIO); 723 724 mutex_enter(&smi->smi_lock); 725 726 /* 727 * Use cached result if still valid. 728 */ 729 recheck: 730 now = gethrtime(); 731 if (now < smi->smi_statfstime) { 732 goto cache_hit; 733 } 734 735 /* 736 * FS attributes are stale, so someone 737 * needs to do an OTW call to get them. 738 * Serialize here so only one thread 739 * does the OTW call. 740 */ 741 if (smi->smi_status & SM_STATUS_STATFS_BUSY) { 742 smi->smi_status |= SM_STATUS_STATFS_WANT; 743 if (!cv_wait_sig(&smi->smi_statvfs_cv, &smi->smi_lock)) { 744 mutex_exit(&smi->smi_lock); 745 return (EINTR); 746 } 747 /* Hope status is valid now. */ 748 goto recheck; 749 } 750 smi->smi_status |= SM_STATUS_STATFS_BUSY; 751 mutex_exit(&smi->smi_lock); 752 753 /* 754 * Do the OTW call. Note: lock NOT held. 755 */ 756 smb_credinit(&scred, curproc, NULL); 757 bzero(&stvfs, sizeof (stvfs)); 758 error = smbfs_smb_statfs(ssp, &stvfs, &scred); 759 smb_credrele(&scred); 760 761 mutex_enter(&smi->smi_lock); 762 if (smi->smi_status & SM_STATUS_STATFS_WANT) 763 cv_broadcast(&smi->smi_statvfs_cv); 764 smi->smi_status &= ~(SM_STATUS_STATFS_BUSY | SM_STATUS_STATFS_WANT); 765 766 if (error) { 767 SMBVDEBUG("statfs error=%d\n", error); 768 mutex_exit(&smi->smi_lock); 769 return (error); 770 } 771 772 /* 773 * Set a few things the OTW call didn't get. 774 */ 775 stvfs.f_frsize = stvfs.f_bsize; 776 stvfs.f_favail = stvfs.f_ffree; 777 stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0]; 778 (void) strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ); 779 stvfs.f_flag = vf_to_stf(vfsp->vfs_flag); 780 stvfs.f_namemax = (uint32_t)MAXNAMELEN - 1; 781 782 /* 783 * Save the result, update lifetime 784 */ 785 now = gethrtime(); 786 smi->smi_statfstime = now + 787 (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC); 788 smi->smi_statvfsbuf = stvfs; /* struct assign! */ 789 790 /* 791 * Copy the statvfs data to caller's buf. 792 * Note: struct assignment 793 */ 794 cache_hit: 795 *sbp = smi->smi_statvfsbuf; 796 mutex_exit(&smi->smi_lock); 797 return (error); 798 } 799 800 static kmutex_t smbfs_syncbusy; 801 802 /* 803 * Flush dirty smbfs files for file system vfsp. 804 * If vfsp == NULL, all smbfs files are flushed. 805 */ 806 /*ARGSUSED*/ 807 static int 808 smbfs_sync(vfs_t *vfsp, short flag, cred_t *cr) 809 { 810 /* 811 * Cross-zone calls are OK here, since this translates to a 812 * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone. 813 */ 814 #ifdef APPLE 815 if (!(flag & SYNC_ATTR) && mutex_tryenter(&smbfs_syncbusy) != 0) { 816 smbfs_rflush(vfsp, cr); 817 mutex_exit(&smbfs_syncbusy); 818 } 819 #endif /* APPLE */ 820 return (0); 821 } 822 823 /* 824 * Initialization routine for VFS routines. Should only be called once 825 */ 826 int 827 smbfs_vfsinit(void) 828 { 829 mutex_init(&smbfs_syncbusy, NULL, MUTEX_DEFAULT, NULL); 830 return (0); 831 } 832 833 /* 834 * Shutdown routine for VFS routines. Should only be called once 835 */ 836 void 837 smbfs_vfsfini(void) 838 { 839 mutex_destroy(&smbfs_syncbusy); 840 } 841 842 void 843 smbfs_freevfs(vfs_t *vfsp) 844 { 845 smbmntinfo_t *smi; 846 smb_share_t *ssp; 847 848 /* free up the resources */ 849 smi = VFTOSMI(vfsp); 850 851 /* 852 * By this time we should have already deleted the 853 * smi kstats in the unmount code. If they are still around 854 * something is wrong 855 */ 856 ASSERT(smi->smi_io_kstats == NULL); 857 858 /* 859 * Drop our reference to the share. 860 * This usually leads to VC close. 861 */ 862 ssp = smi->smi_share; 863 smi->smi_share = NULL; 864 ssp->ss_mount = NULL; 865 866 smb_share_rele(ssp); 867 868 zone_rele(smi->smi_zone); 869 870 smbfs_free_smi(smi); 871 872 /* 873 * Allow _fini() to succeed now, if so desired. 874 */ 875 atomic_dec_32(&smbfs_mountcount); 876 } 877 878 /* 879 * smbfs_mount_label_policy: 880 * Determine whether the mount is allowed according to MAC check, 881 * by comparing (where appropriate) label of the remote server 882 * against the label of the zone being mounted into. 883 * 884 * Returns: 885 * 0 : access allowed 886 * -1 : read-only access allowed (i.e., read-down) 887 * >0 : error code, such as EACCES 888 * 889 * NB: 890 * NFS supports Cipso labels by parsing the vfs_resource 891 * to see what the Solaris server global zone has shared. 892 * We can't support that for CIFS since resource names 893 * contain share names, not paths. 894 */ 895 static int 896 smbfs_mount_label_policy(vfs_t *vfsp, void *ipaddr, int addr_type, cred_t *cr) 897 { 898 bslabel_t *server_sl, *mntlabel; 899 zone_t *mntzone = NULL; 900 ts_label_t *zlabel; 901 tsol_tpc_t *tp; 902 ts_label_t *tsl = NULL; 903 int retv; 904 905 /* 906 * Get the zone's label. Each zone on a labeled system has a label. 907 */ 908 mntzone = zone_find_by_any_path(refstr_value(vfsp->vfs_mntpt), B_FALSE); 909 zlabel = mntzone->zone_slabel; 910 ASSERT(zlabel != NULL); 911 label_hold(zlabel); 912 913 retv = EACCES; /* assume the worst */ 914 915 /* 916 * Next, get the assigned label of the remote server. 917 */ 918 tp = find_tpc(ipaddr, addr_type, B_FALSE); 919 if (tp == NULL) 920 goto out; /* error getting host entry */ 921 922 if (tp->tpc_tp.tp_doi != zlabel->tsl_doi) 923 goto rel_tpc; /* invalid domain */ 924 if ((tp->tpc_tp.host_type != UNLABELED)) 925 goto rel_tpc; /* invalid hosttype */ 926 927 server_sl = &tp->tpc_tp.tp_def_label; 928 mntlabel = label2bslabel(zlabel); 929 930 /* 931 * Now compare labels to complete the MAC check. If the labels 932 * are equal or if the requestor is in the global zone and has 933 * NET_MAC_AWARE, then allow read-write access. (Except for 934 * mounts into the global zone itself; restrict these to 935 * read-only.) 936 * 937 * If the requestor is in some other zone, but his label 938 * dominates the server, then allow read-down. 939 * 940 * Otherwise, access is denied. 941 */ 942 if (blequal(mntlabel, server_sl) || 943 (crgetzoneid(cr) == GLOBAL_ZONEID && 944 getpflags(NET_MAC_AWARE, cr) != 0)) { 945 if ((mntzone == global_zone) || 946 !blequal(mntlabel, server_sl)) 947 retv = -1; /* read-only */ 948 else 949 retv = 0; /* access OK */ 950 } else if (bldominates(mntlabel, server_sl)) { 951 retv = -1; /* read-only */ 952 } else { 953 retv = EACCES; 954 } 955 956 if (tsl != NULL) 957 label_rele(tsl); 958 959 rel_tpc: 960 /*LINTED*/ 961 TPC_RELE(tp); 962 out: 963 if (mntzone) 964 zone_rele(mntzone); 965 label_rele(zlabel); 966 return (retv); 967 } 968