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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <smbsrv/smb_incl.h> 29 #include <smbsrv/smb_fsops.h> 30 #include <sys/sdt.h> 31 #include <sys/fcntl.h> 32 #include <sys/vfs.h> 33 #include <sys/vfs_opreg.h> 34 #include <sys/vnode.h> 35 #include <sys/fem.h> 36 37 extern caller_context_t smb_ct; 38 39 static boolean_t smb_fem_initialized = B_FALSE; 40 static fem_t *smb_fcn_ops = NULL; 41 static fem_t *smb_oplock_ops = NULL; 42 43 /* 44 * Declarations for FCN (file change notification) FEM monitors 45 */ 46 47 void smb_fem_fcn_install(smb_node_t *); 48 void smb_fem_fcn_uninstall(smb_node_t *); 49 50 static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int, 51 vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *); 52 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *, 53 caller_context_t *, int); 54 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *, 55 cred_t *, caller_context_t *, int); 56 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **, 57 cred_t *, caller_context_t *, int, vsecattr_t *); 58 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *, 59 caller_context_t *, int); 60 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *, 61 caller_context_t *, int); 62 static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *, 63 char *, cred_t *, caller_context_t *, int); 64 65 static const fs_operation_def_t smb_fcn_tmpl[] = { 66 VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create }, 67 VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove}, 68 VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename}, 69 VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir}, 70 VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir}, 71 VOPNAME_LINK, {.femop_link = smb_fem_fcn_link}, 72 VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink}, 73 NULL, NULL 74 }; 75 76 /* 77 * Declarations for oplock FEM monitors 78 */ 79 80 int smb_fem_oplock_install(smb_node_t *); 81 void smb_fem_oplock_uninstall(smb_node_t *); 82 83 static int smb_fem_oplock_open(femarg_t *, int, cred_t *, 84 struct caller_context *); 85 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *, 86 struct caller_context *); 87 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *, 88 struct caller_context *); 89 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *, 90 caller_context_t *); 91 static int smb_fem_oplock_rwlock(femarg_t *, int, caller_context_t *); 92 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int, 93 offset_t, cred_t *, caller_context_t *); 94 static int smb_fem_oplock_setsecattr(femarg_t *, vsecattr_t *, int, cred_t *, 95 caller_context_t *); 96 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *, 97 caller_context_t *); 98 99 static const fs_operation_def_t smb_oplock_tmpl[] = { 100 VOPNAME_OPEN, { .femop_open = smb_fem_oplock_open }, 101 VOPNAME_READ, { .femop_read = smb_fem_oplock_read }, 102 VOPNAME_WRITE, { .femop_write = smb_fem_oplock_write }, 103 VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr }, 104 VOPNAME_RWLOCK, { .femop_rwlock = smb_fem_oplock_rwlock }, 105 VOPNAME_SPACE, { .femop_space = smb_fem_oplock_space }, 106 VOPNAME_SETSECATTR, { .femop_setsecattr = smb_fem_oplock_setsecattr }, 107 VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent }, 108 NULL, NULL 109 }; 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_process_node_notify_change_queue(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_process_node_notify_change_queue(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_process_node_notify_change_queue(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_process_node_notify_change_queue(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_process_node_notify_change_queue(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_process_node_notify_change_queue(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_process_node_notify_change_queue(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 401 #define SMB_FEM_OPLOCK_BREAK(arg) \ 402 smb_oplock_break((smb_node_t *)((arg)->fa_fnode->fn_available)); 403 404 static int 405 smb_fem_oplock_open( 406 femarg_t *arg, 407 int mode, 408 cred_t *cr, 409 struct caller_context *ct) 410 { 411 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 412 SMB_FEM_OPLOCK_BREAK(arg); 413 414 return (vnext_open(arg, mode, cr, ct)); 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 struct caller_context *ct) 429 { 430 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 431 SMB_FEM_OPLOCK_BREAK(arg); 432 433 return (vnext_read(arg, uiop, ioflag, cr, ct)); 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 struct caller_context *ct) 448 { 449 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 450 SMB_FEM_OPLOCK_BREAK(arg); 451 452 return (vnext_write(arg, uiop, ioflag, cr, ct)); 453 } 454 455 static int 456 smb_fem_oplock_setattr( 457 femarg_t *arg, 458 vattr_t *vap, 459 int flags, 460 cred_t *cr, 461 caller_context_t *ct) 462 { 463 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 464 SMB_FEM_OPLOCK_BREAK(arg); 465 466 return (vnext_setattr(arg, vap, flags, cr, ct)); 467 } 468 469 static int 470 smb_fem_oplock_rwlock( 471 femarg_t *arg, 472 int write_lock, 473 caller_context_t *ct) 474 { 475 if (write_lock) { 476 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 477 SMB_FEM_OPLOCK_BREAK(arg); 478 } 479 480 return (vnext_rwlock(arg, write_lock, ct)); 481 } 482 483 static int 484 smb_fem_oplock_space( 485 femarg_t *arg, 486 int cmd, 487 flock64_t *bfp, 488 int flag, 489 offset_t offset, 490 cred_t *cr, 491 caller_context_t *ct) 492 { 493 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 494 SMB_FEM_OPLOCK_BREAK(arg); 495 496 return (vnext_space(arg, cmd, bfp, flag, offset, cr, ct)); 497 } 498 499 static int 500 smb_fem_oplock_setsecattr( 501 femarg_t *arg, 502 vsecattr_t *vsap, 503 int flag, 504 cred_t *cr, 505 caller_context_t *ct) 506 { 507 if (ct == NULL || ct->cc_caller_id != smb_ct.cc_caller_id) 508 SMB_FEM_OPLOCK_BREAK(arg); 509 510 return (vnext_setsecattr(arg, vsap, flag, cr, ct)); 511 } 512 513 /* 514 * smb_fem_oplock_vnevent() 515 * 516 * To intercept NFS and local renames and removes in order to break any 517 * existing oplock prior to the operation. 518 * 519 * Note: Currently, this monitor is traversed only when an FS is mounted 520 * non-nbmand. (When the FS is mounted nbmand, share reservation checking 521 * will detect a share violation and return an error prior to the VOP layer 522 * being reached.) Thus, for nbmand NFS and local renames and removes, 523 * an existing oplock is never broken prior to share checking (contrary to 524 * how it is with intra-CIFS remove and rename requests). 525 */ 526 527 static int 528 smb_fem_oplock_vnevent( 529 femarg_t *arg, 530 vnevent_t vnevent, 531 vnode_t *dvp, 532 char *name, 533 caller_context_t *ct) 534 { 535 switch (vnevent) { 536 case VE_REMOVE: 537 case VE_RENAME_DEST: 538 case VE_RENAME_SRC: 539 540 SMB_FEM_OPLOCK_BREAK(arg); 541 break; 542 543 default: 544 break; 545 } 546 return (vnext_vnevent(arg, vnevent, dvp, name, ct)); 547 } 548