1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2013 Damian Bogel. All rights reserved.
14 */
15
16 #include <sys/debug.h>
17 #include <sys/fsh.h>
18 #include <sys/fsh_impl.h>
19 #include <sys/ksynch.h>
20 #include <sys/sunddi.h>
21 #include <sys/types.h>
22 #include <sys/vfs.h>
23 #include <sys/vnode.h>
24
25 /*
26 * TODO:
27 * - support more operations
28 * - adjust the code for use of kernel list operations
29 * - add big theory about callbacks
30 */
31
32 /*
33 * Filesystem hook framework (FSH)
34 *
35 * 1. Abstract.
36 * The main goal of the filesystem hook framework is to provide an easy way to
37 * inject consumer-defined behaviour into vfs/vnode calls. Because of what
38 * zones and ZFS offer, we narrow our hooking system to whole filesystems, not
39 * single vnodes or filesystem subtrees.
40 *
41 * 2. Overview.
42 * fsh_t is the main object in the FSH. An fsh_t is a structure containing:
43 * - pointers to hookin functions (hook_foo, where foo is the name of
44 * a corresponding vnodeop/vfsop)
45 * - a pointer to an argument to pass (this is shared for all the
46 * hooks in a given fsh_t)
47 *
48 * We install a fsh_t on a whole filesystem, but one fsh_t can be installed on
49 * many filesystems.
50 *
51 * 3. Usage.
52 * It is assumed that vfs_t/vnode_t that are passed to fsh_foo() functions are
53 * held by the caller.
54 *
55 * fsh_t is a structure filled out by the consumer. If a consumer does not
56 * want to add/remove a hook for function foo(), he should fill the
57 * hook_foo() field of fsh_t with NULL before calling
58 * fsh_hook_install/remove(). The type of hook_foo() is the type of foo() with
59 * two additional arguments:
60 * - const fsh_node_t *fsh_node - this argument MUST be passed to
61 * hook_next_foo(). FSH would't know which hook to execute next
62 * without it.
63 * - void *arg - this is the argument passed with fsh_t during
64 * installation
65 * All the information passed with fsh_t is copied by FSH, so it's safe for
66 * the caller to do anything with it. Keep in mind that this structure is also
67 * used for removing hooks, so the consumer should somehow remember all of
68 * it's contents.
69 *
70 * Every hook function is responsible for passing the control to the next
71 * hook associated with a particular call. In order to provide an easy way to
72 * modify the behaviour of a function call both before and after the
73 * underlying vfsop/vnodeop (or next hook) execution, a hook has to call
74 * fsh_next_foo() at some point. This function does necessary internal
75 * operations and calls the next hook, until there's no hook left, then it
76 * calls the underlying vfsop/vnodeop.
77 * Example:
78 * my_freefs(const fsh_node_t *fsh_node, void *arg, vfs_t *vfsp) {
79 * cmn_err(CE_NOTE, "freefs called!\n");
80 * return (fsh_next_freefs(fsh_node, vfsp));
81 * }
82 *
83 * A consumer might want to receive some notifications about vfs_t entries
84 * being created/destroyed. There's a fsh_callback_t structure provided to
85 * install such callbacks.
86 *
87 * 4. API (fsh.h)
88 * fsh_fs_enable(vfs_t *vfsp)
89 * fsh_fs_disable(vfs_t *vfsp)
90 * Enables/disables fshook per filesystem.
91 *
92 * fsh_hook_install(vfs_t *vfsp, fsh_t *fsh)
93 * Installs hooks on vfsp filesystem. It's important that hooks are
94 * executed in LIFO installation order, which means that if there are
95 * hooks A and B installed in this order, B is going to be execute
96 * before A.
97 *
98 * fsh_hook_remove(vfs_t *vfsp, fsh_t *fsh)
99 * Removes hooks from vfsp filesystem.
100 *
101 * fsh_next_xxx(fsh_node_t *fsh_node, void *arg, ARGUMENTS)
102 * This is the function which should be called once in every hook. It
103 * does the necessary internal operations and passes control to the
104 * next hook or, if there's no hook left, to the underlying
105 * vfsop/vnodeop.
106 *
107 * fsh_callback_install(fsh_callback_t *fsh_callback)
108 * fsh_callback_remove(fsh_callback_t *fsh_callback)
109 * Installs/remove a callback for vfs_t mount/free. The mount callback
110 * is executed right before domount() returns. The free callback is
111 * called right before VFS_FREEVFS() is called.
112 *
113 * 5. API for vfs.c and vnode.c (fsh_impl.h)
114 * fsh_init()
115 * This call has to be done in vfsinit(). It initialises the FSH. It
116 * is absolutely necessary that this call is made before any other FSH
117 * operation.
118 *
119 * fsh_exec_mount_callbacks(vfs_t *vfsp)
120 * fsh_exec_free_callbacks(vfs_t *vfsp)
121 * Used to execute all FSH callback for vfs_t mount/free.
122 *
123 * fsh_fsrec_destroy(struct fsh_fsrecord *fsrecp)
124 * Destroys a fsh_fsrecord structure.
125 *
126 * fsh_foo(ARGUMENTS)
127 * Function used to start executing the hook chain for a given call
128 * (foo).
129 *
130 * 6. Internals.
131 * fsh_fsrecord_t is a structure which lives inside vfs_t.
132 * fsh_fsrecord_t contains:
133 * - an array of fsh_list_t, one for each vfsop/vnodeop. fsh_list_t
134 * is just a list of hooks for a particular vfsop/vnodeop
135 * - a flag which tells if FSH is enabled on this filesystem
136 *
137 * Unfortunately, because of unexpected behaviour of some filesystems (no
138 * use of vfs_alloc()/vfs_init()) there's no good place to initialise the
139 * fsh_fshrecord structure. The approach being used here is to check if it's
140 * initialised in every call. Because of the fact that no lock could be used
141 * here (the same problem with initialisation), a spinlock is used. This is
142 * explained in more detail in a comment before FSH_PREPARE_FSREC(), a macro
143 * that should be used whenever a vfsp->vfs_fshrecord needs to be fetched.
144 * After doing that, it's completely safe to keep this pointer locally,
145 * because it won't change until vfs_free() is called.
146 *
147 * fsh_list_t is a RW locked hook list, designed to containt hooks for one
148 * operation (that's why it's nodes don't contain such information). Every
149 * hook is internally kept as an fsh_int_t structure, which is filled out
150 * using information from a consumer-provided fsh_t. The fsh_node_t is just
151 * this list node containig fsh_int_t. Since fsh_list_t is an RW locked list,
152 * installing and removing hooks may cause delays in filesystem operations.
153 *
154 * fsh_next_xxx()
155 * This function is quite simple. It takes the next node pointer from
156 * fsh_node_t passed to this function and passes control to the next hook or
157 * to the underlying vnodeop/vfsop.
158 *
159 * Callbacks installed with fsh_callback_install/remove() are executed by
160 * calling fsh_exec_mount/fre_callbacks() in domount()/vfs_rele() (when
161 * vfs_t's reference count drops to 0).
162 *
163 * 7. Concurrency
164 * FSH does no vfs_t nor vnode_t locking. It is expected that whenever it is
165 * needed, the consumer does that before/after calling FSH API.
166 *
167 * It is unsafe to call fsh_foo() for a given vfs_t when it's being destroyed.
168 * It's because of the fact that it's expected that vfs_fshrecord is set only
169 * once for the whole vfs_t lifetime.
170 */
171
172
173 /* Structure used for mount/free callbacks. */
174 static fsh_callback_list_t fsh_global_callback_list;
175
176 /*
177 * A reserved pointer to FSH. It is used because of the method chosen for
178 * solving concurrency issues for vfs_fshrecord. The full explanation
179 * is in the big theory statement at the beginning of this file.
180 * It is initialised in fsh_init().
181 */
182 static void *fsh_res_ptr;
183
184 static struct fsh_fsrecord *fsh_fsrec_create();
185
186 /*
187 * Important note:
188 * Before using this macro, fsh_init() MUST be called. We do that in
189 * vfsinit()@vfs.c.
190 *
191 * One would ask, why isn't the vfsp->vfs_fshrecord initialised when the
192 * vfs_t is created. Unfortunately, some filesystems (e.g. fifofs) does not
193 * call vfs_init() or even vfs_alloc(), It's possible that some unbundled
194 * filesystems could do the same thing. That's why this macro is introduced.
195 * It should be called before any code that needs access to vfs_fshrecord.
196 *
197 * Locking:
198 * There are no locks here, because there's no good place to initialise
199 * the lock. Concurrency issues are solved by using atomic instructions
200 * and a spinlock, which is spinning only once for a given vfs_t. Because
201 * of that, the usage of spinlock isn't bad at all.
202 *
203 * How it works:
204 * a) if vfsp->vfs_fshrecord is NULL, atomic_cas_ptr changes it to
205 * fsh_res_ptr. That's a signal for other calls, that the structure
206 * is being initialised. Then the creation of vfs_fshrecord is done.
207 * b) if vfsp->vfs_fshrecord is fsh_res_ptr, that means we have to wait,
208 * because vfs_fshrecord is being initialised by another call.
209 * c) other cases:
210 * vfs_fshrecord is already initialised, so we can use it.
211 */
212 #define FSH_PREPARE_FSREC(vfsp) \
213 do { \
214 fsh_fsrecord_t *fsrec; \
215 \
216 while ((fsrec = atomic_cas_ptr(&(vfsp)->vfs_fshrecord, NULL, \
217 fsh_res_ptr)) == fsh_res_ptr); \
218 if ((fsrec) == NULL) { \
219 atomic_swap_ptr(&(vfsp)->vfs_fshrecord, \
220 fsh_fsrec_create()); \
221 } \
222 _NOTE(CONSTCOND) \
223 } while (0)
224
225 /*
226 * API for enabling/disabling FSH per vfs_t.
227 * Atomic operations are used for changing vfs_fshrecord->fshfsr_enabled.
228 */
229 void
230 fsh_fs_enable(vfs_t *vfsp)
231 {
232 FSH_PREPARE_FSREC(vfsp);
233 atomic_or_uint(&vfsp->vfs_fshrecord->fshfsr_enabled, 1);
234 }
235
236 void
237 fsh_fs_disable(vfs_t *vfsp)
238 {
239 FSH_PREPARE_FSREC(vfsp);
240 atomic_and_uint(&vfsp->vfs_fshrecord->fshfsr_enabled, 0);
241 }
242
243 /*
244 * This macro, like FSH_REMOVE is introduced because of the fact that
245 * the code for installing a hook looks almost exactly the same for
246 * every vop/vfsop.
247 * The usage of these macros is pretty simple. One has to provide pointers to
248 * fsh_t, fsh_fsrecord_t and a name of operation to be installed both in
249 * lowercase and uppercase.
250 */
251 #define FSH_INSTALL(type, hooks, fsrec, lower, upper) \
252 do { \
253 fsh_node_t *node; \
254 fsh_list_t *list; \
255 \
256 if (hooks->hook_##lower) { \
257 node = (fsh_node_t *)kmem_alloc(sizeof (*node), \
258 KM_SLEEP); \
259 node->fshn_hooki.fshi_fn.hook_##lower = \
260 hooks->hook_##lower; \
261 node->fshn_hooki.fshi_arg = hooks->arg; \
262 \
263 list = &fsrec->fshfsr_opv[FSH_##type##_##upper]; \
264 rw_enter(&list->fshl_lock, RW_WRITER); \
265 node->fshn_next = \
266 fsrec \
267 ->fshfsr_opv[FSH_##type##_##upper].fshl_head; \
268 fsrec->fshfsr_opv[FSH_##type##_##upper].fshl_head \
269 = node; \
270 rw_exit(&list->fshl_lock); \
271 } \
272 _NOTE(CONSTCOND) \
273 } while (0)
274
275 #define FSH_INSTALL_VN(hooks, fsrec, lower, upper) \
276 FSH_INSTALL(VOP, hooks, fsrec, lower, upper)
277
278 #define FSH_INSTALL_VFS(hooks, fsrec, lower, upper) \
279 FSH_INSTALL(VFS, hooks, fsrec, lower, upper)
280
281 void
282 fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
283 {
284 fsh_fsrecord_t *fsrec;
285
286 FSH_PREPARE_FSREC(vfsp);
287 fsrec = vfsp->vfs_fshrecord;
288
289 FSH_INSTALL_VN(hooks, fsrec, open, OPEN);
290 FSH_INSTALL_VN(hooks, fsrec, close, CLOSE);
291 FSH_INSTALL_VN(hooks, fsrec, read, READ);
292 FSH_INSTALL_VN(hooks, fsrec, write, WRITE);
293 FSH_INSTALL_VFS(hooks, fsrec, mount, MOUNT);
294 FSH_INSTALL_VFS(hooks, fsrec, unmount, UNMOUNT);
295 FSH_INSTALL_VFS(hooks, fsrec, root, ROOT);
296 FSH_INSTALL_VFS(hooks, fsrec, vget, VGET);
297 FSH_INSTALL_VFS(hooks, fsrec, statfs, STATFS);
298 }
299
300 /*
301 * See comment above FSH_INSTALL.
302 * Note: This macro does nothing when a hook to remove was not found.
303 */
304 #define FSH_REMOVE(type, hooks, fsrec, lower, upper) \
305 do { \
306 fsh_node_t *node, *prev; \
307 fsh_list_t *list; \
308 if (hooks->hook_##lower == NULL) \
309 break; \
310 \
311 list = &fsrec->fshfsr_opv[FSH_##type##_##upper]; \
312 rw_enter(&list->fshl_lock, RW_WRITER); \
313 node = list->fshl_head; \
314 \
315 if (node == NULL) { \
316 rw_exit(&list->fshl_lock); \
317 break; \
318 } \
319 \
320 while (node && \
321 !(node->fshn_hooki.fshi_fn.hook_##lower == \
322 hooks->hook_##lower && \
323 node->fshn_hooki.fshi_arg == hooks->arg)) { \
324 prev = node; \
325 node = node->fshn_next; \
326 } \
327 \
328 if (node == NULL) { \
329 rw_exit(&list->fshl_lock); \
330 break; \
331 } \
332 \
333 if (node == list->fshl_head) \
334 list->fshl_head = node->fshn_next; \
335 else \
336 prev->fshn_next = node->fshn_next; \
337 rw_exit(&list->fshl_lock); \
338 \
339 kmem_free(node, sizeof (*node)); \
340 _NOTE(CONSTCOND) \
341 } while (0)
342
343 #define FSH_REMOVE_VN(hooks, fsrec, lower, upper) \
344 FSH_REMOVE(VOP, hooks, fsrec, lower, upper)
345
346 #define FSH_REMOVE_VFS(hooks, fsrec, lower, upper) \
347 FSH_REMOVE(VFS, hooks, fsrec, lower, upper)
348
349 void
350 fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks)
351 {
352 fsh_fsrecord_t *fsrec;
353
354 FSH_PREPARE_FSREC(vfsp);
355 fsrec = vfsp->vfs_fshrecord;
356
357 FSH_REMOVE_VN(hooks, fsrec, open, OPEN);
358 FSH_REMOVE_VN(hooks, fsrec, close, CLOSE);
359 FSH_REMOVE_VN(hooks, fsrec, read, READ);
360 FSH_REMOVE_VN(hooks, fsrec, write, WRITE);
361 FSH_REMOVE_VFS(hooks, fsrec, mount, MOUNT);
362 FSH_REMOVE_VFS(hooks, fsrec, unmount, UNMOUNT);
363 FSH_REMOVE_VFS(hooks, fsrec, root, ROOT);
364 FSH_REMOVE_VFS(hooks, fsrec, vget, VGET);
365 FSH_REMOVE_VFS(hooks, fsrec, statfs, STATFS);
366 }
367
368 /* TODO: check which hooks are installed */
369 void
370 fsh_hook_check(vfs_t *vfsp, fsh_t *hooks, fsh_t *mask)
371 {
372 _NOTE(ARGUNUSED(vfsp));
373 _NOTE(ARGUNUSED(hooks));
374 _NOTE(ARGUNUSED(mask));
375 }
376
377 /*
378 * API for installing/removing global mount/free callbacks.
379 * It's safe to call these functions whenever after fsh_init() was called.
380 * The fsh_global_callback_list is rwlocked. fsh_callback_install/remove() are
381 * the only writers and fsh_exec_mount/free_callbacks() are the only readers.
382 */
383 void
384 fsh_callback_install(fsh_callback_t *fsh_callback)
385 {
386 fsh_callback_node_t *node;
387
388 node = (fsh_callback_node_t *)kmem_alloc(sizeof (*node), KM_SLEEP);
389 node->fshcn_callback = *fsh_callback;
390
391 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER);
392 node->fshcn_next = fsh_global_callback_list.fshcl_head;
393 fsh_global_callback_list.fshcl_head = node;
394 rw_exit(&fsh_global_callback_list.fshcl_lock);
395 }
396
397 /*
398 * fsh_callback_t objects are compared by a simple memcmp() here. That's
399 * by design, because we'd like to be absolutely sure that we delete the
400 * callbacks we wanted (the same argument and callback functions).
401 *
402 * TODO: Adding an unique ID to the fsh_callback_t is worth considering,
403 * but it's not yet implemented.
404 */
405 void
406 fsh_callback_remove(fsh_callback_t *fsh_callback)
407 {
408 fsh_callback_node_t *node;
409 fsh_callback_node_t *prev;
410 fsh_callback_list_t *list;
411
412 list = &fsh_global_callback_list;
413
414 rw_enter(&list->fshcl_lock, RW_WRITER);
415 node = list->fshcl_head;
416
417 if (node == NULL) {
418 rw_exit(&list->fshcl_lock);
419 return;
420 }
421
422 while (node && memcmp(fsh_callback, &node->fshcn_callback,
423 sizeof (*fsh_callback))) {
424 prev = node;
425 node = node->fshcn_next;
426 }
427
428 if (node == NULL) {
429 rw_exit(&list->fshcl_lock);
430 return;
431 }
432
433 prev->fshcn_next = node->fshcn_next;
434 kmem_free(node, sizeof (*node));
435
436 rw_exit(&list->fshcl_lock);
437 }
438
439 /*
440 * This function is executed right before returning from domount()@vfs.c.
441 * We are sure that it's called only after fsh_init().
442 * It does all the mount callbacks installed in the FSH.
443 */
444 void
445 fsh_exec_mount_callbacks(vfs_t *vfsp)
446 {
447 fsh_callback_node_t *node;
448 fsh_callback_t *callback;
449
450 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
451 node = fsh_global_callback_list.fshcl_head;
452 while (node) {
453 callback = &node->fshcn_callback;
454 (*(callback->fshc_mount))(vfsp, callback->fshc_arg);
455 node = node->fshcn_next;
456 }
457 rw_exit(&fsh_global_callback_list.fshcl_lock);
458 }
459
460 /*
461 * This function is executed right before VFS_FREEVFS() is called in
462 * vfs_rele()@vfs.c. We are sure that it's called only after fsh_init().
463 * It does all the free callbacks installed in the FSH.
464 */
465 void
466 fsh_exec_free_callbacks(vfs_t *vfsp)
467 {
468 fsh_callback_node_t *node;
469 fsh_callback_t *callback;
470
471 rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
472 node = fsh_global_callback_list.fshcl_head;
473 while (node) {
474 callback = &node->fshcn_callback;
475 (*(callback->fshc_free))(vfsp, callback->fshc_arg);
476 node = node->fshcn_next;
477 }
478 rw_exit(&fsh_global_callback_list.fshcl_lock);
479 }
480
481 /*
482 * API for vnode.c/vfs.c to start executing the FSH for a given operation.
483 *
484 * These interfaces are using fsh_res_ptr (in FSH_PREPARE_FSREC()), so it's
485 * absolutely necessary to call fsh_init() before using them. That's done in
486 * vfsinit().
487 *
488 * While these functions are executing, it's expected that necessary vfs_t's
489 * are held so that vfs_free() isn't called. vfs_free() expects that noone
490 * else accesses vfs_fshrecord of a given vfs_t.
491 * It's also the caller responsibility to keep vnode_t passed to fsh_xxx()
492 * alive and valid.
493 * All these expectations are met because these functions are used only in
494 * correspondng fop/fsop_xxx().
495 *
496 * Although fsrec->fshfsr_enabled is shared by everyone who uses a given
497 * vfs_t, it can be accessed in the usual way. There's no locking involved
498 * because all the changes of this field are made by atomic operations in
499 * fsh_fs_enable/disable().
500 */
501 int
502 fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
503 {
504 fsh_fsrecord_t *fsrec;
505 fsh_list_t *list;
506 int ret;
507
508 FSH_PREPARE_FSREC((*vpp)->v_vfsp);
509 fsrec = (*vpp)->v_vfsp->vfs_fshrecord;
510 if (!(fsrec->fshfsr_enabled))
511 return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct));
512
513 list = &fsrec->fshfsr_opv[FSH_VOP_OPEN];
514 rw_enter(&list->fshl_lock, RW_READER);
515 if (list->fshl_head == NULL)
516 ret = (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct);
517 else
518 ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct);
519 rw_exit(&list->fshl_lock);
520
521 return (ret);
522 }
523
524 int
525 fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
526 caller_context_t *ct)
527 {
528 fsh_fsrecord_t *fsrec;
529 fsh_list_t *list;
530 int ret;
531
532 FSH_PREPARE_FSREC(vp->v_vfsp);
533 fsrec = vp->v_vfsp->vfs_fshrecord;
534 if (!(fsrec->fshfsr_enabled))
535 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
536 cr, ct));
537
538 list = &fsrec->fshfsr_opv[FSH_VOP_CLOSE];
539 rw_enter(&list->fshl_lock, RW_READER);
540 if (list->fshl_head == NULL)
541 ret = (*(vp->v_op->vop_close))(vp, flag, count, offset,
542 cr, ct);
543 else
544 ret = fsh_next_close(list->fshl_head, vp, flag, count,
545 offset, cr, ct);
546 rw_exit(&list->fshl_lock);
547
548 return (ret);
549 }
550
551 int
552 fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
553 caller_context_t *ct)
554 {
555 fsh_fsrecord_t *fsrec;
556 fsh_list_t *list;
557 int ret;
558
559 FSH_PREPARE_FSREC(vp->v_vfsp);
560 fsrec = vp->v_vfsp->vfs_fshrecord;
561 if (!(fsrec->fshfsr_enabled))
562 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
563
564 list = &fsrec->fshfsr_opv[FSH_VOP_READ];
565 rw_enter(&list->fshl_lock, RW_READER);
566 if (list->fshl_head == NULL)
567 ret = (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct);
568 else
569 ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag,
570 cr, ct);
571 rw_exit(&list->fshl_lock);
572
573 return (ret);
574 }
575
576 int
577 fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
578 caller_context_t *ct)
579 {
580 fsh_fsrecord_t *fsrec;
581 fsh_list_t *list;
582 int ret;
583
584 FSH_PREPARE_FSREC(vp->v_vfsp);
585 fsrec = vp->v_vfsp->vfs_fshrecord;
586 if (!(fsrec->fshfsr_enabled))
587 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
588
589 list = &fsrec->fshfsr_opv[FSH_VOP_WRITE];
590 rw_enter(&list->fshl_lock, RW_READER);
591 if (list->fshl_head == NULL)
592 ret = (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct);
593 else
594 ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag,
595 cr, ct);
596 rw_exit(&list->fshl_lock);
597
598 return (ret);
599 }
600
601 int
602 fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
603 {
604 fsh_fsrecord_t *fsrec;
605 fsh_list_t *list;
606 int ret;
607
608 FSH_PREPARE_FSREC(vfsp);
609 fsrec = vfsp->vfs_fshrecord;
610 if (!(fsrec->fshfsr_enabled))
611 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
612
613 list = &fsrec->fshfsr_opv[FSH_VFS_MOUNT];
614 rw_enter(&list->fshl_lock, RW_READER);
615 if (list->fshl_head == NULL)
616 ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr);
617 else
618 ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap,
619 cr);
620 rw_exit(&list->fshl_lock);
621
622 return (ret);
623 }
624
625 int
626 fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
627 {
628 fsh_fsrecord_t *fsrec;
629 fsh_list_t *list;
630 int ret;
631
632 FSH_PREPARE_FSREC(vfsp);
633 fsrec = vfsp->vfs_fshrecord;
634 if (!(fsrec->fshfsr_enabled))
635 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
636
637 list = &fsrec->fshfsr_opv[FSH_VFS_UNMOUNT];
638 rw_enter(&list->fshl_lock, RW_READER);
639 if (list->fshl_head == NULL)
640 ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr);
641 else
642 ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr);
643 rw_exit(&list->fshl_lock);
644
645 return (ret);
646 }
647
648 int
649 fsh_root(vfs_t *vfsp, vnode_t **vpp)
650 {
651 fsh_fsrecord_t *fsrec;
652 fsh_list_t *list;
653 int ret;
654
655 FSH_PREPARE_FSREC(vfsp);
656 fsrec = vfsp->vfs_fshrecord;
657 if (!(fsrec->fshfsr_enabled))
658 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp));
659
660 list = &fsrec->fshfsr_opv[FSH_VFS_ROOT];
661 rw_enter(&list->fshl_lock, RW_READER);
662 if (list->fshl_head == NULL)
663 ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp);
664 else
665 ret = fsh_next_root(list->fshl_head, vfsp, vpp);
666 rw_exit(&list->fshl_lock);
667
668 return (ret);
669 }
670
671 int
672 fsh_statfs(vfs_t *vfsp, statvfs64_t *sp)
673 {
674 fsh_fsrecord_t *fsrec;
675 fsh_list_t *list;
676 int ret;
677
678 FSH_PREPARE_FSREC(vfsp);
679 fsrec = vfsp->vfs_fshrecord;
680 if (!(fsrec->fshfsr_enabled))
681 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp));
682
683 list = &fsrec->fshfsr_opv[FSH_VFS_STATFS];
684 rw_enter(&list->fshl_lock, RW_READER);
685 if (list->fshl_head == NULL)
686 ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp);
687 else
688 ret = fsh_next_statfs(list->fshl_head, vfsp, sp);
689 rw_exit(&list->fshl_lock);
690
691 return (ret);
692 }
693
694 int
695 fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
696 {
697 fsh_fsrecord_t *fsrec;
698 fsh_list_t *list;
699 int ret;
700
701 FSH_PREPARE_FSREC(vfsp);
702 fsrec = vfsp->vfs_fshrecord;
703 if (!(fsrec->fshfsr_enabled))
704 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp));
705
706 list = &fsrec->fshfsr_opv[FSH_VFS_VGET];
707 rw_enter(&list->fshl_lock, RW_READER);
708 if (list->fshl_head == NULL)
709 ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp);
710 else
711 ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp);
712 rw_exit(&list->fshl_lock);
713
714 return (ret);
715 }
716
717 /*
718 * This is the funtion used by FSH_PREPARE_FSREC() to allocate a new
719 * fsh_fsrecord. This function is called by the first function which
720 * access the vfs_fshrecord and finds out it's NULL.
721 */
722 static struct fsh_fsrecord *
723 fsh_fsrec_create()
724 {
725 struct fsh_fsrecord *fsrecp;
726 int i;
727
728 fsrecp = (struct fsh_fsrecord *)kmem_zalloc(sizeof (*fsrecp),
729 KM_SLEEP);
730 fsrecp->fshfsr_enabled = 1;
731 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++)
732 rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER,
733 NULL);
734 return (fsrecp);
735 }
736
737 /*
738 * This call can be used ONLY in vfs_free(). It's assumed that no other
739 * fsh_xxx() using the vfs_t that owns the fsh_fsrecord to be destroyed
740 * is executing while call to fsh_fsrec_destroy() is made. With this
741 * assumptions, no concurrency issues occur. All rwlocks of fshfsr_opv
742 * elements are released.
743 */
744 void
745 fsh_fsrec_destroy(struct fsh_fsrecord *volatile fsrecp)
746 {
747 int i;
748 fsh_node_t *node, *next_node;
749
750 for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) {
751 node = fsrecp->fshfsr_opv[i].fshl_head;
752 while (node) {
753 next_node = node->fshn_next;
754 kmem_free(node, sizeof (*node));
755 node = next_node;
756 }
757 rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock);
758 }
759 kmem_free(fsrecp, sizeof (*fsrecp));
760 }
761
762 /*
763 * fsh_init() is called in vfsinit()@vfs.c. This function MUST be called
764 * before every other FSH call.
765 */
766 void
767 fsh_init(void)
768 {
769 rw_init(&fsh_global_callback_list.fshcl_lock, NULL, RW_DRIVER, NULL);
770 fsh_res_ptr = kmem_alloc(1, KM_SLEEP);
771 }
772
773 /*
774 * These functions are used to pass control to the next hook or underlying
775 * vop/vfsop. It's consumer doesn't have to worry about any locking, because
776 * all the necessities are guaranteed by the fsh_
777 */
778 int
779 fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr,
780 caller_context_t *ct)
781 {
782 if (fsh_node == NULL)
783 return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct));
784
785 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))(
786 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
787 vpp, mode, cr, ct));
788 }
789
790 int
791 fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count,
792 offset_t offset, cred_t *cr, caller_context_t *ct)
793 {
794 if (fsh_node == NULL)
795 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
796 cr, ct));
797
798 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))(
799 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
800 vp, flag, count, offset, cr, ct));
801 }
802
803 int
804 fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
805 cred_t *cr, caller_context_t *ct)
806 {
807 if (fsh_node == NULL)
808 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
809
810 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))(
811 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
812 vp, uiop, ioflag, cr, ct));
813 }
814
815 int
816 fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
817 cred_t *cr, caller_context_t *ct)
818 {
819 if (fsh_node == NULL)
820 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
821
822 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))(
823 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
824 vp, uiop, ioflag, cr, ct));
825 }
826
827 int
828 fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp,
829 struct mounta *uap, cred_t *cr)
830 {
831 if (fsh_node == NULL)
832 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
833
834 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))(
835 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
836 vfsp, mvp, uap, cr));
837 }
838
839 int
840 fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr)
841 {
842 if (fsh_node == NULL)
843 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
844
845 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))(
846 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
847 vfsp, flag, cr));
848 }
849
850 int
851 fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp)
852 {
853 if (fsh_node == NULL)
854 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp));
855
856 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))(
857 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
858 vfsp, vpp));
859 }
860
861 int
862 fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp)
863 {
864 if (fsh_node == NULL)
865 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp));
866
867 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))(
868 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
869 vfsp, sp));
870 }
871
872 int
873 fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
874 {
875 if (fsh_node == NULL)
876 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp));
877
878 return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))(
879 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
880 vfsp, vpp, fidp));
881 }