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>
298c10a865Sas200622 #include <smbsrv/smb_fsops.h>
30da6c28aaSamw #include <sys/sdt.h>
318c10a865Sas200622 #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
378c10a865Sas200622 extern caller_context_t smb_ct;
388c10a865Sas200622
398c10a865Sas200622 static boolean_t smb_fem_initialized = B_FALSE;
408c10a865Sas200622 static fem_t *smb_fcn_ops = NULL;
418c10a865Sas200622 static fem_t *smb_oplock_ops = NULL;
428c10a865Sas200622
438c10a865Sas200622 /*
448c10a865Sas200622 * Declarations for FCN (file change notification) FEM monitors
458c10a865Sas200622 */
468c10a865Sas200622
478c10a865Sas200622 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 *);
498c10a865Sas200622 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *,
50da6c28aaSamw caller_context_t *, int);
518c10a865Sas200622 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *,
52da6c28aaSamw cred_t *, caller_context_t *, int);
538c10a865Sas200622 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **,
54da6c28aaSamw cred_t *, caller_context_t *, int, vsecattr_t *);
558c10a865Sas200622 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *,
56da6c28aaSamw caller_context_t *, int);
578c10a865Sas200622 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *,
58da6c28aaSamw caller_context_t *, int);
598c10a865Sas200622 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
738c10a865Sas200622 /*
748c10a865Sas200622 * Declarations for oplock FEM monitors
758c10a865Sas200622 */
768c10a865Sas200622
778c10a865Sas200622 static int smb_fem_oplock_open(femarg_t *, int, cred_t *,
788c10a865Sas200622 struct caller_context *);
798c10a865Sas200622 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *,
808c10a865Sas200622 struct caller_context *);
818c10a865Sas200622 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *,
828c10a865Sas200622 struct caller_context *);
838c10a865Sas200622 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *,
848c10a865Sas200622 caller_context_t *);
858c10a865Sas200622 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int,
868c10a865Sas200622 offset_t, cred_t *, caller_context_t *);
878c10a865Sas200622 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *,
888c10a865Sas200622 caller_context_t *);
898c10a865Sas200622
908c10a865Sas200622 static const fs_operation_def_t smb_oplock_tmpl[] = {
918c10a865Sas200622 VOPNAME_OPEN, { .femop_open = smb_fem_oplock_open },
928c10a865Sas200622 VOPNAME_READ, { .femop_read = smb_fem_oplock_read },
938c10a865Sas200622 VOPNAME_WRITE, { .femop_write = smb_fem_oplock_write },
948c10a865Sas200622 VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr },
958c10a865Sas200622 VOPNAME_SPACE, { .femop_space = smb_fem_oplock_space },
968c10a865Sas200622 VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent },
978c10a865Sas200622 NULL, NULL
988c10a865Sas200622 };
998c10a865Sas200622
10094047d49SGordon Ross static int smb_fem_oplock_wait(smb_node_t *, caller_context_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
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
1858c10a865Sas200622 int
smb_fem_oplock_install(smb_node_t * node)1868c10a865Sas200622 smb_fem_oplock_install(smb_node_t *node)
1878c10a865Sas200622 {
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);
1958c10a865Sas200622 }
1968c10a865Sas200622
1978622ec45SGordon Ross void
smb_fem_oplock_uninstall(smb_node_t * node)1988c10a865Sas200622 smb_fem_oplock_uninstall(smb_node_t *node)
1998c10a865Sas200622 {
2008622ec45SGordon Ross if (smb_oplock_ops == NULL)
2018622ec45SGordon Ross return;
2028622ec45SGordon Ross VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node));
2038c10a865Sas200622 }
2048c10a865Sas200622
2058c10a865Sas200622 /*
2068c10a865Sas200622 * FEM FCN monitors
2078c10a865Sas200622 *
2088c10a865Sas200622 * The FCN monitors intercept the respective VOP_* call regardless
2098c10a865Sas200622 * 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.
2208c10a865Sas200622 */
2218c10a865Sas200622
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
2338c10a865Sas200622 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
2738c10a865Sas200622 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
2968c10a865Sas200622 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) {
316ccc71be5SGordon Ross /*
317ccc71be5SGordon Ross * Note that renames in the same directory are normally
318ccc71be5SGordon Ross * delivered in {old,new} pairs, and clients expect them
319ccc71be5SGordon Ross * in that order, if both events are delivered.
320ccc71be5SGordon 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
3308c10a865Sas200622 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
3568c10a865Sas200622 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
3808c10a865Sas200622 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
4048c10a865Sas200622 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 }
4288c10a865Sas200622
4298c10a865Sas200622 /*
4308c10a865Sas200622 * FEM oplock monitors
4318c10a865Sas200622 *
4328c10a865Sas200622 * The monitors below are not intended to intercept CIFS calls.
4338c10a865Sas200622 * CIFS higher-level routines will break oplocks as needed prior
4348c10a865Sas200622 * to getting to the VFS layer.
4358c10a865Sas200622 */
4368c10a865Sas200622 static int
smb_fem_oplock_open(femarg_t * arg,int mode,cred_t * cr,caller_context_t * ct)4378c10a865Sas200622 smb_fem_oplock_open(
4388c10a865Sas200622 femarg_t *arg,
4398c10a865Sas200622 int mode,
4408c10a865Sas200622 cred_t *cr,
4412c2961f8Sjose borrego caller_context_t *ct)
4428c10a865Sas200622 {
44394047d49SGordon Ross smb_node_t *node;
44494047d49SGordon Ross uint32_t status;
445b819cea2SGordon Ross int rc = 0;
4468c10a865Sas200622
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);
4788c10a865Sas200622 }
4798c10a865Sas200622
4808c10a865Sas200622 /*
4818c10a865Sas200622 * Should normally be hit only via NFSv2/v3. All other accesses
4828c10a865Sas200622 * (CIFS/NFS/local) should call VOP_OPEN first.
4838c10a865Sas200622 */
4848c10a865Sas200622
4858c10a865Sas200622 static int
smb_fem_oplock_read(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)4868c10a865Sas200622 smb_fem_oplock_read(
4878c10a865Sas200622 femarg_t *arg,
4888c10a865Sas200622 uio_t *uiop,
4898c10a865Sas200622 int ioflag,
4908c10a865Sas200622 cred_t *cr,
4912c2961f8Sjose borrego caller_context_t *ct)
4928c10a865Sas200622 {
49394047d49SGordon Ross smb_node_t *node;
49494047d49SGordon Ross uint32_t status;
495b819cea2SGordon Ross int rc = 0;
4968c10a865Sas200622
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);
5118c10a865Sas200622 }
5128c10a865Sas200622
5138c10a865Sas200622 /*
5148c10a865Sas200622 * Should normally be hit only via NFSv2/v3. All other accesses
5158c10a865Sas200622 * (CIFS/NFS/local) should call VOP_OPEN first.
5168c10a865Sas200622 */
5178c10a865Sas200622
5188c10a865Sas200622 static int
smb_fem_oplock_write(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)5198c10a865Sas200622 smb_fem_oplock_write(
5208c10a865Sas200622 femarg_t *arg,
5218c10a865Sas200622 uio_t *uiop,
5228c10a865Sas200622 int ioflag,
5238c10a865Sas200622 cred_t *cr,
5242c2961f8Sjose borrego caller_context_t *ct)
5258c10a865Sas200622 {
52694047d49SGordon Ross smb_node_t *node;
52794047d49SGordon Ross uint32_t status;
528b819cea2SGordon Ross int rc = 0;
5298c10a865Sas200622
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);
5448c10a865Sas200622 }
5458c10a865Sas200622
5468c10a865Sas200622 static int
smb_fem_oplock_setattr(femarg_t * arg,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)5478c10a865Sas200622 smb_fem_oplock_setattr(
5488c10a865Sas200622 femarg_t *arg,
5498c10a865Sas200622 vattr_t *vap,
5508c10a865Sas200622 int flags,
5518c10a865Sas200622 cred_t *cr,
5528c10a865Sas200622 caller_context_t *ct)
5538c10a865Sas200622 {
55494047d49SGordon Ross smb_node_t *node;
55594047d49SGordon Ross uint32_t status;
556cb174861Sjoyce mcintosh int rc = 0;
5578c10a865Sas200622
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);
56194047d49SGordon Ross
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;
56894047d49SGordon Ross }
5692c2961f8Sjose borrego if (rc == 0)
5702c2961f8Sjose borrego rc = vnext_setattr(arg, vap, flags, cr, ct);
5712c2961f8Sjose borrego return (rc);
5728c10a865Sas200622 }
5738c10a865Sas200622
5748c10a865Sas200622 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)5758c10a865Sas200622 smb_fem_oplock_space(
5768c10a865Sas200622 femarg_t *arg,
5778c10a865Sas200622 int cmd,
5788c10a865Sas200622 flock64_t *bfp,
5798c10a865Sas200622 int flag,
5808c10a865Sas200622 offset_t offset,
5818c10a865Sas200622 cred_t *cr,
5828c10a865Sas200622 caller_context_t *ct)
5838c10a865Sas200622 {
58494047d49SGordon Ross smb_node_t *node;
58594047d49SGordon Ross uint32_t status;
586b819cea2SGordon Ross int rc = 0;
5878c10a865Sas200622
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);
6028c10a865Sas200622 }
6038c10a865Sas200622
6048c10a865Sas200622 /*
6058c10a865Sas200622 * smb_fem_oplock_vnevent()
6068c10a865Sas200622 *
6078c10a865Sas200622 * To intercept NFS and local renames and removes in order to break any
6088c10a865Sas200622 * existing oplock prior to the operation.
6098c10a865Sas200622 *
6108c10a865Sas200622 * Note: Currently, this monitor is traversed only when an FS is mounted
6118c10a865Sas200622 * non-nbmand. (When the FS is mounted nbmand, share reservation checking
6128c10a865Sas200622 * will detect a share violation and return an error prior to the VOP layer
6138c10a865Sas200622 * being reached.) Thus, for nbmand NFS and local renames and removes,
6148c10a865Sas200622 * an existing oplock is never broken prior to share checking (contrary to
6158c10a865Sas200622 * how it is with intra-CIFS remove and rename requests).
6168c10a865Sas200622 */
6178c10a865Sas200622
6188c10a865Sas200622 static int
smb_fem_oplock_vnevent(femarg_t * arg,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)6198c10a865Sas200622 smb_fem_oplock_vnevent(
6208c10a865Sas200622 femarg_t *arg,
6218c10a865Sas200622 vnevent_t vnevent,
6228c10a865Sas200622 vnode_t *dvp,
6238c10a865Sas200622 char *name,
6248c10a865Sas200622 caller_context_t *ct)
6258c10a865Sas200622 {
62694047d49SGordon Ross smb_node_t *node;
62794047d49SGordon Ross uint32_t status;
628b819cea2SGordon Ross int rc = 0;
6292c2961f8Sjose borrego
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
6348c10a865Sas200622 switch (vnevent) {
6358c10a865Sas200622 case VE_REMOVE:
63654207fd2SJerry Jelinek case VE_PRE_RENAME_DEST:
6378c10a865Sas200622 case VE_RENAME_DEST:
63894047d49SGordon Ross status = smb_oplock_break_HANDLE(node, NULL);
6398c10a865Sas200622 break;
64054207fd2SJerry Jelinek case VE_PRE_RENAME_SRC:
641cb174861Sjoyce mcintosh case VE_RENAME_SRC:
64294047d49SGordon Ross status = smb_oplock_break_SETINFO(node, NULL,
64394047d49SGordon Ross FileRenameInformation);
644cb174861Sjoyce mcintosh break;
6458c10a865Sas200622 default:
64694047d49SGordon Ross status = 0;
6478c10a865Sas200622 break;
6488c10a865Sas200622 }
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;
653b819cea2SGordon Ross }
654b819cea2SGordon Ross if (rc == 0)
655b819cea2SGordon Ross rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
656cb174861Sjoyce mcintosh
657cb174861Sjoyce mcintosh return (rc);
6588c10a865Sas200622 }
6592c2961f8Sjose borrego
66094047d49SGordon Ross int smb_fem_oplock_timeout = 5000; /* mSec. */
6612c2961f8Sjose borrego
66294047d49SGordon Ross 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)
66494047d49SGordon Ross {
66594047d49SGordon Ross int rc = 0;
6662c2961f8Sjose borrego
667b819cea2SGordon Ross ASSERT(ct != &smb_ct);
6682c2961f8Sjose borrego
669cb174861Sjoyce mcintosh if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
6702c2961f8Sjose borrego 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