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 2017 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2015 Joyent, Inc.
25 */
26
27 #include <smbsrv/smb_kproto.h>
28 #include <smbsrv/smb_fsops.h>
29 #include <sys/sdt.h>
30 #include <sys/fcntl.h>
31 #include <sys/vfs.h>
32 #include <sys/vfs_opreg.h>
33 #include <sys/vnode.h>
34 #include <sys/fem.h>
35
36 extern caller_context_t smb_ct;
37
38 static boolean_t smb_fem_initialized = B_FALSE;
39 static fem_t *smb_fcn_ops = NULL;
40 static fem_t *smb_oplock_ops = NULL;
41
42 /*
43 * Declarations for FCN (file change notification) FEM monitors
44 */
45
46 static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int,
47 vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *);
48 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *,
49 caller_context_t *, int);
50 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *,
51 cred_t *, caller_context_t *, int);
52 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **,
53 cred_t *, caller_context_t *, int, vsecattr_t *);
54 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *,
55 caller_context_t *, int);
56 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *,
57 caller_context_t *, int);
58 static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *,
59 char *, cred_t *, caller_context_t *, int);
60
61 static const fs_operation_def_t smb_fcn_tmpl[] = {
62 VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create },
63 VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove},
64 VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename},
65 VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir},
66 VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir},
67 VOPNAME_LINK, {.femop_link = smb_fem_fcn_link},
68 VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink},
69 NULL, NULL
70 };
71
72 /*
73 * Declarations for oplock FEM monitors
74 */
75
76 static int smb_fem_oplock_open(femarg_t *, int, cred_t *,
77 struct caller_context *);
78 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *,
79 struct caller_context *);
80 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *,
81 struct caller_context *);
82 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *,
83 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_SPACE, { .femop_space = smb_fem_oplock_space },
95 VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent },
96 NULL, NULL
97 };
98
99 static int smb_fem_oplock_wait(smb_node_t *, caller_context_t *);
100
101 /*
102 * smb_fem_init
103 *
104 * This function is not multi-thread safe. The caller must make sure only one
105 * thread makes the call.
106 */
107 int
108 smb_fem_init(void)
109 {
110 int rc = 0;
111
112 if (smb_fem_initialized)
113 return (0);
114
115 rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops);
116 if (rc)
117 return (rc);
118
119 rc = fem_create("smb_oplock_ops", smb_oplock_tmpl,
120 &smb_oplock_ops);
121
122 if (rc) {
123 fem_free(smb_fcn_ops);
124 smb_fcn_ops = NULL;
125 return (rc);
126 }
127
128 smb_fem_initialized = B_TRUE;
129
130 return (0);
131 }
132
133 /*
134 * smb_fem_fini
135 *
136 * This function is not multi-thread safe. The caller must make sure only one
137 * thread makes the call.
138 */
139 void
140 smb_fem_fini(void)
141 {
142 if (!smb_fem_initialized)
143 return;
144
145 if (smb_fcn_ops != NULL) {
146 fem_free(smb_fcn_ops);
147 smb_fcn_ops = NULL;
148 }
149 if (smb_oplock_ops != NULL) {
150 fem_free(smb_oplock_ops);
151 smb_oplock_ops = NULL;
152 }
153 smb_fem_initialized = B_FALSE;
154 }
155
156 /*
157 * Install our fem hooks for change notify.
158 * Not using hold/rele function here because we
159 * remove the fem hooks before node destroy.
160 */
161 int
162 smb_fem_fcn_install(smb_node_t *node)
163 {
164 int rc;
165
166 if (smb_fcn_ops == NULL)
167 return (ENOSYS);
168 rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ,
169 NULL, NULL);
170 return (rc);
171 }
172
173 void
174 smb_fem_fcn_uninstall(smb_node_t *node)
175 {
176 if (smb_fcn_ops == NULL)
177 return;
178 VERIFY0(fem_uninstall(node->vp, smb_fcn_ops, (void *)node));
179 }
180
181 int
182 smb_fem_oplock_install(smb_node_t *node)
183 {
184 int rc;
185
186 if (smb_oplock_ops == NULL)
187 return (ENOSYS);
188 rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ,
189 (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
190 return (rc);
191 }
192
193 void
194 smb_fem_oplock_uninstall(smb_node_t *node)
195 {
196 if (smb_oplock_ops == NULL)
197 return;
198 VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node));
199 }
200
201 /*
202 * FEM FCN monitors
203 *
204 * The FCN monitors intercept the respective VOP_* call regardless
205 * of whether the call originates from CIFS, NFS, or a local process.
206 */
207
208 /*
209 * smb_fem_fcn_create()
210 *
211 * This monitor will catch only changes to VREG files and not to extended
212 * attribute files. This is fine because, for CIFS files, stream creates
213 * should not trigger any file change notification on the VDIR directory
214 * being monitored. Creates of any other kind of extended attribute in
215 * the directory will also not trigger any file change notification on the
216 * VDIR directory being monitored.
217 */
218
219 static int
220 smb_fem_fcn_create(
221 femarg_t *arg,
222 char *name,
223 vattr_t *vap,
224 vcexcl_t excl,
225 int mode,
226 vnode_t **vpp,
227 cred_t *cr,
228 int flag,
229 caller_context_t *ct,
230 vsecattr_t *vsecp)
231 {
232 smb_node_t *dnode;
233 int error;
234
235 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
236
237 ASSERT(dnode);
238
239 error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag,
240 ct, vsecp);
241
242 if (error == 0 && ct != &smb_ct)
243 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
244
245 return (error);
246 }
247
248 /*
249 * smb_fem_fcn_remove()
250 *
251 * This monitor will catch only changes to VREG files and to not extended
252 * attribute files. This is fine because, for CIFS files, stream deletes
253 * should not trigger any file change notification on the VDIR directory
254 * being monitored. Deletes of any other kind of extended attribute in
255 * the directory will also not trigger any file change notification on the
256 * VDIR directory being monitored.
257 */
258
259 static int
260 smb_fem_fcn_remove(
261 femarg_t *arg,
262 char *name,
263 cred_t *cr,
264 caller_context_t *ct,
265 int flags)
266 {
267 smb_node_t *dnode;
268 int error;
269
270 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
271
272 ASSERT(dnode);
273
274 error = vnext_remove(arg, name, cr, ct, flags);
275
276 if (error == 0 && ct != &smb_ct)
277 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
278
279 return (error);
280 }
281
282 static int
283 smb_fem_fcn_rename(
284 femarg_t *arg,
285 char *snm,
286 vnode_t *tdvp,
287 char *tnm,
288 cred_t *cr,
289 caller_context_t *ct,
290 int flags)
291 {
292 smb_node_t *dnode;
293 int error;
294
295 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
296
297 ASSERT(dnode);
298
299 error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags);
300
301 if (error == 0 && ct != &smb_ct) {
302 /*
303 * Note that renames in the same directory are normally
304 * delivered in {old,new} pairs, and clients expect them
305 * in that order, if both events are delivered.
306 */
307 smb_node_notify_change(dnode,
308 FILE_ACTION_RENAMED_OLD_NAME, snm);
309 smb_node_notify_change(dnode,
310 FILE_ACTION_RENAMED_NEW_NAME, tnm);
311 }
312
313 return (error);
314 }
315
316 static int
317 smb_fem_fcn_mkdir(
318 femarg_t *arg,
319 char *name,
320 vattr_t *vap,
321 vnode_t **vpp,
322 cred_t *cr,
323 caller_context_t *ct,
324 int flags,
325 vsecattr_t *vsecp)
326 {
327 smb_node_t *dnode;
328 int error;
329
330 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
331
332 ASSERT(dnode);
333
334 error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp);
335
336 if (error == 0 && ct != &smb_ct)
337 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
338
339 return (error);
340 }
341
342 static int
343 smb_fem_fcn_rmdir(
344 femarg_t *arg,
345 char *name,
346 vnode_t *cdir,
347 cred_t *cr,
348 caller_context_t *ct,
349 int flags)
350 {
351 smb_node_t *dnode;
352 int error;
353
354 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
355
356 ASSERT(dnode);
357
358 error = vnext_rmdir(arg, name, cdir, cr, ct, flags);
359
360 if (error == 0 && ct != &smb_ct)
361 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
362
363 return (error);
364 }
365
366 static int
367 smb_fem_fcn_link(
368 femarg_t *arg,
369 vnode_t *svp,
370 char *tnm,
371 cred_t *cr,
372 caller_context_t *ct,
373 int flags)
374 {
375 smb_node_t *dnode;
376 int error;
377
378 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
379
380 ASSERT(dnode);
381
382 error = vnext_link(arg, svp, tnm, cr, ct, flags);
383
384 if (error == 0 && ct != &smb_ct)
385 smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm);
386
387 return (error);
388 }
389
390 static int
391 smb_fem_fcn_symlink(
392 femarg_t *arg,
393 char *linkname,
394 vattr_t *vap,
395 char *target,
396 cred_t *cr,
397 caller_context_t *ct,
398 int flags)
399 {
400 smb_node_t *dnode;
401 int error;
402
403 dnode = (smb_node_t *)arg->fa_fnode->fn_available;
404
405 ASSERT(dnode);
406
407 error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags);
408
409 if (error == 0 && ct != &smb_ct)
410 smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname);
411
412 return (error);
413 }
414
415 /*
416 * FEM oplock monitors
417 *
418 * The monitors below are not intended to intercept CIFS calls.
419 * CIFS higher-level routines will break oplocks as needed prior
420 * to getting to the VFS layer.
421 */
422 static int
423 smb_fem_oplock_open(
424 femarg_t *arg,
425 int mode,
426 cred_t *cr,
427 caller_context_t *ct)
428 {
429 smb_node_t *node;
430 uint32_t status;
431 int rc = 0;
432
433 if (ct != &smb_ct) {
434 uint32_t req_acc = FILE_READ_DATA;
435 uint32_t cr_disp = FILE_OPEN_IF;
436
437 node = (smb_node_t *)(arg->fa_fnode->fn_available);
438 SMB_NODE_VALID(node);
439
440 /*
441 * Get req_acc, cr_disp just accurate enough so
442 * the oplock break call does the right thing.
443 */
444 if (mode & FWRITE) {
445 req_acc = FILE_READ_DATA | FILE_WRITE_DATA;
446 cr_disp = (mode & FTRUNC) ?
447 FILE_OVERWRITE_IF : FILE_OPEN_IF;
448 } else {
449 req_acc = FILE_READ_DATA;
450 cr_disp = FILE_OPEN_IF;
451 }
452
453 status = smb_oplock_break_OPEN(node, NULL,
454 req_acc, cr_disp);
455 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
456 rc = smb_fem_oplock_wait(node, ct);
457 else if (status != 0)
458 rc = EIO;
459 }
460 if (rc == 0)
461 rc = vnext_open(arg, mode, 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
472 smb_fem_oplock_read(
473 femarg_t *arg,
474 uio_t *uiop,
475 int ioflag,
476 cred_t *cr,
477 caller_context_t *ct)
478 {
479 smb_node_t *node;
480 uint32_t status;
481 int rc = 0;
482
483 if (ct != &smb_ct) {
484 node = (smb_node_t *)(arg->fa_fnode->fn_available);
485 SMB_NODE_VALID(node);
486
487 status = smb_oplock_break_READ(node, NULL);
488 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
489 rc = smb_fem_oplock_wait(node, ct);
490 else if (status != 0)
491 rc = EIO;
492 }
493 if (rc == 0)
494 rc = vnext_read(arg, uiop, ioflag, cr, ct);
495
496 return (rc);
497 }
498
499 /*
500 * Should normally be hit only via NFSv2/v3. All other accesses
501 * (CIFS/NFS/local) should call VOP_OPEN first.
502 */
503
504 static int
505 smb_fem_oplock_write(
506 femarg_t *arg,
507 uio_t *uiop,
508 int ioflag,
509 cred_t *cr,
510 caller_context_t *ct)
511 {
512 smb_node_t *node;
513 uint32_t status;
514 int rc = 0;
515
516 if (ct != &smb_ct) {
517 node = (smb_node_t *)(arg->fa_fnode->fn_available);
518 SMB_NODE_VALID(node);
519
520 status = smb_oplock_break_WRITE(node, NULL);
521 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
522 rc = smb_fem_oplock_wait(node, ct);
523 else if (status != 0)
524 rc = EIO;
525 }
526 if (rc == 0)
527 rc = vnext_write(arg, uiop, ioflag, cr, ct);
528
529 return (rc);
530 }
531
532 static int
533 smb_fem_oplock_setattr(
534 femarg_t *arg,
535 vattr_t *vap,
536 int flags,
537 cred_t *cr,
538 caller_context_t *ct)
539 {
540 smb_node_t *node;
541 uint32_t status;
542 int rc = 0;
543
544 if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) {
545 node = (smb_node_t *)(arg->fa_fnode->fn_available);
546 SMB_NODE_VALID(node);
547
548 status = smb_oplock_break_SETINFO(node, NULL,
549 FileEndOfFileInformation);
550 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
551 rc = smb_fem_oplock_wait(node, ct);
552 else if (status != 0)
553 rc = EIO;
554 }
555 if (rc == 0)
556 rc = vnext_setattr(arg, vap, flags, cr, ct);
557 return (rc);
558 }
559
560 static int
561 smb_fem_oplock_space(
562 femarg_t *arg,
563 int cmd,
564 flock64_t *bfp,
565 int flag,
566 offset_t offset,
567 cred_t *cr,
568 caller_context_t *ct)
569 {
570 smb_node_t *node;
571 uint32_t status;
572 int rc = 0;
573
574 if (ct != &smb_ct) {
575 node = (smb_node_t *)(arg->fa_fnode->fn_available);
576 SMB_NODE_VALID(node);
577
578 status = smb_oplock_break_SETINFO(node, NULL,
579 FileAllocationInformation);
580 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
581 rc = smb_fem_oplock_wait(node, ct);
582 else if (status != 0)
583 rc = EIO;
584 }
585 if (rc == 0)
586 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct);
587 return (rc);
588 }
589
590 /*
591 * smb_fem_oplock_vnevent()
592 *
593 * To intercept NFS and local renames and removes in order to break any
594 * existing oplock prior to the operation.
595 *
596 * Note: Currently, this monitor is traversed only when an FS is mounted
597 * non-nbmand. (When the FS is mounted nbmand, share reservation checking
598 * will detect a share violation and return an error prior to the VOP layer
599 * being reached.) Thus, for nbmand NFS and local renames and removes,
600 * an existing oplock is never broken prior to share checking (contrary to
601 * how it is with intra-CIFS remove and rename requests).
602 */
603
604 static int
605 smb_fem_oplock_vnevent(
606 femarg_t *arg,
607 vnevent_t vnevent,
608 vnode_t *dvp,
609 char *name,
610 caller_context_t *ct)
611 {
612 smb_node_t *node;
613 uint32_t status;
614 int rc = 0;
615
616 if (ct != &smb_ct) {
617 node = (smb_node_t *)(arg->fa_fnode->fn_available);
618 SMB_NODE_VALID(node);
619
620 switch (vnevent) {
621 case VE_REMOVE:
622 case VE_PRE_RENAME_DEST:
623 case VE_RENAME_DEST:
624 status = smb_oplock_break_HANDLE(node, NULL);
625 break;
626 case VE_PRE_RENAME_SRC:
627 case VE_RENAME_SRC:
628 status = smb_oplock_break_SETINFO(node, NULL,
629 FileRenameInformation);
630 break;
631 default:
632 status = 0;
633 break;
634 }
635 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
636 rc = smb_fem_oplock_wait(node, ct);
637 else if (status != 0)
638 rc = EIO;
639 }
640 if (rc == 0)
641 rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
642
643 return (rc);
644 }
645
646 int smb_fem_oplock_timeout = 5000; /* mSec. */
647
648 static int
649 smb_fem_oplock_wait(smb_node_t *node, caller_context_t *ct)
650 {
651 int rc = 0;
652
653 ASSERT(ct != &smb_ct);
654
655 if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
656 ct->cc_flags |= CC_WOULDBLOCK;
657 rc = EAGAIN;
658 } else {
659 (void) smb_oplock_wait_break(node,
660 smb_fem_oplock_timeout);
661 rc = 0;
662 }
663
664 return (rc);
665 }