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 }