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