4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
29 #include <sys/fcntl.h>
31 #include <sys/vfs_opreg.h>
32 #include <sys/vnode.h>
35 extern caller_context_t smb_ct
;
37 static boolean_t smb_fem_initialized
= B_FALSE
;
38 static fem_t
*smb_fcn_ops
= NULL
;
39 static fem_t
*smb_oplock_ops
= NULL
;
42 * Declarations for FCN (file change notification) FEM monitors
45 static int smb_fem_fcn_create(femarg_t
*, char *, vattr_t
*, vcexcl_t
, int,
46 vnode_t
**, cred_t
*, int, caller_context_t
*, vsecattr_t
*);
47 static int smb_fem_fcn_remove(femarg_t
*, char *, cred_t
*,
48 caller_context_t
*, int);
49 static int smb_fem_fcn_rename(femarg_t
*, char *, vnode_t
*, char *,
50 cred_t
*, caller_context_t
*, int);
51 static int smb_fem_fcn_mkdir(femarg_t
*, char *, vattr_t
*, vnode_t
**,
52 cred_t
*, caller_context_t
*, int, vsecattr_t
*);
53 static int smb_fem_fcn_rmdir(femarg_t
*, char *, vnode_t
*, cred_t
*,
54 caller_context_t
*, int);
55 static int smb_fem_fcn_link(femarg_t
*, vnode_t
*, char *, cred_t
*,
56 caller_context_t
*, int);
57 static int smb_fem_fcn_symlink(femarg_t
*, char *, vattr_t
*,
58 char *, cred_t
*, caller_context_t
*, int);
60 static const fs_operation_def_t smb_fcn_tmpl
[] = {
61 VOPNAME_CREATE
, { .femop_create
= smb_fem_fcn_create
},
62 VOPNAME_REMOVE
, {.femop_remove
= smb_fem_fcn_remove
},
63 VOPNAME_RENAME
, {.femop_rename
= smb_fem_fcn_rename
},
64 VOPNAME_MKDIR
, {.femop_mkdir
= smb_fem_fcn_mkdir
},
65 VOPNAME_RMDIR
, {.femop_rmdir
= smb_fem_fcn_rmdir
},
66 VOPNAME_LINK
, {.femop_link
= smb_fem_fcn_link
},
67 VOPNAME_SYMLINK
, {.femop_symlink
= smb_fem_fcn_symlink
},
72 * Declarations for oplock FEM monitors
75 static int smb_fem_oplock_open(femarg_t
*, int, cred_t
*,
76 struct caller_context
*);
77 static int smb_fem_oplock_read(femarg_t
*, uio_t
*, int, cred_t
*,
78 struct caller_context
*);
79 static int smb_fem_oplock_write(femarg_t
*, uio_t
*, int, cred_t
*,
80 struct caller_context
*);
81 static int smb_fem_oplock_setattr(femarg_t
*, vattr_t
*, int, cred_t
*,
83 static int smb_fem_oplock_rwlock(femarg_t
*, int, caller_context_t
*);
84 static int smb_fem_oplock_space(femarg_t
*, int, flock64_t
*, int,
85 offset_t
, cred_t
*, caller_context_t
*);
86 static int smb_fem_oplock_vnevent(femarg_t
*, vnevent_t
, vnode_t
*, char *,
89 static const fs_operation_def_t smb_oplock_tmpl
[] = {
90 VOPNAME_OPEN
, { .femop_open
= smb_fem_oplock_open
},
91 VOPNAME_READ
, { .femop_read
= smb_fem_oplock_read
},
92 VOPNAME_WRITE
, { .femop_write
= smb_fem_oplock_write
},
93 VOPNAME_SETATTR
, { .femop_setattr
= smb_fem_oplock_setattr
},
94 VOPNAME_RWLOCK
, { .femop_rwlock
= smb_fem_oplock_rwlock
},
95 VOPNAME_SPACE
, { .femop_space
= smb_fem_oplock_space
},
96 VOPNAME_VNEVENT
, { .femop_vnevent
= smb_fem_oplock_vnevent
},
100 static int smb_fem_oplock_break(femarg_t
*, caller_context_t
*, uint32_t);
105 * This function is not multi-thread safe. The caller must make sure only one
106 * thread makes the call.
113 if (smb_fem_initialized
)
116 rc
= fem_create("smb_fcn_ops", smb_fcn_tmpl
, &smb_fcn_ops
);
120 rc
= fem_create("smb_oplock_ops", smb_oplock_tmpl
,
124 fem_free(smb_fcn_ops
);
129 smb_fem_initialized
= B_TRUE
;
137 * This function is not multi-thread safe. The caller must make sure only one
138 * thread makes the call.
143 if (!smb_fem_initialized
)
146 if (smb_fcn_ops
!= NULL
) {
147 fem_free(smb_fcn_ops
);
150 if (smb_oplock_ops
!= NULL
) {
151 fem_free(smb_oplock_ops
);
152 smb_oplock_ops
= NULL
;
154 smb_fem_initialized
= B_FALSE
;
158 smb_fem_fcn_install(smb_node_t
*node
)
162 if (smb_fcn_ops
== NULL
)
164 rc
= fem_install(node
->vp
, smb_fcn_ops
, (void *)node
, OPARGUNIQ
,
165 (fem_func_t
)smb_node_ref
, (fem_func_t
)smb_node_release
);
170 smb_fem_fcn_uninstall(smb_node_t
*node
)
172 if (smb_fcn_ops
== NULL
)
174 VERIFY0(fem_uninstall(node
->vp
, smb_fcn_ops
, (void *)node
));
178 smb_fem_oplock_install(smb_node_t
*node
)
182 if (smb_oplock_ops
== NULL
)
184 rc
= fem_install(node
->vp
, smb_oplock_ops
, (void *)node
, OPARGUNIQ
,
185 (fem_func_t
)smb_node_ref
, (fem_func_t
)smb_node_release
);
190 smb_fem_oplock_uninstall(smb_node_t
*node
)
192 if (smb_oplock_ops
== NULL
)
194 VERIFY0(fem_uninstall(node
->vp
, smb_oplock_ops
, (void *)node
));
200 * The FCN monitors intercept the respective VOP_* call regardless
201 * of whether the call originates from CIFS, NFS, or a local process.
205 * smb_fem_fcn_create()
207 * This monitor will catch only changes to VREG files and not to extended
208 * attribute files. This is fine because, for CIFS files, stream creates
209 * should not trigger any file change notification on the VDIR directory
210 * being monitored. Creates of any other kind of extended attribute in
211 * the directory will also not trigger any file change notification on the
212 * VDIR directory being monitored.
225 caller_context_t
*ct
,
231 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
235 error
= vnext_create(arg
, name
, vap
, excl
, mode
, vpp
, cr
, flag
,
238 if (error
== 0 && ct
!= &smb_ct
)
239 smb_node_notify_change(dnode
, FILE_ACTION_ADDED
, name
);
245 * smb_fem_fcn_remove()
247 * This monitor will catch only changes to VREG files and to not extended
248 * attribute files. This is fine because, for CIFS files, stream deletes
249 * should not trigger any file change notification on the VDIR directory
250 * being monitored. Deletes of any other kind of extended attribute in
251 * the directory will also not trigger any file change notification on the
252 * VDIR directory being monitored.
260 caller_context_t
*ct
,
266 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
270 error
= vnext_remove(arg
, name
, cr
, ct
, flags
);
272 if (error
== 0 && ct
!= &smb_ct
)
273 smb_node_notify_change(dnode
, FILE_ACTION_REMOVED
, name
);
285 caller_context_t
*ct
,
291 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
295 error
= vnext_rename(arg
, snm
, tdvp
, tnm
, cr
, ct
, flags
);
297 if (error
== 0 && ct
!= &smb_ct
) {
299 * Note that renames in the same directory are normally
300 * delivered in {old,new} pairs, and clients expect them
301 * in that order, if both events are delivered.
303 smb_node_notify_change(dnode
,
304 FILE_ACTION_RENAMED_OLD_NAME
, snm
);
305 smb_node_notify_change(dnode
,
306 FILE_ACTION_RENAMED_NEW_NAME
, tnm
);
319 caller_context_t
*ct
,
326 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
330 error
= vnext_mkdir(arg
, name
, vap
, vpp
, cr
, ct
, flags
, vsecp
);
332 if (error
== 0 && ct
!= &smb_ct
)
333 smb_node_notify_change(dnode
, FILE_ACTION_ADDED
, name
);
344 caller_context_t
*ct
,
350 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
354 error
= vnext_rmdir(arg
, name
, cdir
, cr
, ct
, flags
);
356 if (error
== 0 && ct
!= &smb_ct
)
357 smb_node_notify_change(dnode
, FILE_ACTION_REMOVED
, name
);
368 caller_context_t
*ct
,
374 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
378 error
= vnext_link(arg
, svp
, tnm
, cr
, ct
, flags
);
380 if (error
== 0 && ct
!= &smb_ct
)
381 smb_node_notify_change(dnode
, FILE_ACTION_ADDED
, tnm
);
393 caller_context_t
*ct
,
399 dnode
= (smb_node_t
*)arg
->fa_fnode
->fn_available
;
403 error
= vnext_symlink(arg
, linkname
, vap
, target
, cr
, ct
, flags
);
405 if (error
== 0 && ct
!= &smb_ct
)
406 smb_node_notify_change(dnode
, FILE_ACTION_ADDED
, linkname
);
412 * FEM oplock monitors
414 * The monitors below are not intended to intercept CIFS calls.
415 * CIFS higher-level routines will break oplocks as needed prior
416 * to getting to the VFS layer.
423 caller_context_t
*ct
)
429 if (mode
& (FWRITE
|FTRUNC
))
430 flags
= SMB_OPLOCK_BREAK_TO_NONE
;
432 flags
= SMB_OPLOCK_BREAK_TO_LEVEL_II
;
433 rc
= smb_fem_oplock_break(arg
, ct
, flags
);
436 rc
= vnext_open(arg
, mode
, cr
, ct
);
442 * Should normally be hit only via NFSv2/v3. All other accesses
443 * (CIFS/NFS/local) should call VOP_OPEN first.
452 caller_context_t
*ct
)
457 rc
= smb_fem_oplock_break(arg
, ct
,
458 SMB_OPLOCK_BREAK_TO_LEVEL_II
);
461 rc
= vnext_read(arg
, uiop
, ioflag
, cr
, ct
);
467 * Should normally be hit only via NFSv2/v3. All other accesses
468 * (CIFS/NFS/local) should call VOP_OPEN first.
472 smb_fem_oplock_write(
477 caller_context_t
*ct
)
482 rc
= smb_fem_oplock_break(arg
, ct
, SMB_OPLOCK_BREAK_TO_NONE
);
484 rc
= vnext_write(arg
, uiop
, ioflag
, cr
, ct
);
490 smb_fem_oplock_setattr(
495 caller_context_t
*ct
)
499 if (ct
!= &smb_ct
&& (vap
->va_mask
& AT_SIZE
) != 0)
500 rc
= smb_fem_oplock_break(arg
, ct
, SMB_OPLOCK_BREAK_TO_NONE
);
502 rc
= vnext_setattr(arg
, vap
, flags
, cr
, ct
);
507 smb_fem_oplock_rwlock(
510 caller_context_t
*ct
)
517 flags
= SMB_OPLOCK_BREAK_TO_NONE
;
519 flags
= SMB_OPLOCK_BREAK_TO_LEVEL_II
;
520 rc
= smb_fem_oplock_break(arg
, ct
, flags
);
523 rc
= vnext_rwlock(arg
, write_lock
, ct
);
529 smb_fem_oplock_space(
536 caller_context_t
*ct
)
541 rc
= smb_fem_oplock_break(arg
, ct
, SMB_OPLOCK_BREAK_TO_NONE
);
543 rc
= vnext_space(arg
, cmd
, bfp
, flag
, offset
, cr
, ct
);
548 * smb_fem_oplock_vnevent()
550 * To intercept NFS and local renames and removes in order to break any
551 * existing oplock prior to the operation.
553 * Note: Currently, this monitor is traversed only when an FS is mounted
554 * non-nbmand. (When the FS is mounted nbmand, share reservation checking
555 * will detect a share violation and return an error prior to the VOP layer
556 * being reached.) Thus, for nbmand NFS and local renames and removes,
557 * an existing oplock is never broken prior to share checking (contrary to
558 * how it is with intra-CIFS remove and rename requests).
562 smb_fem_oplock_vnevent(
567 caller_context_t
*ct
)
576 flags
= SMB_OPLOCK_BREAK_TO_NONE
|
577 SMB_OPLOCK_BREAK_BATCH
;
578 rc
= smb_fem_oplock_break(arg
, ct
, flags
);
581 flags
= SMB_OPLOCK_BREAK_TO_LEVEL_II
|
582 SMB_OPLOCK_BREAK_BATCH
;
583 rc
= smb_fem_oplock_break(arg
, ct
, flags
);
591 rc
= vnext_vnevent(arg
, vnevent
, dvp
, name
, ct
);
597 smb_fem_oplock_break(femarg_t
*arg
, caller_context_t
*ct
, uint32_t flags
)
602 node
= (smb_node_t
*)((arg
)->fa_fnode
->fn_available
);
603 SMB_NODE_VALID(node
);
605 ASSERT(ct
!= &smb_ct
);
607 if (ct
&& (ct
->cc_flags
& CC_DONTBLOCK
)) {
608 flags
|= SMB_OPLOCK_BREAK_NOWAIT
;
609 rc
= smb_oplock_break(NULL
, node
, flags
);
611 ct
->cc_flags
|= CC_WOULDBLOCK
;
613 rc
= smb_oplock_break(NULL
, node
, flags
);