xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_fem.c (revision f8e30ca2)
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.
234b69ad09SGordon Ross  * Copyright 2020 Tintri by DDN, Inc.  All rights reserved.
2454207fd2SJerry Jelinek  * Copyright 2015 Joyent, Inc.
25*f8e30ca2SGordon Ross  * Copyright 2022 RackTop Systems, Inc.
26da6c28aaSamw  */
27da6c28aaSamw 
28bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h>
298c10a865Sas #include <smbsrv/smb_fsops.h>
30da6c28aaSamw #include <sys/sdt.h>
318c10a865Sas #include <sys/fcntl.h>
32da6c28aaSamw #include <sys/vfs.h>
33da6c28aaSamw #include <sys/vfs_opreg.h>
34da6c28aaSamw #include <sys/vnode.h>
35da6c28aaSamw #include <sys/fem.h>
36da6c28aaSamw 
378c10a865Sas extern caller_context_t	smb_ct;
388c10a865Sas 
398c10a865Sas static boolean_t	smb_fem_initialized = B_FALSE;
408c10a865Sas static fem_t		*smb_fcn_ops = NULL;
418c10a865Sas static fem_t		*smb_oplock_ops = NULL;
428c10a865Sas 
438c10a865Sas /*
448c10a865Sas  * Declarations for FCN (file change notification) FEM monitors
458c10a865Sas  */
468c10a865Sas 
478c10a865Sas static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int,
48da6c28aaSamw     vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *);
498c10a865Sas static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *,
50da6c28aaSamw     caller_context_t *, int);
518c10a865Sas static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *,
52da6c28aaSamw     cred_t *, caller_context_t *, int);
538c10a865Sas static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **,
54da6c28aaSamw     cred_t *, caller_context_t *, int, vsecattr_t *);
558c10a865Sas static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *,
56da6c28aaSamw     caller_context_t *, int);
578c10a865Sas static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *,
58da6c28aaSamw     caller_context_t *, int);
598c10a865Sas static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *,
60da6c28aaSamw     char *, cred_t *, caller_context_t *, int);
61da6c28aaSamw 
62da6c28aaSamw static const fs_operation_def_t smb_fcn_tmpl[] = {
63da6c28aaSamw 	VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create },
64da6c28aaSamw 	VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove},
65da6c28aaSamw 	VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename},
66da6c28aaSamw 	VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir},
67da6c28aaSamw 	VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir},
68da6c28aaSamw 	VOPNAME_LINK, {.femop_link = smb_fem_fcn_link},
69da6c28aaSamw 	VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink},
70da6c28aaSamw 	NULL, NULL
71da6c28aaSamw };
72da6c28aaSamw 
738c10a865Sas /*
748c10a865Sas  * Declarations for oplock FEM monitors
758c10a865Sas  */
768c10a865Sas 
778c10a865Sas static int smb_fem_oplock_open(femarg_t *, int, cred_t *,
788c10a865Sas     struct caller_context *);
798c10a865Sas static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *,
808c10a865Sas     struct caller_context *);
818c10a865Sas static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *,
828c10a865Sas     struct caller_context *);
838c10a865Sas static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *,
848c10a865Sas     caller_context_t *);
858c10a865Sas static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int,
868c10a865Sas     offset_t, cred_t *, caller_context_t *);
878c10a865Sas static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *,
888c10a865Sas     caller_context_t *);
898c10a865Sas 
908c10a865Sas static const fs_operation_def_t smb_oplock_tmpl[] = {
918c10a865Sas 	VOPNAME_OPEN,	{ .femop_open = smb_fem_oplock_open },
928c10a865Sas 	VOPNAME_READ,	{ .femop_read = smb_fem_oplock_read },
938c10a865Sas 	VOPNAME_WRITE,	{ .femop_write = smb_fem_oplock_write },
948c10a865Sas 	VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr },
958c10a865Sas 	VOPNAME_SPACE,	{ .femop_space = smb_fem_oplock_space },
968c10a865Sas 	VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent },
978c10a865Sas 	NULL, NULL
988c10a865Sas };
998c10a865Sas 
10094047d49SGordon Ross static int smb_fem_oplock_wait(smb_node_t *, caller_context_t *);
1012c2961f8Sjose borrego 
102faa1795aSjb /*
103faa1795aSjb  * smb_fem_init
104faa1795aSjb  *
105faa1795aSjb  * This function is not multi-thread safe. The caller must make sure only one
106faa1795aSjb  * thread makes the call.
107faa1795aSjb  */
108da6c28aaSamw int
smb_fem_init(void)109faa1795aSjb smb_fem_init(void)
110da6c28aaSamw {
111faa1795aSjb 	int	rc = 0;
112faa1795aSjb 
113faa1795aSjb 	if (smb_fem_initialized)
114faa1795aSjb 		return (0);
115faa1795aSjb 
116faa1795aSjb 	rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops);
117faa1795aSjb 	if (rc)
118faa1795aSjb 		return (rc);
119faa1795aSjb 
1208c10a865Sas 	rc = fem_create("smb_oplock_ops", smb_oplock_tmpl,
1218c10a865Sas 	    &smb_oplock_ops);
1228c10a865Sas 
1238c10a865Sas 	if (rc) {
1248c10a865Sas 		fem_free(smb_fcn_ops);
1258c10a865Sas 		smb_fcn_ops = NULL;
1268c10a865Sas 		return (rc);
1278c10a865Sas 	}
1288c10a865Sas 
129faa1795aSjb 	smb_fem_initialized = B_TRUE;
130faa1795aSjb 
131faa1795aSjb 	return (0);
132da6c28aaSamw }
133da6c28aaSamw 
134faa1795aSjb /*
135faa1795aSjb  * smb_fem_fini
136faa1795aSjb  *
137faa1795aSjb  * This function is not multi-thread safe. The caller must make sure only one
138faa1795aSjb  * thread makes the call.
139faa1795aSjb  */
140da6c28aaSamw void
smb_fem_fini(void)141faa1795aSjb smb_fem_fini(void)
142da6c28aaSamw {
143faa1795aSjb 	if (!smb_fem_initialized)
144faa1795aSjb 		return;
145faa1795aSjb 
1468622ec45SGordon Ross 	if (smb_fcn_ops != NULL) {
1478622ec45SGordon Ross 		fem_free(smb_fcn_ops);
1488622ec45SGordon Ross 		smb_fcn_ops = NULL;
1498622ec45SGordon Ross 	}
1508622ec45SGordon Ross 	if (smb_oplock_ops != NULL) {
1518622ec45SGordon Ross 		fem_free(smb_oplock_ops);
1528622ec45SGordon Ross 		smb_oplock_ops = NULL;
1538622ec45SGordon Ross 	}
154faa1795aSjb 	smb_fem_initialized = B_FALSE;
155da6c28aaSamw }
156da6c28aaSamw 
157bfe5e737SGordon Ross /*
158bfe5e737SGordon Ross  * Install our fem hooks for change notify.
159bfe5e737SGordon Ross  * Not using hold/rele function here because we
160bfe5e737SGordon Ross  * remove the fem hooks before node destroy.
161bfe5e737SGordon Ross  */
1628622ec45SGordon Ross int
smb_fem_fcn_install(smb_node_t * node)163da6c28aaSamw smb_fem_fcn_install(smb_node_t *node)
164da6c28aaSamw {
1658622ec45SGordon Ross 	int rc;
1668622ec45SGordon Ross 
1678622ec45SGordon Ross 	if (smb_fcn_ops == NULL)
1688622ec45SGordon Ross 		return (ENOSYS);
1698622ec45SGordon Ross 	rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ,
170bfe5e737SGordon Ross 	    NULL, NULL);
1718622ec45SGordon Ross 	return (rc);
172da6c28aaSamw }
173da6c28aaSamw 
1744b69ad09SGordon Ross int
smb_fem_fcn_uninstall(smb_node_t * node)175da6c28aaSamw smb_fem_fcn_uninstall(smb_node_t *node)
176da6c28aaSamw {
1774b69ad09SGordon Ross 	int rc;
1784b69ad09SGordon Ross 
1798622ec45SGordon Ross 	if (smb_fcn_ops == NULL)
1804b69ad09SGordon Ross 		return (ENOSYS);
1814b69ad09SGordon Ross 	rc = fem_uninstall(node->vp, smb_fcn_ops, (void *)node);
1824b69ad09SGordon Ross 	return (rc);
183da6c28aaSamw }
184da6c28aaSamw 
1858c10a865Sas int
smb_fem_oplock_install(smb_node_t * node)1868c10a865Sas smb_fem_oplock_install(smb_node_t *node)
1878c10a865Sas {
1888622ec45SGordon Ross 	int rc;
1898622ec45SGordon Ross 
1908622ec45SGordon Ross 	if (smb_oplock_ops == NULL)
1918622ec45SGordon Ross 		return (ENOSYS);
1928622ec45SGordon Ross 	rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ,
1938622ec45SGordon Ross 	    (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
1948622ec45SGordon Ross 	return (rc);
1958c10a865Sas }
1968c10a865Sas 
1978622ec45SGordon Ross void
smb_fem_oplock_uninstall(smb_node_t * node)1988c10a865Sas smb_fem_oplock_uninstall(smb_node_t *node)
1998c10a865Sas {
2008622ec45SGordon Ross 	if (smb_oplock_ops == NULL)
2018622ec45SGordon Ross 		return;
2028622ec45SGordon Ross 	VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node));
2038c10a865Sas }
2048c10a865Sas 
2058c10a865Sas /*
2068c10a865Sas  * FEM FCN monitors
2078c10a865Sas  *
2088c10a865Sas  * The FCN monitors intercept the respective VOP_* call regardless
2098c10a865Sas  * of whether the call originates from CIFS, NFS, or a local process.
210*f8e30ca2SGordon Ross  *
211*f8e30ca2SGordon Ross  * Here we're only interested in operations that change the list of
212*f8e30ca2SGordon Ross  * names contained in the directory.  SMB clients can also ask to be
213*f8e30ca2SGordon Ross  * notified about events where a file in this directory has had its
214*f8e30ca2SGordon Ross  * meta-data changed (size, times, etc) but that's outside of the
215*f8e30ca2SGordon Ross  * design intent for these FEM hooks.  Those meta-data events DO
216*f8e30ca2SGordon Ross  * happen when caused by SMB clients (via smb_node_notify_modified)
217*f8e30ca2SGordon Ross  * but not by other FS activity because we don't have a good way to
218*f8e30ca2SGordon Ross  * place all the FEM hooks that would be required for that, and if
219*f8e30ca2SGordon Ross  * we did, the performance cost could be severe.
2208c10a865Sas  */
2218c10a865Sas 
222da6c28aaSamw /*
223da6c28aaSamw  * smb_fem_fcn_create()
224da6c28aaSamw  *
225da6c28aaSamw  * This monitor will catch only changes to VREG files and not to extended
226da6c28aaSamw  * attribute files.  This is fine because, for CIFS files, stream creates
227da6c28aaSamw  * should not trigger any file change notification on the VDIR directory
228da6c28aaSamw  * being monitored.  Creates of any other kind of extended attribute in
229da6c28aaSamw  * the directory will also not trigger any file change notification on the
230da6c28aaSamw  * VDIR directory being monitored.
231da6c28aaSamw  */
232da6c28aaSamw 
2338c10a865Sas 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)234da6c28aaSamw smb_fem_fcn_create(
235da6c28aaSamw     femarg_t *arg,
236da6c28aaSamw     char *name,
237da6c28aaSamw     vattr_t *vap,
238da6c28aaSamw     vcexcl_t excl,
239da6c28aaSamw     int mode,
240da6c28aaSamw     vnode_t **vpp,
241da6c28aaSamw     cred_t *cr,
242da6c28aaSamw     int flag,
243da6c28aaSamw     caller_context_t *ct,
244da6c28aaSamw     vsecattr_t *vsecp)
245da6c28aaSamw {
246da6c28aaSamw 	smb_node_t *dnode;
247da6c28aaSamw 	int error;
248da6c28aaSamw 
249da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
250da6c28aaSamw 
251da6c28aaSamw 	ASSERT(dnode);
252da6c28aaSamw 
253da6c28aaSamw 	error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag,
254da6c28aaSamw 	    ct, vsecp);
255da6c28aaSamw 
256b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
257ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
258da6c28aaSamw 
259da6c28aaSamw 	return (error);
260da6c28aaSamw }
261da6c28aaSamw 
262da6c28aaSamw /*
263da6c28aaSamw  * smb_fem_fcn_remove()
264da6c28aaSamw  *
265da6c28aaSamw  * This monitor will catch only changes to VREG files and to not extended
266da6c28aaSamw  * attribute files.  This is fine because, for CIFS files, stream deletes
267da6c28aaSamw  * should not trigger any file change notification on the VDIR directory
268da6c28aaSamw  * being monitored.  Deletes of any other kind of extended attribute in
269da6c28aaSamw  * the directory will also not trigger any file change notification on the
270da6c28aaSamw  * VDIR directory being monitored.
271da6c28aaSamw  */
272da6c28aaSamw 
2738c10a865Sas static int
smb_fem_fcn_remove(femarg_t * arg,char * name,cred_t * cr,caller_context_t * ct,int flags)274da6c28aaSamw smb_fem_fcn_remove(
275da6c28aaSamw     femarg_t *arg,
276da6c28aaSamw     char *name,
277da6c28aaSamw     cred_t *cr,
278da6c28aaSamw     caller_context_t *ct,
279da6c28aaSamw     int flags)
280da6c28aaSamw {
281da6c28aaSamw 	smb_node_t *dnode;
282da6c28aaSamw 	int error;
283da6c28aaSamw 
284da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
285da6c28aaSamw 
286da6c28aaSamw 	ASSERT(dnode);
287da6c28aaSamw 
288da6c28aaSamw 	error = vnext_remove(arg, name, cr, ct, flags);
289da6c28aaSamw 
290b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
291ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
292da6c28aaSamw 
293da6c28aaSamw 	return (error);
294da6c28aaSamw }
295da6c28aaSamw 
2968c10a865Sas 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)297da6c28aaSamw smb_fem_fcn_rename(
298da6c28aaSamw     femarg_t *arg,
299da6c28aaSamw     char *snm,
300da6c28aaSamw     vnode_t *tdvp,
301da6c28aaSamw     char *tnm,
302da6c28aaSamw     cred_t *cr,
303da6c28aaSamw     caller_context_t *ct,
304da6c28aaSamw     int flags)
305da6c28aaSamw {
306da6c28aaSamw 	smb_node_t *dnode;
307da6c28aaSamw 	int error;
308da6c28aaSamw 
309da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
310da6c28aaSamw 
311da6c28aaSamw 	ASSERT(dnode);
312da6c28aaSamw 
313da6c28aaSamw 	error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags);
314da6c28aaSamw 
315b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct) {
316b819cea2SGordon Ross 		/*
317b819cea2SGordon Ross 		 * Note that renames in the same directory are normally
318b819cea2SGordon Ross 		 * delivered in {old,new} pairs, and clients expect them
319b819cea2SGordon Ross 		 * in that order, if both events are delivered.
320b819cea2SGordon Ross 		 */
321b819cea2SGordon Ross 		smb_node_notify_change(dnode,
322b819cea2SGordon Ross 		    FILE_ACTION_RENAMED_OLD_NAME, snm);
323b819cea2SGordon Ross 		smb_node_notify_change(dnode,
324b819cea2SGordon Ross 		    FILE_ACTION_RENAMED_NEW_NAME, tnm);
325b819cea2SGordon Ross 	}
326ccc71be5SGordon Ross 
327b819cea2SGordon Ross 	return (error);
328da6c28aaSamw }
329da6c28aaSamw 
3308c10a865Sas 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)331da6c28aaSamw smb_fem_fcn_mkdir(
332da6c28aaSamw     femarg_t *arg,
333da6c28aaSamw     char *name,
334da6c28aaSamw     vattr_t *vap,
335da6c28aaSamw     vnode_t **vpp,
336da6c28aaSamw     cred_t *cr,
337da6c28aaSamw     caller_context_t *ct,
338da6c28aaSamw     int flags,
339da6c28aaSamw     vsecattr_t *vsecp)
340da6c28aaSamw {
341da6c28aaSamw 	smb_node_t *dnode;
342da6c28aaSamw 	int error;
343da6c28aaSamw 
344da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
345da6c28aaSamw 
346da6c28aaSamw 	ASSERT(dnode);
347da6c28aaSamw 
348da6c28aaSamw 	error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp);
349da6c28aaSamw 
350b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
351ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
352da6c28aaSamw 
353da6c28aaSamw 	return (error);
354da6c28aaSamw }
355da6c28aaSamw 
3568c10a865Sas static int
smb_fem_fcn_rmdir(femarg_t * arg,char * name,vnode_t * cdir,cred_t * cr,caller_context_t * ct,int flags)357da6c28aaSamw smb_fem_fcn_rmdir(
358da6c28aaSamw     femarg_t *arg,
359da6c28aaSamw     char *name,
360da6c28aaSamw     vnode_t *cdir,
361da6c28aaSamw     cred_t *cr,
362da6c28aaSamw     caller_context_t *ct,
363da6c28aaSamw     int flags)
364da6c28aaSamw {
365da6c28aaSamw 	smb_node_t *dnode;
366da6c28aaSamw 	int error;
367da6c28aaSamw 
368da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
369da6c28aaSamw 
370da6c28aaSamw 	ASSERT(dnode);
371da6c28aaSamw 
372da6c28aaSamw 	error = vnext_rmdir(arg, name, cdir, cr, ct, flags);
373da6c28aaSamw 
374b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
375ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
376da6c28aaSamw 
377da6c28aaSamw 	return (error);
378da6c28aaSamw }
379da6c28aaSamw 
3808c10a865Sas static int
smb_fem_fcn_link(femarg_t * arg,vnode_t * svp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)381da6c28aaSamw smb_fem_fcn_link(
382da6c28aaSamw     femarg_t *arg,
383da6c28aaSamw     vnode_t *svp,
384da6c28aaSamw     char *tnm,
385da6c28aaSamw     cred_t *cr,
386da6c28aaSamw     caller_context_t *ct,
387da6c28aaSamw     int flags)
388da6c28aaSamw {
389da6c28aaSamw 	smb_node_t *dnode;
390da6c28aaSamw 	int error;
391da6c28aaSamw 
392da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
393da6c28aaSamw 
394da6c28aaSamw 	ASSERT(dnode);
395da6c28aaSamw 
396da6c28aaSamw 	error = vnext_link(arg, svp, tnm, cr, ct, flags);
397da6c28aaSamw 
398b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
399ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm);
400da6c28aaSamw 
401da6c28aaSamw 	return (error);
402da6c28aaSamw }
403da6c28aaSamw 
4048c10a865Sas 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)405da6c28aaSamw smb_fem_fcn_symlink(
406da6c28aaSamw     femarg_t *arg,
407da6c28aaSamw     char *linkname,
408da6c28aaSamw     vattr_t *vap,
409da6c28aaSamw     char *target,
410da6c28aaSamw     cred_t *cr,
411da6c28aaSamw     caller_context_t *ct,
412da6c28aaSamw     int flags)
413da6c28aaSamw {
414da6c28aaSamw 	smb_node_t *dnode;
415da6c28aaSamw 	int error;
416da6c28aaSamw 
417da6c28aaSamw 	dnode = (smb_node_t *)arg->fa_fnode->fn_available;
418da6c28aaSamw 
419da6c28aaSamw 	ASSERT(dnode);
420da6c28aaSamw 
421da6c28aaSamw 	error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags);
422da6c28aaSamw 
423b819cea2SGordon Ross 	if (error == 0 && ct != &smb_ct)
424ccc71be5SGordon Ross 		smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname);
425da6c28aaSamw 
426da6c28aaSamw 	return (error);
427da6c28aaSamw }
4288c10a865Sas 
4298c10a865Sas /*
4308c10a865Sas  * FEM oplock monitors
4318c10a865Sas  *
4328c10a865Sas  * The monitors below are not intended to intercept CIFS calls.
4338c10a865Sas  * CIFS higher-level routines will break oplocks as needed prior
4348c10a865Sas  * to getting to the VFS layer.
4358c10a865Sas  */
4368c10a865Sas static int
smb_fem_oplock_open(femarg_t * arg,int mode,cred_t * cr,caller_context_t * ct)4378c10a865Sas smb_fem_oplock_open(
4382c2961f8Sjose borrego     femarg_t		*arg,
4392c2961f8Sjose borrego     int			mode,
4402c2961f8Sjose borrego     cred_t		*cr,
4412c2961f8Sjose borrego     caller_context_t	*ct)
4428c10a865Sas {
44394047d49SGordon Ross 	smb_node_t	*node;
44494047d49SGordon Ross 	uint32_t	status;
445b819cea2SGordon Ross 	int		rc = 0;
4468c10a865Sas 
447b819cea2SGordon Ross 	if (ct != &smb_ct) {
44894047d49SGordon Ross 		uint32_t req_acc = FILE_READ_DATA;
44994047d49SGordon Ross 		uint32_t cr_disp = FILE_OPEN_IF;
45094047d49SGordon Ross 
45194047d49SGordon Ross 		node = (smb_node_t *)(arg->fa_fnode->fn_available);
45294047d49SGordon Ross 		SMB_NODE_VALID(node);
45394047d49SGordon Ross 
45494047d49SGordon Ross 		/*
45594047d49SGordon Ross 		 * Get req_acc, cr_disp just accurate enough so
45694047d49SGordon Ross 		 * the oplock break call does the right thing.
45794047d49SGordon Ross 		 */
45894047d49SGordon Ross 		if (mode & FWRITE) {
45994047d49SGordon Ross 			req_acc = FILE_READ_DATA | FILE_WRITE_DATA;
46094047d49SGordon Ross 			cr_disp = (mode & FTRUNC) ?
46194047d49SGordon Ross 			    FILE_OVERWRITE_IF : FILE_OPEN_IF;
46294047d49SGordon Ross 		} else {
46394047d49SGordon Ross 			req_acc = FILE_READ_DATA;
46494047d49SGordon Ross 			cr_disp = FILE_OPEN_IF;
46594047d49SGordon Ross 		}
46694047d49SGordon Ross 
46794047d49SGordon Ross 		status = smb_oplock_break_OPEN(node, NULL,
46894047d49SGordon Ross 		    req_acc, cr_disp);
46994047d49SGordon Ross 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
47094047d49SGordon Ross 			rc = smb_fem_oplock_wait(node, ct);
47194047d49SGordon Ross 		else if (status != 0)
47294047d49SGordon Ross 			rc = EIO;
473b819cea2SGordon Ross 	}
4742c2961f8Sjose borrego 	if (rc == 0)
4752c2961f8Sjose borrego 		rc = vnext_open(arg, mode, cr, ct);
476b819cea2SGordon Ross 
4772c2961f8Sjose borrego 	return (rc);
4788c10a865Sas }
4798c10a865Sas 
4808c10a865Sas /*
4818c10a865Sas  * Should normally be hit only via NFSv2/v3.  All other accesses
4828c10a865Sas  * (CIFS/NFS/local) should call VOP_OPEN first.
4838c10a865Sas  */
4848c10a865Sas 
4858c10a865Sas static int
smb_fem_oplock_read(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4868c10a865Sas smb_fem_oplock_read(
4872c2961f8Sjose borrego     femarg_t		*arg,
4882c2961f8Sjose borrego     uio_t		*uiop,
4892c2961f8Sjose borrego     int			ioflag,
4902c2961f8Sjose borrego     cred_t		*cr,
4912c2961f8Sjose borrego     caller_context_t	*ct)
4928c10a865Sas {
49394047d49SGordon Ross 	smb_node_t	*node;
49494047d49SGordon Ross 	uint32_t	status;
495b819cea2SGordon Ross 	int	rc = 0;
4968c10a865Sas 
497b819cea2SGordon Ross 	if (ct != &smb_ct) {
49894047d49SGordon Ross 		node = (smb_node_t *)(arg->fa_fnode->fn_available);
49994047d49SGordon Ross 		SMB_NODE_VALID(node);
50094047d49SGordon Ross 
50194047d49SGordon Ross 		status = smb_oplock_break_READ(node, NULL);
50294047d49SGordon Ross 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
50394047d49SGordon Ross 			rc = smb_fem_oplock_wait(node, ct);
50494047d49SGordon Ross 		else if (status != 0)
50594047d49SGordon Ross 			rc = EIO;
506b819cea2SGordon Ross 	}
5072c2961f8Sjose borrego 	if (rc == 0)
5082c2961f8Sjose borrego 		rc = vnext_read(arg, uiop, ioflag, cr, ct);
509b819cea2SGordon Ross 
5102c2961f8Sjose borrego 	return (rc);
5118c10a865Sas }
5128c10a865Sas 
5138c10a865Sas /*
5148c10a865Sas  * Should normally be hit only via NFSv2/v3.  All other accesses
5158c10a865Sas  * (CIFS/NFS/local) should call VOP_OPEN first.
5168c10a865Sas  */
5178c10a865Sas 
5188c10a865Sas static int
smb_fem_oplock_write(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)5198c10a865Sas smb_fem_oplock_write(
5202c2961f8Sjose borrego     femarg_t		*arg,
5212c2961f8Sjose borrego     uio_t		*uiop,
5222c2961f8Sjose borrego     int			ioflag,
5232c2961f8Sjose borrego     cred_t		*cr,
5242c2961f8Sjose borrego     caller_context_t	*ct)
5258c10a865Sas {
52694047d49SGordon Ross 	smb_node_t	*node;
52794047d49SGordon Ross 	uint32_t	status;
528b819cea2SGordon Ross 	int	rc = 0;
5298c10a865Sas 
53094047d49SGordon Ross 	if (ct != &smb_ct) {
53194047d49SGordon Ross 		node = (smb_node_t *)(arg->fa_fnode->fn_available);
53294047d49SGordon Ross 		SMB_NODE_VALID(node);
53394047d49SGordon Ross 
53494047d49SGordon Ross 		status = smb_oplock_break_WRITE(node, NULL);
53594047d49SGordon Ross 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
53694047d49SGordon Ross 			rc = smb_fem_oplock_wait(node, ct);
53794047d49SGordon Ross 		else if (status != 0)
53894047d49SGordon Ross 			rc = EIO;
53994047d49SGordon Ross 	}
5402c2961f8Sjose borrego 	if (rc == 0)
5412c2961f8Sjose borrego 		rc = vnext_write(arg, uiop, ioflag, cr, ct);
542b819cea2SGordon Ross 
5432c2961f8Sjose borrego 	return (rc);
5448c10a865Sas }
5458c10a865Sas 
5468c10a865Sas static int
smb_fem_oplock_setattr(femarg_t * arg,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)5478c10a865Sas smb_fem_oplock_setattr(
5482c2961f8Sjose borrego     femarg_t		*arg,
5492c2961f8Sjose borrego     vattr_t		*vap,
5502c2961f8Sjose borrego     int			flags,
5512c2961f8Sjose borrego     cred_t		*cr,
5522c2961f8Sjose borrego     caller_context_t	*ct)
5538c10a865Sas {
55494047d49SGordon Ross 	smb_node_t	*node;
55594047d49SGordon Ross 	uint32_t	status;
556cb174861Sjoyce mcintosh 	int	rc = 0;
5578c10a865Sas 
55894047d49SGordon Ross 	if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) {
55994047d49SGordon Ross 		node = (smb_node_t *)(arg->fa_fnode->fn_available);
56094047d49SGordon Ross 		SMB_NODE_VALID(node);
5618c10a865Sas 
56294047d49SGordon Ross 		status = smb_oplock_break_SETINFO(node, NULL,
56394047d49SGordon Ross 		    FileEndOfFileInformation);
56494047d49SGordon Ross 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
56594047d49SGordon Ross 			rc = smb_fem_oplock_wait(node, ct);
56694047d49SGordon Ross 		else if (status != 0)
56794047d49SGordon Ross 			rc = EIO;
568b819cea2SGordon Ross 	}
569cb174861Sjoyce mcintosh 	if (rc == 0)
57094047d49SGordon Ross 		rc = vnext_setattr(arg, vap, flags, cr, ct);
571cb174861Sjoyce mcintosh 	return (rc);
5728c10a865Sas }
5738c10a865Sas 
5748c10a865Sas 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)5758c10a865Sas smb_fem_oplock_space(
5762c2961f8Sjose borrego     femarg_t		*arg,
5772c2961f8Sjose borrego     int			cmd,
5782c2961f8Sjose borrego     flock64_t		*bfp,
5792c2961f8Sjose borrego     int			flag,
5802c2961f8Sjose borrego     offset_t		offset,
5812c2961f8Sjose borrego     cred_t		*cr,
5822c2961f8Sjose borrego     caller_context_t	*ct)
5838c10a865Sas {
58494047d49SGordon Ross 	smb_node_t	*node;
58594047d49SGordon Ross 	uint32_t	status;
586b819cea2SGordon Ross 	int	rc = 0;
5878c10a865Sas 
58894047d49SGordon Ross 	if (ct != &smb_ct) {
58994047d49SGordon Ross 		node = (smb_node_t *)(arg->fa_fnode->fn_available);
59094047d49SGordon Ross 		SMB_NODE_VALID(node);
59194047d49SGordon Ross 
59294047d49SGordon Ross 		status = smb_oplock_break_SETINFO(node, NULL,
59394047d49SGordon Ross 		    FileAllocationInformation);
59494047d49SGordon Ross 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
59594047d49SGordon Ross 			rc = smb_fem_oplock_wait(node, ct);
59694047d49SGordon Ross 		else if (status != 0)
59794047d49SGordon Ross 			rc = EIO;
59894047d49SGordon Ross 	}
5992c2961f8Sjose borrego 	if (rc == 0)
6002c2961f8Sjose borrego 		rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct);
6012c2961f8Sjose borrego 	return (rc);
6028c10a865Sas }
6038c10a865Sas 
6048c10a865Sas /*
6058c10a865Sas  * smb_fem_oplock_vnevent()
6068c10a865Sas  *
6078c10a865Sas  * To intercept NFS and local renames and removes in order to break any
6088c10a865Sas  * existing oplock prior to the operation.
6098c10a865Sas  *
6108c10a865Sas  * Note: Currently, this monitor is traversed only when an FS is mounted
6118c10a865Sas  * non-nbmand.  (When the FS is mounted nbmand, share reservation checking
6128c10a865Sas  * will detect a share violation and return an error prior to the VOP layer
6138c10a865Sas  * being reached.)  Thus, for nbmand NFS and local renames and removes,
6148c10a865Sas  * an existing oplock is never broken prior to share checking (contrary to
6158c10a865Sas  * how it is with intra-CIFS remove and rename requests).
6168c10a865Sas  */
6178c10a865Sas 
6188c10a865Sas static int
smb_fem_oplock_vnevent(femarg_t * arg,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)6198c10a865Sas smb_fem_oplock_vnevent(
6202c2961f8Sjose borrego     femarg_t		*arg,
6212c2961f8Sjose borrego     vnevent_t		vnevent,
6222c2961f8Sjose borrego     vnode_t		*dvp,
6232c2961f8Sjose borrego     char		*name,
6242c2961f8Sjose borrego     caller_context_t	*ct)
6258c10a865Sas {
62694047d49SGordon Ross 	smb_node_t	*node;
62794047d49SGordon Ross 	uint32_t	status;
628b819cea2SGordon Ross 	int		rc = 0;
629b819cea2SGordon Ross 
630b819cea2SGordon Ross 	if (ct != &smb_ct) {
63194047d49SGordon Ross 		node = (smb_node_t *)(arg->fa_fnode->fn_available);
63294047d49SGordon Ross 		SMB_NODE_VALID(node);
63394047d49SGordon Ross 
634b819cea2SGordon Ross 		switch (vnevent) {
635b819cea2SGordon Ross 		case VE_REMOVE:
63654207fd2SJerry Jelinek 		case VE_PRE_RENAME_DEST:
637b819cea2SGordon Ross 		case VE_RENAME_DEST:
63894047d49SGordon Ross 			status = smb_oplock_break_HANDLE(node, NULL);
639b819cea2SGordon Ross 			break;
64054207fd2SJerry Jelinek 		case VE_PRE_RENAME_SRC:
641b819cea2SGordon Ross 		case VE_RENAME_SRC:
64294047d49SGordon Ross 			status = smb_oplock_break_SETINFO(node, NULL,
64394047d49SGordon Ross 			    FileRenameInformation);
644b819cea2SGordon Ross 			break;
645b819cea2SGordon Ross 		default:
64694047d49SGordon Ross 			status = 0;
647b819cea2SGordon Ross 			break;
648b819cea2SGordon Ross 		}
64994047d49SGordon Ross 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
65094047d49SGordon Ross 			rc = smb_fem_oplock_wait(node, ct);
65194047d49SGordon Ross 		else if (status != 0)
65294047d49SGordon Ross 			rc = EIO;
6538c10a865Sas 	}
654b819cea2SGordon Ross 	if (rc == 0)
655b819cea2SGordon Ross 		rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
656cb174861Sjoyce mcintosh 
657b819cea2SGordon Ross 	return (rc);
6588c10a865Sas }
6592c2961f8Sjose borrego 
66094047d49SGordon Ross int smb_fem_oplock_timeout = 5000; /* mSec. */
66194047d49SGordon Ross 
6622c2961f8Sjose borrego static int
smb_fem_oplock_wait(smb_node_t * node,caller_context_t * ct)66394047d49SGordon Ross smb_fem_oplock_wait(smb_node_t *node, caller_context_t *ct)
6642c2961f8Sjose borrego {
66594047d49SGordon Ross 	int		rc = 0;
6662c2961f8Sjose borrego 
667b819cea2SGordon Ross 	ASSERT(ct != &smb_ct);
6682c2961f8Sjose borrego 
669cb174861Sjoyce mcintosh 	if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
67094047d49SGordon Ross 		ct->cc_flags |= CC_WOULDBLOCK;
67194047d49SGordon Ross 		rc = EAGAIN;
6722c2961f8Sjose borrego 	} else {
673525641e8SGordon Ross 		(void) smb_oplock_wait_break_fem(node,
67494047d49SGordon Ross 		    smb_fem_oplock_timeout);
67594047d49SGordon Ross 		rc = 0;
6762c2961f8Sjose borrego 	}
677cb174861Sjoyce mcintosh 
6782c2961f8Sjose borrego 	return (rc);
6792c2961f8Sjose borrego }
680