1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
28 #include <sys/sdt.h>
29 #include <sys/fcntl.h>
30 #include <sys/vfs.h>
31 #include <sys/vfs_opreg.h>
32 #include <sys/vnode.h>
33 #include <sys/fem.h>
34
35 extern caller_context_t smb_ct;
36
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;
40
41 /*
42 * Declarations for FCN (file change notification) FEM monitors
43 */
44
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);
59
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},
68 NULL, NULL
69 };
70
71 /*
72 * Declarations for oplock FEM monitors
73 */
74
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 *,
82 caller_context_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 *,
87 caller_context_t *);
88
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 },
97 NULL, NULL
98 };
99
100 static int smb_fem_oplock_break(femarg_t *, caller_context_t *, uint32_t);
101
102 /*
103 * smb_fem_init
104 *
105 * This function is not multi-thread safe. The caller must make sure only one
106 * thread makes the call.
107 */
108 int
smb_fem_init(void)109 smb_fem_init(void)
110 {
111 int rc = 0;
112
113 if (smb_fem_initialized)
114 return (0);
115
116 rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops);
117 if (rc)
118 return (rc);
119
120 rc = fem_create("smb_oplock_ops", smb_oplock_tmpl,
121 &smb_oplock_ops);
122
123 if (rc) {
124 fem_free(smb_fcn_ops);
125 smb_fcn_ops = NULL;
126 return (rc);
127 }
128
129 smb_fem_initialized = B_TRUE;
130
131 return (0);
132 }
133
134 /*
135 * smb_fem_fini
136 *
137 * This function is not multi-thread safe. The caller must make sure only one
138 * thread makes the call.
139 */
140 void
smb_fem_fini(void)141 smb_fem_fini(void)
142 {
143 if (!smb_fem_initialized)
144 return;
145
146 if (smb_fcn_ops != NULL) {
147 fem_free(smb_fcn_ops);
148 smb_fcn_ops = NULL;
149 }
150 if (smb_oplock_ops != NULL) {
151 fem_free(smb_oplock_ops);
152 smb_oplock_ops = NULL;
153 }
154 smb_fem_initialized = B_FALSE;
155 }
156
157 int
smb_fem_fcn_install(smb_node_t * node)158 smb_fem_fcn_install(smb_node_t *node)
159 {
160 int rc;
161
162 if (smb_fcn_ops == NULL)
163 return (ENOSYS);
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);
166 return (rc);
167 }
168
169 void
smb_fem_fcn_uninstall(smb_node_t * node)170 smb_fem_fcn_uninstall(smb_node_t *node)
171 {
172 if (smb_fcn_ops == NULL)
173 return;
174 VERIFY0(fem_uninstall(node->vp, smb_fcn_ops, (void *)node));
175 }
176
177 int
smb_fem_oplock_install(smb_node_t * node)178 smb_fem_oplock_install(smb_node_t *node)
179 {
180 int rc;
181
182 if (smb_oplock_ops == NULL)
183 return (ENOSYS);
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);
186 return (rc);
187 }
188
189 void
smb_fem_oplock_uninstall(smb_node_t * node)190 smb_fem_oplock_uninstall(smb_node_t *node)
191 {
192 if (smb_oplock_ops == NULL)
193 return;
194 VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node));
195 }
196
197 /*
198 * FEM FCN monitors
199 *
200 * The FCN monitors intercept the respective VOP_* call regardless
201 * of whether the call originates from CIFS, NFS, or a local process.
202 */
203
204 /*
205 * smb_fem_fcn_create()
206 *
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.
213 */
214
215 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)216 smb_fem_fcn_create(
217 femarg_t *arg,
218 char *name,
219 vattr_t *vap,
220 vcexcl_t excl,
221 int mode,
222 vnode_t **vpp,
223 cred_t *cr,
224 int flag,
225 caller_context_t *ct,
226 vsecattr_t *vsecp)
227 {
228 smb_node_t *dnode;
229 int error;
230
231 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
232
233 ASSERT(dnode);
234
235 error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag,
236 ct, vsecp);
237
238 if (error == 0 && ct != &smb_ct)
239 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
240
241 return (error);
242 }
243
244 /*
245 * smb_fem_fcn_remove()
246 *
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.
253 */
254
255 static int
smb_fem_fcn_remove(femarg_t * arg,char * name,cred_t * cr,caller_context_t * ct,int flags)256 smb_fem_fcn_remove(
257 femarg_t *arg,
258 char *name,
259 cred_t *cr,
260 caller_context_t *ct,
261 int flags)
262 {
263 smb_node_t *dnode;
264 int error;
265
266 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
267
268 ASSERT(dnode);
269
270 error = vnext_remove(arg, name, cr, ct, flags);
271
272 if (error == 0 && ct != &smb_ct)
273 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
274
275 return (error);
276 }
277
278 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)279 smb_fem_fcn_rename(
280 femarg_t *arg,
281 char *snm,
282 vnode_t *tdvp,
283 char *tnm,
284 cred_t *cr,
285 caller_context_t *ct,
286 int flags)
287 {
288 smb_node_t *dnode;
289 int error;
290
291 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
292
293 ASSERT(dnode);
294
295 error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags);
296
297 if (error == 0 && ct != &smb_ct) {
298 /*
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.
302 */
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);
307 }
308
309 return (error);
310 }
311
312 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)313 smb_fem_fcn_mkdir(
314 femarg_t *arg,
315 char *name,
316 vattr_t *vap,
317 vnode_t **vpp,
318 cred_t *cr,
319 caller_context_t *ct,
320 int flags,
321 vsecattr_t *vsecp)
322 {
323 smb_node_t *dnode;
324 int error;
325
326 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
327
328 ASSERT(dnode);
329
330 error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp);
331
332 if (error == 0 && ct != &smb_ct)
333 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
334
335 return (error);
336 }
337
338 static int
smb_fem_fcn_rmdir(femarg_t * arg,char * name,vnode_t * cdir,cred_t * cr,caller_context_t * ct,int flags)339 smb_fem_fcn_rmdir(
340 femarg_t *arg,
341 char *name,
342 vnode_t *cdir,
343 cred_t *cr,
344 caller_context_t *ct,
345 int flags)
346 {
347 smb_node_t *dnode;
348 int error;
349
350 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
351
352 ASSERT(dnode);
353
354 error = vnext_rmdir(arg, name, cdir, cr, ct, flags);
355
356 if (error == 0 && ct != &smb_ct)
357 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
358
359 return (error);
360 }
361
362 static int
smb_fem_fcn_link(femarg_t * arg,vnode_t * svp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)363 smb_fem_fcn_link(
364 femarg_t *arg,
365 vnode_t *svp,
366 char *tnm,
367 cred_t *cr,
368 caller_context_t *ct,
369 int flags)
370 {
371 smb_node_t *dnode;
372 int error;
373
374 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
375
376 ASSERT(dnode);
377
378 error = vnext_link(arg, svp, tnm, cr, ct, flags);
379
380 if (error == 0 && ct != &smb_ct)
381 smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm);
382
383 return (error);
384 }
385
386 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)387 smb_fem_fcn_symlink(
388 femarg_t *arg,
389 char *linkname,
390 vattr_t *vap,
391 char *target,
392 cred_t *cr,
393 caller_context_t *ct,
394 int flags)
395 {
396 smb_node_t *dnode;
397 int error;
398
399 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
400
401 ASSERT(dnode);
402
403 error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags);
404
405 if (error == 0 && ct != &smb_ct)
406 smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname);
407
408 return (error);
409 }
410
411 /*
412 * FEM oplock monitors
413 *
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.
417 */
418 static int
smb_fem_oplock_open(femarg_t * arg,int mode,cred_t * cr,caller_context_t * ct)419 smb_fem_oplock_open(
420 femarg_t *arg,
421 int mode,
422 cred_t *cr,
423 caller_context_t *ct)
424 {
425 uint32_t flags;
426 int rc = 0;
427
428 if (ct != &smb_ct) {
429 if (mode & (FWRITE|FTRUNC))
430 flags = SMB_OPLOCK_BREAK_TO_NONE;
431 else
432 flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
433 rc = smb_fem_oplock_break(arg, ct, flags);
434 }
435 if (rc == 0)
436 rc = vnext_open(arg, mode, cr, ct);
437
438 return (rc);
439 }
440
441 /*
442 * Should normally be hit only via NFSv2/v3. All other accesses
443 * (CIFS/NFS/local) should call VOP_OPEN first.
444 */
445
446 static int
smb_fem_oplock_read(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)447 smb_fem_oplock_read(
448 femarg_t *arg,
449 uio_t *uiop,
450 int ioflag,
451 cred_t *cr,
452 caller_context_t *ct)
453 {
454 int rc = 0;
455
456 if (ct != &smb_ct) {
457 rc = smb_fem_oplock_break(arg, ct,
458 SMB_OPLOCK_BREAK_TO_LEVEL_II);
459 }
460 if (rc == 0)
461 rc = vnext_read(arg, uiop, ioflag, cr, ct);
462
463 return (rc);
464 }
465
466 /*
467 * Should normally be hit only via NFSv2/v3. All other accesses
468 * (CIFS/NFS/local) should call VOP_OPEN first.
469 */
470
471 static int
smb_fem_oplock_write(femarg_t * arg,uio_t * uiop,int ioflag,cred_t * cr,caller_context_t * ct)472 smb_fem_oplock_write(
473 femarg_t *arg,
474 uio_t *uiop,
475 int ioflag,
476 cred_t *cr,
477 caller_context_t *ct)
478 {
479 int rc = 0;
480
481 if (ct != &smb_ct)
482 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
483 if (rc == 0)
484 rc = vnext_write(arg, uiop, ioflag, cr, ct);
485
486 return (rc);
487 }
488
489 static int
smb_fem_oplock_setattr(femarg_t * arg,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)490 smb_fem_oplock_setattr(
491 femarg_t *arg,
492 vattr_t *vap,
493 int flags,
494 cred_t *cr,
495 caller_context_t *ct)
496 {
497 int rc = 0;
498
499 if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0)
500 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
501 if (rc == 0)
502 rc = vnext_setattr(arg, vap, flags, cr, ct);
503 return (rc);
504 }
505
506 static int
smb_fem_oplock_rwlock(femarg_t * arg,int write_lock,caller_context_t * ct)507 smb_fem_oplock_rwlock(
508 femarg_t *arg,
509 int write_lock,
510 caller_context_t *ct)
511 {
512 uint32_t flags;
513 int rc = 0;
514
515 if (ct != &smb_ct) {
516 if (write_lock)
517 flags = SMB_OPLOCK_BREAK_TO_NONE;
518 else
519 flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
520 rc = smb_fem_oplock_break(arg, ct, flags);
521 }
522 if (rc == 0)
523 rc = vnext_rwlock(arg, write_lock, ct);
524
525 return (rc);
526 }
527
528 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)529 smb_fem_oplock_space(
530 femarg_t *arg,
531 int cmd,
532 flock64_t *bfp,
533 int flag,
534 offset_t offset,
535 cred_t *cr,
536 caller_context_t *ct)
537 {
538 int rc = 0;
539
540 if (ct != &smb_ct)
541 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
542 if (rc == 0)
543 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct);
544 return (rc);
545 }
546
547 /*
548 * smb_fem_oplock_vnevent()
549 *
550 * To intercept NFS and local renames and removes in order to break any
551 * existing oplock prior to the operation.
552 *
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).
559 */
560
561 static int
smb_fem_oplock_vnevent(femarg_t * arg,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)562 smb_fem_oplock_vnevent(
563 femarg_t *arg,
564 vnevent_t vnevent,
565 vnode_t *dvp,
566 char *name,
567 caller_context_t *ct)
568 {
569 uint32_t flags;
570 int rc = 0;
571
572 if (ct != &smb_ct) {
573 switch (vnevent) {
574 case VE_REMOVE:
575 case VE_RENAME_DEST:
576 flags = SMB_OPLOCK_BREAK_TO_NONE |
577 SMB_OPLOCK_BREAK_BATCH;
578 rc = smb_fem_oplock_break(arg, ct, flags);
579 break;
580 case VE_RENAME_SRC:
581 flags = SMB_OPLOCK_BREAK_TO_LEVEL_II |
582 SMB_OPLOCK_BREAK_BATCH;
583 rc = smb_fem_oplock_break(arg, ct, flags);
584 break;
585 default:
586 rc = 0;
587 break;
588 }
589 }
590 if (rc == 0)
591 rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
592
593 return (rc);
594 }
595
596 static int
smb_fem_oplock_break(femarg_t * arg,caller_context_t * ct,uint32_t flags)597 smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct, uint32_t flags)
598 {
599 smb_node_t *node;
600 int rc;
601
602 node = (smb_node_t *)((arg)->fa_fnode->fn_available);
603 SMB_NODE_VALID(node);
604
605 ASSERT(ct != &smb_ct);
606
607 if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
608 flags |= SMB_OPLOCK_BREAK_NOWAIT;
609 rc = smb_oplock_break(NULL, node, flags);
610 if (rc == EAGAIN)
611 ct->cc_flags |= CC_WOULDBLOCK;
612 } else {
613 rc = smb_oplock_break(NULL, node, flags);
614 }
615
616 return (rc);
617 }
618