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_FSREC(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_FSREC(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_FSREC(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                 rw_enter(&listp->fshl_lock, RW_WRITER);                  \
 137                 nodep->fshn_next =                                   \
 138                         fsrecp                                          \
 139                         ->fshfsr_opv[FSH_##type##_##upper].fshl_head;        \
 140                 fsrecp->fshfsr_opv[FSH_##type##_##upper].fshl_head   \
 141                         = nodep;                                        \
 142                 rw_exit(&listp->fshl_lock);                              \
 143         }                                                               \
 144 } while (0)
 145 
 146 #define FSH_INSTALL_VN(hooks, fsrecp, listp, nodep, lower, upper)       \
 147         FSH_INSTALL(VOP, hooks, fsrecp, listp, nodep, lower, upper)
 148 
 149 #define FSH_INSTALL_VFS(hooks, fsrecp, listp, nodep, lower, upper)      \
 150         FSH_INSTALL(VFS, hooks, fsrecp, listp, nodep, lower, upper)
 151 
 152 int
 153 fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
 154 {
 155         fsh_fsrecord_t  *fsrec;
 156         fsh_list_t      *list;
 157         fsh_node_t      *node;
 158 
 159         fsrec = FSH_GET_FSREC(vfsp);
 160 
 161         FSH_INSTALL_VN(hooks, fsrec, list, node, open, OPEN);
 162         FSH_INSTALL_VN(hooks, fsrec, list, node, close, CLOSE);
 163         FSH_INSTALL_VN(hooks, fsrec, list, node, read, READ);
 164         FSH_INSTALL_VN(hooks, fsrec, list, node, write, WRITE);
 165         FSH_INSTALL_VFS(hooks, fsrec, list, node, mount, MOUNT);
 166         FSH_INSTALL_VFS(hooks, fsrec, list, node, unmount, UNMOUNT);
 167         FSH_INSTALL_VFS(hooks, fsrec, list, node, root, ROOT);
 168         FSH_INSTALL_VFS(hooks, fsrec, list, node, vget, VGET);
 169         FSH_INSTALL_VFS(hooks, fsrec, list, node, statfs, STATFS);
 170 
 171         return (0);
 172 }
 173 
 174 
 175 #define FSH_REMOVE(type, hooks, fsrec, list, node, prev, lower, upper)  \
 176 do {                                                                    \
 177         if (hooks->hook_ ## lower == NULL)                           \
 178                 break;                                                  \
 179                                                                         \
 180         list = &fsrec->fshfsr_opv[FSH_ ## type ## _ ## upper];           \
 181         rw_enter(&list->fshl_lock, RW_WRITER);                           \
 182         node = list->fshl_head;                                              \
 183                                                                         \
 184         if (node == NULL) {                                             \
 185                 rw_exit(&list->fshl_lock);                               \
 186                 break;                                                  \
 187         }                                                               \
 188                                                                         \
 189         while (node &&                                                  \
 190                 !(node->fshn_hooki.fshi_fn.hook_ ## lower ==         \
 191                     hooks->hook_ ## lower &&                         \
 192                     node->fshn_hooki.fshi_arg == hooks->arg)) {           \
 193                 prev = node;                                            \
 194                 node = node->fshn_next;                                      \
 195         }                                                               \
 196                                                                         \
 197         if (node == NULL) {                                             \
 198                 rw_exit(&list->fshl_lock);                               \
 199                 break;                                                  \
 200         }                                                               \
 201                                                                         \
 202         if (node == list->fshl_head)                                 \
 203                 list->fshl_head = node->fshn_next;                        \
 204         else                                                            \
 205                 prev->fshn_next = node->fshn_next;                        \
 206         rw_exit(&list->fshl_lock);                                       \
 207                                                                         \
 208         kmem_free(node, sizeof (*node));                                \
 209 } while (0)
 210 
 211 #define FSH_REMOVE_VN(hooks, fsrec, list, node, prev, lower, upper)     \
 212         FSH_REMOVE(VOP, hooks, fsrec, list, node, prev, lower, upper)
 213 
 214 #define FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, lower, upper)    \
 215         FSH_REMOVE(VFS, hooks, fsrec, list, node, prev, lower, upper)
 216 
 217 int
 218 fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks)
 219 {
 220         fsh_fsrecord_t  *fsrec;
 221         fsh_list_t      *list;
 222         fsh_node_t      *node;
 223         fsh_node_t      *prev;
 224 
 225         fsrec = FSH_GET_FSREC(vfsp);
 226 
 227         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, open, OPEN);
 228         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, close, CLOSE);
 229         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, read, READ);
 230         FSH_REMOVE_VN(hooks, fsrec, list, node, prev, write, WRITE);
 231         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, mount, MOUNT);
 232         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, unmount, UNMOUNT);
 233         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, root, ROOT);
 234         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, vget, VGET);
 235         FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, statfs, STATFS);
 236 
 237         return (0);
 238 }
 239 
 240 
 241 int
 242 fsh_callback_install(fsh_callback_t *fsh_callback)
 243 {
 244         fsh_callback_node_t *node;
 245 
 246         node = (fsh_callback_node_t *) kmem_alloc(sizeof (*node), KM_SLEEP);
 247         node->fshcn_callback = *fsh_callback;
 248 
 249         rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER);
 250         node->fshcn_next = fsh_global_callback_list.fshcl_head;
 251         fsh_global_callback_list.fshcl_head = node;
 252         rw_exit(&fsh_global_callback_list.fshcl_lock);
 253 
 254         return (0);
 255 }
 256 
 257 int
 258 fsh_callback_remove(fsh_callback_t *fsh_callback)
 259 {
 260         fsh_callback_node_t *node;
 261         fsh_callback_node_t *prev;
 262         fsh_callback_list_t *list;
 263 
 264         list = &fsh_global_callback_list;
 265 
 266         rw_enter(&list->fshcl_lock, RW_WRITER);
 267         node = list->fshcl_head;
 268 
 269         if (node == NULL) {
 270                 rw_exit(&list->fshcl_lock);
 271                 return (0);
 272         }
 273 
 274         while (node && memcmp(fsh_callback, &node->fshcn_callback,
 275                 sizeof (*fsh_callback))) {
 276                 prev = node;
 277                 node = node->fshcn_next;
 278         }
 279 
 280         if (node == NULL) {
 281                 rw_exit(&list->fshcl_lock);
 282                 return (0);
 283         }
 284 
 285         prev->fshcn_next = node->fshcn_next;
 286         kmem_free(node, sizeof (*node));
 287 
 288         rw_exit(&list->fshcl_lock);
 289         return (0);
 290 }
 291 
 292 
 293 
 294 
 295 #define FSH_ENABLED(vfsp, enabled)                                      \
 296 do {                                                                    \
 297         rw_enter(&FSH_GET_FSREC(vfsp)->fshfsr_en_lock, RW_READER);       \
 298         *enabled = FSH_GET_FSREC(vfsp)->fshfsr_enabled;                      \
 299         rw_exit(&FSH_GET_FSREC(vfsp)->fshfsr_en_lock);           \
 300 } while (0)
 301 
 302 int
 303 fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
 304 {
 305         fsh_list_t *list;
 306         int enabled;
 307         int ret;
 308 
 309         FSH_ENABLED((*vpp)->v_vfsp, &enabled);
 310         if (!enabled)
 311                 return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct));
 312 
 313         list = &FSH_GET_FSREC((*vpp)->v_vfsp)->fshfsr_opv[FSH_VOP_OPEN];
 314         rw_enter(&list->fshl_lock, RW_READER);
 315         if (list->fshl_head == NULL)
 316                 ret =  (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct);
 317         else
 318                 ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct);
 319         rw_exit(&list->fshl_lock);
 320 
 321         return (ret);
 322 }
 323 
 324 int
 325 fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
 326         caller_context_t *ct)
 327 {
 328         fsh_list_t *list;
 329         int enabled;
 330         int ret;
 331 
 332         FSH_ENABLED(vp->v_vfsp, &enabled);
 333         if (!enabled)
 334                 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
 335                         cr, ct));
 336 
 337         list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_CLOSE];
 338         rw_enter(&list->fshl_lock, RW_READER);
 339         if (list->fshl_head == NULL)
 340                 ret =  (*(vp->v_op->vop_close))(vp, flag, count, offset,
 341                         cr, ct);
 342         else
 343                 ret = fsh_next_close(list->fshl_head, vp, flag, count,
 344                         offset, cr, ct);
 345         rw_exit(&list->fshl_lock);
 346 
 347         return (ret);
 348 }
 349 
 350 int
 351 fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
 352         caller_context_t *ct)
 353 {
 354         fsh_list_t *list;
 355         int enabled;
 356         int ret;
 357 
 358         FSH_ENABLED(vp->v_vfsp, &enabled);
 359         if (!enabled)
 360                 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
 361 
 362         list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_READ];
 363         rw_enter(&list->fshl_lock, RW_READER);
 364         if (list->fshl_head == NULL)
 365                 ret =  (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct);
 366         else
 367                 ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag,
 368                         cr, ct);
 369         rw_exit(&list->fshl_lock);
 370 
 371         return (ret);
 372 }
 373 
 374 int
 375 fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
 376         caller_context_t *ct)
 377 {
 378         fsh_list_t *list;
 379         int enabled;
 380         int ret;
 381 
 382         FSH_ENABLED(vp->v_vfsp, &enabled);
 383         if (!enabled)
 384                 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
 385 
 386         list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_WRITE];
 387         rw_enter(&list->fshl_lock, RW_READER);
 388         if (list->fshl_head == NULL)
 389                 ret =  (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct);
 390         else
 391                 ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag,
 392                         cr, ct);
 393         rw_exit(&list->fshl_lock);
 394 
 395         return (ret);
 396 }
 397 
 398 int
 399 fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 400 {
 401         fsh_list_t *list;
 402         int ret;
 403 
 404         list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_MOUNT];
 405         rw_enter(&list->fshl_lock, RW_READER);
 406         if (list->fshl_head == NULL)
 407                 ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr);
 408         else
 409                 ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap,
 410                         cr);
 411         rw_exit(&list->fshl_lock);
 412 
 413         return (ret);
 414 }
 415 
 416 int
 417 fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
 418 {
 419         fsh_list_t *list;
 420         int ret;
 421 
 422         list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_UNMOUNT];
 423         rw_enter(&list->fshl_lock, RW_READER);
 424         if (list->fshl_head == NULL)
 425                 ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr);
 426         else
 427                 ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr);
 428         rw_exit(&list->fshl_lock);
 429 
 430         return (ret);
 431 }
 432 
 433 int
 434 fsh_root(vfs_t *vfsp, vnode_t **vpp)
 435 {
 436         fsh_list_t *list;
 437         int ret;
 438 
 439         list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_ROOT];
 440         rw_enter(&list->fshl_lock, RW_READER);
 441         if (list->fshl_head == NULL)
 442                 ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp);
 443         else
 444                 ret = fsh_next_root(list->fshl_head, vfsp, vpp);
 445         rw_exit(&list->fshl_lock);
 446 
 447         return (ret);
 448 }
 449 
 450 int
 451 fsh_statfs(vfs_t *vfsp, statvfs64_t *sp)
 452 {
 453         fsh_list_t *list;
 454         int ret;
 455 
 456         list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_STATFS];
 457         rw_enter(&list->fshl_lock, RW_READER);
 458         if (list->fshl_head == NULL)
 459                 ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp);
 460         else
 461                 ret = fsh_next_statfs(list->fshl_head, vfsp, sp);
 462         rw_exit(&list->fshl_lock);
 463 
 464         return (ret);
 465 }
 466 
 467 int
 468 fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
 469 {
 470         fsh_list_t *list;
 471         int ret;
 472 
 473         list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_VGET];
 474         rw_enter(&list->fshl_lock, RW_READER);
 475         if (list->fshl_head == NULL)
 476                 ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp);
 477         else
 478                 ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp);
 479         rw_exit(&list->fshl_lock);
 480 
 481         return (ret);
 482 }
 483 
 484 void
 485 fsh_exec_create_callbacks(vfs_t *vfsp)
 486 {
 487         fsh_callback_node_t *node;
 488         fsh_callback_t *callback;
 489 
 490         rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
 491         node = fsh_global_callback_list.fshcl_head;
 492         while (node) {
 493                 callback = &node->fshcn_callback;
 494                 (*(callback->fshc_create))(vfsp, callback->fshc_arg);
 495                 node = node->fshcn_next;
 496         }
 497         rw_exit(&fsh_global_callback_list.fshcl_lock);
 498 }
 499 
 500 void
 501 fsh_exec_destroy_callbacks(vfs_t *vfsp)
 502 {
 503         fsh_callback_node_t *node;
 504         fsh_callback_t *callback;
 505 
 506         rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
 507         node = fsh_global_callback_list.fshcl_head;
 508         while (node) {
 509                 callback = &node->fshcn_callback;
 510                 (*(callback->fshc_destroy))(vfsp, callback->fshc_arg);
 511                 node = node->fshcn_next;
 512         }
 513         rw_exit(&fsh_global_callback_list.fshcl_lock);
 514 }
 515 
 516 /* To be used ONLY in vfs_alloc() */
 517 struct fsh_fsrecord *
 518 fsh_fsrec_create()
 519 {
 520         struct fsh_fsrecord *fsrecp;
 521         int i;
 522 
 523         fsrecp = (fsh_fsrecord_t *) kmem_alloc(sizeof (*fsrecp), KM_SLEEP);
 524         bzero(fsrecp, sizeof (*fsrecp));
 525 
 526         rw_init(&fsrecp->fshfsr_en_lock, NULL, RW_DRIVER, NULL);
 527         fsrecp->fshfsr_enabled = 1;
 528 
 529         for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++)
 530                 rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER,
 531                         NULL);
 532         return fsrecp;
 533 }
 534 
 535 /* To be used ONLY in vfs_free() */
 536 void
 537 fsh_fsrec_destroy(fsh_fsrecord_t *fsrecp)
 538 {
 539         int i;
 540         fsh_node_t *node, *next_node;
 541 
 542         for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) {
 543                 node = fsrecp->fshfsr_opv[i].fshl_head;
 544                 while (node) {
 545                         next_node = node->fshn_next;
 546                         kmem_free(node, sizeof (*node));
 547                         node = next_node;
 548                 }
 549                 rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock);
 550         }
 551         rw_destroy(&fsrecp->fshfsr_en_lock);
 552         kmem_free(fsrecp, sizeof (*fsrecp));
 553 }
 554 
 555 
 556 /* control passing */
 557 int
 558 fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr,
 559         caller_context_t *ct)
 560 {
 561         if (fsh_node == NULL)
 562                 return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct));
 563 
 564         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))(
 565                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 566                 vpp, mode, cr, ct));
 567 
 568 }
 569 
 570 int
 571 fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count,
 572         offset_t offset, cred_t *cr, caller_context_t *ct)
 573 {
 574         if (fsh_node == NULL)
 575                 return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
 576                         cr, ct));
 577 
 578         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))(
 579                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 580                 vp, flag, count, offset, cr, ct));
 581 }
 582 
 583 int
 584 fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
 585         cred_t *cr, caller_context_t *ct)
 586 {
 587         if (fsh_node == NULL)
 588                 return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
 589 
 590         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))(
 591                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 592                 vp, uiop, ioflag, cr, ct));
 593 }
 594 
 595 int
 596 fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
 597         cred_t *cr, caller_context_t *ct)
 598 {
 599         if (fsh_node == NULL)
 600                 return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
 601 
 602         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))(
 603                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 604                 vp, uiop, ioflag, cr, ct));
 605 }
 606 
 607 int
 608 fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp,
 609         struct mounta *uap, cred_t *cr)
 610 {
 611         if (fsh_node == NULL)
 612                 return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
 613 
 614         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))(
 615                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 616                 vfsp, mvp, uap, cr));
 617 }
 618 
 619 int
 620 fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr)
 621 {
 622         if (fsh_node == NULL)
 623                 return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
 624 
 625         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))(
 626                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 627                 vfsp, flag, cr));
 628 }
 629 
 630 int
 631 fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp)
 632 {
 633         if (fsh_node == NULL)
 634                 return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp));
 635 
 636         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))(
 637                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 638                 vfsp, vpp));
 639 }
 640 
 641 int
 642 fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp)
 643 {
 644         if (fsh_node == NULL)
 645                 return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp));
 646 
 647         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))(
 648                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 649                 vfsp, sp));
 650 }
 651 
 652 int
 653 fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
 654 {
 655         if (fsh_node == NULL)
 656                 return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp));
 657 
 658         return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))(
 659                 fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
 660                 vfsp, vpp, fidp));
 661 }