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 2013 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_rwlock(femarg_t *, int, caller_context_t *); 85 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int, 86 offset_t, cred_t *, caller_context_t *); 87 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *, 88 caller_context_t *); 89 90 static const fs_operation_def_t smb_oplock_tmpl[] = { 91 VOPNAME_OPEN, { .femop_open = smb_fem_oplock_open }, 92 VOPNAME_READ, { .femop_read = smb_fem_oplock_read }, 93 VOPNAME_WRITE, { .femop_write = smb_fem_oplock_write }, 94 VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr }, 95 VOPNAME_RWLOCK, { .femop_rwlock = smb_fem_oplock_rwlock }, 96 VOPNAME_SPACE, { .femop_space = smb_fem_oplock_space }, 97 VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent }, 98 NULL, NULL 99 }; 100 101 static int smb_fem_oplock_break(femarg_t *, caller_context_t *, uint32_t); 102 103 /* 104 * smb_fem_init 105 * 106 * This function is not multi-thread safe. The caller must make sure only one 107 * thread makes the call. 108 */ 109 int 110 smb_fem_init(void) 111 { 112 int rc = 0; 113 114 if (smb_fem_initialized) 115 return (0); 116 117 rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops); 118 if (rc) 119 return (rc); 120 121 rc = fem_create("smb_oplock_ops", smb_oplock_tmpl, 122 &smb_oplock_ops); 123 124 if (rc) { 125 fem_free(smb_fcn_ops); 126 smb_fcn_ops = NULL; 127 return (rc); 128 } 129 130 smb_fem_initialized = B_TRUE; 131 132 return (0); 133 } 134 135 /* 136 * smb_fem_fini 137 * 138 * This function is not multi-thread safe. The caller must make sure only one 139 * thread makes the call. 140 */ 141 void 142 smb_fem_fini(void) 143 { 144 if (!smb_fem_initialized) 145 return; 146 147 if (smb_fcn_ops != NULL) { 148 fem_free(smb_fcn_ops); 149 smb_fcn_ops = NULL; 150 } 151 if (smb_oplock_ops != NULL) { 152 fem_free(smb_oplock_ops); 153 smb_oplock_ops = NULL; 154 } 155 smb_fem_initialized = B_FALSE; 156 } 157 158 /* 159 * Install our fem hooks for change notify. 160 * Not using hold/rele function here because we 161 * remove the fem hooks before node destroy. 162 */ 163 int 164 smb_fem_fcn_install(smb_node_t *node) 165 { 166 int rc; 167 168 if (smb_fcn_ops == NULL) 169 return (ENOSYS); 170 rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ, 171 NULL, NULL); 172 return (rc); 173 } 174 175 void 176 smb_fem_fcn_uninstall(smb_node_t *node) 177 { 178 if (smb_fcn_ops == NULL) 179 return; 180 VERIFY0(fem_uninstall(node->vp, smb_fcn_ops, (void *)node)); 181 } 182 183 int 184 smb_fem_oplock_install(smb_node_t *node) 185 { 186 int rc; 187 188 if (smb_oplock_ops == NULL) 189 return (ENOSYS); 190 rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ, 191 (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release); 192 return (rc); 193 } 194 195 void 196 smb_fem_oplock_uninstall(smb_node_t *node) 197 { 198 if (smb_oplock_ops == NULL) 199 return; 200 VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node)); 201 } 202 203 /* 204 * FEM FCN monitors 205 * 206 * The FCN monitors intercept the respective VOP_* call regardless 207 * of whether the call originates from CIFS, NFS, or a local process. 208 */ 209 210 /* 211 * smb_fem_fcn_create() 212 * 213 * This monitor will catch only changes to VREG files and not to extended 214 * attribute files. This is fine because, for CIFS files, stream creates 215 * should not trigger any file change notification on the VDIR directory 216 * being monitored. Creates of any other kind of extended attribute in 217 * the directory will also not trigger any file change notification on the 218 * VDIR directory being monitored. 219 */ 220 221 static int 222 smb_fem_fcn_create( 223 femarg_t *arg, 224 char *name, 225 vattr_t *vap, 226 vcexcl_t excl, 227 int mode, 228 vnode_t **vpp, 229 cred_t *cr, 230 int flag, 231 caller_context_t *ct, 232 vsecattr_t *vsecp) 233 { 234 smb_node_t *dnode; 235 int error; 236 237 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 238 239 ASSERT(dnode); 240 241 error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag, 242 ct, vsecp); 243 244 if (error == 0 && ct != &smb_ct) 245 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 246 247 return (error); 248 } 249 250 /* 251 * smb_fem_fcn_remove() 252 * 253 * This monitor will catch only changes to VREG files and to not extended 254 * attribute files. This is fine because, for CIFS files, stream deletes 255 * should not trigger any file change notification on the VDIR directory 256 * being monitored. Deletes of any other kind of extended attribute in 257 * the directory will also not trigger any file change notification on the 258 * VDIR directory being monitored. 259 */ 260 261 static int 262 smb_fem_fcn_remove( 263 femarg_t *arg, 264 char *name, 265 cred_t *cr, 266 caller_context_t *ct, 267 int flags) 268 { 269 smb_node_t *dnode; 270 int error; 271 272 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 273 274 ASSERT(dnode); 275 276 error = vnext_remove(arg, name, cr, ct, flags); 277 278 if (error == 0 && ct != &smb_ct) 279 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 280 281 return (error); 282 } 283 284 static int 285 smb_fem_fcn_rename( 286 femarg_t *arg, 287 char *snm, 288 vnode_t *tdvp, 289 char *tnm, 290 cred_t *cr, 291 caller_context_t *ct, 292 int flags) 293 { 294 smb_node_t *dnode; 295 int error; 296 297 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 298 299 ASSERT(dnode); 300 301 error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags); 302 303 if (error == 0 && ct != &smb_ct) { 304 /* 305 * Note that renames in the same directory are normally 306 * delivered in {old,new} pairs, and clients expect them 307 * in that order, if both events are delivered. 308 */ 309 smb_node_notify_change(dnode, 310 FILE_ACTION_RENAMED_OLD_NAME, snm); 311 smb_node_notify_change(dnode, 312 FILE_ACTION_RENAMED_NEW_NAME, tnm); 313 } 314 315 return (error); 316 } 317 318 static int 319 smb_fem_fcn_mkdir( 320 femarg_t *arg, 321 char *name, 322 vattr_t *vap, 323 vnode_t **vpp, 324 cred_t *cr, 325 caller_context_t *ct, 326 int flags, 327 vsecattr_t *vsecp) 328 { 329 smb_node_t *dnode; 330 int error; 331 332 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 333 334 ASSERT(dnode); 335 336 error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp); 337 338 if (error == 0 && ct != &smb_ct) 339 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 340 341 return (error); 342 } 343 344 static int 345 smb_fem_fcn_rmdir( 346 femarg_t *arg, 347 char *name, 348 vnode_t *cdir, 349 cred_t *cr, 350 caller_context_t *ct, 351 int flags) 352 { 353 smb_node_t *dnode; 354 int error; 355 356 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 357 358 ASSERT(dnode); 359 360 error = vnext_rmdir(arg, name, cdir, cr, ct, flags); 361 362 if (error == 0 && ct != &smb_ct) 363 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 364 365 return (error); 366 } 367 368 static int 369 smb_fem_fcn_link( 370 femarg_t *arg, 371 vnode_t *svp, 372 char *tnm, 373 cred_t *cr, 374 caller_context_t *ct, 375 int flags) 376 { 377 smb_node_t *dnode; 378 int error; 379 380 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 381 382 ASSERT(dnode); 383 384 error = vnext_link(arg, svp, tnm, cr, ct, flags); 385 386 if (error == 0 && ct != &smb_ct) 387 smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm); 388 389 return (error); 390 } 391 392 static int 393 smb_fem_fcn_symlink( 394 femarg_t *arg, 395 char *linkname, 396 vattr_t *vap, 397 char *target, 398 cred_t *cr, 399 caller_context_t *ct, 400 int flags) 401 { 402 smb_node_t *dnode; 403 int error; 404 405 dnode = (smb_node_t *)arg->fa_fnode->fn_available; 406 407 ASSERT(dnode); 408 409 error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags); 410 411 if (error == 0 && ct != &smb_ct) 412 smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname); 413 414 return (error); 415 } 416 417 /* 418 * FEM oplock monitors 419 * 420 * The monitors below are not intended to intercept CIFS calls. 421 * CIFS higher-level routines will break oplocks as needed prior 422 * to getting to the VFS layer. 423 */ 424 static int 425 smb_fem_oplock_open( 426 femarg_t *arg, 427 int mode, 428 cred_t *cr, 429 caller_context_t *ct) 430 { 431 uint32_t flags; 432 int rc = 0; 433 434 if (ct != &smb_ct) { 435 if (mode & (FWRITE|FTRUNC)) 436 flags = SMB_OPLOCK_BREAK_TO_NONE; 437 else 438 flags = SMB_OPLOCK_BREAK_TO_LEVEL_II; 439 rc = smb_fem_oplock_break(arg, ct, flags); 440 } 441 if (rc == 0) 442 rc = vnext_open(arg, mode, cr, ct); 443 444 return (rc); 445 } 446 447 /* 448 * Should normally be hit only via NFSv2/v3. All other accesses 449 * (CIFS/NFS/local) should call VOP_OPEN first. 450 */ 451 452 static int 453 smb_fem_oplock_read( 454 femarg_t *arg, 455 uio_t *uiop, 456 int ioflag, 457 cred_t *cr, 458 caller_context_t *ct) 459 { 460 int rc = 0; 461 462 if (ct != &smb_ct) { 463 rc = smb_fem_oplock_break(arg, ct, 464 SMB_OPLOCK_BREAK_TO_LEVEL_II); 465 } 466 if (rc == 0) 467 rc = vnext_read(arg, uiop, ioflag, cr, ct); 468 469 return (rc); 470 } 471 472 /* 473 * Should normally be hit only via NFSv2/v3. All other accesses 474 * (CIFS/NFS/local) should call VOP_OPEN first. 475 */ 476 477 static int 478 smb_fem_oplock_write( 479 femarg_t *arg, 480 uio_t *uiop, 481 int ioflag, 482 cred_t *cr, 483 caller_context_t *ct) 484 { 485 int rc = 0; 486 487 if (ct != &smb_ct) 488 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE); 489 if (rc == 0) 490 rc = vnext_write(arg, uiop, ioflag, cr, ct); 491 492 return (rc); 493 } 494 495 static int 496 smb_fem_oplock_setattr( 497 femarg_t *arg, 498 vattr_t *vap, 499 int flags, 500 cred_t *cr, 501 caller_context_t *ct) 502 { 503 int rc = 0; 504 505 if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) 506 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE); 507 if (rc == 0) 508 rc = vnext_setattr(arg, vap, flags, cr, ct); 509 return (rc); 510 } 511 512 static int 513 smb_fem_oplock_rwlock( 514 femarg_t *arg, 515 int write_lock, 516 caller_context_t *ct) 517 { 518 uint32_t flags; 519 int rc = 0; 520 521 if (ct != &smb_ct) { 522 if (write_lock) 523 flags = SMB_OPLOCK_BREAK_TO_NONE; 524 else 525 flags = SMB_OPLOCK_BREAK_TO_LEVEL_II; 526 rc = smb_fem_oplock_break(arg, ct, flags); 527 } 528 if (rc == 0) 529 rc = vnext_rwlock(arg, write_lock, ct); 530 531 return (rc); 532 } 533 534 static int 535 smb_fem_oplock_space( 536 femarg_t *arg, 537 int cmd, 538 flock64_t *bfp, 539 int flag, 540 offset_t offset, 541 cred_t *cr, 542 caller_context_t *ct) 543 { 544 int rc = 0; 545 546 if (ct != &smb_ct) 547 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE); 548 if (rc == 0) 549 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct); 550 return (rc); 551 } 552 553 /* 554 * smb_fem_oplock_vnevent() 555 * 556 * To intercept NFS and local renames and removes in order to break any 557 * existing oplock prior to the operation. 558 * 559 * Note: Currently, this monitor is traversed only when an FS is mounted 560 * non-nbmand. (When the FS is mounted nbmand, share reservation checking 561 * will detect a share violation and return an error prior to the VOP layer 562 * being reached.) Thus, for nbmand NFS and local renames and removes, 563 * an existing oplock is never broken prior to share checking (contrary to 564 * how it is with intra-CIFS remove and rename requests). 565 */ 566 567 static int 568 smb_fem_oplock_vnevent( 569 femarg_t *arg, 570 vnevent_t vnevent, 571 vnode_t *dvp, 572 char *name, 573 caller_context_t *ct) 574 { 575 uint32_t flags; 576 int rc = 0; 577 578 if (ct != &smb_ct) { 579 switch (vnevent) { 580 case VE_REMOVE: 581 case VE_PRE_RENAME_DEST: 582 case VE_RENAME_DEST: 583 flags = SMB_OPLOCK_BREAK_TO_NONE | 584 SMB_OPLOCK_BREAK_BATCH; 585 rc = smb_fem_oplock_break(arg, ct, flags); 586 break; 587 case VE_PRE_RENAME_SRC: 588 case VE_RENAME_SRC: 589 flags = SMB_OPLOCK_BREAK_TO_LEVEL_II | 590 SMB_OPLOCK_BREAK_BATCH; 591 rc = smb_fem_oplock_break(arg, ct, flags); 592 break; 593 default: 594 rc = 0; 595 break; 596 } 597 } 598 if (rc == 0) 599 rc = vnext_vnevent(arg, vnevent, dvp, name, ct); 600 601 return (rc); 602 } 603 604 static int 605 smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct, uint32_t flags) 606 { 607 smb_node_t *node; 608 int rc; 609 610 node = (smb_node_t *)((arg)->fa_fnode->fn_available); 611 SMB_NODE_VALID(node); 612 613 ASSERT(ct != &smb_ct); 614 615 if (ct && (ct->cc_flags & CC_DONTBLOCK)) { 616 flags |= SMB_OPLOCK_BREAK_NOWAIT; 617 rc = smb_oplock_break(NULL, node, flags); 618 if (rc == EAGAIN) 619 ct->cc_flags |= CC_WOULDBLOCK; 620 } else { 621 rc = smb_oplock_break(NULL, node, flags); 622 } 623 624 return (rc); 625 } 626