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/sunddi.h>
  17 #include <sys/fsh.h>
  18 #include <sys/fsh_impl.h>
  19 #include <sys/ksynch.h>
  20 #include <sys/types.h>
  21 #include <sys/vfs.h>
  22 #include <sys/vnode.h>
  23 
  24 /*
  25  * TODO:
  26  * - support more operations
  27  * - describe the design of FSH in a comment
  28  * - add DTrace and kstat
  29  */
  30 
  31 #define FSH_VFS_MOUNT           0
  32 #define FSH_VFS_UNMOUNT         1
  33 #define FSH_VFS_ROOT            2
  34 #define FSH_VFS_STATFS          3
  35 #define FSH_VFS_VGET            4
  36 
  37 #define FSH_VOP_OPEN            5
  38 #define FSH_VOP_CLOSE           6
  39 #define FSH_VOP_READ            7
  40 #define FSH_VOP_WRITE           8
  41 
  42 #define FSH_SUPPORTED_OPS_COUNT 9
  43 
  44 typedef union fsh_fn {
  45         FSH_OPS;
  46 } fsh_fn_t;
  47 
  48 typedef struct fsh_int {
  49         fsh_fn_t        fshi_fn;
  50         void            *fshi_arg;
  51 } fsh_int_t;
  52 
  53 
  54 struct fsh_node {
  55         fsh_int_t       fshn_hooki;
  56         struct fsh_node *fshn_next;
  57 };
  58 /* typedef struct fsh_node fsh_node_t; in fsh.h */
  59 
  60 /*
  61  * fshl_lock is being read-locked by every call to a fsh_vop/vfsop() for
  62  * entire execution. This way, we guarantee that a list of hooks is unchanged
  63  * during one fop_foo()/fsop_foo() execution.
  64  */
  65 typedef struct fsh_list {
  66         krwlock_t       fshl_lock;
  67         fsh_node_t      *fshl_head;
  68 } fsh_list_t;
  69 
  70 typedef fsh_list_t fsh_opvector[FSH_SUPPORTED_OPS_COUNT];
  71 typedef fsh_opvector fsh_opvector_t;
  72 
  73 
  74 typedef struct fsh_fsrecord {
  75         krwlock_t               fshfsr_en_lock; /* lock for fshfsr_enabled */
  76         int                     fshfsr_enabled;
  77         fsh_opvector_t          fshfsr_opv;
  78 } fsh_fsrecord_t;
  79 
  80 
  81 typedef struct fsh_callback_node {
  82         fsh_callback_t                  fshcn_callback;
  83         struct fsh_callback_node        *fshcn_next;
  84 } fsh_callback_node_t;
  85 
  86 typedef struct fsh_callback_list {
  87         krwlock_t               fshcl_lock;
  88         fsh_callback_node_t     *fshcl_head;
  89 } fsh_callback_list_t;
  90 
  91 fsh_callback_list_t fsh_global_callback_list;
  92 
  93 /*
  94  * It is assumed that VFS_HOLD() has been called before calling any of the
  95  * fsh_fs_xxx()/fsh_hook_xxx() API. VFS_RELE() should be called after.
  96  */
  97 
  98 #define FSH_GET_FSRECP(vfsp)    (vfsp->vfs_fshrecord)
  99 
 100 int
 101 fsh_fs_enable(vfs_t *vfsp)
 102 {
 103         fsh_fsrecord_t *fsrec;
 104 
 105         fsrec = FSH_GET_FSRECP(vfsp);
 106         rw_enter(&fsrec->fshfsr_en_lock, RW_WRITER);
 107         fsrec->fshfsr_enabled = 1;
 108         rw_exit(&fsrec->fshfsr_en_lock);
 109 
 110         return (0);
 111 }
 112 
 113 int
 114 fsh_fs_disable(vfs_t *vfsp)
 115 {
 116         fsh_fsrecord_t *fsrec;
 117 
 118         fsrec = FSH_GET_FSRECP(vfsp);
 119         rw_enter(&fsrec->fshfsr_en_lock, RW_WRITER);
 120         fsrec->fshfsr_enabled = 0;
 121         rw_exit(&fsrec->fshfsr_en_lock);
 122 
 123         return (0);
 124 }
 125 
 126 
 127 #define FSH_INSTALL(type, hooks, fsrecp, listp, nodep, lower, upper)    \
 128 do {                                                                    \
 129         if (hooks->hook_##lower) {                                   \
 130                 nodep = (fsh_node_t *) kmem_alloc(sizeof (*nodep),      \
 131                                         KM_SLEEP);                      \
 132                 nodep->fshn_hooki.fshi_fn.hook_##lower =             \
 133                         hooks->hook_##lower;                         \
 134                 nodep->fshn_hooki.fshi_arg = hooks->arg;          \
 135                                                                         \
 136                 listp = &fsrecp->fshfsr_opv[FSH_##type##_##upper];       \
 137                 rw_enter(&listp->fshl_lock, RW_WRITER);                  \
 138                 nodep->fshn_next =                                   \
 139                         fsrecp                                          \
 140                         ->fshfsr_opv[FSH_##type##_##upper].fshl_head;        \
 141                 fsrecp->fshfsr_opv[FSH_##type##_##upper].fshl_head   \
 142                         = nodep;                                        \
 143                 rw_exit(&listp->fshl_lock);                              \
 144         }                                                               \
 145 _NOTE(CONSTCOND)                                                        \
 146 } while (0)
 147 
 148 #define FSH_INSTALL_VN(hooks, fsrecp, listp, nodep, lower, upper)       \
 149         FSH_INSTALL(VOP, hooks, fsrecp, listp, nodep, lower, upper)
 150 
 151 #define FSH_INSTALL_VFS(hooks, fsrecp, listp, nodep, lower, upper)      \
 152         FSH_INSTALL(VFS, hooks, fsrecp, listp, nodep, lower, upper)
 153 
 154 int
 155 fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
 156 {
 157         fsh_fsrecord_t  *fsrec;
 158         fsh_list_t      *list;
 159         fsh_node_t      *node;
 160 
 161         fsrec = FSH_GET_FSRECP(vfsp);
 162 
 163         FSH_INSTALL_VN(hooks, fsrec, list, node, open, OPEN);
 164         FSH_INSTALL_VN(hooks, fsrec, list, node, close, CLOSE);
 165         FSH_INSTALL_VN(hooks, fsrec, list, node, read, READ);
 166         FSH_INSTALL_VN(hooks, fsrec, list, node, write, WRITE);
 167         FSH_INSTALL_VFS(hooks, fsrec, list, node, mount, MOUNT);
 168         FSH_INSTALL_VFS(hooks, fsrec, list, node, unmount, UNMOUNT);
 169         FSH_INSTALL_VFS(hooks, fsrec, list, node, root, ROOT);
 170         FSH_INSTALL_VFS(hooks, fsrec, list, node, vget, VGET);
 171         FSH_INSTALL_VFS(hooks, fsrec, list, node, statfs, STATFS);
 172 
 173         return (0);
 174 }
 175 
 176 
 177 #define FSH_REMOVE(type, hooks, fsrec, list, node, prev, lower, upper)  \
 178 do {                                                                    \
 179         if (hooks->hook_ ## lower == NULL)                           \
 180                 break;                                                  \
 181                                                                         \
 182         list = &fsrec->fshfsr_opv[FSH_ ## type ## _ ## upper];           \
 183         rw_enter(&list->fshl_lock, RW_WRITER);                           \
 184         node = list->fshl_head;                                              \
 185                                                                         \
 186         if (node == NULL) {                                             \
 187                 rw_exit(&list->fshl_lock);                               \
 188                 break;                                                  \
 189         }                                                               \
 190                                                                         \
 191         while (node &&                                                  \
 192                 !(node->fshn_hooki.fshi_fn.hook_ ## lower ==         \
 193                     hooks->hook_ ## lower &&                         \
 194                     node->fshn_hooki.fshi_arg == hooks->arg)) {           \
 195                 prev = node;                                            \
 196                 node = node->fshn_next;                                      \
 197         }                                                               \
 198                                                                         \
 199         if (node == NULL) {                                             \
 200                 rw_exit(&list->fshl_lock);                               \
 201                 break;                                                  \
 202         }                                                               \
 203                                                                         \
 204         if (node == list->fshl_head)                                 \
 205                 list->fshl_head = node->fshn_next;                        \
 206         else                                                            \
 207                 prev->fshn_next = node->fshn_next;                        \
 208         rw_exit(&list->fshl_lock);                                       \
 209                                                                         \
 210         kmem_free(node, sizeof (*node));                                \
 211 _NOTE(CONSTCOND)                                                        \
 212 } while (0)
 213 
 214 #define FSH_REMOVE_VN(hooks, fsrec, list, node, prev, lower, upper)     \
 215         FSH_REMOVE(VOP, hooks, fsrec, list, node, prev, lower, upper)
 216 
 217 #define FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, lower, upper)    \
 218         FSH_REMOVE(VFS, hooks, fsrec, list, node, prev, lower, upper)
 219 
 220 int
 221 fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks)
 222 {
 223         fsh_fsrecord_t  *fsrec;
 224         fsh_list_t      *list;
 225         fsh_node_t      *node;
 226         fsh_node_t      *prev;
 227 
 228         fsrec = FSH_GET_FSRECP(vfsp);
 229 
 230         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, open, OPEN);
 231         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, close, CLOSE);
 232         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, read, READ);
 233         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, write, WRITE);
 234         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, mount, MOUNT);
 235         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, unmount, UNMOUNT);
 236         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, root, ROOT);
 237         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, vget, VGET);
 238         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, statfs, STATFS);
 239 
 240         return (0);
 241 }
 242 
 243 
 244 int
 245 fsh_callback_install(fsh_callback_t *fsh_callback)
 246 {
 247         fsh_callback_node_t *node;
 248 
 249         node = (fsh_callback_node_t *) kmem_alloc(sizeof (*node), KM_SLEEP);
 250         node->fshcn_callback = *fsh_callback;
 251 
 252         rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER);
 253         node->fshcn_next = fsh_global_callback_list.fshcl_head;
 254         fsh_global_callback_list.fshcl_head = node;
 255         rw_exit(&fsh_global_callback_list.fshcl_lock);
 256 
 257         return (0);
 258 }
 259 
 260 int
 261 fsh_callback_remove(fsh_callback_t *fsh_callback)
 262 {
 263         fsh_callback_node_t *node;
 264         fsh_callback_node_t *prev;
 265         fsh_callback_list_t *list;
 266 
 267         list = &fsh_global_callback_list;
 268 
 269         rw_enter(&list->fshcl_lock, RW_WRITER);
 270         node = list->fshcl_head;
 271 
 272         if (node == NULL) {
 273                 rw_exit(&list->fshcl_lock);
 274                 return (0);
 275         }
 276 
 277         while (node && memcmp(fsh_callback, &node->fshcn_callback,
 278                 sizeof (*fsh_callback))) {
 279                 prev = node;
 280                 node = node->fshcn_next;
 281         }
 282 
 283         if (node == NULL) {
 284                 rw_exit(&list->fshcl_lock);
 285                 return (0);
 286         }
 287 
 288         prev->fshcn_next = node->fshcn_next;
 289         kmem_free(node, sizeof (*node));
 290 
 291         rw_exit(&list->fshcl_lock);
 292         return (0);
 293 }
 294 
 295 
 296 
 297 
 298 #define FSH_ENABLED(vfsp, enabled)                                      \
 299 do {                                                                    \
 300         rw_enter(&FSH_GET_FSRECP(vfsp)->fshfsr_en_lock, RW_READER);      \
 301         *enabled = FSH_GET_FSRECP(vfsp)->fshfsr_enabled;                     \
 302         rw_exit(&FSH_GET_FSRECP(vfsp)->fshfsr_en_lock);                  \
 303 _NOTE(CONSTCOND)                                                        \
 304 } while (0)
 305 
 306 int
 307 fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
 308 {
 309         fsh_list_t *list;
 310         int enabled;
 311         int ret;
 312 
 313         FSH_ENABLED((*vpp)->v_vfsp, &enabled);
 314         if (!enabled)
 315                 return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct));
 316 
 317         list = &FSH_GET_FSRECP((*vpp)->v_vfsp)->fshfsr_opv[FSH_VOP_OPEN];
 318         rw_enter(&list->fshl_lock, RW_READER);
 319         if (list->fshl_head == NULL)
 320                 ret =  (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct);
 321         else
 322                 ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct);
 323         rw_exit(&list->fshl_lock);
 324 
 325         return (ret);
 326 }
 327 
 328 int
 329 fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
 330         caller_context_t *ct)
 331 {
 332         fsh_list_t *list;
 333         int enabled;
 334         int ret;
 335 
 336         FSH_ENABLED(vp->v_vfsp, &enabled);
 337         if (!enabled)
 338                 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
 339                         cr, ct));
 340 
 341         list = &FSH_GET_FSRECP(vp->v_vfsp)->fshfsr_opv[FSH_VOP_CLOSE];
 342         rw_enter(&list->fshl_lock, RW_READER);
 343         if (list->fshl_head == NULL)
 344                 ret =  (*(vp->v_op->vop_close))(vp, flag, count, offset,
 345                         cr, ct);
 346         else
 347                 ret = fsh_next_close(list->fshl_head, vp, flag, count,
 348                         offset, cr, ct);
 349         rw_exit(&list->fshl_lock);
 350 
 351         return (ret);
 352 }
 353 
 354 int
 355 fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
 356         caller_context_t *ct)
 357 {
 358         fsh_list_t *list;
 359         int enabled;
 360         int ret;
 361 
 362         FSH_ENABLED(vp->v_vfsp, &enabled);
 363         if (!enabled)
 364                 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
 365 
 366         list = &FSH_GET_FSRECP(vp->v_vfsp)->fshfsr_opv[FSH_VOP_READ];
 367         rw_enter(&list->fshl_lock, RW_READER);
 368         if (list->fshl_head == NULL)
 369                 ret =  (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct);
 370         else
 371                 ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag,
 372                         cr, ct);
 373         rw_exit(&list->fshl_lock);
 374 
 375         return (ret);
 376 }
 377 
 378 int
 379 fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
 380         caller_context_t *ct)
 381 {
 382         fsh_list_t *list;
 383         int enabled;
 384         int ret;
 385 
 386         FSH_ENABLED(vp->v_vfsp, &enabled);
 387         if (!enabled)
 388                 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
 389 
 390         list = &FSH_GET_FSRECP(vp->v_vfsp)->fshfsr_opv[FSH_VOP_WRITE];
 391         rw_enter(&list->fshl_lock, RW_READER);
 392         if (list->fshl_head == NULL)
 393                 ret =  (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct);
 394         else
 395                 ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag,
 396                         cr, ct);
 397         rw_exit(&list->fshl_lock);
 398 
 399         return (ret);
 400 }
 401 
 402 int
 403 fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 404 {
 405         fsh_list_t *list;
 406         int ret;
 407 
 408         list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_MOUNT];
 409         rw_enter(&list->fshl_lock, RW_READER);
 410         if (list->fshl_head == NULL)
 411                 ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr);
 412         else
 413                 ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap,
 414                         cr);
 415         rw_exit(&list->fshl_lock);
 416 
 417         return (ret);
 418 }
 419 
 420 int
 421 fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
 422 {
 423         fsh_list_t *list;
 424         int ret;
 425 
 426         list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_UNMOUNT];
 427         rw_enter(&list->fshl_lock, RW_READER);
 428         if (list->fshl_head == NULL)
 429                 ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr);
 430         else
 431                 ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr);
 432         rw_exit(&list->fshl_lock);
 433 
 434         return (ret);
 435 }
 436 
 437 int
 438 fsh_root(vfs_t *vfsp, vnode_t **vpp)
 439 {
 440         fsh_list_t *list;
 441         int ret;
 442 
 443         list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_ROOT];
 444         rw_enter(&list->fshl_lock, RW_READER);
 445         if (list->fshl_head == NULL)
 446                 ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp);
 447         else
 448                 ret = fsh_next_root(list->fshl_head, vfsp, vpp);
 449         rw_exit(&list->fshl_lock);
 450 
 451         return (ret);
 452 }
 453 
 454 int
 455 fsh_statfs(vfs_t *vfsp, statvfs64_t *sp)
 456 {
 457         fsh_list_t *list;
 458         int ret;
 459 
 460         list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_STATFS];
 461         rw_enter(&list->fshl_lock, RW_READER);
 462         if (list->fshl_head == NULL)
 463                 ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp);
 464         else
 465                 ret = fsh_next_statfs(list->fshl_head, vfsp, sp);
 466         rw_exit(&list->fshl_lock);
 467 
 468         return (ret);
 469 }
 470 
 471 int
 472 fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
 473 {
 474         fsh_list_t *list;
 475         int ret;
 476 
 477         list = &FSH_GET_FSRECP(vfsp)->fshfsr_opv[FSH_VFS_VGET];
 478         rw_enter(&list->fshl_lock, RW_READER);
 479         if (list->fshl_head == NULL)
 480                 ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp);
 481         else
 482                 ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp);
 483         rw_exit(&list->fshl_lock);
 484 
 485         return (ret);
 486 }
 487 
 488 void
 489 fsh_exec_create_callbacks(vfs_t *vfsp)
 490 {
 491         fsh_callback_node_t *node;
 492         fsh_callback_t *callback;
 493 
 494         rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
 495         node = fsh_global_callback_list.fshcl_head;
 496         while (node) {
 497                 callback = &node->fshcn_callback;
 498                 (*(callback->fshc_create))(vfsp, callback->fshc_arg);
 499                 node = node->fshcn_next;
 500         }
 501         rw_exit(&fsh_global_callback_list.fshcl_lock);
 502 }
 503 
 504 void
 505 fsh_exec_destroy_callbacks(vfs_t *vfsp)
 506 {
 507         fsh_callback_node_t *node;
 508         fsh_callback_t *callback;
 509 
 510         rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
 511         node = fsh_global_callback_list.fshcl_head;
 512         while (node) {
 513                 callback = &node->fshcn_callback;
 514                 (*(callback->fshc_destroy))(vfsp, callback->fshc_arg);
 515                 node = node->fshcn_next;
 516         }
 517         rw_exit(&fsh_global_callback_list.fshcl_lock);
 518 }
 519 
 520 /* To be used ONLY in vfs_alloc() */
 521 struct fsh_fsrecord *
 522 fsh_fsrec_create()
 523 {
 524         struct fsh_fsrecord *fsrecp;
 525         int i;
 526 
 527         fsrecp = (fsh_fsrecord_t *) kmem_alloc(sizeof (*fsrecp), KM_SLEEP);
 528         bzero(fsrecp, sizeof (*fsrecp));
 529 
 530         rw_init(&fsrecp->fshfsr_en_lock, NULL, RW_DRIVER, NULL);
 531         fsrecp->fshfsr_enabled = 0; // DEBUG
 532 
 533         for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++)
 534                 rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER,
 535                         NULL);
 536         return (fsrecp);
 537 }
 538 
 539 /* To be used ONLY in vfs_free() */
 540 void
 541 fsh_fsrec_destroy(fsh_fsrecord_t *fsrecp)
 542 {
 543         int i;
 544         fsh_node_t *node, *next_node;
 545 
 546         for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) {
 547                 node = fsrecp->fshfsr_opv[i].fshl_head;
 548                 while (node) {
 549                         next_node = node->fshn_next;
 550                         kmem_free(node, sizeof (*node));
 551                         node = next_node;
 552                 }
 553                 rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock);
 554         }
 555         rw_destroy(&fsrecp->fshfsr_en_lock);
 556         kmem_free(fsrecp, sizeof (*fsrecp));
 557 }
 558 
 559 
 560 /* control passing */
 561 int
 562 fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr,
 563         caller_context_t *ct)
 564 {
 565         if (fsh_node == NULL)
 566                 return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct));
 567 
 568         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))(
 569                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 570                 vpp, mode, cr, ct));
 571 
 572 }
 573 
 574 int
 575 fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count,
 576         offset_t offset, cred_t *cr, caller_context_t *ct)
 577 {
 578         if (fsh_node == NULL)
 579                 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
 580                         cr, ct));
 581 
 582         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))(
 583                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 584                 vp, flag, count, offset, cr, ct));
 585 }
 586 
 587 int
 588 fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
 589         cred_t *cr, caller_context_t *ct)
 590 {
 591         if (fsh_node == NULL)
 592                 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
 593 
 594         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))(
 595                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 596                 vp, uiop, ioflag, cr, ct));
 597 }
 598 
 599 int
 600 fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
 601         cred_t *cr, caller_context_t *ct)
 602 {
 603         if (fsh_node == NULL)
 604                 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
 605 
 606         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))(
 607                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 608                 vp, uiop, ioflag, cr, ct));
 609 }
 610 
 611 int
 612 fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp,
 613         struct mounta *uap, cred_t *cr)
 614 {
 615         if (fsh_node == NULL)
 616                 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
 617 
 618         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))(
 619                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 620                 vfsp, mvp, uap, cr));
 621 }
 622 
 623 int
 624 fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr)
 625 {
 626         if (fsh_node == NULL)
 627                 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
 628 
 629         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))(
 630                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 631                 vfsp, flag, cr));
 632 }
 633 
 634 int
 635 fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp)
 636 {
 637         if (fsh_node == NULL)
 638                 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp));
 639 
 640         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))(
 641                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 642                 vfsp, vpp));
 643 }
 644 
 645 int
 646 fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp)
 647 {
 648         if (fsh_node == NULL)
 649                 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp));
 650 
 651         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))(
 652                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 653                 vfsp, sp));
 654 }
 655 
 656 int
 657 fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
 658 {
 659         if (fsh_node == NULL)
 660                 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp));
 661 
 662         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))(
 663                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 664                 vfsp, vpp, fidp));
 665 }