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