xref: /titanic_50/usr/src/uts/common/fs/smbsrv/smb_fem.c (revision b819cea2f73f98c5662230cc9affc8cc84f77fcf)
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*b819cea2SGordon Ross  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
24da6c28aaSamw  */
25da6c28aaSamw 
26bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h>
278c10a865Sas200622 #include <smbsrv/smb_fsops.h>
28da6c28aaSamw #include <sys/sdt.h>
298c10a865Sas200622 #include <sys/fcntl.h>
30da6c28aaSamw #include <sys/vfs.h>
31da6c28aaSamw #include <sys/vfs_opreg.h>
32da6c28aaSamw #include <sys/vnode.h>
33da6c28aaSamw #include <sys/fem.h>
34da6c28aaSamw 
358c10a865Sas200622 extern caller_context_t	smb_ct;
368c10a865Sas200622 
378c10a865Sas200622 static boolean_t	smb_fem_initialized = B_FALSE;
388c10a865Sas200622 static fem_t		*smb_fcn_ops = NULL;
398c10a865Sas200622 static fem_t		*smb_oplock_ops = NULL;
408c10a865Sas200622 
418c10a865Sas200622 /*
428c10a865Sas200622  * Declarations for FCN (file change notification) FEM monitors
438c10a865Sas200622  */
448c10a865Sas200622 
458c10a865Sas200622 static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int,
46da6c28aaSamw     vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *);
478c10a865Sas200622 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *,
48da6c28aaSamw     caller_context_t *, int);
498c10a865Sas200622 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *,
50da6c28aaSamw     cred_t *, caller_context_t *, int);
518c10a865Sas200622 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **,
52da6c28aaSamw     cred_t *, caller_context_t *, int, vsecattr_t *);
538c10a865Sas200622 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *,
54da6c28aaSamw     caller_context_t *, int);
558c10a865Sas200622 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *,
56da6c28aaSamw     caller_context_t *, int);
578c10a865Sas200622 static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *,
58da6c28aaSamw     char *, cred_t *, caller_context_t *, int);
59da6c28aaSamw 
60da6c28aaSamw static const fs_operation_def_t smb_fcn_tmpl[] = {
61da6c28aaSamw 	VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create },
62da6c28aaSamw 	VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove},
63da6c28aaSamw 	VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename},
64da6c28aaSamw 	VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir},
65da6c28aaSamw 	VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir},
66da6c28aaSamw 	VOPNAME_LINK, {.femop_link = smb_fem_fcn_link},
67da6c28aaSamw 	VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink},
68da6c28aaSamw 	NULL, NULL
69da6c28aaSamw };
70da6c28aaSamw 
718c10a865Sas200622 /*
728c10a865Sas200622  * Declarations for oplock FEM monitors
738c10a865Sas200622  */
748c10a865Sas200622 
758c10a865Sas200622 static int smb_fem_oplock_open(femarg_t *, int, cred_t *,
768c10a865Sas200622     struct caller_context *);
778c10a865Sas200622 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *,
788c10a865Sas200622     struct caller_context *);
798c10a865Sas200622 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *,
808c10a865Sas200622     struct caller_context *);
818c10a865Sas200622 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *,
828c10a865Sas200622     caller_context_t *);
838c10a865Sas200622 static int smb_fem_oplock_rwlock(femarg_t *, int, 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_RWLOCK, { .femop_rwlock = smb_fem_oplock_rwlock },
958c10a865Sas200622 	VOPNAME_SPACE,	{ .femop_space = smb_fem_oplock_space },
968c10a865Sas200622 	VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent },
978c10a865Sas200622 	NULL, NULL
988c10a865Sas200622 };
998c10a865Sas200622 
100cb174861Sjoyce mcintosh static int smb_fem_oplock_break(femarg_t *, caller_context_t *, uint32_t);
1012c2961f8Sjose borrego 
102faa1795aSjb150015 /*
103faa1795aSjb150015  * smb_fem_init
104faa1795aSjb150015  *
105faa1795aSjb150015  * This function is not multi-thread safe. The caller must make sure only one
106faa1795aSjb150015  * thread makes the call.
107faa1795aSjb150015  */
108da6c28aaSamw int
smb_fem_init(void)109faa1795aSjb150015 smb_fem_init(void)
110da6c28aaSamw {
111faa1795aSjb150015 	int	rc = 0;
112faa1795aSjb150015 
113faa1795aSjb150015 	if (smb_fem_initialized)
114faa1795aSjb150015 		return (0);
115faa1795aSjb150015 
116faa1795aSjb150015 	rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops);
117faa1795aSjb150015 	if (rc)
118faa1795aSjb150015 		return (rc);
119faa1795aSjb150015 
1208c10a865Sas200622 	rc = fem_create("smb_oplock_ops", smb_oplock_tmpl,
1218c10a865Sas200622 	    &smb_oplock_ops);
1228c10a865Sas200622 
1238c10a865Sas200622 	if (rc) {
1248c10a865Sas200622 		fem_free(smb_fcn_ops);
1258c10a865Sas200622 		smb_fcn_ops = NULL;
1268c10a865Sas200622 		return (rc);
1278c10a865Sas200622 	}
1288c10a865Sas200622 
129faa1795aSjb150015 	smb_fem_initialized = B_TRUE;
130faa1795aSjb150015 
131faa1795aSjb150015 	return (0);
132da6c28aaSamw }
133da6c28aaSamw 
134faa1795aSjb150015 /*
135faa1795aSjb150015  * smb_fem_fini
136faa1795aSjb150015  *
137faa1795aSjb150015  * This function is not multi-thread safe. The caller must make sure only one
138faa1795aSjb150015  * thread makes the call.
139faa1795aSjb150015  */
140da6c28aaSamw void
smb_fem_fini(void)141faa1795aSjb150015 smb_fem_fini(void)
142da6c28aaSamw {
143faa1795aSjb150015 	if (!smb_fem_initialized)
144faa1795aSjb150015 		return;
145faa1795aSjb150015 
1468622ec45SGordon Ross 	if (smb_fcn_ops != NULL) {
147da6c28aaSamw 		fem_free(smb_fcn_ops);
148faa1795aSjb150015 		smb_fcn_ops = NULL;
1498622ec45SGordon Ross 	}
1508622ec45SGordon Ross 	if (smb_oplock_ops != NULL) {
1518622ec45SGordon Ross 		fem_free(smb_oplock_ops);
1528c10a865Sas200622 		smb_oplock_ops = NULL;
1538622ec45SGordon Ross 	}
154faa1795aSjb150015 	smb_fem_initialized = B_FALSE;
155da6c28aaSamw }
156da6c28aaSamw 
1578622ec45SGordon Ross int
smb_fem_fcn_install(smb_node_t * node)158da6c28aaSamw smb_fem_fcn_install(smb_node_t *node)
159da6c28aaSamw {
1608622ec45SGordon Ross 	int rc;
1618622ec45SGordon Ross 
1628622ec45SGordon Ross 	if (smb_fcn_ops == NULL)
1638622ec45SGordon Ross 		return (ENOSYS);
1648622ec45SGordon Ross 	rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ,
1658c10a865Sas200622 	    (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
1668622ec45SGordon Ross 	return (rc);
167da6c28aaSamw }
168da6c28aaSamw 
169da6c28aaSamw void
smb_fem_fcn_uninstall(smb_node_t * node)170da6c28aaSamw smb_fem_fcn_uninstall(smb_node_t *node)
171da6c28aaSamw {
1728622ec45SGordon Ross 	if (smb_fcn_ops == NULL)
1738622ec45SGordon Ross 		return;
1748622ec45SGordon Ross 	VERIFY0(fem_uninstall(node->vp, smb_fcn_ops, (void *)node));
175da6c28aaSamw }
176da6c28aaSamw 
1778c10a865Sas200622 int
smb_fem_oplock_install(smb_node_t * node)1788c10a865Sas200622 smb_fem_oplock_install(smb_node_t *node)
1798c10a865Sas200622 {
1808622ec45SGordon Ross 	int rc;
1818622ec45SGordon Ross 
1828622ec45SGordon Ross 	if (smb_oplock_ops == NULL)
1838622ec45SGordon Ross 		return (ENOSYS);
1848622ec45SGordon Ross 	rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ,
1858622ec45SGordon Ross 	    (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
1868622ec45SGordon Ross 	return (rc);
1878c10a865Sas200622 }
1888c10a865Sas200622 
1898622ec45SGordon Ross void
smb_fem_oplock_uninstall(smb_node_t * node)1908c10a865Sas200622 smb_fem_oplock_uninstall(smb_node_t *node)
1918c10a865Sas200622 {
1928622ec45SGordon Ross 	if (smb_oplock_ops == NULL)
1938622ec45SGordon Ross 		return;
1948622ec45SGordon Ross 	VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node));
1958c10a865Sas200622 }
1968c10a865Sas200622 
1978c10a865Sas200622 /*
1988c10a865Sas200622  * FEM FCN monitors
1998c10a865Sas200622  *
2008c10a865Sas200622  * The FCN monitors intercept the respective VOP_* call regardless
2018c10a865Sas200622  * of whether the call originates from CIFS, NFS, or a local process.
2028c10a865Sas200622  */
2038c10a865Sas200622 
204da6c28aaSamw /*
205da6c28aaSamw  * smb_fem_fcn_create()
206da6c28aaSamw  *
207da6c28aaSamw  * This monitor will catch only changes to VREG files and not to extended
208da6c28aaSamw  * attribute files.  This is fine because, for CIFS files, stream creates
209da6c28aaSamw  * should not trigger any file change notification on the VDIR directory
210da6c28aaSamw  * being monitored.  Creates of any other kind of extended attribute in
211da6c28aaSamw  * the directory will also not trigger any file change notification on the
212da6c28aaSamw  * VDIR directory being monitored.
213da6c28aaSamw  */
214da6c28aaSamw 
2158c10a865Sas200622 static int
smb_fem_fcn_create(femarg_t * arg,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)216da6c28aaSamw smb_fem_fcn_create(
217da6c28aaSamw     femarg_t *arg,
218da6c28aaSamw     char *name,
219da6c28aaSamw     vattr_t *vap,
220da6c28aaSamw     vcexcl_t excl,
221da6c28aaSamw     int mode,
222da6c28aaSamw     vnode_t **vpp,
223da6c28aaSamw     cred_t *cr,
224da6c28aaSamw     int flag,
225da6c28aaSamw     caller_context_t *ct,
226da6c28aaSamw     vsecattr_t *vsecp)
227da6c28aaSamw {
228da6c28aaSamw 	smb_node_t *dnode;
229da6c28aaSamw 	int error;
230da6c28aaSamw 
231da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
232da6c28aaSamw 
233da6c28aaSamw 	ASSERT(dnode);
234da6c28aaSamw 
235da6c28aaSamw 	error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag,
236da6c28aaSamw 	    ct, vsecp);
237da6c28aaSamw 
238*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
239ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
240da6c28aaSamw 
241da6c28aaSamw 	return (error);
242da6c28aaSamw }
243da6c28aaSamw 
244da6c28aaSamw /*
245da6c28aaSamw  * smb_fem_fcn_remove()
246da6c28aaSamw  *
247da6c28aaSamw  * This monitor will catch only changes to VREG files and to not extended
248da6c28aaSamw  * attribute files.  This is fine because, for CIFS files, stream deletes
249da6c28aaSamw  * should not trigger any file change notification on the VDIR directory
250da6c28aaSamw  * being monitored.  Deletes of any other kind of extended attribute in
251da6c28aaSamw  * the directory will also not trigger any file change notification on the
252da6c28aaSamw  * VDIR directory being monitored.
253da6c28aaSamw  */
254da6c28aaSamw 
2558c10a865Sas200622 static int
smb_fem_fcn_remove(femarg_t * arg,char * name,cred_t * cr,caller_context_t * ct,int flags)256da6c28aaSamw smb_fem_fcn_remove(
257da6c28aaSamw     femarg_t *arg,
258da6c28aaSamw     char *name,
259da6c28aaSamw     cred_t *cr,
260da6c28aaSamw     caller_context_t *ct,
261da6c28aaSamw     int flags)
262da6c28aaSamw {
263da6c28aaSamw 	smb_node_t *dnode;
264da6c28aaSamw 	int error;
265da6c28aaSamw 
266da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
267da6c28aaSamw 
268da6c28aaSamw 	ASSERT(dnode);
269da6c28aaSamw 
270da6c28aaSamw 	error = vnext_remove(arg, name, cr, ct, flags);
271da6c28aaSamw 
272*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
273ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
274da6c28aaSamw 
275da6c28aaSamw 	return (error);
276da6c28aaSamw }
277da6c28aaSamw 
2788c10a865Sas200622 static int
smb_fem_fcn_rename(femarg_t * arg,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)279da6c28aaSamw smb_fem_fcn_rename(
280da6c28aaSamw     femarg_t *arg,
281da6c28aaSamw     char *snm,
282da6c28aaSamw     vnode_t *tdvp,
283da6c28aaSamw     char *tnm,
284da6c28aaSamw     cred_t *cr,
285da6c28aaSamw     caller_context_t *ct,
286da6c28aaSamw     int flags)
287da6c28aaSamw {
288da6c28aaSamw 	smb_node_t *dnode;
289da6c28aaSamw 	int error;
290da6c28aaSamw 
291da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
292da6c28aaSamw 
293da6c28aaSamw 	ASSERT(dnode);
294da6c28aaSamw 
295da6c28aaSamw 	error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags);
296da6c28aaSamw 
297*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct) {
298ccc71be5SGordon Ross 		/*
299ccc71be5SGordon Ross 		 * Note that renames in the same directory are normally
300ccc71be5SGordon Ross 		 * delivered in {old,new} pairs, and clients expect them
301ccc71be5SGordon Ross 		 * in that order, if both events are delivered.
302ccc71be5SGordon Ross 		 */
303*b819cea2SGordon Ross 		smb_node_notify_change(dnode,
304*b819cea2SGordon Ross 		    FILE_ACTION_RENAMED_OLD_NAME, snm);
305*b819cea2SGordon Ross 		smb_node_notify_change(dnode,
306*b819cea2SGordon Ross 		    FILE_ACTION_RENAMED_NEW_NAME, tnm);
307*b819cea2SGordon Ross 	}
308ccc71be5SGordon Ross 
309*b819cea2SGordon Ross 	return (error);
310da6c28aaSamw }
311da6c28aaSamw 
3128c10a865Sas200622 static int
smb_fem_fcn_mkdir(femarg_t * arg,char * name,vattr_t * vap,vnode_t ** vpp,cred_t * cr,caller_context_t * ct,int flags,vsecattr_t * vsecp)313da6c28aaSamw smb_fem_fcn_mkdir(
314da6c28aaSamw     femarg_t *arg,
315da6c28aaSamw     char *name,
316da6c28aaSamw     vattr_t *vap,
317da6c28aaSamw     vnode_t **vpp,
318da6c28aaSamw     cred_t *cr,
319da6c28aaSamw     caller_context_t *ct,
320da6c28aaSamw     int flags,
321da6c28aaSamw     vsecattr_t *vsecp)
322da6c28aaSamw {
323da6c28aaSamw 	smb_node_t *dnode;
324da6c28aaSamw 	int error;
325da6c28aaSamw 
326da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
327da6c28aaSamw 
328da6c28aaSamw 	ASSERT(dnode);
329da6c28aaSamw 
330da6c28aaSamw 	error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp);
331da6c28aaSamw 
332*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
333ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
334da6c28aaSamw 
335da6c28aaSamw 	return (error);
336da6c28aaSamw }
337da6c28aaSamw 
3388c10a865Sas200622 static int
smb_fem_fcn_rmdir(femarg_t * arg,char * name,vnode_t * cdir,cred_t * cr,caller_context_t * ct,int flags)339da6c28aaSamw smb_fem_fcn_rmdir(
340da6c28aaSamw     femarg_t *arg,
341da6c28aaSamw     char *name,
342da6c28aaSamw     vnode_t *cdir,
343da6c28aaSamw     cred_t *cr,
344da6c28aaSamw     caller_context_t *ct,
345da6c28aaSamw     int flags)
346da6c28aaSamw {
347da6c28aaSamw 	smb_node_t *dnode;
348da6c28aaSamw 	int error;
349da6c28aaSamw 
350da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
351da6c28aaSamw 
352da6c28aaSamw 	ASSERT(dnode);
353da6c28aaSamw 
354da6c28aaSamw 	error = vnext_rmdir(arg, name, cdir, cr, ct, flags);
355da6c28aaSamw 
356*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
357ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
358da6c28aaSamw 
359da6c28aaSamw 	return (error);
360da6c28aaSamw }
361da6c28aaSamw 
3628c10a865Sas200622 static int
smb_fem_fcn_link(femarg_t * arg,vnode_t * svp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)363da6c28aaSamw smb_fem_fcn_link(
364da6c28aaSamw     femarg_t *arg,
365da6c28aaSamw     vnode_t *svp,
366da6c28aaSamw     char *tnm,
367da6c28aaSamw     cred_t *cr,
368da6c28aaSamw     caller_context_t *ct,
369da6c28aaSamw     int flags)
370da6c28aaSamw {
371da6c28aaSamw 	smb_node_t *dnode;
372da6c28aaSamw 	int error;
373da6c28aaSamw 
374da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
375da6c28aaSamw 
376da6c28aaSamw 	ASSERT(dnode);
377da6c28aaSamw 
378da6c28aaSamw 	error = vnext_link(arg, svp, tnm, cr, ct, flags);
379da6c28aaSamw 
380*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
381ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm);
382da6c28aaSamw 
383da6c28aaSamw 	return (error);
384da6c28aaSamw }
385da6c28aaSamw 
3868c10a865Sas200622 static int
smb_fem_fcn_symlink(femarg_t * arg,char * linkname,vattr_t * vap,char * target,cred_t * cr,caller_context_t * ct,int flags)387da6c28aaSamw smb_fem_fcn_symlink(
388da6c28aaSamw     femarg_t *arg,
389da6c28aaSamw     char *linkname,
390da6c28aaSamw     vattr_t *vap,
391da6c28aaSamw     char *target,
392da6c28aaSamw     cred_t *cr,
393da6c28aaSamw     caller_context_t *ct,
394da6c28aaSamw     int flags)
395da6c28aaSamw {
396da6c28aaSamw 	smb_node_t *dnode;
397da6c28aaSamw 	int error;
398da6c28aaSamw 
399da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
400da6c28aaSamw 
401da6c28aaSamw 	ASSERT(dnode);
402da6c28aaSamw 
403da6c28aaSamw 	error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags);
404da6c28aaSamw 
405*b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
406ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname);
407da6c28aaSamw 
408da6c28aaSamw 	return (error);
409da6c28aaSamw }
4108c10a865Sas200622 
4118c10a865Sas200622 /*
4128c10a865Sas200622  * FEM oplock monitors
4138c10a865Sas200622  *
4148c10a865Sas200622  * The monitors below are not intended to intercept CIFS calls.
4158c10a865Sas200622  * CIFS higher-level routines will break oplocks as needed prior
4168c10a865Sas200622  * to getting to the VFS layer.
4178c10a865Sas200622  */
4188c10a865Sas200622 static int
smb_fem_oplock_open(femarg_t * arg,int mode,cred_t * cr,caller_context_t * ct)4198c10a865Sas200622 smb_fem_oplock_open(
4208c10a865Sas200622     femarg_t		*arg,
4218c10a865Sas200622     int			mode,
4228c10a865Sas200622     cred_t		*cr,
4232c2961f8Sjose borrego     caller_context_t	*ct)
4248c10a865Sas200622 {
425cb174861Sjoyce mcintosh 	uint32_t	flags;
426*b819cea2SGordon Ross 	int		rc = 0;
4278c10a865Sas200622 
428*b819cea2SGordon Ross 	if (ct != &smb_ct) {
429cb174861Sjoyce mcintosh 		if (mode & (FWRITE|FTRUNC))
430cb174861Sjoyce mcintosh 			flags = SMB_OPLOCK_BREAK_TO_NONE;
431cb174861Sjoyce mcintosh 		else
432cb174861Sjoyce mcintosh 			flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
433cb174861Sjoyce mcintosh 		rc = smb_fem_oplock_break(arg, ct, flags);
434*b819cea2SGordon Ross 	}
4352c2961f8Sjose borrego 	if (rc == 0)
4362c2961f8Sjose borrego 		rc = vnext_open(arg, mode, cr, ct);
437*b819cea2SGordon Ross 
4382c2961f8Sjose borrego 	return (rc);
4398c10a865Sas200622 }
4408c10a865Sas200622 
4418c10a865Sas200622 /*
4428c10a865Sas200622  * Should normally be hit only via NFSv2/v3.  All other accesses
4438c10a865Sas200622  * (CIFS/NFS/local) should call VOP_OPEN first.
4448c10a865Sas200622  */
4458c10a865Sas200622 
4468c10a865Sas200622 static int
smb_fem_oplock_read(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4478c10a865Sas200622 smb_fem_oplock_read(
4488c10a865Sas200622     femarg_t		*arg,
4498c10a865Sas200622     uio_t		*uiop,
4508c10a865Sas200622     int			ioflag,
4518c10a865Sas200622     cred_t		*cr,
4522c2961f8Sjose borrego     caller_context_t	*ct)
4538c10a865Sas200622 {
454*b819cea2SGordon Ross 	int	rc = 0;
4558c10a865Sas200622 
456*b819cea2SGordon Ross 	if (ct != &smb_ct) {
457*b819cea2SGordon Ross 		rc = smb_fem_oplock_break(arg, ct,
458*b819cea2SGordon Ross 		    SMB_OPLOCK_BREAK_TO_LEVEL_II);
459*b819cea2SGordon Ross 	}
4602c2961f8Sjose borrego 	if (rc == 0)
4612c2961f8Sjose borrego 		rc = vnext_read(arg, uiop, ioflag, cr, ct);
462*b819cea2SGordon Ross 
4632c2961f8Sjose borrego 	return (rc);
4648c10a865Sas200622 }
4658c10a865Sas200622 
4668c10a865Sas200622 /*
4678c10a865Sas200622  * Should normally be hit only via NFSv2/v3.  All other accesses
4688c10a865Sas200622  * (CIFS/NFS/local) should call VOP_OPEN first.
4698c10a865Sas200622  */
4708c10a865Sas200622 
4718c10a865Sas200622 static int
smb_fem_oplock_write(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4728c10a865Sas200622 smb_fem_oplock_write(
4738c10a865Sas200622     femarg_t		*arg,
4748c10a865Sas200622     uio_t		*uiop,
4758c10a865Sas200622     int			ioflag,
4768c10a865Sas200622     cred_t		*cr,
4772c2961f8Sjose borrego     caller_context_t	*ct)
4788c10a865Sas200622 {
479*b819cea2SGordon Ross 	int	rc = 0;
4808c10a865Sas200622 
481*b819cea2SGordon Ross 	if (ct != &smb_ct)
482cb174861Sjoyce mcintosh 		rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
4832c2961f8Sjose borrego 	if (rc == 0)
4842c2961f8Sjose borrego 		rc = vnext_write(arg, uiop, ioflag, cr, ct);
485*b819cea2SGordon Ross 
4862c2961f8Sjose borrego 	return (rc);
4878c10a865Sas200622 }
4888c10a865Sas200622 
4898c10a865Sas200622 static int
smb_fem_oplock_setattr(femarg_t * arg,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)4908c10a865Sas200622 smb_fem_oplock_setattr(
4918c10a865Sas200622     femarg_t		*arg,
4928c10a865Sas200622     vattr_t		*vap,
4938c10a865Sas200622     int			flags,
4948c10a865Sas200622     cred_t		*cr,
4958c10a865Sas200622     caller_context_t	*ct)
4968c10a865Sas200622 {
497cb174861Sjoyce mcintosh 	int	rc = 0;
4988c10a865Sas200622 
499*b819cea2SGordon Ross 	if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0)
500cb174861Sjoyce mcintosh 		rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
5012c2961f8Sjose borrego 	if (rc == 0)
5022c2961f8Sjose borrego 		rc = vnext_setattr(arg, vap, flags, cr, ct);
5032c2961f8Sjose borrego 	return (rc);
5048c10a865Sas200622 }
5058c10a865Sas200622 
5068c10a865Sas200622 static int
smb_fem_oplock_rwlock(femarg_t * arg,int write_lock,caller_context_t * ct)5078c10a865Sas200622 smb_fem_oplock_rwlock(
5088c10a865Sas200622     femarg_t		*arg,
5098c10a865Sas200622     int			write_lock,
5108c10a865Sas200622     caller_context_t	*ct)
5118c10a865Sas200622 {
512cb174861Sjoyce mcintosh 	uint32_t	flags;
513*b819cea2SGordon Ross 	int		rc = 0;
5148c10a865Sas200622 
515*b819cea2SGordon Ross 	if (ct != &smb_ct) {
516cb174861Sjoyce mcintosh 		if (write_lock)
517cb174861Sjoyce mcintosh 			flags = SMB_OPLOCK_BREAK_TO_NONE;
518cb174861Sjoyce mcintosh 		else
519cb174861Sjoyce mcintosh 			flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
520cb174861Sjoyce mcintosh 		rc = smb_fem_oplock_break(arg, ct, flags);
521*b819cea2SGordon Ross 	}
522cb174861Sjoyce mcintosh 	if (rc == 0)
523cb174861Sjoyce mcintosh 		rc = vnext_rwlock(arg, write_lock, ct);
524cb174861Sjoyce mcintosh 
5252c2961f8Sjose borrego 	return (rc);
5262c2961f8Sjose borrego }
5278c10a865Sas200622 
5288c10a865Sas200622 static int
smb_fem_oplock_space(femarg_t * arg,int cmd,flock64_t * bfp,int flag,offset_t offset,cred_t * cr,caller_context_t * ct)5298c10a865Sas200622 smb_fem_oplock_space(
5308c10a865Sas200622     femarg_t		*arg,
5318c10a865Sas200622     int			cmd,
5328c10a865Sas200622     flock64_t		*bfp,
5338c10a865Sas200622     int			flag,
5348c10a865Sas200622     offset_t		offset,
5358c10a865Sas200622     cred_t		*cr,
5368c10a865Sas200622     caller_context_t	*ct)
5378c10a865Sas200622 {
538*b819cea2SGordon Ross 	int	rc = 0;
5398c10a865Sas200622 
540*b819cea2SGordon Ross 	if (ct != &smb_ct)
541cb174861Sjoyce mcintosh 		rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
5422c2961f8Sjose borrego 	if (rc == 0)
5432c2961f8Sjose borrego 		rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct);
5442c2961f8Sjose borrego 	return (rc);
5458c10a865Sas200622 }
5468c10a865Sas200622 
5478c10a865Sas200622 /*
5488c10a865Sas200622  * smb_fem_oplock_vnevent()
5498c10a865Sas200622  *
5508c10a865Sas200622  * To intercept NFS and local renames and removes in order to break any
5518c10a865Sas200622  * existing oplock prior to the operation.
5528c10a865Sas200622  *
5538c10a865Sas200622  * Note: Currently, this monitor is traversed only when an FS is mounted
5548c10a865Sas200622  * non-nbmand.  (When the FS is mounted nbmand, share reservation checking
5558c10a865Sas200622  * will detect a share violation and return an error prior to the VOP layer
5568c10a865Sas200622  * being reached.)  Thus, for nbmand NFS and local renames and removes,
5578c10a865Sas200622  * an existing oplock is never broken prior to share checking (contrary to
5588c10a865Sas200622  * how it is with intra-CIFS remove and rename requests).
5598c10a865Sas200622  */
5608c10a865Sas200622 
5618c10a865Sas200622 static int
smb_fem_oplock_vnevent(femarg_t * arg,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)5628c10a865Sas200622 smb_fem_oplock_vnevent(
5638c10a865Sas200622     femarg_t		*arg,
5648c10a865Sas200622     vnevent_t		vnevent,
5658c10a865Sas200622     vnode_t		*dvp,
5668c10a865Sas200622     char		*name,
5678c10a865Sas200622     caller_context_t	*ct)
5688c10a865Sas200622 {
569cb174861Sjoyce mcintosh 	uint32_t	flags;
570*b819cea2SGordon Ross 	int		rc = 0;
5712c2961f8Sjose borrego 
572*b819cea2SGordon Ross 	if (ct != &smb_ct) {
5738c10a865Sas200622 		switch (vnevent) {
5748c10a865Sas200622 		case VE_REMOVE:
5758c10a865Sas200622 		case VE_RENAME_DEST:
576*b819cea2SGordon Ross 			flags = SMB_OPLOCK_BREAK_TO_NONE |
577*b819cea2SGordon Ross 			    SMB_OPLOCK_BREAK_BATCH;
578cb174861Sjoyce mcintosh 			rc = smb_fem_oplock_break(arg, ct, flags);
5798c10a865Sas200622 			break;
580cb174861Sjoyce mcintosh 		case VE_RENAME_SRC:
581*b819cea2SGordon Ross 			flags = SMB_OPLOCK_BREAK_TO_LEVEL_II |
582*b819cea2SGordon Ross 			    SMB_OPLOCK_BREAK_BATCH;
583cb174861Sjoyce mcintosh 			rc = smb_fem_oplock_break(arg, ct, flags);
584cb174861Sjoyce mcintosh 			break;
5858c10a865Sas200622 		default:
586cb174861Sjoyce mcintosh 			rc = 0;
5878c10a865Sas200622 			break;
5888c10a865Sas200622 		}
589*b819cea2SGordon Ross 	}
590*b819cea2SGordon Ross 	if (rc == 0)
591*b819cea2SGordon Ross 		rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
592cb174861Sjoyce mcintosh 
593cb174861Sjoyce mcintosh 	return (rc);
5948c10a865Sas200622 }
5952c2961f8Sjose borrego 
5962c2961f8Sjose borrego static int
smb_fem_oplock_break(femarg_t * arg,caller_context_t * ct,uint32_t flags)597cb174861Sjoyce mcintosh smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct, uint32_t flags)
5982c2961f8Sjose borrego {
5992c2961f8Sjose borrego 	smb_node_t	*node;
6002c2961f8Sjose borrego 	int		rc;
6012c2961f8Sjose borrego 
6022c2961f8Sjose borrego 	node = (smb_node_t *)((arg)->fa_fnode->fn_available);
6032c2961f8Sjose borrego 	SMB_NODE_VALID(node);
6042c2961f8Sjose borrego 
605*b819cea2SGordon Ross 	ASSERT(ct != &smb_ct);
6062c2961f8Sjose borrego 
607cb174861Sjoyce mcintosh 	if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
608cb174861Sjoyce mcintosh 		flags |= SMB_OPLOCK_BREAK_NOWAIT;
609cb174861Sjoyce mcintosh 		rc = smb_oplock_break(NULL, node, flags);
610cb174861Sjoyce mcintosh 		if (rc == EAGAIN)
6112c2961f8Sjose borrego 			ct->cc_flags |= CC_WOULDBLOCK;
6122c2961f8Sjose borrego 	} else {
613cb174861Sjoyce mcintosh 		rc = smb_oplock_break(NULL, node, flags);
6142c2961f8Sjose borrego 	}
615cb174861Sjoyce mcintosh 
6162c2961f8Sjose borrego 	return (rc);
6172c2961f8Sjose borrego }
618