1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21da6c28aaSamw /* 22cb174861Sjoyce mcintosh * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23*4b69ad09SGordon Ross * Copyright 2020 Tintri by DDN, Inc. All rights reserved. 2454207fd2SJerry Jelinek * Copyright 2015 Joyent, Inc. 25da6c28aaSamw */ 26da6c28aaSamw 27bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h> 288c10a865Sas200622 #include <smbsrv/smb_fsops.h> 29da6c28aaSamw #include <sys/sdt.h> 308c10a865Sas200622 #include <sys/fcntl.h> 31da6c28aaSamw #include <sys/vfs.h> 32da6c28aaSamw #include <sys/vfs_opreg.h> 33da6c28aaSamw #include <sys/vnode.h> 34da6c28aaSamw #include <sys/fem.h> 35da6c28aaSamw 368c10a865Sas200622 extern caller_context_t smb_ct; 378c10a865Sas200622 388c10a865Sas200622 static boolean_t smb_fem_initialized = B_FALSE; 398c10a865Sas200622 static fem_t *smb_fcn_ops = NULL; 408c10a865Sas200622 static fem_t *smb_oplock_ops = NULL; 418c10a865Sas200622 428c10a865Sas200622 /* 438c10a865Sas200622 * Declarations for FCN (file change notification) FEM monitors 448c10a865Sas200622 */ 458c10a865Sas200622 468c10a865Sas200622 static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int, 47da6c28aaSamw vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *); 488c10a865Sas200622 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *, 49da6c28aaSamw caller_context_t *, int); 508c10a865Sas200622 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *, 51da6c28aaSamw cred_t *, caller_context_t *, int); 528c10a865Sas200622 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **, 53da6c28aaSamw cred_t *, caller_context_t *, int, vsecattr_t *); 548c10a865Sas200622 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *, 55da6c28aaSamw caller_context_t *, int); 568c10a865Sas200622 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *, 57da6c28aaSamw caller_context_t *, int); 588c10a865Sas200622 static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *, 59da6c28aaSamw char *, cred_t *, caller_context_t *, int); 60da6c28aaSamw 61da6c28aaSamw static const fs_operation_def_t smb_fcn_tmpl[] = { 62da6c28aaSamw VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create }, 63da6c28aaSamw VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove}, 64da6c28aaSamw VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename}, 65da6c28aaSamw VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir}, 66da6c28aaSamw VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir}, 67da6c28aaSamw VOPNAME_LINK, {.femop_link = smb_fem_fcn_link}, 68da6c28aaSamw VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink}, 69da6c28aaSamw NULL, NULL 70da6c28aaSamw }; 71da6c28aaSamw 728c10a865Sas200622 /* 738c10a865Sas200622 * Declarations for oplock FEM monitors 748c10a865Sas200622 */ 758c10a865Sas200622 768c10a865Sas200622 static int smb_fem_oplock_open(femarg_t *, int, cred_t *, 778c10a865Sas200622 struct caller_context *); 788c10a865Sas200622 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *, 798c10a865Sas200622 struct caller_context *); 808c10a865Sas200622 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *, 818c10a865Sas200622 struct caller_context *); 828c10a865Sas200622 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *, 838c10a865Sas200622 caller_context_t *); 848c10a865Sas200622 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int, 858c10a865Sas200622 offset_t, cred_t *, caller_context_t *); 868c10a865Sas200622 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *, 878c10a865Sas200622 caller_context_t *); 888c10a865Sas200622 898c10a865Sas200622 static const fs_operation_def_t smb_oplock_tmpl[] = { 908c10a865Sas200622 VOPNAME_OPEN, { .femop_open = smb_fem_oplock_open }, 918c10a865Sas200622 VOPNAME_READ, { .femop_read = smb_fem_oplock_read }, 928c10a865Sas200622 VOPNAME_WRITE, { .femop_write = smb_fem_oplock_write }, 938c10a865Sas200622 VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr }, 948c10a865Sas200622 VOPNAME_SPACE, { .femop_space = smb_fem_oplock_space }, 958c10a865Sas200622 VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent }, 968c10a865Sas200622 NULL, NULL 978c10a865Sas200622 }; 988c10a865Sas200622 9994047d49SGordon Ross static int smb_fem_oplock_wait(smb_node_t *, caller_context_t *); 1002c2961f8Sjose borrego 101faa1795aSjb150015 /* 102faa1795aSjb150015 * smb_fem_init 103faa1795aSjb150015 * 104faa1795aSjb150015 * This function is not multi-thread safe. The caller must make sure only one 105faa1795aSjb150015 * thread makes the call. 106faa1795aSjb150015 */ 107da6c28aaSamw int 108faa1795aSjb150015 smb_fem_init(void) 109da6c28aaSamw { 110faa1795aSjb150015 int rc = 0; 111faa1795aSjb150015 112faa1795aSjb150015 if (smb_fem_initialized) 113faa1795aSjb150015 return (0); 114faa1795aSjb150015 115faa1795aSjb150015 rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops); 116faa1795aSjb150015 if (rc) 117faa1795aSjb150015 return (rc); 118faa1795aSjb150015 1198c10a865Sas200622 rc = fem_create("smb_oplock_ops", smb_oplock_tmpl, 1208c10a865Sas200622 &smb_oplock_ops); 1218c10a865Sas200622 1228c10a865Sas200622 if (rc) { 1238c10a865Sas200622 fem_free(smb_fcn_ops); 1248c10a865Sas200622 smb_fcn_ops = NULL; 1258c10a865Sas200622 return (rc); 1268c10a865Sas200622 } 1278c10a865Sas200622 128faa1795aSjb150015 smb_fem_initialized = B_TRUE; 129faa1795aSjb150015 130faa1795aSjb150015 return (0); 131da6c28aaSamw } 132da6c28aaSamw 133faa1795aSjb150015 /* 134faa1795aSjb150015 * smb_fem_fini 135faa1795aSjb150015 * 136faa1795aSjb150015 * This function is not multi-thread safe. The caller must make sure only one 137faa1795aSjb150015 * thread makes the call. 138faa1795aSjb150015 */ 139da6c28aaSamw void 140faa1795aSjb150015 smb_fem_fini(void) 141da6c28aaSamw { 142faa1795aSjb150015 if (!smb_fem_initialized) 143faa1795aSjb150015 return; 144faa1795aSjb150015 1458622ec45SGordon Ross if (smb_fcn_ops != NULL) { 146da6c28aaSamw fem_free(smb_fcn_ops); 147faa1795aSjb150015 smb_fcn_ops = NULL; 1488622ec45SGordon Ross } 1498622ec45SGordon Ross if (smb_oplock_ops != NULL) { 1508622ec45SGordon Ross fem_free(smb_oplock_ops); 1518c10a865Sas200622 smb_oplock_ops = NULL; 1528622ec45SGordon Ross } 153faa1795aSjb150015 smb_fem_initialized = B_FALSE; 154da6c28aaSamw } 155da6c28aaSamw 156bfe5e737SGordon Ross /* 157bfe5e737SGordon Ross * Install our fem hooks for change notify. 158bfe5e737SGordon Ross * Not using hold/rele function here because we 159bfe5e737SGordon Ross * remove the fem hooks before node destroy. 160bfe5e737SGordon Ross */ 1618622ec45SGordon Ross int 162da6c28aaSamw smb_fem_fcn_install(smb_node_t *node) 163da6c28aaSamw { 1648622ec45SGordon Ross int rc; 1658622ec45SGordon Ross 1668622ec45SGordon Ross if (smb_fcn_ops == NULL) 1678622ec45SGordon Ross return (ENOSYS); 1688622ec45SGordon Ross rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ, 169bfe5e737SGordon Ross NULL, NULL); 1708622ec45SGordon Ross return (rc); 171da6c28aaSamw } 172da6c28aaSamw 173*4b69ad09SGordon Ross int 174da6c28aaSamw smb_fem_fcn_uninstall(smb_node_t *node) 175da6c28aaSamw { 176*4b69ad09SGordon Ross int rc; 177*4b69ad09SGordon Ross 1788622ec45SGordon Ross if (smb_fcn_ops == NULL) 179*4b69ad09SGordon Ross return (ENOSYS); 180*4b69ad09SGordon Ross rc = fem_uninstall(node->vp, smb_fcn_ops, (void *)node); 181*4b69ad09SGordon Ross return (rc); 182da6c28aaSamw } 183da6c28aaSamw 1848c10a865Sas200622 int 1858c10a865Sas200622 smb_fem_oplock_install(smb_node_t *node) 1868c10a865Sas200622 { 1878622ec45SGordon Ross int rc; 1888622ec45SGordon Ross 1898622ec45SGordon Ross if (smb_oplock_ops == NULL) 1908622ec45SGordon Ross return (ENOSYS); 1918622ec45SGordon Ross rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ, 1928622ec45SGordon Ross (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release); 1938622ec45SGordon Ross return (rc); 1948c10a865Sas200622 } 1958c10a865Sas200622 1968622ec45SGordon Ross void 1978c10a865Sas200622 smb_fem_oplock_uninstall(smb_node_t *node) 1988c10a865Sas200622 { 1998622ec45SGordon Ross if (smb_oplock_ops == NULL) 2008622ec45SGordon Ross return; 2018622ec45SGordon Ross VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node)); 2028c10a865Sas200622 } 2038c10a865Sas200622 2048c10a865Sas200622 /* 2058c10a865Sas200622 * FEM FCN monitors 2068c10a865Sas200622 * 2078c10a865Sas200622 * The FCN monitors intercept the respective VOP_* call regardless 2088c10a865Sas200622 * of whether the call originates from CIFS, NFS, or a local process. 2098c10a865Sas200622 */ 2108c10a865Sas200622 211da6c28aaSamw /* 212da6c28aaSamw * smb_fem_fcn_create() 213da6c28aaSamw * 214da6c28aaSamw * This monitor will catch only changes to VREG files and not to extended 215da6c28aaSamw * attribute files. This is fine because, for CIFS files, stream creates 216da6c28aaSamw * should not trigger any file change notification on the VDIR directory 217da6c28aaSamw * being monitored. Creates of any other kind of extended attribute in 218da6c28aaSamw * the directory will also not trigger any file change notification on the 219da6c28aaSamw * VDIR directory being monitored. 220da6c28aaSamw */ 221da6c28aaSamw 2228c10a865Sas200622 static int 223da6c28aaSamw smb_fem_fcn_create( 224da6c28aaSamw femarg_t *arg, 225da6c28aaSamw char *name, 226da6c28aaSamw vattr_t *vap, 227da6c28aaSamw vcexcl_t excl, 228da6c28aaSamw int mode, 229da6c28aaSamw vnode_t **vpp, 230da6c28aaSamw cred_t *cr, 231da6c28aaSamw int flag, 232da6c28aaSamw caller_context_t *ct, 233da6c28aaSamw vsecattr_t *vsecp) 234da6c28aaSamw { 235da6c28aaSamw smb_node_t *dnode; 236da6c28aaSamw int error; 237da6c28aaSamw 238da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 239da6c28aaSamw 240da6c28aaSamw ASSERT(dnode); 241da6c28aaSamw 242da6c28aaSamw error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag, 243da6c28aaSamw ct, vsecp); 244da6c28aaSamw 245b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) 246ccc71be5SGordon Ross smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 247da6c28aaSamw 248da6c28aaSamw return (error); 249da6c28aaSamw } 250da6c28aaSamw 251da6c28aaSamw /* 252da6c28aaSamw * smb_fem_fcn_remove() 253da6c28aaSamw * 254da6c28aaSamw * This monitor will catch only changes to VREG files and to not extended 255da6c28aaSamw * attribute files. This is fine because, for CIFS files, stream deletes 256da6c28aaSamw * should not trigger any file change notification on the VDIR directory 257da6c28aaSamw * being monitored. Deletes of any other kind of extended attribute in 258da6c28aaSamw * the directory will also not trigger any file change notification on the 259da6c28aaSamw * VDIR directory being monitored. 260da6c28aaSamw */ 261da6c28aaSamw 2628c10a865Sas200622 static int 263da6c28aaSamw smb_fem_fcn_remove( 264da6c28aaSamw femarg_t *arg, 265da6c28aaSamw char *name, 266da6c28aaSamw cred_t *cr, 267da6c28aaSamw caller_context_t *ct, 268da6c28aaSamw int flags) 269da6c28aaSamw { 270da6c28aaSamw smb_node_t *dnode; 271da6c28aaSamw int error; 272da6c28aaSamw 273da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 274da6c28aaSamw 275da6c28aaSamw ASSERT(dnode); 276da6c28aaSamw 277da6c28aaSamw error = vnext_remove(arg, name, cr, ct, flags); 278da6c28aaSamw 279b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) 280ccc71be5SGordon Ross smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 281da6c28aaSamw 282da6c28aaSamw return (error); 283da6c28aaSamw } 284da6c28aaSamw 2858c10a865Sas200622 static int 286da6c28aaSamw smb_fem_fcn_rename( 287da6c28aaSamw femarg_t *arg, 288da6c28aaSamw char *snm, 289da6c28aaSamw vnode_t *tdvp, 290da6c28aaSamw char *tnm, 291da6c28aaSamw cred_t *cr, 292da6c28aaSamw caller_context_t *ct, 293da6c28aaSamw int flags) 294da6c28aaSamw { 295da6c28aaSamw smb_node_t *dnode; 296da6c28aaSamw int error; 297da6c28aaSamw 298da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 299da6c28aaSamw 300da6c28aaSamw ASSERT(dnode); 301da6c28aaSamw 302da6c28aaSamw error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags); 303da6c28aaSamw 304b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) { 305ccc71be5SGordon Ross /* 306ccc71be5SGordon Ross * Note that renames in the same directory are normally 307ccc71be5SGordon Ross * delivered in {old,new} pairs, and clients expect them 308ccc71be5SGordon Ross * in that order, if both events are delivered. 309ccc71be5SGordon Ross */ 310b819cea2SGordon Ross smb_node_notify_change(dnode, 311b819cea2SGordon Ross FILE_ACTION_RENAMED_OLD_NAME, snm); 312b819cea2SGordon Ross smb_node_notify_change(dnode, 313b819cea2SGordon Ross FILE_ACTION_RENAMED_NEW_NAME, tnm); 314b819cea2SGordon Ross } 315ccc71be5SGordon Ross 316b819cea2SGordon Ross return (error); 317da6c28aaSamw } 318da6c28aaSamw 3198c10a865Sas200622 static int 320da6c28aaSamw smb_fem_fcn_mkdir( 321da6c28aaSamw femarg_t *arg, 322da6c28aaSamw char *name, 323da6c28aaSamw vattr_t *vap, 324da6c28aaSamw vnode_t **vpp, 325da6c28aaSamw cred_t *cr, 326da6c28aaSamw caller_context_t *ct, 327da6c28aaSamw int flags, 328da6c28aaSamw vsecattr_t *vsecp) 329da6c28aaSamw { 330da6c28aaSamw smb_node_t *dnode; 331da6c28aaSamw int error; 332da6c28aaSamw 333da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 334da6c28aaSamw 335da6c28aaSamw ASSERT(dnode); 336da6c28aaSamw 337da6c28aaSamw error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp); 338da6c28aaSamw 339b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) 340ccc71be5SGordon Ross smb_node_notify_change(dnode, FILE_ACTION_ADDED, name); 341da6c28aaSamw 342da6c28aaSamw return (error); 343da6c28aaSamw } 344da6c28aaSamw 3458c10a865Sas200622 static int 346da6c28aaSamw smb_fem_fcn_rmdir( 347da6c28aaSamw femarg_t *arg, 348da6c28aaSamw char *name, 349da6c28aaSamw vnode_t *cdir, 350da6c28aaSamw cred_t *cr, 351da6c28aaSamw caller_context_t *ct, 352da6c28aaSamw int flags) 353da6c28aaSamw { 354da6c28aaSamw smb_node_t *dnode; 355da6c28aaSamw int error; 356da6c28aaSamw 357da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 358da6c28aaSamw 359da6c28aaSamw ASSERT(dnode); 360da6c28aaSamw 361da6c28aaSamw error = vnext_rmdir(arg, name, cdir, cr, ct, flags); 362da6c28aaSamw 363b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) 364ccc71be5SGordon Ross smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name); 365da6c28aaSamw 366da6c28aaSamw return (error); 367da6c28aaSamw } 368da6c28aaSamw 3698c10a865Sas200622 static int 370da6c28aaSamw smb_fem_fcn_link( 371da6c28aaSamw femarg_t *arg, 372da6c28aaSamw vnode_t *svp, 373da6c28aaSamw char *tnm, 374da6c28aaSamw cred_t *cr, 375da6c28aaSamw caller_context_t *ct, 376da6c28aaSamw int flags) 377da6c28aaSamw { 378da6c28aaSamw smb_node_t *dnode; 379da6c28aaSamw int error; 380da6c28aaSamw 381da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 382da6c28aaSamw 383da6c28aaSamw ASSERT(dnode); 384da6c28aaSamw 385da6c28aaSamw error = vnext_link(arg, svp, tnm, cr, ct, flags); 386da6c28aaSamw 387b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) 388ccc71be5SGordon Ross smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm); 389da6c28aaSamw 390da6c28aaSamw return (error); 391da6c28aaSamw } 392da6c28aaSamw 3938c10a865Sas200622 static int 394da6c28aaSamw smb_fem_fcn_symlink( 395da6c28aaSamw femarg_t *arg, 396da6c28aaSamw char *linkname, 397da6c28aaSamw vattr_t *vap, 398da6c28aaSamw char *target, 399da6c28aaSamw cred_t *cr, 400da6c28aaSamw caller_context_t *ct, 401da6c28aaSamw int flags) 402da6c28aaSamw { 403da6c28aaSamw smb_node_t *dnode; 404da6c28aaSamw int error; 405da6c28aaSamw 406da6c28aaSamw dnode = (smb_node_t *)arg->fa_fnode->fn_available; 407da6c28aaSamw 408da6c28aaSamw ASSERT(dnode); 409da6c28aaSamw 410da6c28aaSamw error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags); 411da6c28aaSamw 412b819cea2SGordon Ross if (error == 0 && ct != &smb_ct) 413ccc71be5SGordon Ross smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname); 414da6c28aaSamw 415da6c28aaSamw return (error); 416da6c28aaSamw } 4178c10a865Sas200622 4188c10a865Sas200622 /* 4198c10a865Sas200622 * FEM oplock monitors 4208c10a865Sas200622 * 4218c10a865Sas200622 * The monitors below are not intended to intercept CIFS calls. 4228c10a865Sas200622 * CIFS higher-level routines will break oplocks as needed prior 4238c10a865Sas200622 * to getting to the VFS layer. 4248c10a865Sas200622 */ 4258c10a865Sas200622 static int 4268c10a865Sas200622 smb_fem_oplock_open( 4278c10a865Sas200622 femarg_t *arg, 4288c10a865Sas200622 int mode, 4298c10a865Sas200622 cred_t *cr, 4302c2961f8Sjose borrego caller_context_t *ct) 4318c10a865Sas200622 { 43294047d49SGordon Ross smb_node_t *node; 43394047d49SGordon Ross uint32_t status; 434b819cea2SGordon Ross int rc = 0; 4358c10a865Sas200622 436b819cea2SGordon Ross if (ct != &smb_ct) { 43794047d49SGordon Ross uint32_t req_acc = FILE_READ_DATA; 43894047d49SGordon Ross uint32_t cr_disp = FILE_OPEN_IF; 43994047d49SGordon Ross 44094047d49SGordon Ross node = (smb_node_t *)(arg->fa_fnode->fn_available); 44194047d49SGordon Ross SMB_NODE_VALID(node); 44294047d49SGordon Ross 44394047d49SGordon Ross /* 44494047d49SGordon Ross * Get req_acc, cr_disp just accurate enough so 44594047d49SGordon Ross * the oplock break call does the right thing. 44694047d49SGordon Ross */ 44794047d49SGordon Ross if (mode & FWRITE) { 44894047d49SGordon Ross req_acc = FILE_READ_DATA | FILE_WRITE_DATA; 44994047d49SGordon Ross cr_disp = (mode & FTRUNC) ? 45094047d49SGordon Ross FILE_OVERWRITE_IF : FILE_OPEN_IF; 45194047d49SGordon Ross } else { 45294047d49SGordon Ross req_acc = FILE_READ_DATA; 45394047d49SGordon Ross cr_disp = FILE_OPEN_IF; 45494047d49SGordon Ross } 45594047d49SGordon Ross 45694047d49SGordon Ross status = smb_oplock_break_OPEN(node, NULL, 45794047d49SGordon Ross req_acc, cr_disp); 45894047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 45994047d49SGordon Ross rc = smb_fem_oplock_wait(node, ct); 46094047d49SGordon Ross else if (status != 0) 46194047d49SGordon Ross rc = EIO; 462b819cea2SGordon Ross } 4632c2961f8Sjose borrego if (rc == 0) 4642c2961f8Sjose borrego rc = vnext_open(arg, mode, cr, ct); 465b819cea2SGordon Ross 4662c2961f8Sjose borrego return (rc); 4678c10a865Sas200622 } 4688c10a865Sas200622 4698c10a865Sas200622 /* 4708c10a865Sas200622 * Should normally be hit only via NFSv2/v3. All other accesses 4718c10a865Sas200622 * (CIFS/NFS/local) should call VOP_OPEN first. 4728c10a865Sas200622 */ 4738c10a865Sas200622 4748c10a865Sas200622 static int 4758c10a865Sas200622 smb_fem_oplock_read( 4768c10a865Sas200622 femarg_t *arg, 4778c10a865Sas200622 uio_t *uiop, 4788c10a865Sas200622 int ioflag, 4798c10a865Sas200622 cred_t *cr, 4802c2961f8Sjose borrego caller_context_t *ct) 4818c10a865Sas200622 { 48294047d49SGordon Ross smb_node_t *node; 48394047d49SGordon Ross uint32_t status; 484b819cea2SGordon Ross int rc = 0; 4858c10a865Sas200622 486b819cea2SGordon Ross if (ct != &smb_ct) { 48794047d49SGordon Ross node = (smb_node_t *)(arg->fa_fnode->fn_available); 48894047d49SGordon Ross SMB_NODE_VALID(node); 48994047d49SGordon Ross 49094047d49SGordon Ross status = smb_oplock_break_READ(node, NULL); 49194047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 49294047d49SGordon Ross rc = smb_fem_oplock_wait(node, ct); 49394047d49SGordon Ross else if (status != 0) 49494047d49SGordon Ross rc = EIO; 495b819cea2SGordon Ross } 4962c2961f8Sjose borrego if (rc == 0) 4972c2961f8Sjose borrego rc = vnext_read(arg, uiop, ioflag, cr, ct); 498b819cea2SGordon Ross 4992c2961f8Sjose borrego return (rc); 5008c10a865Sas200622 } 5018c10a865Sas200622 5028c10a865Sas200622 /* 5038c10a865Sas200622 * Should normally be hit only via NFSv2/v3. All other accesses 5048c10a865Sas200622 * (CIFS/NFS/local) should call VOP_OPEN first. 5058c10a865Sas200622 */ 5068c10a865Sas200622 5078c10a865Sas200622 static int 5088c10a865Sas200622 smb_fem_oplock_write( 5098c10a865Sas200622 femarg_t *arg, 5108c10a865Sas200622 uio_t *uiop, 5118c10a865Sas200622 int ioflag, 5128c10a865Sas200622 cred_t *cr, 5132c2961f8Sjose borrego caller_context_t *ct) 5148c10a865Sas200622 { 51594047d49SGordon Ross smb_node_t *node; 51694047d49SGordon Ross uint32_t status; 517b819cea2SGordon Ross int rc = 0; 5188c10a865Sas200622 51994047d49SGordon Ross if (ct != &smb_ct) { 52094047d49SGordon Ross node = (smb_node_t *)(arg->fa_fnode->fn_available); 52194047d49SGordon Ross SMB_NODE_VALID(node); 52294047d49SGordon Ross 52394047d49SGordon Ross status = smb_oplock_break_WRITE(node, NULL); 52494047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 52594047d49SGordon Ross rc = smb_fem_oplock_wait(node, ct); 52694047d49SGordon Ross else if (status != 0) 52794047d49SGordon Ross rc = EIO; 52894047d49SGordon Ross } 5292c2961f8Sjose borrego if (rc == 0) 5302c2961f8Sjose borrego rc = vnext_write(arg, uiop, ioflag, cr, ct); 531b819cea2SGordon Ross 5322c2961f8Sjose borrego return (rc); 5338c10a865Sas200622 } 5348c10a865Sas200622 5358c10a865Sas200622 static int 5368c10a865Sas200622 smb_fem_oplock_setattr( 5378c10a865Sas200622 femarg_t *arg, 5388c10a865Sas200622 vattr_t *vap, 5398c10a865Sas200622 int flags, 5408c10a865Sas200622 cred_t *cr, 5418c10a865Sas200622 caller_context_t *ct) 5428c10a865Sas200622 { 54394047d49SGordon Ross smb_node_t *node; 54494047d49SGordon Ross uint32_t status; 545cb174861Sjoyce mcintosh int rc = 0; 5468c10a865Sas200622 54794047d49SGordon Ross if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) { 54894047d49SGordon Ross node = (smb_node_t *)(arg->fa_fnode->fn_available); 54994047d49SGordon Ross SMB_NODE_VALID(node); 55094047d49SGordon Ross 55194047d49SGordon Ross status = smb_oplock_break_SETINFO(node, NULL, 55294047d49SGordon Ross FileEndOfFileInformation); 55394047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 55494047d49SGordon Ross rc = smb_fem_oplock_wait(node, ct); 55594047d49SGordon Ross else if (status != 0) 55694047d49SGordon Ross rc = EIO; 55794047d49SGordon Ross } 5582c2961f8Sjose borrego if (rc == 0) 5592c2961f8Sjose borrego rc = vnext_setattr(arg, vap, flags, cr, ct); 5602c2961f8Sjose borrego return (rc); 5618c10a865Sas200622 } 5628c10a865Sas200622 5638c10a865Sas200622 static int 5648c10a865Sas200622 smb_fem_oplock_space( 5658c10a865Sas200622 femarg_t *arg, 5668c10a865Sas200622 int cmd, 5678c10a865Sas200622 flock64_t *bfp, 5688c10a865Sas200622 int flag, 5698c10a865Sas200622 offset_t offset, 5708c10a865Sas200622 cred_t *cr, 5718c10a865Sas200622 caller_context_t *ct) 5728c10a865Sas200622 { 57394047d49SGordon Ross smb_node_t *node; 57494047d49SGordon Ross uint32_t status; 575b819cea2SGordon Ross int rc = 0; 5768c10a865Sas200622 57794047d49SGordon Ross if (ct != &smb_ct) { 57894047d49SGordon Ross node = (smb_node_t *)(arg->fa_fnode->fn_available); 57994047d49SGordon Ross SMB_NODE_VALID(node); 58094047d49SGordon Ross 58194047d49SGordon Ross status = smb_oplock_break_SETINFO(node, NULL, 58294047d49SGordon Ross FileAllocationInformation); 58394047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 58494047d49SGordon Ross rc = smb_fem_oplock_wait(node, ct); 58594047d49SGordon Ross else if (status != 0) 58694047d49SGordon Ross rc = EIO; 58794047d49SGordon Ross } 5882c2961f8Sjose borrego if (rc == 0) 5892c2961f8Sjose borrego rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct); 5902c2961f8Sjose borrego return (rc); 5918c10a865Sas200622 } 5928c10a865Sas200622 5938c10a865Sas200622 /* 5948c10a865Sas200622 * smb_fem_oplock_vnevent() 5958c10a865Sas200622 * 5968c10a865Sas200622 * To intercept NFS and local renames and removes in order to break any 5978c10a865Sas200622 * existing oplock prior to the operation. 5988c10a865Sas200622 * 5998c10a865Sas200622 * Note: Currently, this monitor is traversed only when an FS is mounted 6008c10a865Sas200622 * non-nbmand. (When the FS is mounted nbmand, share reservation checking 6018c10a865Sas200622 * will detect a share violation and return an error prior to the VOP layer 6028c10a865Sas200622 * being reached.) Thus, for nbmand NFS and local renames and removes, 6038c10a865Sas200622 * an existing oplock is never broken prior to share checking (contrary to 6048c10a865Sas200622 * how it is with intra-CIFS remove and rename requests). 6058c10a865Sas200622 */ 6068c10a865Sas200622 6078c10a865Sas200622 static int 6088c10a865Sas200622 smb_fem_oplock_vnevent( 6098c10a865Sas200622 femarg_t *arg, 6108c10a865Sas200622 vnevent_t vnevent, 6118c10a865Sas200622 vnode_t *dvp, 6128c10a865Sas200622 char *name, 6138c10a865Sas200622 caller_context_t *ct) 6148c10a865Sas200622 { 61594047d49SGordon Ross smb_node_t *node; 61694047d49SGordon Ross uint32_t status; 617b819cea2SGordon Ross int rc = 0; 6182c2961f8Sjose borrego 619b819cea2SGordon Ross if (ct != &smb_ct) { 62094047d49SGordon Ross node = (smb_node_t *)(arg->fa_fnode->fn_available); 62194047d49SGordon Ross SMB_NODE_VALID(node); 62294047d49SGordon Ross 6238c10a865Sas200622 switch (vnevent) { 6248c10a865Sas200622 case VE_REMOVE: 62554207fd2SJerry Jelinek case VE_PRE_RENAME_DEST: 6268c10a865Sas200622 case VE_RENAME_DEST: 62794047d49SGordon Ross status = smb_oplock_break_HANDLE(node, NULL); 6288c10a865Sas200622 break; 62954207fd2SJerry Jelinek case VE_PRE_RENAME_SRC: 630cb174861Sjoyce mcintosh case VE_RENAME_SRC: 63194047d49SGordon Ross status = smb_oplock_break_SETINFO(node, NULL, 63294047d49SGordon Ross FileRenameInformation); 633cb174861Sjoyce mcintosh break; 6348c10a865Sas200622 default: 63594047d49SGordon Ross status = 0; 6368c10a865Sas200622 break; 6378c10a865Sas200622 } 63894047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) 63994047d49SGordon Ross rc = smb_fem_oplock_wait(node, ct); 64094047d49SGordon Ross else if (status != 0) 64194047d49SGordon Ross rc = EIO; 642b819cea2SGordon Ross } 643b819cea2SGordon Ross if (rc == 0) 644b819cea2SGordon Ross rc = vnext_vnevent(arg, vnevent, dvp, name, ct); 645cb174861Sjoyce mcintosh 646cb174861Sjoyce mcintosh return (rc); 6478c10a865Sas200622 } 6482c2961f8Sjose borrego 64994047d49SGordon Ross int smb_fem_oplock_timeout = 5000; /* mSec. */ 6502c2961f8Sjose borrego 65194047d49SGordon Ross static int 65294047d49SGordon Ross smb_fem_oplock_wait(smb_node_t *node, caller_context_t *ct) 65394047d49SGordon Ross { 65494047d49SGordon Ross int rc = 0; 6552c2961f8Sjose borrego 656b819cea2SGordon Ross ASSERT(ct != &smb_ct); 6572c2961f8Sjose borrego 658cb174861Sjoyce mcintosh if (ct && (ct->cc_flags & CC_DONTBLOCK)) { 6592c2961f8Sjose borrego ct->cc_flags |= CC_WOULDBLOCK; 66094047d49SGordon Ross rc = EAGAIN; 6612c2961f8Sjose borrego } else { 66294047d49SGordon Ross (void) smb_oplock_wait_break(node, 66394047d49SGordon Ross smb_fem_oplock_timeout); 66494047d49SGordon Ross rc = 0; 6652c2961f8Sjose borrego } 666cb174861Sjoyce mcintosh 6672c2961f8Sjose borrego return (rc); 6682c2961f8Sjose borrego } 669