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