--- /dev/null	Fri Jul 19 18:39:52 2013
+++ new/usr/src/uts/common/fs/fsh.c	Fri Jul 19 18:39:52 2013
@@ -0,0 +1,661 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2013 Damian Bogel.  All rights reserved.
+ */
+
+#include <sys/sunddi.h>
+#include <sys/fsh.h>
+#include <sys/fsh_impl.h>
+#include <sys/ksynch.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+
+/*
+ * TODO:
+ * - support more operations
+ * - describe the design of FSH in a comment
+ * - add DTrace and kstat
+ */
+
+#define	FSH_VFS_MOUNT		0
+#define	FSH_VFS_UNMOUNT		1
+#define	FSH_VFS_ROOT		2
+#define	FSH_VFS_STATFS		3
+#define	FSH_VFS_VGET		4
+
+#define	FSH_VOP_OPEN		5
+#define	FSH_VOP_CLOSE		6
+#define	FSH_VOP_READ		7
+#define	FSH_VOP_WRITE		8
+
+#define	FSH_SUPPORTED_OPS_COUNT	9
+
+typedef union fsh_fn {
+	FSH_OPS;
+} fsh_fn_t;
+
+typedef struct fsh_int {
+	fsh_fn_t	fshi_fn;
+	void		*fshi_arg;
+} fsh_int_t;
+
+
+struct fsh_node {
+	fsh_int_t	fshn_hooki;
+	struct fsh_node	*fshn_next;
+};
+/* typedef struct fsh_node fsh_node_t; in fsh.h */
+
+/*
+ * fshl_lock is being read-locked by every call to a fsh_vop/vfsop() for
+ * entire execution. This way, we guarantee that a list of hooks is unchanged
+ * during one fop_foo()/fsop_foo() execution.
+ */
+typedef struct fsh_list {
+	krwlock_t	fshl_lock;
+	fsh_node_t	*fshl_head;
+} fsh_list_t;
+
+typedef fsh_list_t fsh_opvector[FSH_SUPPORTED_OPS_COUNT];
+typedef fsh_opvector fsh_opvector_t;
+
+
+typedef struct fsh_fsrecord {
+	krwlock_t		fshfsr_en_lock;	/* lock for fshfsr_enabled */
+	int			fshfsr_enabled;
+	fsh_opvector_t		fshfsr_opv;
+} fsh_fsrecord_t;
+
+
+typedef struct fsh_callback_node {
+	fsh_callback_t			fshcn_callback;
+	struct fsh_callback_node	*fshcn_next;
+} fsh_callback_node_t;
+
+typedef struct fsh_callback_list {
+	krwlock_t		fshcl_lock;
+	fsh_callback_node_t	*fshcl_head;
+} fsh_callback_list_t;
+
+fsh_callback_list_t fsh_global_callback_list;
+
+/*
+ * It is assumed that VFS_HOLD() has been called before calling any of the
+ * fsh_fs_xxx()/fsh_hook_xxx() API. VFS_RELE() should be called after.
+ */
+
+#define	FSH_GET_FSREC(vfsp)	(vfsp->vfs_fshrecord)
+
+int
+fsh_fs_enable(vfs_t *vfsp)
+{
+	fsh_fsrecord_t *fsrec;
+
+	fsrec = FSH_GET_FSREC(vfsp);
+	rw_enter(&fsrec->fshfsr_en_lock, RW_WRITER);
+	fsrec->fshfsr_enabled = 1;
+	rw_exit(&fsrec->fshfsr_en_lock);
+
+	return (0);
+}
+
+int
+fsh_fs_disable(vfs_t *vfsp)
+{
+	fsh_fsrecord_t *fsrec;
+
+	fsrec = FSH_GET_FSREC(vfsp);
+	rw_enter(&fsrec->fshfsr_en_lock, RW_WRITER);
+	fsrec->fshfsr_enabled = 0;
+	rw_exit(&fsrec->fshfsr_en_lock);
+
+	return (0);
+}
+
+
+#define	FSH_INSTALL(type, hooks, fsrecp, listp, nodep, lower, upper)	\
+do {									\
+	if (hooks->hook_##lower) {					\
+		nodep = (fsh_node_t *) kmem_alloc(sizeof (*nodep),	\
+					KM_SLEEP);			\
+		nodep->fshn_hooki.fshi_fn.hook_##lower = 		\
+			hooks->hook_##lower;				\
+		nodep->fshn_hooki.fshi_arg = hooks->arg;		\
+									\
+		rw_enter(&listp->fshl_lock, RW_WRITER);			\
+		nodep->fshn_next =					\
+			fsrecp						\
+			->fshfsr_opv[FSH_##type##_##upper].fshl_head;	\
+		fsrecp->fshfsr_opv[FSH_##type##_##upper].fshl_head 	\
+			= nodep;					\
+		rw_exit(&listp->fshl_lock);				\
+	}								\
+} while (0)
+
+#define	FSH_INSTALL_VN(hooks, fsrecp, listp, nodep, lower, upper)	\
+	FSH_INSTALL(VOP, hooks, fsrecp, listp, nodep, lower, upper)
+
+#define	FSH_INSTALL_VFS(hooks, fsrecp, listp, nodep, lower, upper)	\
+	FSH_INSTALL(VFS, hooks, fsrecp, listp, nodep, lower, upper)
+
+int
+fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
+{
+	fsh_fsrecord_t	*fsrec;
+	fsh_list_t	*list;
+	fsh_node_t	*node;
+
+	fsrec = FSH_GET_FSREC(vfsp);
+
+	FSH_INSTALL_VN(hooks, fsrec, list, node, open, OPEN);
+	FSH_INSTALL_VN(hooks, fsrec, list, node, close, CLOSE);
+	FSH_INSTALL_VN(hooks, fsrec, list, node, read, READ);
+	FSH_INSTALL_VN(hooks, fsrec, list, node, write, WRITE);
+	FSH_INSTALL_VFS(hooks, fsrec, list, node, mount, MOUNT);
+	FSH_INSTALL_VFS(hooks, fsrec, list, node, unmount, UNMOUNT);
+	FSH_INSTALL_VFS(hooks, fsrec, list, node, root, ROOT);
+	FSH_INSTALL_VFS(hooks, fsrec, list, node, vget, VGET);
+	FSH_INSTALL_VFS(hooks, fsrec, list, node, statfs, STATFS);
+
+	return (0);
+}
+
+
+#define	FSH_REMOVE(type, hooks, fsrec, list, node, prev, lower, upper)	\
+do {									\
+	if (hooks->hook_ ## lower == NULL)				\
+		break;							\
+									\
+	list = &fsrec->fshfsr_opv[FSH_ ## type ## _ ## upper];		\
+	rw_enter(&list->fshl_lock, RW_WRITER);				\
+	node = list->fshl_head;						\
+									\
+	if (node == NULL) {						\
+		rw_exit(&list->fshl_lock);				\
+		break;							\
+	}								\
+									\
+	while (node && 							\
+		!(node->fshn_hooki.fshi_fn.hook_ ## lower ==		\
+		    hooks->hook_ ## lower &&				\
+		    node->fshn_hooki.fshi_arg == hooks->arg)) {		\
+		prev = node;						\
+		node = node->fshn_next;					\
+	}								\
+									\
+	if (node == NULL) {						\
+		rw_exit(&list->fshl_lock);				\
+		break;							\
+	}								\
+									\
+	if (node == list->fshl_head)					\
+		list->fshl_head = node->fshn_next;			\
+	else								\
+		prev->fshn_next = node->fshn_next;			\
+	rw_exit(&list->fshl_lock);					\
+									\
+	kmem_free(node, sizeof (*node));				\
+} while (0)
+
+#define	FSH_REMOVE_VN(hooks, fsrec, list, node, prev, lower, upper)	\
+	FSH_REMOVE(VOP, hooks, fsrec, list, node, prev, lower, upper)
+
+#define	FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, lower, upper)	\
+	FSH_REMOVE(VFS, hooks, fsrec, list, node, prev, lower, upper)
+
+int
+fsh_hook_remove(vfs_t *vfsp, fsh_t *hooks)
+{
+	fsh_fsrecord_t	*fsrec;
+	fsh_list_t 	*list;
+	fsh_node_t	*node;
+	fsh_node_t	*prev;
+
+	fsrec = FSH_GET_FSREC(vfsp);
+
+	FSH_REMOVE_VN(hooks, fsrec, list, node, prev, open, OPEN);
+	FSH_REMOVE_VN(hooks, fsrec, list, node, prev, close, CLOSE);
+	FSH_REMOVE_VN(hooks, fsrec, list, node, prev, read, READ);
+	FSH_REMOVE_VN(hooks, fsrec, list, node, prev, write, WRITE);
+	FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, mount, MOUNT);
+	FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, unmount, UNMOUNT);
+	FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, root, ROOT);
+	FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, vget, VGET);
+	FSH_REMOVE_VFS(hooks, fsrec, list, node, prev, statfs, STATFS);
+
+	return (0);
+}
+
+
+int
+fsh_callback_install(fsh_callback_t *fsh_callback)
+{
+	fsh_callback_node_t *node;
+
+	node = (fsh_callback_node_t *) kmem_alloc(sizeof (*node), KM_SLEEP);
+	node->fshcn_callback = *fsh_callback;
+
+	rw_enter(&fsh_global_callback_list.fshcl_lock, RW_WRITER);
+	node->fshcn_next = fsh_global_callback_list.fshcl_head;
+	fsh_global_callback_list.fshcl_head = node;
+	rw_exit(&fsh_global_callback_list.fshcl_lock);
+
+	return (0);
+}
+
+int
+fsh_callback_remove(fsh_callback_t *fsh_callback)
+{
+	fsh_callback_node_t *node;
+	fsh_callback_node_t *prev;
+	fsh_callback_list_t *list;
+
+	list = &fsh_global_callback_list;
+
+	rw_enter(&list->fshcl_lock, RW_WRITER);
+	node = list->fshcl_head;
+
+	if (node == NULL) {
+		rw_exit(&list->fshcl_lock);
+		return (0);
+	}
+
+	while (node && memcmp(fsh_callback, &node->fshcn_callback,
+		sizeof (*fsh_callback))) {
+		prev = node;
+		node = node->fshcn_next;
+	}
+
+	if (node == NULL) {
+		rw_exit(&list->fshcl_lock);
+		return (0);
+	}
+
+	prev->fshcn_next = node->fshcn_next;
+	kmem_free(node, sizeof (*node));
+
+	rw_exit(&list->fshcl_lock);
+	return (0);
+}
+
+
+
+
+#define	FSH_ENABLED(vfsp, enabled)					\
+do {									\
+	rw_enter(&FSH_GET_FSREC(vfsp)->fshfsr_en_lock, RW_READER);	\
+	*enabled = FSH_GET_FSREC(vfsp)->fshfsr_enabled;			\
+	rw_exit(&FSH_GET_FSREC(vfsp)->fshfsr_en_lock);		\
+} while (0)
+
+int
+fsh_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
+{
+	fsh_list_t *list;
+	int enabled;
+	int ret;
+
+	FSH_ENABLED((*vpp)->v_vfsp, &enabled);
+	if (!enabled)
+		return ((*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct));
+
+	list = &FSH_GET_FSREC((*vpp)->v_vfsp)->fshfsr_opv[FSH_VOP_OPEN];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret =  (*((*vpp)->v_op->vop_open))(vpp, mode, cr, ct);
+	else
+		ret = fsh_next_open(list->fshl_head, vpp, mode, cr, ct);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+	caller_context_t *ct)
+{
+	fsh_list_t *list;
+	int enabled;
+	int ret;
+
+	FSH_ENABLED(vp->v_vfsp, &enabled);
+	if (!enabled)
+		return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
+			cr, ct));
+
+	list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_CLOSE];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret =  (*(vp->v_op->vop_close))(vp, flag, count, offset,
+			cr, ct);
+	else
+		ret = fsh_next_close(list->fshl_head, vp, flag, count,
+			offset, cr, ct);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+	caller_context_t *ct)
+{
+	fsh_list_t *list;
+	int enabled;
+	int ret;
+
+	FSH_ENABLED(vp->v_vfsp, &enabled);
+	if (!enabled)
+		return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
+
+	list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_READ];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret =  (*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct);
+	else
+		ret = fsh_next_read(list->fshl_head, vp, uiop, ioflag,
+			cr, ct);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+	caller_context_t *ct)
+{
+	fsh_list_t *list;
+	int enabled;
+	int ret;
+
+	FSH_ENABLED(vp->v_vfsp, &enabled);
+	if (!enabled)
+		return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
+
+	list = &FSH_GET_FSREC(vp->v_vfsp)->fshfsr_opv[FSH_VOP_WRITE];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret =  (*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct);
+	else
+		ret = fsh_next_write(list->fshl_head, vp, uiop, ioflag,
+			cr, ct);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+	fsh_list_t *list;
+	int ret;
+
+	list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_MOUNT];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret = (*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr);
+	else
+		ret = fsh_next_mount(list->fshl_head, vfsp, mvp, uap,
+			cr);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+	fsh_list_t *list;
+	int ret;
+
+	list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_UNMOUNT];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret = (*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr);
+	else
+		ret = fsh_next_unmount(list->fshl_head, vfsp, flag, cr);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_root(vfs_t *vfsp, vnode_t **vpp)
+{
+	fsh_list_t *list;
+	int ret;
+
+	list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_ROOT];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret = (*(vfsp->vfs_op->vfs_root))(vfsp, vpp);
+	else
+		ret = fsh_next_root(list->fshl_head, vfsp, vpp);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_statfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+	fsh_list_t *list;
+	int ret;
+
+	list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_STATFS];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret = (*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp);
+	else
+		ret = fsh_next_statfs(list->fshl_head, vfsp, sp);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+int
+fsh_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
+{
+	fsh_list_t *list;
+	int ret;
+
+	list = &FSH_GET_FSREC(vfsp)->fshfsr_opv[FSH_VFS_VGET];
+	rw_enter(&list->fshl_lock, RW_READER);
+	if (list->fshl_head == NULL)
+		ret = (*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp);
+	else
+		ret = fsh_next_vget(list->fshl_head, vfsp, vpp, fidp);
+	rw_exit(&list->fshl_lock);
+
+	return (ret);
+}
+
+void
+fsh_exec_create_callbacks(vfs_t *vfsp)
+{
+	fsh_callback_node_t *node;
+	fsh_callback_t *callback;
+
+	rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
+	node = fsh_global_callback_list.fshcl_head;
+	while (node) {
+		callback = &node->fshcn_callback;
+		(*(callback->fshc_create))(vfsp, callback->fshc_arg);
+		node = node->fshcn_next;
+	}
+	rw_exit(&fsh_global_callback_list.fshcl_lock);
+}
+
+void
+fsh_exec_destroy_callbacks(vfs_t *vfsp)
+{
+	fsh_callback_node_t *node;
+	fsh_callback_t *callback;
+
+	rw_enter(&fsh_global_callback_list.fshcl_lock, RW_READER);
+	node = fsh_global_callback_list.fshcl_head;
+	while (node) {
+		callback = &node->fshcn_callback;
+		(*(callback->fshc_destroy))(vfsp, callback->fshc_arg);
+		node = node->fshcn_next;
+	}
+	rw_exit(&fsh_global_callback_list.fshcl_lock);
+}
+
+/* To be used ONLY in vfs_alloc() */
+struct fsh_fsrecord *
+fsh_fsrec_create()
+{
+	struct fsh_fsrecord *fsrecp;
+	int i;
+
+	fsrecp = (fsh_fsrecord_t *) kmem_alloc(sizeof (*fsrecp), KM_SLEEP);
+	bzero(fsrecp, sizeof (*fsrecp));
+
+	rw_init(&fsrecp->fshfsr_en_lock, NULL, RW_DRIVER, NULL);
+	fsrecp->fshfsr_enabled = 1;
+
+	for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++)
+		rw_init(&fsrecp->fshfsr_opv[i].fshl_lock, NULL, RW_DRIVER,
+			NULL);
+	return fsrecp;
+}
+
+/* To be used ONLY in vfs_free() */
+void
+fsh_fsrec_destroy(fsh_fsrecord_t *fsrecp)
+{
+	int i;
+	fsh_node_t *node, *next_node;
+
+	for (i = 0; i < FSH_SUPPORTED_OPS_COUNT; i++) {
+		node = fsrecp->fshfsr_opv[i].fshl_head;
+		while (node) {
+			next_node = node->fshn_next;
+			kmem_free(node, sizeof (*node));
+			node = next_node;
+		}
+		rw_destroy(&fsrecp->fshfsr_opv[i].fshl_lock);
+	}
+	rw_destroy(&fsrecp->fshfsr_en_lock);
+	kmem_free(fsrecp, sizeof (*fsrecp));
+}
+
+
+/* control passing */
+int
+fsh_next_open(fsh_node_t *fsh_node, vnode_t **vpp, int mode, cred_t *cr,
+	caller_context_t *ct)
+{
+	if (fsh_node == NULL)
+		return ((*(*vpp)->v_op->vop_open)(vpp, mode, cr, ct));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_open))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vpp, mode, cr, ct));
+
+}
+
+int
+fsh_next_close(fsh_node_t *fsh_node, vnode_t *vp, int flag, int count,
+	offset_t offset, cred_t *cr, caller_context_t *ct)
+{
+	if (fsh_node == NULL)
+		return ((*(vp->v_op->vop_close))(vp, flag, count, offset,
+			cr, ct));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_close))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vp, flag, count, offset, cr, ct));
+}
+
+int
+fsh_next_read(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
+	cred_t *cr, caller_context_t *ct)
+{
+	if (fsh_node == NULL)
+		return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_read))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vp, uiop, ioflag, cr, ct));
+}
+
+int
+fsh_next_write(fsh_node_t *fsh_node, vnode_t *vp, uio_t *uiop, int ioflag,
+	cred_t *cr, caller_context_t *ct)
+{
+	if (fsh_node == NULL)
+		return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_write))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vp, uiop, ioflag, cr, ct));
+}
+
+int
+fsh_next_mount(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t *mvp,
+	struct mounta *uap, cred_t *cr)
+{
+	if (fsh_node == NULL)
+		return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_mount))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vfsp, mvp, uap, cr));
+}
+
+int
+fsh_next_unmount(fsh_node_t *fsh_node, vfs_t *vfsp, int flag, cred_t *cr)
+{
+	if (fsh_node == NULL)
+		return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_unmount))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vfsp, flag, cr));
+}
+
+int
+fsh_next_root(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp)
+{
+	if (fsh_node == NULL)
+		return ((*(vfsp->vfs_op->vfs_root))(vfsp, vpp));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_root))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vfsp, vpp));
+}
+
+int
+fsh_next_statfs(fsh_node_t *fsh_node, vfs_t *vfsp, statvfs64_t *sp)
+{
+	if (fsh_node == NULL)
+		return ((*(vfsp->vfs_op->vfs_statvfs))(vfsp, sp));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_statfs))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vfsp, sp));
+}
+
+int
+fsh_next_vget(fsh_node_t *fsh_node, vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
+{
+	if (fsh_node == NULL)
+		return ((*(vfsp->vfs_op->vfs_vget))(vfsp, vpp, fidp));
+
+	return ((*(fsh_node->fshn_hooki.fshi_fn.hook_vget))(
+		fsh_node->fshn_next, fsh_node->fshn_hooki.fshi_arg,
+		vfsp, vpp, fidp));
+}
