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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 37 * Copyright 2013, Joyent, Inc. All rights reserved. 38 * Copyright (c) 2016 by Delphix. All rights reserved. 39 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 40 */ 41 42 #include <sys/systm.h> 43 #include <sys/cred.h> 44 #include <sys/time.h> 45 #include <sys/vfs.h> 46 #include <sys/vnode.h> 47 #include <fs/fs_subr.h> 48 #include <sys/sysmacros.h> 49 #include <sys/kmem.h> 50 #include <sys/mkdev.h> 51 #include <sys/mount.h> 52 #include <sys/statvfs.h> 53 #include <sys/errno.h> 54 #include <sys/debug.h> 55 #include <sys/disp.h> 56 #include <sys/cmn_err.h> 57 #include <sys/modctl.h> 58 #include <sys/policy.h> 59 #include <sys/atomic.h> 60 #include <sys/zone.h> 61 #include <sys/vfs_opreg.h> 62 #include <sys/mntent.h> 63 #include <sys/priv.h> 64 #include <sys/taskq.h> 65 #include <sys/tsol/label.h> 66 #include <sys/tsol/tndb.h> 67 #include <inet/ip.h> 68 69 #include <netsmb/smb_osdep.h> 70 #include <netsmb/smb.h> 71 #include <netsmb/smb_conn.h> 72 #include <netsmb/smb_subr.h> 73 #include <netsmb/smb_dev.h> 74 75 #include <smbfs/smbfs.h> 76 #include <smbfs/smbfs_node.h> 77 #include <smbfs/smbfs_subr.h> 78 79 #ifndef _KERNEL 80 81 #include <libfksmbfs.h> 82 83 #define STRUCT_DECL(s, a) struct s a 84 #define STRUCT_FGET(handle, field) ((handle).field) 85 #define _init(v) fksmbfs_init(v) 86 #define _fini(v) fksmbfs_fini(v) 87 88 #endif /* !_KERNEL */ 89 90 /* 91 * Should smbfs mount enable "-o acl" by default? There are good 92 * arguments for both. The most common use case is individual users 93 * accessing files on some SMB server, for which "noacl" is the more 94 * convenient default. A less common use case is data migration, 95 * where the "acl" option might be a desirable default. We'll make 96 * the common use case the default. This default can be changed via 97 * /etc/system, and/or set per-mount via the "acl" mount option. 98 */ 99 int smbfs_default_opt_acl = 0; 100 101 /* 102 * How many taskq threads per-mount should we use. 103 * Just one is fine (until we do more async work). 104 */ 105 int smbfs_tq_nthread = 1; 106 107 /* 108 * Local functions definitions. 109 */ 110 int smbfsinit(int fstyp, char *name); 111 void smbfsfini(); 112 113 #ifdef _KERNEL 114 static int smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *); 115 #endif /* _KERNEL */ 116 117 /* 118 * SMBFS Mount options table for MS_OPTIONSTR 119 * Note: These are not all the options. 120 * Some options come in via MS_DATA. 121 * Others are generic (see vfs.c) 122 */ 123 static char *intr_cancel[] = { MNTOPT_NOINTR, NULL }; 124 static char *nointr_cancel[] = { MNTOPT_INTR, NULL }; 125 static char *acl_cancel[] = { MNTOPT_NOACL, NULL }; 126 static char *noacl_cancel[] = { MNTOPT_ACL, NULL }; 127 static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL }; 128 static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL }; 129 130 static mntopt_t mntopts[] = { 131 /* 132 * option name cancel option default arg flags 133 * ufs arg flag 134 */ 135 { MNTOPT_INTR, intr_cancel, NULL, MO_DEFAULT, 0 }, 136 { MNTOPT_NOINTR, nointr_cancel, NULL, 0, 0 }, 137 { MNTOPT_ACL, acl_cancel, NULL, 0, 0 }, 138 { MNTOPT_NOACL, noacl_cancel, NULL, 0, 0 }, 139 { MNTOPT_XATTR, xattr_cancel, NULL, MO_DEFAULT, 0 }, 140 { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, 0 }, 141 #ifndef _KERNEL 142 /* See vfs_optionisset MNTOPT_NOAC below. */ 143 { MNTOPT_NOAC, NULL, NULL, 0, 0 }, 144 #endif /* !_KERNEL */ 145 }; 146 147 static mntopts_t smbfs_mntopts = { 148 sizeof (mntopts) / sizeof (mntopt_t), 149 mntopts 150 }; 151 152 static const char fs_type_name[FSTYPSZ] = "smbfs"; 153 154 static vfsdef_t vfw = { 155 VFSDEF_VERSION, 156 (char *)fs_type_name, 157 smbfsinit, /* init routine */ 158 VSW_HASPROTO|VSW_NOTZONESAFE, /* flags */ 159 &smbfs_mntopts /* mount options table prototype */ 160 }; 161 162 #ifdef _KERNEL 163 static struct modlfs modlfs = { 164 &mod_fsops, 165 "SMBFS filesystem", 166 &vfw 167 }; 168 169 static struct modlinkage modlinkage = { 170 MODREV_1, (void *)&modlfs, NULL 171 }; 172 #endif /* _KERNEL */ 173 174 /* 175 * Mutex to protect the following variables: 176 * smbfs_major 177 * smbfs_minor 178 */ 179 extern kmutex_t smbfs_minor_lock; 180 extern int smbfs_major; 181 extern int smbfs_minor; 182 183 /* 184 * Prevent unloads while we have mounts 185 */ 186 uint32_t smbfs_mountcount; 187 188 /* 189 * smbfs vfs operations. 190 */ 191 static int smbfs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *); 192 static int smbfs_unmount(vfs_t *, int, cred_t *); 193 static int smbfs_root(vfs_t *, vnode_t **); 194 static int smbfs_statvfs(vfs_t *, statvfs64_t *); 195 static int smbfs_sync(vfs_t *, short, cred_t *); 196 static void smbfs_freevfs(vfs_t *); 197 198 /* 199 * Module loading 200 */ 201 202 /* 203 * This routine is invoked automatically when the kernel module 204 * containing this routine is loaded. This allows module specific 205 * initialization to be done when the module is loaded. 206 */ 207 int 208 _init(void) 209 { 210 int error; 211 212 /* 213 * Check compiled-in version of "nsmb" 214 * that we're linked with. (paranoid) 215 */ 216 if (nsmb_version != NSMB_VERSION) { 217 cmn_err(CE_WARN, "_init: nsmb version mismatch"); 218 return (ENOTTY); 219 } 220 221 smbfs_mountcount = 0; 222 223 /* 224 * NFS calls these two in _clntinit 225 * Easier to follow this way. 226 */ 227 if ((error = smbfs_subrinit()) != 0) { 228 cmn_err(CE_WARN, "_init: smbfs_subrinit failed"); 229 return (error); 230 } 231 232 if ((error = smbfs_vfsinit()) != 0) { 233 cmn_err(CE_WARN, "_init: smbfs_vfsinit failed"); 234 smbfs_subrfini(); 235 return (error); 236 } 237 238 if ((error = smbfs_clntinit()) != 0) { 239 cmn_err(CE_WARN, "_init: smbfs_clntinit failed"); 240 smbfs_vfsfini(); 241 smbfs_subrfini(); 242 return (error); 243 } 244 245 #ifdef _KERNEL 246 error = mod_install((struct modlinkage *)&modlinkage); 247 #else /* _KERNEL */ 248 error = fake_installfs(&vfw); 249 #endif /* _KERNEL */ 250 251 return (error); 252 } 253 254 /* 255 * Free kernel module resources that were allocated in _init 256 * and remove the linkage information into the kernel 257 */ 258 int 259 _fini(void) 260 { 261 int error; 262 263 /* 264 * If a forcedly unmounted instance is still hanging around, 265 * we cannot allow the module to be unloaded because that would 266 * cause panics once the VFS framework decides it's time to call 267 * into VFS_FREEVFS(). 268 */ 269 if (smbfs_mountcount) 270 return (EBUSY); 271 272 #ifdef _KERNEL 273 error = mod_remove(&modlinkage); 274 #else /* _KERNEL */ 275 error = fake_removefs(&vfw); 276 #endif /* _KERNEL */ 277 if (error) 278 return (error); 279 280 /* 281 * Free the allocated smbnodes, etc. 282 */ 283 smbfs_clntfini(); 284 285 /* NFS calls these two in _clntfini */ 286 smbfs_vfsfini(); 287 smbfs_subrfini(); 288 289 /* 290 * Free the ops vectors 291 */ 292 smbfsfini(); 293 return (0); 294 } 295 296 /* 297 * Return information about the module 298 */ 299 #ifdef _KERNEL 300 int 301 _info(struct modinfo *modinfop) 302 { 303 return (mod_info((struct modlinkage *)&modlinkage, modinfop)); 304 } 305 #endif /* _KERNEL */ 306 307 /* 308 * Initialize the vfs structure 309 */ 310 311 int smbfs_fstyp; 312 vfsops_t *smbfs_vfsops = NULL; 313 314 static const fs_operation_def_t smbfs_vfsops_template[] = { 315 { VFSNAME_MOUNT, { .vfs_mount = smbfs_mount } }, 316 { VFSNAME_UNMOUNT, { .vfs_unmount = smbfs_unmount } }, 317 { VFSNAME_ROOT, { .vfs_root = smbfs_root } }, 318 { VFSNAME_STATVFS, { .vfs_statvfs = smbfs_statvfs } }, 319 { VFSNAME_SYNC, { .vfs_sync = smbfs_sync } }, 320 { VFSNAME_VGET, { .error = fs_nosys } }, 321 { VFSNAME_MOUNTROOT, { .error = fs_nosys } }, 322 { VFSNAME_FREEVFS, { .vfs_freevfs = smbfs_freevfs } }, 323 { NULL, NULL } 324 }; 325 326 /* 327 * This is the VFS switch initialization routine, normally called 328 * via vfssw[x].vsw_init by vfsinit() or mod_install 329 */ 330 int 331 smbfsinit(int fstyp, char *name) 332 { 333 int error; 334 335 error = vfs_setfsops(fstyp, smbfs_vfsops_template, &smbfs_vfsops); 336 if (error != 0) { 337 cmn_err(CE_WARN, 338 "smbfsinit: bad vfs ops template"); 339 return (error); 340 } 341 342 error = vn_make_ops(name, smbfs_vnodeops_template, &smbfs_vnodeops); 343 if (error != 0) { 344 (void) vfs_freevfsops_by_type(fstyp); 345 cmn_err(CE_WARN, 346 "smbfsinit: bad vnode ops template"); 347 return (error); 348 } 349 350 smbfs_fstyp = fstyp; 351 352 return (0); 353 } 354 355 void 356 smbfsfini() 357 { 358 if (smbfs_vfsops) { 359 (void) vfs_freevfsops_by_type(smbfs_fstyp); 360 smbfs_vfsops = NULL; 361 } 362 if (smbfs_vnodeops) { 363 vn_freevnodeops(smbfs_vnodeops); 364 smbfs_vnodeops = NULL; 365 } 366 } 367 368 void 369 smbfs_free_smi(smbmntinfo_t *smi) 370 { 371 if (smi == NULL) 372 return; 373 374 #ifdef _KERNEL 375 if (smi->smi_zone_ref.zref_zone != NULL) 376 zone_rele_ref(&smi->smi_zone_ref, ZONE_REF_SMBFS); 377 #endif /* _KERNEL */ 378 379 if (smi->smi_share != NULL) 380 smb_share_rele(smi->smi_share); 381 382 avl_destroy(&smi->smi_hash_avl); 383 rw_destroy(&smi->smi_hash_lk); 384 cv_destroy(&smi->smi_statvfs_cv); 385 mutex_destroy(&smi->smi_lock); 386 387 kmem_free(smi, sizeof (smbmntinfo_t)); 388 } 389 390 /* 391 * smbfs mount vfsop 392 * Set up mount info record and attach it to vfs struct. 393 */ 394 static int 395 smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) 396 { 397 char *data = uap->dataptr; 398 int error; 399 smbnode_t *rtnp = NULL; /* root of this fs */ 400 smbmntinfo_t *smi = NULL; 401 dev_t smbfs_dev; 402 int version; 403 int devfd; 404 zone_t *zone = curzone; 405 #ifdef _KERNEL 406 zone_t *mntzone = NULL; 407 #else /* _KERNEL */ 408 short minclsyspri = MINCLSYSPRI; 409 #endif /* _KERNEL */ 410 smb_share_t *ssp = NULL; 411 smb_cred_t scred; 412 int flags, sec; 413 STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */ 414 415 #ifdef _KERNEL 416 if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 417 return (error); 418 #endif /* _KERNEL */ 419 420 if (mvp->v_type != VDIR) 421 return (ENOTDIR); 422 423 /* 424 * get arguments 425 * 426 * uap->datalen might be different from sizeof (args) 427 * in a compatible situation. 428 */ 429 #ifdef _KERNEL 430 STRUCT_INIT(args, get_udatamodel()); 431 bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)); 432 if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen, 433 SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)))) 434 return (EFAULT); 435 #else /* _KERNEL */ 436 bzero(&args, sizeof (args)); 437 if (copyin(data, &args, MIN(uap->datalen, sizeof (args)))) 438 return (EFAULT); 439 #endif /* _KERNEL */ 440 441 /* 442 * Check mount program version 443 */ 444 version = STRUCT_FGET(args, version); 445 if (version != SMBFS_VERSION) { 446 cmn_err(CE_WARN, "mount version mismatch:" 447 " kernel=%d, mount=%d\n", 448 SMBFS_VERSION, version); 449 return (EINVAL); 450 } 451 452 /* 453 * Deal with re-mount requests. 454 */ 455 if (uap->flags & MS_REMOUNT) { 456 cmn_err(CE_WARN, "MS_REMOUNT not implemented"); 457 return (ENOTSUP); 458 } 459 460 /* 461 * Check for busy 462 */ 463 mutex_enter(&mvp->v_lock); 464 if (!(uap->flags & MS_OVERLAY) && 465 (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 466 mutex_exit(&mvp->v_lock); 467 return (EBUSY); 468 } 469 mutex_exit(&mvp->v_lock); 470 471 /* 472 * Get the "share" from the netsmb driver (ssp). 473 * It is returned with a "ref" (hold) for us. 474 * Release this hold: at errout below, or in 475 * smbfs_freevfs(). 476 */ 477 devfd = STRUCT_FGET(args, devfd); 478 error = smb_dev2share(devfd, &ssp); 479 if (error) { 480 cmn_err(CE_WARN, "invalid device handle %d (%d)\n", 481 devfd, error); 482 return (error); 483 } 484 485 /* 486 * Use "goto errout" from here on. 487 * See: ssp, smi, rtnp, mntzone 488 */ 489 490 #ifdef _KERNEL 491 /* 492 * Determine the zone we're being mounted into. 493 */ 494 zone_hold(mntzone = zone); /* start with this assumption */ 495 if (getzoneid() == GLOBAL_ZONEID) { 496 zone_rele(mntzone); 497 mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); 498 ASSERT(mntzone != NULL); 499 if (mntzone != zone) { 500 error = EBUSY; 501 goto errout; 502 } 503 } 504 505 /* 506 * Stop the mount from going any further if the zone is going away. 507 */ 508 if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) { 509 error = EBUSY; 510 goto errout; 511 } 512 513 /* 514 * On a Trusted Extensions client, we may have to force read-only 515 * for read-down mounts. 516 */ 517 if (is_system_labeled()) { 518 void *addr; 519 int ipvers = 0; 520 struct smb_vc *vcp; 521 522 vcp = SSTOVC(ssp); 523 addr = smb_vc_getipaddr(vcp, &ipvers); 524 error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr); 525 526 if (error > 0) 527 goto errout; 528 529 if (error == -1) { 530 /* change mount to read-only to prevent write-down */ 531 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 532 } 533 } 534 #endif /* _KERNEL */ 535 536 /* Prevent unload. */ 537 atomic_inc_32(&smbfs_mountcount); 538 539 /* 540 * Create a mount record and link it to the vfs struct. 541 * No more possiblities for errors from here on. 542 * Tear-down of this stuff is in smbfs_free_smi() 543 * 544 * Compare with NFS: nfsrootvp() 545 */ 546 smi = kmem_zalloc(sizeof (*smi), KM_SLEEP); 547 548 mutex_init(&smi->smi_lock, NULL, MUTEX_DEFAULT, NULL); 549 cv_init(&smi->smi_statvfs_cv, NULL, CV_DEFAULT, NULL); 550 551 rw_init(&smi->smi_hash_lk, NULL, RW_DEFAULT, NULL); 552 smbfs_init_hash_avl(&smi->smi_hash_avl); 553 554 smi->smi_share = ssp; 555 ssp = NULL; 556 557 #ifdef _KERNEL 558 /* 559 * Convert the anonymous zone hold acquired via zone_hold() above 560 * into a zone reference. 561 */ 562 zone_init_ref(&smi->smi_zone_ref); 563 zone_hold_ref(mntzone, &smi->smi_zone_ref, ZONE_REF_SMBFS); 564 zone_rele(mntzone); 565 mntzone = NULL; 566 #else /* _KERNEL */ 567 smi->smi_zone_ref.zref_zone = curzone; 568 #endif /* _KERNEL */ 569 570 /* 571 * Initialize option defaults 572 */ 573 smi->smi_acregmin = SEC2HR(SMBFS_ACREGMIN); 574 smi->smi_acregmax = SEC2HR(SMBFS_ACREGMAX); 575 smi->smi_acdirmin = SEC2HR(SMBFS_ACDIRMIN); 576 smi->smi_acdirmax = SEC2HR(SMBFS_ACDIRMAX); 577 smi->smi_flags = SMI_LLOCK; 578 #ifndef _KERNEL 579 /* Always direct IO with fakekernel */ 580 smi->smi_flags |= SMI_DIRECTIO; 581 #endif /* _KERNEL */ 582 583 /* 584 * All "generic" mount options have already been 585 * handled in vfs.c:domount() - see mntopts stuff. 586 * Query generic options using vfs_optionisset(). 587 * Give ACL an adjustable system-wide default. 588 */ 589 if (smbfs_default_opt_acl || 590 vfs_optionisset(vfsp, MNTOPT_ACL, NULL)) 591 smi->smi_flags |= SMI_ACL; 592 if (vfs_optionisset(vfsp, MNTOPT_NOACL, NULL)) 593 smi->smi_flags &= ~SMI_ACL; 594 if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL)) 595 smi->smi_flags |= SMI_INT; 596 597 /* 598 * Get the mount options that come in as smbfs_args, 599 * starting with args.flags (SMBFS_MF_xxx) 600 */ 601 flags = STRUCT_FGET(args, flags); 602 smi->smi_fmode = STRUCT_FGET(args, file_mode) & 0777; 603 smi->smi_dmode = STRUCT_FGET(args, dir_mode) & 0777; 604 #ifdef _KERNEL 605 smi->smi_uid = STRUCT_FGET(args, uid); 606 smi->smi_gid = STRUCT_FGET(args, gid); 607 #else /* _KERNEL */ 608 /* 609 * Need uid/gid to match our fake cred we'll fail in 610 * smbfs_access_rwx later. 611 */ 612 smi->smi_uid = crgetuid(cr); 613 smi->smi_gid = crgetgid(cr); 614 615 /* 616 * Our user-level do_mount() passes the mount options sting 617 * as-is, where the real mount program would convert some 618 * of those options to bits set in smbfs_args.flags. 619 * To avoid replicating all that conversion code, this 620 * uses the generic vfs option support to handle those 621 * option flag bits we need, i.e.: "noac" 622 */ 623 if (vfs_optionisset(vfsp, MNTOPT_NOAC, NULL)) 624 flags |= SMBFS_MF_NOAC; 625 #endif /* _KERNEL */ 626 627 /* 628 * Hande the SMBFS_MF_xxx flags. 629 */ 630 if (flags & SMBFS_MF_NOAC) 631 smi->smi_flags |= SMI_NOAC; 632 if (flags & SMBFS_MF_ACREGMIN) { 633 sec = STRUCT_FGET(args, acregmin); 634 if (sec < 0 || sec > SMBFS_ACMINMAX) 635 sec = SMBFS_ACMINMAX; 636 smi->smi_acregmin = SEC2HR(sec); 637 } 638 if (flags & SMBFS_MF_ACREGMAX) { 639 sec = STRUCT_FGET(args, acregmax); 640 if (sec < 0 || sec > SMBFS_ACMAXMAX) 641 sec = SMBFS_ACMAXMAX; 642 smi->smi_acregmax = SEC2HR(sec); 643 } 644 if (flags & SMBFS_MF_ACDIRMIN) { 645 sec = STRUCT_FGET(args, acdirmin); 646 if (sec < 0 || sec > SMBFS_ACMINMAX) 647 sec = SMBFS_ACMINMAX; 648 smi->smi_acdirmin = SEC2HR(sec); 649 } 650 if (flags & SMBFS_MF_ACDIRMAX) { 651 sec = STRUCT_FGET(args, acdirmax); 652 if (sec < 0 || sec > SMBFS_ACMAXMAX) 653 sec = SMBFS_ACMAXMAX; 654 smi->smi_acdirmax = SEC2HR(sec); 655 } 656 657 /* 658 * Get attributes of the remote file system, 659 * i.e. ACL support, named streams, etc. 660 */ 661 smb_credinit(&scred, cr); 662 error = smbfs_smb_qfsattr(smi->smi_share, &smi->smi_fsa, &scred); 663 smb_credrele(&scred); 664 if (error) { 665 SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error); 666 } 667 668 /* 669 * We enable XATTR by default (via smbfs_mntopts) 670 * but if the share does not support named streams, 671 * force the NOXATTR option (also clears XATTR). 672 * Caller will set or clear VFS_XATTR after this. 673 */ 674 if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0) 675 vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0); 676 677 /* 678 * Ditto ACLs (disable if not supported on this share) 679 */ 680 if ((smi->smi_fsattr & FILE_PERSISTENT_ACLS) == 0) { 681 vfs_setmntopt(vfsp, MNTOPT_NOACL, NULL, 0); 682 smi->smi_flags &= ~SMI_ACL; 683 } 684 685 /* 686 * Assign a unique device id to the mount 687 */ 688 mutex_enter(&smbfs_minor_lock); 689 do { 690 smbfs_minor = (smbfs_minor + 1) & MAXMIN32; 691 smbfs_dev = makedevice(smbfs_major, smbfs_minor); 692 } while (vfs_devismounted(smbfs_dev)); 693 mutex_exit(&smbfs_minor_lock); 694 695 vfsp->vfs_dev = smbfs_dev; 696 vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfs_fstyp); 697 vfsp->vfs_data = (caddr_t)smi; 698 vfsp->vfs_fstype = smbfs_fstyp; 699 vfsp->vfs_bsize = MAXBSIZE; 700 vfsp->vfs_bcount = 0; 701 702 smi->smi_vfsp = vfsp; 703 smbfs_zonelist_add(smi); /* undo in smbfs_freevfs */ 704 705 /* PSARC 2007/227 VFS Feature Registration */ 706 vfs_set_feature(vfsp, VFSFT_XVATTR); 707 vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS); 708 709 /* 710 * Create the root vnode, which we need in unmount 711 * for the call to smbfs_check_table(), etc. 712 * Release this hold in smbfs_unmount. 713 */ 714 rtnp = smbfs_node_findcreate(smi, "\\", 1, NULL, 0, 0, 715 &smbfs_fattr0); 716 ASSERT(rtnp != NULL); 717 rtnp->r_vnode->v_type = VDIR; 718 rtnp->r_vnode->v_flag |= VROOT; 719 smi->smi_root = rtnp; 720 721 /* 722 * Create a taskq for async work (i.e. putpage) 723 */ 724 smi->smi_taskq = taskq_create_proc("smbfs", 725 smbfs_tq_nthread, minclsyspri, 726 smbfs_tq_nthread, smbfs_tq_nthread * 2, 727 zone->zone_zsched, TASKQ_PREPOPULATE); 728 729 /* 730 * NFS does other stuff here too: 731 * async worker threads 732 * init kstats 733 * 734 * End of code from NFS nfsrootvp() 735 */ 736 return (0); 737 738 #ifdef _KERNEL 739 errout: 740 vfsp->vfs_data = NULL; 741 if (smi != NULL) 742 smbfs_free_smi(smi); 743 744 if (mntzone != NULL) 745 zone_rele(mntzone); 746 747 if (ssp != NULL) 748 smb_share_rele(ssp); 749 750 return (error); 751 #endif /* _KERNEL */ 752 } 753 754 /* 755 * vfs operations 756 */ 757 static int 758 smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr) 759 { 760 smbmntinfo_t *smi; 761 smbnode_t *rtnp; 762 763 smi = VFTOSMI(vfsp); 764 765 #ifdef _KERNEL 766 if (secpolicy_fs_unmount(cr, vfsp) != 0) 767 return (EPERM); 768 #endif /* _KERNEL */ 769 770 if ((flag & MS_FORCE) == 0) { 771 smbfs_rflush(vfsp, cr); 772 773 /* 774 * If there are any active vnodes on this file system, 775 * (other than the root vnode) then the file system is 776 * busy and can't be umounted. 777 */ 778 if (smbfs_check_table(vfsp, smi->smi_root)) 779 return (EBUSY); 780 781 /* 782 * We normally hold a ref to the root vnode, so 783 * check for references beyond the one we expect: 784 * smbmntinfo_t -> smi_root 785 * Note that NFS does not hold the root vnode. 786 */ 787 if (smi->smi_root && 788 smi->smi_root->r_vnode->v_count > 1) 789 return (EBUSY); 790 } 791 792 /* 793 * common code for both forced and non-forced 794 * 795 * Setting VFS_UNMOUNTED prevents new operations. 796 * Operations already underway may continue, 797 * but not for long. 798 */ 799 vfsp->vfs_flag |= VFS_UNMOUNTED; 800 801 /* 802 * If we hold the root VP (and we normally do) 803 * then it's safe to release it now. 804 */ 805 if (smi->smi_root) { 806 rtnp = smi->smi_root; 807 smi->smi_root = NULL; 808 VN_RELE(rtnp->r_vnode); /* release root vnode */ 809 } 810 811 /* 812 * Remove all nodes from the node hash tables. 813 * This (indirectly) calls: smbfs_addfree, smbinactive, 814 * which will try to flush dirty pages, etc. so 815 * don't destroy the underlying share just yet. 816 * 817 * Also, with a forced unmount, some nodes may 818 * remain active, and those will get cleaned up 819 * after their last vn_rele. 820 */ 821 smbfs_destroy_table(vfsp); 822 823 /* 824 * Shutdown any outstanding I/O requests on this share, 825 * and force a tree disconnect. The share object will 826 * continue to hang around until smb_share_rele(). 827 * This should also cause most active nodes to be 828 * released as their operations fail with EIO. 829 */ 830 smb_share_kill(smi->smi_share); 831 832 /* 833 * Any async taskq work should be giving up. 834 * Wait for those to exit. 835 */ 836 taskq_destroy(smi->smi_taskq); 837 838 /* 839 * Delete our kstats... 840 * 841 * Doing it here, rather than waiting until 842 * smbfs_freevfs so these are not visible 843 * after the unmount. 844 */ 845 if (smi->smi_io_kstats) { 846 kstat_delete(smi->smi_io_kstats); 847 smi->smi_io_kstats = NULL; 848 } 849 if (smi->smi_ro_kstats) { 850 kstat_delete(smi->smi_ro_kstats); 851 smi->smi_ro_kstats = NULL; 852 } 853 854 /* 855 * The rest happens in smbfs_freevfs() 856 */ 857 return (0); 858 } 859 860 861 /* 862 * find root of smbfs 863 */ 864 static int 865 smbfs_root(vfs_t *vfsp, vnode_t **vpp) 866 { 867 smbmntinfo_t *smi; 868 vnode_t *vp; 869 870 smi = VFTOSMI(vfsp); 871 872 if (curproc->p_zone != smi->smi_zone_ref.zref_zone) 873 return (EPERM); 874 875 if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) 876 return (EIO); 877 878 /* 879 * The root vp is created in mount and held 880 * until unmount, so this is paranoia. 881 */ 882 if (smi->smi_root == NULL) 883 return (EIO); 884 885 /* Just take a reference and return it. */ 886 vp = SMBTOV(smi->smi_root); 887 VN_HOLD(vp); 888 *vpp = vp; 889 890 return (0); 891 } 892 893 /* 894 * Get file system statistics. 895 */ 896 static int 897 smbfs_statvfs(vfs_t *vfsp, statvfs64_t *sbp) 898 { 899 int error; 900 smbmntinfo_t *smi = VFTOSMI(vfsp); 901 smb_share_t *ssp = smi->smi_share; 902 statvfs64_t stvfs; 903 hrtime_t now; 904 smb_cred_t scred; 905 906 if (curproc->p_zone != smi->smi_zone_ref.zref_zone) 907 return (EPERM); 908 909 if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) 910 return (EIO); 911 912 mutex_enter(&smi->smi_lock); 913 914 /* 915 * Use cached result if still valid. 916 */ 917 recheck: 918 now = gethrtime(); 919 if (now < smi->smi_statfstime) { 920 error = 0; 921 goto cache_hit; 922 } 923 924 /* 925 * FS attributes are stale, so someone 926 * needs to do an OTW call to get them. 927 * Serialize here so only one thread 928 * does the OTW call. 929 */ 930 if (smi->smi_status & SM_STATUS_STATFS_BUSY) { 931 smi->smi_status |= SM_STATUS_STATFS_WANT; 932 if (!cv_wait_sig(&smi->smi_statvfs_cv, &smi->smi_lock)) { 933 mutex_exit(&smi->smi_lock); 934 return (EINTR); 935 } 936 /* Hope status is valid now. */ 937 goto recheck; 938 } 939 smi->smi_status |= SM_STATUS_STATFS_BUSY; 940 mutex_exit(&smi->smi_lock); 941 942 /* 943 * Do the OTW call. Note: lock NOT held. 944 */ 945 smb_credinit(&scred, NULL); 946 bzero(&stvfs, sizeof (stvfs)); 947 error = smbfs_smb_statfs(ssp, &stvfs, &scred); 948 smb_credrele(&scred); 949 if (error) { 950 SMBVDEBUG("statfs error=%d\n", error); 951 } else { 952 953 /* 954 * Set a few things the OTW call didn't get. 955 */ 956 stvfs.f_frsize = stvfs.f_bsize; 957 stvfs.f_favail = stvfs.f_ffree; 958 stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0]; 959 bcopy(fs_type_name, stvfs.f_basetype, FSTYPSZ); 960 stvfs.f_flag = vf_to_stf(vfsp->vfs_flag); 961 stvfs.f_namemax = smi->smi_fsa.fsa_maxname; 962 963 /* 964 * Save the result, update lifetime 965 */ 966 now = gethrtime(); 967 smi->smi_statfstime = now + 968 (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC); 969 smi->smi_statvfsbuf = stvfs; /* struct assign! */ 970 } 971 972 mutex_enter(&smi->smi_lock); 973 if (smi->smi_status & SM_STATUS_STATFS_WANT) 974 cv_broadcast(&smi->smi_statvfs_cv); 975 smi->smi_status &= ~(SM_STATUS_STATFS_BUSY | SM_STATUS_STATFS_WANT); 976 977 /* 978 * Copy the statvfs data to caller's buf. 979 * Note: struct assignment 980 */ 981 cache_hit: 982 if (error == 0) 983 *sbp = smi->smi_statvfsbuf; 984 mutex_exit(&smi->smi_lock); 985 return (error); 986 } 987 988 /* 989 * Flush dirty smbfs files for file system vfsp. 990 * If vfsp == NULL, all smbfs files are flushed. 991 */ 992 /*ARGSUSED*/ 993 static int 994 smbfs_sync(vfs_t *vfsp, short flag, cred_t *cr) 995 { 996 997 /* 998 * SYNC_ATTR is used by fsflush() to force old filesystems like UFS 999 * to sync metadata, which they would otherwise cache indefinitely. 1000 * Semantically, the only requirement is that the sync be initiated. 1001 * Assume the server-side takes care of attribute sync. 1002 */ 1003 if (flag & SYNC_ATTR) 1004 return (0); 1005 1006 if (vfsp == NULL) { 1007 /* 1008 * Flush ALL smbfs mounts in this zone. 1009 */ 1010 smbfs_flushall(cr); 1011 return (0); 1012 } 1013 1014 smbfs_rflush(vfsp, cr); 1015 1016 return (0); 1017 } 1018 1019 /* 1020 * Initialization routine for VFS routines. Should only be called once 1021 */ 1022 int 1023 smbfs_vfsinit(void) 1024 { 1025 return (0); 1026 } 1027 1028 /* 1029 * Shutdown routine for VFS routines. Should only be called once 1030 */ 1031 void 1032 smbfs_vfsfini(void) 1033 { 1034 } 1035 1036 void 1037 smbfs_freevfs(vfs_t *vfsp) 1038 { 1039 smbmntinfo_t *smi; 1040 1041 /* free up the resources */ 1042 smi = VFTOSMI(vfsp); 1043 1044 /* 1045 * By this time we should have already deleted the 1046 * smi kstats in the unmount code. If they are still around 1047 * something is wrong 1048 */ 1049 ASSERT(smi->smi_io_kstats == NULL); 1050 1051 smbfs_zonelist_remove(smi); 1052 1053 smbfs_free_smi(smi); 1054 1055 /* 1056 * Allow _fini() to succeed now, if so desired. 1057 */ 1058 atomic_dec_32(&smbfs_mountcount); 1059 } 1060 1061 #ifdef _KERNEL 1062 /* 1063 * smbfs_mount_label_policy: 1064 * Determine whether the mount is allowed according to MAC check, 1065 * by comparing (where appropriate) label of the remote server 1066 * against the label of the zone being mounted into. 1067 * 1068 * Returns: 1069 * 0 : access allowed 1070 * -1 : read-only access allowed (i.e., read-down) 1071 * >0 : error code, such as EACCES 1072 * 1073 * NB: 1074 * NFS supports Cipso labels by parsing the vfs_resource 1075 * to see what the Solaris server global zone has shared. 1076 * We can't support that for CIFS since resource names 1077 * contain share names, not paths. 1078 */ 1079 static int 1080 smbfs_mount_label_policy(vfs_t *vfsp, void *ipaddr, int addr_type, cred_t *cr) 1081 { 1082 bslabel_t *server_sl, *mntlabel; 1083 zone_t *mntzone = NULL; 1084 ts_label_t *zlabel; 1085 tsol_tpc_t *tp; 1086 ts_label_t *tsl = NULL; 1087 int retv; 1088 1089 /* 1090 * Get the zone's label. Each zone on a labeled system has a label. 1091 */ 1092 mntzone = zone_find_by_any_path(refstr_value(vfsp->vfs_mntpt), B_FALSE); 1093 zlabel = mntzone->zone_slabel; 1094 ASSERT(zlabel != NULL); 1095 label_hold(zlabel); 1096 1097 retv = EACCES; /* assume the worst */ 1098 1099 /* 1100 * Next, get the assigned label of the remote server. 1101 */ 1102 tp = find_tpc(ipaddr, addr_type, B_FALSE); 1103 if (tp == NULL) 1104 goto out; /* error getting host entry */ 1105 1106 if (tp->tpc_tp.tp_doi != zlabel->tsl_doi) 1107 goto rel_tpc; /* invalid domain */ 1108 if ((tp->tpc_tp.host_type != UNLABELED)) 1109 goto rel_tpc; /* invalid hosttype */ 1110 1111 server_sl = &tp->tpc_tp.tp_def_label; 1112 mntlabel = label2bslabel(zlabel); 1113 1114 /* 1115 * Now compare labels to complete the MAC check. If the labels 1116 * are equal or if the requestor is in the global zone and has 1117 * NET_MAC_AWARE, then allow read-write access. (Except for 1118 * mounts into the global zone itself; restrict these to 1119 * read-only.) 1120 * 1121 * If the requestor is in some other zone, but their label 1122 * dominates the server, then allow read-down. 1123 * 1124 * Otherwise, access is denied. 1125 */ 1126 if (blequal(mntlabel, server_sl) || 1127 (crgetzoneid(cr) == GLOBAL_ZONEID && 1128 getpflags(NET_MAC_AWARE, cr) != 0)) { 1129 if ((mntzone == global_zone) || 1130 !blequal(mntlabel, server_sl)) 1131 retv = -1; /* read-only */ 1132 else 1133 retv = 0; /* access OK */ 1134 } else if (bldominates(mntlabel, server_sl)) { 1135 retv = -1; /* read-only */ 1136 } else { 1137 retv = EACCES; 1138 } 1139 1140 if (tsl != NULL) 1141 label_rele(tsl); 1142 1143 rel_tpc: 1144 /*LINTED*/ 1145 TPC_RELE(tp); 1146 out: 1147 if (mntzone) 1148 zone_rele(mntzone); 1149 label_rele(zlabel); 1150 return (retv); 1151 } 1152 #endif /* _KERNEL */ 1153