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