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