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 2020 Tintri by DDN, 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 int 174 smb_fem_fcn_uninstall(smb_node_t *node) 175 { 176 int rc; 177 178 if (smb_fcn_ops == NULL) 179 return (ENOSYS); 180 rc = fem_uninstall(node->vp, smb_fcn_ops, (void *)node); 181 return (rc); 182 } 183 184 int 185 smb_fem_oplock_install(smb_node_t *node) 186 { 187 int rc; 188 189 if (smb_oplock_ops == NULL) 190 return (ENOSYS); 191 rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ, 192 (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release); 193 return (rc); 194 } 195 196 void 197 smb_fem_oplock_uninstall(smb_node_t *node) 198 { 199 if (smb_oplock_ops == NULL) 200 return; 201 VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node)); 202 } 203 204 /* 205 * FEM FCN monitors 206 * 207 * The FCN monitors intercept the respective VOP_* call regardless 208 * of whether the call originates from CIFS, NFS, or a local process. 209 */ 210 211 /* 212 * smb_fem_fcn_create() 213 * 214 * This monitor will catch only changes to VREG files and not to extended 215 * attribute files. This is fine because, for CIFS files, stream creates 216 * should not trigger any file change notification on the VDIR directory 217 * being monitored. Creates of any other kind of extended attribute in 218 * the directory will also not trigger any file change notification on the 219 * VDIR directory being monitored. 220 */ 221 222 static int 223 smb_fem_fcn_create( 224 femarg_t *arg, 225 char *name, 226 vattr_t *vap, 227 vcexcl_t excl, 228 int mode, 229 vnode_t **vpp, 230 cred_t *cr, 231 int flag, 232 caller_context_t *ct, 233 vsecattr_t *vsecp) 234 { 235 smb_node_t *dnode; 236 int error; 237 238 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 239 240 ASSERT(dnode); 241 242 error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag, 243 ct, vsecp); 244 245 if (error == 0 && ct != &smb_ct) 246 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 247 248 return (error); 249 } 250 251 /* 252 * smb_fem_fcn_remove() 253 * 254 * This monitor will catch only changes to VREG files and to not extended 255 * attribute files. This is fine because, for CIFS files, stream deletes 256 * should not trigger any file change notification on the VDIR directory 257 * being monitored. Deletes of any other kind of extended attribute in 258 * the directory will also not trigger any file change notification on the 259 * VDIR directory being monitored. 260 */ 261 262 static int 263 smb_fem_fcn_remove( 264 femarg_t *arg, 265 char *name, 266 cred_t *cr, 267 caller_context_t *ct, 268 int flags) 269 { 270 smb_node_t *dnode; 271 int error; 272 273 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 274 275 ASSERT(dnode); 276 277 error = vnext_remove(arg, name, cr, ct, flags); 278 279 if (error == 0 && ct != &smb_ct) 280 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 281 282 return (error); 283 } 284 285 static int 286 smb_fem_fcn_rename( 287 femarg_t *arg, 288 char *snm, 289 vnode_t *tdvp, 290 char *tnm, 291 cred_t *cr, 292 caller_context_t *ct, 293 int flags) 294 { 295 smb_node_t *dnode; 296 int error; 297 298 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 299 300 ASSERT(dnode); 301 302 error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags); 303 304 if (error == 0 && ct != &smb_ct) { 305 /* 306 * Note that renames in the same directory are normally 307 * delivered in {old,new} pairs, and clients expect them 308 * in that order, if both events are delivered. 309 */ 310 smb_node_notify_change(dnode, 311 FILE_ACTION_RENAMED_OLD_NAME, snm); 312 smb_node_notify_change(dnode, 313 FILE_ACTION_RENAMED_NEW_NAME, tnm); 314 } 315 316 return (error); 317 } 318 319 static int 320 smb_fem_fcn_mkdir( 321 femarg_t *arg, 322 char *name, 323 vattr_t *vap, 324 vnode_t **vpp, 325 cred_t *cr, 326 caller_context_t *ct, 327 int flags, 328 vsecattr_t *vsecp) 329 { 330 smb_node_t *dnode; 331 int error; 332 333 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 334 335 ASSERT(dnode); 336 337 error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp); 338 339 if (error == 0 && ct != &smb_ct) 340 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 341 342 return (error); 343 } 344 345 static int 346 smb_fem_fcn_rmdir( 347 femarg_t *arg, 348 char *name, 349 vnode_t *cdir, 350 cred_t *cr, 351 caller_context_t *ct, 352 int flags) 353 { 354 smb_node_t *dnode; 355 int error; 356 357 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 358 359 ASSERT(dnode); 360 361 error = vnext_rmdir(arg, name, cdir, cr, ct, flags); 362 363 if (error == 0 && ct != &smb_ct) 364 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 365 366 return (error); 367 } 368 369 static int 370 smb_fem_fcn_link( 371 femarg_t *arg, 372 vnode_t *svp, 373 char *tnm, 374 cred_t *cr, 375 caller_context_t *ct, 376 int flags) 377 { 378 smb_node_t *dnode; 379 int error; 380 381 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 382 383 ASSERT(dnode); 384 385 error = vnext_link(arg, svp, tnm, cr, ct, flags); 386 387 if (error == 0 && ct != &smb_ct) 388 smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm); 389 390 return (error); 391 } 392 393 static int 394 smb_fem_fcn_symlink( 395 femarg_t *arg, 396 char *linkname, 397 vattr_t *vap, 398 char *target, 399 cred_t *cr, 400 caller_context_t *ct, 401 int flags) 402 { 403 smb_node_t *dnode; 404 int error; 405 406 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 407 408 ASSERT(dnode); 409 410 error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags); 411 412 if (error == 0 && ct != &smb_ct) 413 smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname); 414 415 return (error); 416 } 417 418 /* 419 * FEM oplock monitors 420 * 421 * The monitors below are not intended to intercept CIFS calls. 422 * CIFS higher-level routines will break oplocks as needed prior 423 * to getting to the VFS layer. 424 */ 425 static int 426 smb_fem_oplock_open( 427 femarg_t *arg, 428 int mode, 429 cred_t *cr, 430 caller_context_t *ct) 431 { 432 smb_node_t *node; 433 uint32_t status; 434 int rc = 0; 435 436 if (ct != &smb_ct) { 437 uint32_t req_acc = FILE_READ_DATA; 438 uint32_t cr_disp = FILE_OPEN_IF; 439 440 node = (smb_node_t *)(arg->fa_fnode->fn_available); 441 SMB_NODE_VALID(node); 442 443 /* 444 * Get req_acc, cr_disp just accurate enough so 445 * the oplock break call does the right thing. 446 */ 447 if (mode & FWRITE) { 448 req_acc = FILE_READ_DATA | FILE_WRITE_DATA; 449 cr_disp = (mode & FTRUNC) ? 450 FILE_OVERWRITE_IF : FILE_OPEN_IF; 451 } else { 452 req_acc = FILE_READ_DATA; 453 cr_disp = FILE_OPEN_IF; 454 } 455 456 status = smb_oplock_break_OPEN(node, NULL, 457 req_acc, cr_disp); 458 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 459 rc = smb_fem_oplock_wait(node, ct); 460 else if (status != 0) 461 rc = EIO; 462 } 463 if (rc == 0) 464 rc = vnext_open(arg, mode, cr, ct); 465 466 return (rc); 467 } 468 469 /* 470 * Should normally be hit only via NFSv2/v3. All other accesses 471 * (CIFS/NFS/local) should call VOP_OPEN first. 472 */ 473 474 static int 475 smb_fem_oplock_read( 476 femarg_t *arg, 477 uio_t *uiop, 478 int ioflag, 479 cred_t *cr, 480 caller_context_t *ct) 481 { 482 smb_node_t *node; 483 uint32_t status; 484 int rc = 0; 485 486 if (ct != &smb_ct) { 487 node = (smb_node_t *)(arg->fa_fnode->fn_available); 488 SMB_NODE_VALID(node); 489 490 status = smb_oplock_break_READ(node, NULL); 491 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 492 rc = smb_fem_oplock_wait(node, ct); 493 else if (status != 0) 494 rc = EIO; 495 } 496 if (rc == 0) 497 rc = vnext_read(arg, uiop, ioflag, cr, ct); 498 499 return (rc); 500 } 501 502 /* 503 * Should normally be hit only via NFSv2/v3. All other accesses 504 * (CIFS/NFS/local) should call VOP_OPEN first. 505 */ 506 507 static int 508 smb_fem_oplock_write( 509 femarg_t *arg, 510 uio_t *uiop, 511 int ioflag, 512 cred_t *cr, 513 caller_context_t *ct) 514 { 515 smb_node_t *node; 516 uint32_t status; 517 int rc = 0; 518 519 if (ct != &smb_ct) { 520 node = (smb_node_t *)(arg->fa_fnode->fn_available); 521 SMB_NODE_VALID(node); 522 523 status = smb_oplock_break_WRITE(node, NULL); 524 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 525 rc = smb_fem_oplock_wait(node, ct); 526 else if (status != 0) 527 rc = EIO; 528 } 529 if (rc == 0) 530 rc = vnext_write(arg, uiop, ioflag, cr, ct); 531 532 return (rc); 533 } 534 535 static int 536 smb_fem_oplock_setattr( 537 femarg_t *arg, 538 vattr_t *vap, 539 int flags, 540 cred_t *cr, 541 caller_context_t *ct) 542 { 543 smb_node_t *node; 544 uint32_t status; 545 int rc = 0; 546 547 if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) { 548 node = (smb_node_t *)(arg->fa_fnode->fn_available); 549 SMB_NODE_VALID(node); 550 551 status = smb_oplock_break_SETINFO(node, NULL, 552 FileEndOfFileInformation); 553 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 554 rc = smb_fem_oplock_wait(node, ct); 555 else if (status != 0) 556 rc = EIO; 557 } 558 if (rc == 0) 559 rc = vnext_setattr(arg, vap, flags, cr, ct); 560 return (rc); 561 } 562 563 static int 564 smb_fem_oplock_space( 565 femarg_t *arg, 566 int cmd, 567 flock64_t *bfp, 568 int flag, 569 offset_t offset, 570 cred_t *cr, 571 caller_context_t *ct) 572 { 573 smb_node_t *node; 574 uint32_t status; 575 int rc = 0; 576 577 if (ct != &smb_ct) { 578 node = (smb_node_t *)(arg->fa_fnode->fn_available); 579 SMB_NODE_VALID(node); 580 581 status = smb_oplock_break_SETINFO(node, NULL, 582 FileAllocationInformation); 583 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 584 rc = smb_fem_oplock_wait(node, ct); 585 else if (status != 0) 586 rc = EIO; 587 } 588 if (rc == 0) 589 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct); 590 return (rc); 591 } 592 593 /* 594 * smb_fem_oplock_vnevent() 595 * 596 * To intercept NFS and local renames and removes in order to break any 597 * existing oplock prior to the operation. 598 * 599 * Note: Currently, this monitor is traversed only when an FS is mounted 600 * non-nbmand. (When the FS is mounted nbmand, share reservation checking 601 * will detect a share violation and return an error prior to the VOP layer 602 * being reached.) Thus, for nbmand NFS and local renames and removes, 603 * an existing oplock is never broken prior to share checking (contrary to 604 * how it is with intra-CIFS remove and rename requests). 605 */ 606 607 static int 608 smb_fem_oplock_vnevent( 609 femarg_t *arg, 610 vnevent_t vnevent, 611 vnode_t *dvp, 612 char *name, 613 caller_context_t *ct) 614 { 615 smb_node_t *node; 616 uint32_t status; 617 int rc = 0; 618 619 if (ct != &smb_ct) { 620 node = (smb_node_t *)(arg->fa_fnode->fn_available); 621 SMB_NODE_VALID(node); 622 623 switch (vnevent) { 624 case VE_REMOVE: 625 case VE_PRE_RENAME_DEST: 626 case VE_RENAME_DEST: 627 status = smb_oplock_break_HANDLE(node, NULL); 628 break; 629 case VE_PRE_RENAME_SRC: 630 case VE_RENAME_SRC: 631 status = smb_oplock_break_SETINFO(node, NULL, 632 FileRenameInformation); 633 break; 634 default: 635 status = 0; 636 break; 637 } 638 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 639 rc = smb_fem_oplock_wait(node, ct); 640 else if (status != 0) 641 rc = EIO; 642 } 643 if (rc == 0) 644 rc = vnext_vnevent(arg, vnevent, dvp, name, ct); 645 646 return (rc); 647 } 648 649 int smb_fem_oplock_timeout = 5000; /* mSec. */ 650 651 static int 652 smb_fem_oplock_wait(smb_node_t *node, caller_context_t *ct) 653 { 654 int rc = 0; 655 656 ASSERT(ct != &smb_ct); 657 658 if (ct && (ct->cc_flags & CC_DONTBLOCK)) { 659 ct->cc_flags |= CC_WOULDBLOCK; 660 rc = EAGAIN; 661 } else { 662 (void) smb_oplock_wait_break(node, 663 smb_fem_oplock_timeout); 664 rc = 0; 665 } 666 667 return (rc); 668 } 669