1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 24 * Copyright 2015 Joyent, Inc. 25 */ 26 27 #include <smbsrv/smb_kproto.h> 28 #include <smbsrv/smb_fsops.h> 29 #include <sys/sdt.h> 30 #include <sys/fcntl.h> 31 #include <sys/vfs.h> 32 #include <sys/vfs_opreg.h> 33 #include <sys/vnode.h> 34 #include <sys/fem.h> 35 36 extern caller_context_t smb_ct; 37 38 static boolean_t smb_fem_initialized = B_FALSE; 39 static fem_t *smb_fcn_ops = NULL; 40 static fem_t *smb_oplock_ops = NULL; 41 42 /* 43 * Declarations for FCN (file change notification) FEM monitors 44 */ 45 46 static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int, 47 vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *); 48 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *, 49 caller_context_t *, int); 50 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *, 51 cred_t *, caller_context_t *, int); 52 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **, 53 cred_t *, caller_context_t *, int, vsecattr_t *); 54 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *, 55 caller_context_t *, int); 56 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *, 57 caller_context_t *, int); 58 static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *, 59 char *, cred_t *, caller_context_t *, int); 60 61 static const fs_operation_def_t smb_fcn_tmpl[] = { 62 VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create }, 63 VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove}, 64 VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename}, 65 VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir}, 66 VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir}, 67 VOPNAME_LINK, {.femop_link = smb_fem_fcn_link}, 68 VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink}, 69 NULL, NULL 70 }; 71 72 /* 73 * Declarations for oplock FEM monitors 74 */ 75 76 static int smb_fem_oplock_open(femarg_t *, int, cred_t *, 77 struct caller_context *); 78 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *, 79 struct caller_context *); 80 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *, 81 struct caller_context *); 82 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *, 83 caller_context_t *); 84 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int, 85 offset_t, cred_t *, caller_context_t *); 86 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *, 87 caller_context_t *); 88 89 static const fs_operation_def_t smb_oplock_tmpl[] = { 90 VOPNAME_OPEN, { .femop_open = smb_fem_oplock_open }, 91 VOPNAME_READ, { .femop_read = smb_fem_oplock_read }, 92 VOPNAME_WRITE, { .femop_write = smb_fem_oplock_write }, 93 VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr }, 94 VOPNAME_SPACE, { .femop_space = smb_fem_oplock_space }, 95 VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent }, 96 NULL, NULL 97 }; 98 99 static int smb_fem_oplock_wait(smb_node_t *, caller_context_t *); 100 101 /* 102 * smb_fem_init 103 * 104 * This function is not multi-thread safe. The caller must make sure only one 105 * thread makes the call. 106 */ 107 int 108 smb_fem_init(void) 109 { 110 int rc = 0; 111 112 if (smb_fem_initialized) 113 return (0); 114 115 rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops); 116 if (rc) 117 return (rc); 118 119 rc = fem_create("smb_oplock_ops", smb_oplock_tmpl, 120 &smb_oplock_ops); 121 122 if (rc) { 123 fem_free(smb_fcn_ops); 124 smb_fcn_ops = NULL; 125 return (rc); 126 } 127 128 smb_fem_initialized = B_TRUE; 129 130 return (0); 131 } 132 133 /* 134 * smb_fem_fini 135 * 136 * This function is not multi-thread safe. The caller must make sure only one 137 * thread makes the call. 138 */ 139 void 140 smb_fem_fini(void) 141 { 142 if (!smb_fem_initialized) 143 return; 144 145 if (smb_fcn_ops != NULL) { 146 fem_free(smb_fcn_ops); 147 smb_fcn_ops = NULL; 148 } 149 if (smb_oplock_ops != NULL) { 150 fem_free(smb_oplock_ops); 151 smb_oplock_ops = NULL; 152 } 153 smb_fem_initialized = B_FALSE; 154 } 155 156 /* 157 * Install our fem hooks for change notify. 158 * Not using hold/rele function here because we 159 * remove the fem hooks before node destroy. 160 */ 161 int 162 smb_fem_fcn_install(smb_node_t *node) 163 { 164 int rc; 165 166 if (smb_fcn_ops == NULL) 167 return (ENOSYS); 168 rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ, 169 NULL, NULL); 170 return (rc); 171 } 172 173 void 174 smb_fem_fcn_uninstall(smb_node_t *node) 175 { 176 if (smb_fcn_ops == NULL) 177 return; 178 VERIFY0(fem_uninstall(node->vp, smb_fcn_ops, (void *)node)); 179 } 180 181 int 182 smb_fem_oplock_install(smb_node_t *node) 183 { 184 int rc; 185 186 if (smb_oplock_ops == NULL) 187 return (ENOSYS); 188 rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ, 189 (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release); 190 return (rc); 191 } 192 193 void 194 smb_fem_oplock_uninstall(smb_node_t *node) 195 { 196 if (smb_oplock_ops == NULL) 197 return; 198 VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node)); 199 } 200 201 /* 202 * FEM FCN monitors 203 * 204 * The FCN monitors intercept the respective VOP_* call regardless 205 * of whether the call originates from CIFS, NFS, or a local process. 206 */ 207 208 /* 209 * smb_fem_fcn_create() 210 * 211 * This monitor will catch only changes to VREG files and not to extended 212 * attribute files. This is fine because, for CIFS files, stream creates 213 * should not trigger any file change notification on the VDIR directory 214 * being monitored. Creates of any other kind of extended attribute in 215 * the directory will also not trigger any file change notification on the 216 * VDIR directory being monitored. 217 */ 218 219 static int 220 smb_fem_fcn_create( 221 femarg_t *arg, 222 char *name, 223 vattr_t *vap, 224 vcexcl_t excl, 225 int mode, 226 vnode_t **vpp, 227 cred_t *cr, 228 int flag, 229 caller_context_t *ct, 230 vsecattr_t *vsecp) 231 { 232 smb_node_t *dnode; 233 int error; 234 235 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 236 237 ASSERT(dnode); 238 239 error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag, 240 ct, vsecp); 241 242 if (error == 0 && ct != &smb_ct) 243 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 244 245 return (error); 246 } 247 248 /* 249 * smb_fem_fcn_remove() 250 * 251 * This monitor will catch only changes to VREG files and to not extended 252 * attribute files. This is fine because, for CIFS files, stream deletes 253 * should not trigger any file change notification on the VDIR directory 254 * being monitored. Deletes of any other kind of extended attribute in 255 * the directory will also not trigger any file change notification on the 256 * VDIR directory being monitored. 257 */ 258 259 static int 260 smb_fem_fcn_remove( 261 femarg_t *arg, 262 char *name, 263 cred_t *cr, 264 caller_context_t *ct, 265 int flags) 266 { 267 smb_node_t *dnode; 268 int error; 269 270 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 271 272 ASSERT(dnode); 273 274 error = vnext_remove(arg, name, cr, ct, flags); 275 276 if (error == 0 && ct != &smb_ct) 277 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 278 279 return (error); 280 } 281 282 static int 283 smb_fem_fcn_rename( 284 femarg_t *arg, 285 char *snm, 286 vnode_t *tdvp, 287 char *tnm, 288 cred_t *cr, 289 caller_context_t *ct, 290 int flags) 291 { 292 smb_node_t *dnode; 293 int error; 294 295 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 296 297 ASSERT(dnode); 298 299 error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags); 300 301 if (error == 0 && ct != &smb_ct) { 302 /* 303 * Note that renames in the same directory are normally 304 * delivered in {old,new} pairs, and clients expect them 305 * in that order, if both events are delivered. 306 */ 307 smb_node_notify_change(dnode, 308 FILE_ACTION_RENAMED_OLD_NAME, snm); 309 smb_node_notify_change(dnode, 310 FILE_ACTION_RENAMED_NEW_NAME, tnm); 311 } 312 313 return (error); 314 } 315 316 static int 317 smb_fem_fcn_mkdir( 318 femarg_t *arg, 319 char *name, 320 vattr_t *vap, 321 vnode_t **vpp, 322 cred_t *cr, 323 caller_context_t *ct, 324 int flags, 325 vsecattr_t *vsecp) 326 { 327 smb_node_t *dnode; 328 int error; 329 330 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 331 332 ASSERT(dnode); 333 334 error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp); 335 336 if (error == 0 && ct != &smb_ct) 337 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 338 339 return (error); 340 } 341 342 static int 343 smb_fem_fcn_rmdir( 344 femarg_t *arg, 345 char *name, 346 vnode_t *cdir, 347 cred_t *cr, 348 caller_context_t *ct, 349 int flags) 350 { 351 smb_node_t *dnode; 352 int error; 353 354 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 355 356 ASSERT(dnode); 357 358 error = vnext_rmdir(arg, name, cdir, cr, ct, flags); 359 360 if (error == 0 && ct != &smb_ct) 361 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 362 363 return (error); 364 } 365 366 static int 367 smb_fem_fcn_link( 368 femarg_t *arg, 369 vnode_t *svp, 370 char *tnm, 371 cred_t *cr, 372 caller_context_t *ct, 373 int flags) 374 { 375 smb_node_t *dnode; 376 int error; 377 378 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 379 380 ASSERT(dnode); 381 382 error = vnext_link(arg, svp, tnm, cr, ct, flags); 383 384 if (error == 0 && ct != &smb_ct) 385 smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm); 386 387 return (error); 388 } 389 390 static int 391 smb_fem_fcn_symlink( 392 femarg_t *arg, 393 char *linkname, 394 vattr_t *vap, 395 char *target, 396 cred_t *cr, 397 caller_context_t *ct, 398 int flags) 399 { 400 smb_node_t *dnode; 401 int error; 402 403 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 404 405 ASSERT(dnode); 406 407 error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags); 408 409 if (error == 0 && ct != &smb_ct) 410 smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname); 411 412 return (error); 413 } 414 415 /* 416 * FEM oplock monitors 417 * 418 * The monitors below are not intended to intercept CIFS calls. 419 * CIFS higher-level routines will break oplocks as needed prior 420 * to getting to the VFS layer. 421 */ 422 static int 423 smb_fem_oplock_open( 424 femarg_t *arg, 425 int mode, 426 cred_t *cr, 427 caller_context_t *ct) 428 { 429 smb_node_t *node; 430 uint32_t status; 431 int rc = 0; 432 433 if (ct != &smb_ct) { 434 uint32_t req_acc = FILE_READ_DATA; 435 uint32_t cr_disp = FILE_OPEN_IF; 436 437 node = (smb_node_t *)(arg->fa_fnode->fn_available); 438 SMB_NODE_VALID(node); 439 440 /* 441 * Get req_acc, cr_disp just accurate enough so 442 * the oplock break call does the right thing. 443 */ 444 if (mode & FWRITE) { 445 req_acc = FILE_READ_DATA | FILE_WRITE_DATA; 446 cr_disp = (mode & FTRUNC) ? 447 FILE_OVERWRITE_IF : FILE_OPEN_IF; 448 } else { 449 req_acc = FILE_READ_DATA; 450 cr_disp = FILE_OPEN_IF; 451 } 452 453 status = smb_oplock_break_OPEN(node, NULL, 454 req_acc, cr_disp); 455 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 456 rc = smb_fem_oplock_wait(node, ct); 457 else if (status != 0) 458 rc = EIO; 459 } 460 if (rc == 0) 461 rc = vnext_open(arg, mode, cr, ct); 462 463 return (rc); 464 } 465 466 /* 467 * Should normally be hit only via NFSv2/v3. All other accesses 468 * (CIFS/NFS/local) should call VOP_OPEN first. 469 */ 470 471 static int 472 smb_fem_oplock_read( 473 femarg_t *arg, 474 uio_t *uiop, 475 int ioflag, 476 cred_t *cr, 477 caller_context_t *ct) 478 { 479 smb_node_t *node; 480 uint32_t status; 481 int rc = 0; 482 483 if (ct != &smb_ct) { 484 node = (smb_node_t *)(arg->fa_fnode->fn_available); 485 SMB_NODE_VALID(node); 486 487 status = smb_oplock_break_READ(node, NULL); 488 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 489 rc = smb_fem_oplock_wait(node, ct); 490 else if (status != 0) 491 rc = EIO; 492 } 493 if (rc == 0) 494 rc = vnext_read(arg, uiop, ioflag, cr, ct); 495 496 return (rc); 497 } 498 499 /* 500 * Should normally be hit only via NFSv2/v3. All other accesses 501 * (CIFS/NFS/local) should call VOP_OPEN first. 502 */ 503 504 static int 505 smb_fem_oplock_write( 506 femarg_t *arg, 507 uio_t *uiop, 508 int ioflag, 509 cred_t *cr, 510 caller_context_t *ct) 511 { 512 smb_node_t *node; 513 uint32_t status; 514 int rc = 0; 515 516 if (ct != &smb_ct) { 517 node = (smb_node_t *)(arg->fa_fnode->fn_available); 518 SMB_NODE_VALID(node); 519 520 status = smb_oplock_break_WRITE(node, NULL); 521 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 522 rc = smb_fem_oplock_wait(node, ct); 523 else if (status != 0) 524 rc = EIO; 525 } 526 if (rc == 0) 527 rc = vnext_write(arg, uiop, ioflag, cr, ct); 528 529 return (rc); 530 } 531 532 static int 533 smb_fem_oplock_setattr( 534 femarg_t *arg, 535 vattr_t *vap, 536 int flags, 537 cred_t *cr, 538 caller_context_t *ct) 539 { 540 smb_node_t *node; 541 uint32_t status; 542 int rc = 0; 543 544 if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) { 545 node = (smb_node_t *)(arg->fa_fnode->fn_available); 546 SMB_NODE_VALID(node); 547 548 status = smb_oplock_break_SETINFO(node, NULL, 549 FileEndOfFileInformation); 550 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 551 rc = smb_fem_oplock_wait(node, ct); 552 else if (status != 0) 553 rc = EIO; 554 } 555 if (rc == 0) 556 rc = vnext_setattr(arg, vap, flags, cr, ct); 557 return (rc); 558 } 559 560 static int 561 smb_fem_oplock_space( 562 femarg_t *arg, 563 int cmd, 564 flock64_t *bfp, 565 int flag, 566 offset_t offset, 567 cred_t *cr, 568 caller_context_t *ct) 569 { 570 smb_node_t *node; 571 uint32_t status; 572 int rc = 0; 573 574 if (ct != &smb_ct) { 575 node = (smb_node_t *)(arg->fa_fnode->fn_available); 576 SMB_NODE_VALID(node); 577 578 status = smb_oplock_break_SETINFO(node, NULL, 579 FileAllocationInformation); 580 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 581 rc = smb_fem_oplock_wait(node, ct); 582 else if (status != 0) 583 rc = EIO; 584 } 585 if (rc == 0) 586 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct); 587 return (rc); 588 } 589 590 /* 591 * smb_fem_oplock_vnevent() 592 * 593 * To intercept NFS and local renames and removes in order to break any 594 * existing oplock prior to the operation. 595 * 596 * Note: Currently, this monitor is traversed only when an FS is mounted 597 * non-nbmand. (When the FS is mounted nbmand, share reservation checking 598 * will detect a share violation and return an error prior to the VOP layer 599 * being reached.) Thus, for nbmand NFS and local renames and removes, 600 * an existing oplock is never broken prior to share checking (contrary to 601 * how it is with intra-CIFS remove and rename requests). 602 */ 603 604 static int 605 smb_fem_oplock_vnevent( 606 femarg_t *arg, 607 vnevent_t vnevent, 608 vnode_t *dvp, 609 char *name, 610 caller_context_t *ct) 611 { 612 smb_node_t *node; 613 uint32_t status; 614 int rc = 0; 615 616 if (ct != &smb_ct) { 617 node = (smb_node_t *)(arg->fa_fnode->fn_available); 618 SMB_NODE_VALID(node); 619 620 switch (vnevent) { 621 case VE_REMOVE: 622 case VE_PRE_RENAME_DEST: 623 case VE_RENAME_DEST: 624 status = smb_oplock_break_HANDLE(node, NULL); 625 break; 626 case VE_PRE_RENAME_SRC: 627 case VE_RENAME_SRC: 628 status = smb_oplock_break_SETINFO(node, NULL, 629 FileRenameInformation); 630 break; 631 default: 632 status = 0; 633 break; 634 } 635 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 636 rc = smb_fem_oplock_wait(node, ct); 637 else if (status != 0) 638 rc = EIO; 639 } 640 if (rc == 0) 641 rc = vnext_vnevent(arg, vnevent, dvp, name, ct); 642 643 return (rc); 644 } 645 646 int smb_fem_oplock_timeout = 5000; /* mSec. */ 647 648 static int 649 smb_fem_oplock_wait(smb_node_t *node, caller_context_t *ct) 650 { 651 int rc = 0; 652 653 ASSERT(ct != &smb_ct); 654 655 if (ct && (ct->cc_flags & CC_DONTBLOCK)) { 656 ct->cc_flags |= CC_WOULDBLOCK; 657 rc = EAGAIN; 658 } else { 659 (void) smb_oplock_wait_break(node, 660 smb_fem_oplock_timeout); 661 rc = 0; 662 } 663 664 return (rc); 665 } 666