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