--- old/usr/src/cmd/Makefile	Tue Sep  3 18:09:39 2013
+++ new/usr/src/cmd/Makefile	Tue Sep  3 18:09:39 2013
@@ -168,6 +168,7 @@
 	fold		\
 	format		\
 	fs.d		\
+	fsdadm		\
 	fstyp		\
 	fuser		\
 	fwflash		\
--- /dev/null	Tue Sep  3 18:09:39 2013
+++ new/usr/src/cmd/fsdadm/Makefile	Tue Sep  3 18:09:39 2013
@@ -0,0 +1,50 @@
+#
+# 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.
+#
+
+PROG = fsdadm
+OBJS = fsdadm.o
+SRCS = $(OBJS:%.o=%.c)
+
+FILEMODE = 0555
+
+include ../Makefile.cmd
+include	../Makefile.ctf
+
+CLEANFILES += $(OBJS)
+
+CFLAGS += $(CCVERBOSE)
+CFLAG64 += $(CCVERBOSE)
+
+LDLIBS += -lfsd
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o: %.c
+	$(COMPILE.c) $<
+	$(POST_PROCESS_O)
+
+clean:
+	-$(RM) $(CLEANFILES)
+
+lint: lint_PROG
+
+install: all $(ROOTUSRSBINPROG)
+
+include ../Makefile.targ
+
--- /dev/null	Tue Sep  3 18:09:39 2013
+++ new/usr/src/cmd/fsdadm/fsdadm.c	Tue Sep  3 18:09:39 2013
@@ -0,0 +1,287 @@
+/*
+ * 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 <libfsd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+fsd_handle_t handle;
+
+int ret;
+
+int
+errout(fsd_handle_t *handle)
+{
+	(void) fprintf(stderr, "Error: %s: %s\n",
+	    fsd_strerr(handle->fsd_errno), strerror(handle->errno));
+
+	return (-1);
+}
+
+void
+print_fsd(fsd_t *fsd)
+{
+	(void) printf("\tRead less: %d%% chance with range %d - %d\n",
+	    (int)fsd->read_less_chance,
+	    (int)fsd->read_less_r[0],
+	    (int)fsd->read_less_r[1]);
+}
+
+void
+info()
+{
+	fsd_info_t info;
+
+	if (fsd_get_info(&handle, &info) != 0) {
+		ret = errout(&handle);
+		return;
+	}
+
+	if (info.fsdinf_enabled) {
+		(void) printf(
+		    "Enabled: yes\n"
+		    "Filesystems disturbed: %d\n", (int)info.fsdinf_count);
+
+		if (info.fsdinf_omni_on) {
+			(void) printf(
+			    "Omnipresent disturbing: yes\n"
+			    "Omnipresent params:\n");
+			print_fsd(&info.fsdinf_omni_param);
+		} else {
+			(void) printf("Omnipresent disturbing: no\n");
+		}
+	}
+}
+
+void
+list()
+{
+	fsd_info_t info;
+	fsd_fs_t *fslistp;
+	int i;
+	int count;
+
+	if (fsd_get_info(&handle, &info) != 0) {
+		ret = errout(&handle);
+		return;
+	}
+	count = info.fsdinf_count;
+
+	fslistp = calloc(info.fsdinf_count, sizeof (fsd_fs_t));
+	if (fsd_get_list(&handle, fslistp, &count) != 0) {
+		ret = errout(&handle);
+	} else {
+		for (i = 0; i < count; i++) {
+			(void) printf("Mountpoint: %s\n",
+			    fslistp[i].fsdf_name);
+			print_fsd(&fslistp[i].fsdf_param);
+			(void) printf("\n");
+
+		}
+	}
+
+	free(fslistp);
+}
+
+int aflag;
+int cflag;
+int dflag;
+int eflag;
+int gflag;
+int iflag;
+int lflag;
+int mflag;
+int oflag;
+int rflag;
+int xflag;
+
+char *mnt;
+fsd_t param;
+int chance;
+int range[2];
+
+int
+main(int argc, char *argv[])
+{
+	extern char *optarg;
+	extern int optind;
+	int opt;
+
+	if (argc < 2)
+		goto usage;
+
+	if (fsd_open(&handle) != 0)
+		return (errout(&handle));
+
+	while ((opt = getopt(argc, argv, "ediam:gxoc:r:l")) != -1) {
+		switch (opt) {
+		case 'e':
+			eflag = 1;
+			break;
+
+		case 'd':
+			dflag = 1;
+			break;
+
+		case 'i':
+			iflag = 1;
+			break;
+
+		case 'a':
+			aflag = 1;
+			break;
+
+		case 'm':
+			mflag = 1;
+			mnt = optarg;
+			break;
+
+		case 'g':
+			gflag = 1;
+			break;
+
+		case 'x':
+			xflag = 1;
+			break;
+
+		case 'o':
+			oflag = 1;
+			break;
+
+		case 'c':
+			cflag = 1;
+			chance = atoi(optarg);
+			break;
+
+		case 'r':
+			rflag = 1;
+			if (optind > argc - 1) {
+				(void) fprintf(stderr,
+				    "Error: -r requires two arguments\n");
+				ret = -1;
+				goto end;
+			}
+			range[0] = atoi(argv[optind-1]);
+			range[1] = atoi(argv[optind]);
+			optind++;
+			break;
+
+		case 'l':
+			lflag = 1;
+			break;
+
+		case '?':
+			(void) fprintf(stderr,
+			    "Error: Unrecognized option: -%c\n", optopt);
+			ret = -1;
+			goto end;
+		}
+	}
+
+	if (eflag) {
+		if (fsd_enable(&handle) != 0)
+			ret = errout(&handle);
+
+	} else if (dflag) {
+		if (fsd_disable(&handle) != 0)
+			ret = errout(&handle);
+
+	} else if (iflag) {
+		info();
+
+	} else if (aflag) {
+		list();
+
+	} else if (xflag) {
+		if (oflag) {
+			if (fsd_disturb_omni_off(&handle) != 0)
+				ret = errout(&handle);
+
+		} else if (mflag) {
+			if (fsd_disturb_off(&handle, mnt) != 0)
+				ret = errout(&handle);
+
+		} else {
+			(void) fprintf(stderr, "Don't know what to clear. "
+			    "Use -o or -m PATH with -x option.\n");
+		}
+
+	} else if (gflag) {
+		if (mflag) {
+			if (fsd_get_param(&handle, mnt, &param) != 0) {
+				ret = errout(&handle);
+			} else {
+				(void) printf("%s\n", mnt);
+				print_fsd(&param);
+			}
+		} else {
+			(void) fprintf(stderr, "Don't know what to get. "
+			    "Use -m PATH with -g option.\n");
+		}
+
+	} else if (lflag) {	/* add other disturbances here */
+		if (!(cflag && rflag)) {
+			(void) fprintf(stderr, "Need chance and range.");
+			goto end;
+		}
+
+		param.read_less_chance = chance;
+		param.read_less_r[0] = range[0];
+		param.read_less_r[1] = range[1];
+
+		if (oflag) {
+			if (fsd_disturb_omni(&handle, &param) != 0)
+				ret = errout(&handle);
+
+		} else if (mflag) {
+			if (fsd_disturb(&handle, mnt, &param) != 0)
+				ret = errout(&handle);
+
+		} else {
+			(void) fprintf(stderr,
+			    "Don't know what to disturb. "
+			    "Use -o or -m PATH with this options.");
+		}
+
+	} else {
+usage:
+		(void) fprintf(stderr, "Usage: fsdadm "
+		    "[-ed] [-ai] [-o] [-x] [-g] [-l] "
+		    "[-r range_start range_end]\n"
+		    "\t[-c chance] [-m path]\n\n");
+
+		(void) fprintf(stderr,
+		    "\t -e enable fsd\n"
+		    "\t -d disable fsd\n"
+		    "\t -a display disturbance parameters for all disturbed\n"
+		    "\t    filesystems\n"
+		    "\t -i display information about current fsd status\n"
+		    "\t -o omnipresent switch\n"
+		    "\t -x clear switch\n"
+		    "\t -g get disturbance parameters\n"
+		    "\t -l \"read less\" disturbance\n"
+		    "\t    every read operation would read n (from a given\n"
+		    "\t    range) bytes less than it was requested\n"
+		    "\t -r range for some types of disturbances\n"
+		    "\t -c chance of the disturbance\n"
+		    "\t -m path to mountpoint (or a representative file)\n"
+		    "\n");
+	}
+
+end:
+	fsd_close(&handle);
+	return (ret);
+}
--- old/usr/src/lib/Makefile	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/Makefile	Tue Sep  3 18:09:40 2013
@@ -159,7 +159,8 @@
 	librdc		\
 	libinstzones	\
 	libpkg		\
-	libpcidb
+	libpcidb	\
+	libfsd
 
 SUBDIRS += \
 	passwdutil	\
@@ -508,6 +509,7 @@
 	libsun_ima	\
 	mpapi		\
 	libreparse	\
+	libfsd		\
 	$($(MACH)_HDRSUBDIRS)
 
 $(CLOSED_BUILD)HDRSUBDIRS += \
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/Makefile	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,53 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2013 Damian Bogel. All rights reserved.
+#
+
+include ../Makefile.lib
+
+HDRS = libfsd.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all	:= TARGET= all
+clean	:= TARGET= clean
+clobber	:= TARGET= clobber
+install	:= TARGET= install
+lint	:= TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/Makefile.com	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2013 Damian Bogel. All rights reserved.
+#
+
+LIBRARY = libfsd.a
+VERS    = .1
+OBJECTS = libfsd.o
+
+include ../../Makefile.lib
+
+SRCDIR =	../common
+LIBS =		$(DYNLIB) $(LINTLIB)
+$(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
+LDLIBS +=	-lc
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-I../common
+
+.KEEP_STATE:
+
+all:		$(LIBS)
+
+lint:		lintcheck
+
+include ../../Makefile.targ
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/amd64/Makefile	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,19 @@
+#
+# 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 ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
+
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/common/libfsd.c	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,327 @@
+/*
+ * 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 <fcntl.h>
+#include <libfsd.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/fsd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+/*
+ * libfsd
+ * Library used to drive the fsd pseudo-device driver.
+ *
+ * 1. Usage.
+ * fsd_handle_t is used for every operation on the fsd driver. It is acquired by
+ * a call to fsd_open(). Every handle must be released by calling fsd_close().
+ *
+ * A handle is a structure that contains data needed to drive the fsd and get
+ * information about errors.
+ *
+ *
+ * Basics:
+ * A disturber is a hook for filesystem operations. There are different types of
+ * disturbers, but the property that connects them is that EVERY program that
+ * uses filesystem API should expect that kind of behaviour. Doing otherwise is
+ * a bug and could lead to serious problems.
+ * Omnipresent disturber is a hook that is being installed whenever a new vfs_t
+ * is mounted.
+ *
+ *
+ * Errors:
+ * In almost all the functions (except fsd_close()) return value equal to -1
+ * indicates that there was an error. Last error data is contained in the
+ * handle in two fields: fsd_errno and errno.
+ * fsd_errno could be one of EFSD_XXX. errno is nonzero if and only if fsd_errno
+ * is set to EFSD_CANT_OPEN_DRIVER or EFSD_CANT_OPEN_MOUNTPOINT.
+ * fsd_strerr() is used to get the error message from fsd_errno. errno should be
+ * treated according to Intro(2).
+ *
+ * Handle management:
+ * fsd_open()
+ *	Returns a handle.
+ *
+ * fsd_close(handle)
+ *	Destroys a handle.
+ *
+ *
+ * Enabling/disabling fsd:
+ * fsd_enable(handle)
+ * fsd_disable(handle)
+ *	When fsd is enabled, it cannot be detached from the system. Otherwise,
+ *	it could be and all the information that the fsd contains could be lost.
+ *	The client should keep the fsd enabled whenever he needs to. No
+ *	operations on fsd could be made if it's not enabled.
+ *
+ *
+ * Getting information:
+ * fsd_get_info(handle, info)
+ *	Used to retrieve information such as:
+ *	* whether fsd is enabled (system-wide)
+ *	* whether there is an omnipresent disturber installed
+ *	* omnipresent disturber's parameters
+ *	* count of disturbers installed
+ *
+ * fsd_get_list(handle, fslist, count)
+ *	Gets a list of disturbers installed. count is the maximum count of
+ *	entries that could be returned by the function to the user-allocated
+ *	buffer.	count is changed to min(count, number of disturbers installed).
+ *	There is no error returned if the number of disturbers installed exceeds
+ *	the user-provided count. It just gets the maximal allowed amount of
+ *	informaton.
+ *
+ * fsd_get_param(handle, path, param)
+ *	Gets disturber parameters from a filesystem of the file pointed by the
+ *	path.
+ *
+ *
+ * Installing/removing disturbers:
+ * fsd_disturb(handle, path, param)
+ *	Installs (or changes, if a disturber on this filesystem already exists)
+ *	a disturber on a filesystem of the file pointed by the path.
+ *
+ * fsd_disturb_omni(handle, param)
+ *	Installs (or changes, if an omnipresent disturber already exists) an
+ *	omnipresent disturber. Only one omnipresent disturber exists at a time.
+ *
+ * fsd_disturb_off(handle, path)
+ * fsd_disturb_omni_off(handle)
+ *	Removes a disturber. It is guaranteed that after this function returns,
+ *	the disturber won't change the behaviour of filesystem operations.
+ *
+ * 2. Multithreading.
+ * It is safe to use the libfsd API concurrently.
+ *
+ * Error information is encapsulated in a handle, so it's the client's job to
+ * properly share the handle between threads to preserve the consistency of
+ * error data within a handle.
+ */
+
+extern int errno;
+
+const char *
+fsd_strerr(int e)
+{
+	switch (e) {
+	case EFSD_NOERROR:
+		return ("no error");
+	case EFSD_BAD_PARAM:
+		return ("incorrect disturbance parameters");
+	case EFSD_INTERNAL:
+		return ("internal library error");
+	case EFSD_NOT_ENABLED:
+		return ("fsd is not enabled");
+	case EFSD_CANT_OPEN_DRIVER:
+		return ("cannot open fsd device");
+	case EFSD_CANT_OPEN_MOUNTPOINT:
+		return ("cannot open mountpoint");
+	case EFSD_ENTRY_NOT_FOUND:
+		return ("this filesystem is not being disturbed");
+	case EFSD_FAULT:
+		return ("bad pointer");
+	case EFSD_TOO_MANY_HOOKS:
+		return ("too many hooks");
+	case EFSD_UNKNOWN_ERROR:
+	default:
+		return ("unknown error");
+	}
+}
+
+static int
+xlate_errno(int e)
+{
+	switch (e) {
+	case 0:
+		return (0);
+	case (-1):
+		switch (errno) {
+		case 0:
+			return (EFSD_NOERROR);
+		case EFAULT:
+			return (EFSD_FAULT);
+		case ENOTTY:
+			return (EFSD_INTERNAL);
+		default:
+			return (EFSD_UNKNOWN_ERROR);
+		}
+	case ENOTACTIVE:
+		return (EFSD_NOT_ENABLED);
+	case ENOENT:
+		return (EFSD_ENTRY_NOT_FOUND);
+	case EINVAL:
+		return (EFSD_BAD_PARAM);
+	case EBADFD:
+		return (EFSD_INTERNAL);
+	case EAGAIN:
+		return (EFSD_TOO_MANY_HOOKS);
+	default:
+		return (EFSD_UNKNOWN_ERROR);
+	}
+}
+
+static int
+ioctl_set_handle(fsd_handle_t *handle, int ioctlret)
+{
+	handle->fsd_errno = xlate_errno(ioctlret);
+	handle->errno = 0;
+
+	if (handle->fsd_errno == 0)
+		return (0);
+	else
+		return (-1);
+}
+
+int
+fsd_open(fsd_handle_t *handle)
+{
+	if ((handle->fd = open(FSD_DEV_PATH, O_RDWR)) == -1) {
+		handle->fsd_errno = EFSD_CANT_OPEN_DRIVER;
+		handle->errno = errno;
+		return (-1);
+	}
+	return (0);
+}
+
+void
+fsd_close(fsd_handle_t *handle)
+{
+	(void) close(handle->fd);
+	handle->fd = -1;
+}
+
+
+int
+fsd_enable(fsd_handle_t *handle)
+{
+	return (ioctl_set_handle(handle, ioctl(handle->fd, FSD_ENABLE)));
+
+}
+
+int
+fsd_disable(fsd_handle_t *handle)
+{
+	return (ioctl_set_handle(handle, ioctl(handle->fd, FSD_DISABLE)));
+}
+
+int
+fsd_disturb(fsd_handle_t *handle, const char *mnt_path, fsd_t *param)
+{
+	fsd_ioc_t ioc;
+	int error;
+
+	(void) memcpy(&ioc.fsdioc_dis.fsdd_param, param,
+	    sizeof (ioc.fsdioc_dis.fsdd_param));
+
+	if ((ioc.fsdioc_dis.fsdd_mnt = open(mnt_path, O_RDONLY)) == -1) {
+		handle->fsd_errno = EFSD_CANT_OPEN_MOUNTPOINT;
+		handle->errno = errno;
+		return (-1);
+	}
+
+	error = ioctl(handle->fd, FSD_DISTURB, &ioc);
+	(void) close(ioc.fsdioc_dis.fsdd_mnt);
+	return (ioctl_set_handle(handle, error));
+}
+
+int
+fsd_disturb_off(fsd_handle_t *handle, const char *mnt_path)
+{
+	fsd_ioc_t ioc;
+	int error;
+
+	if ((ioc.fsdioc_mnt = open(mnt_path, O_RDONLY)) == -1) {
+		handle->fsd_errno = EFSD_CANT_OPEN_MOUNTPOINT;
+		handle->errno = errno;
+		return (-1);
+	}
+
+	error = ioctl(handle->fd, FSD_DISTURB_OFF, &ioc);
+	(void) close(ioc.fsdioc_mnt);
+	return (ioctl_set_handle(handle, error));
+}
+
+int
+fsd_disturb_omni(fsd_handle_t *handle, fsd_t *param)
+{
+	fsd_ioc_t ioc;
+
+	(void) memcpy(&ioc.fsdioc_param, param, sizeof (ioc.fsdioc_param));
+
+	return (ioctl_set_handle(handle, ioctl(handle->fd, FSD_DISTURB_OMNI,
+	    &ioc)));
+}
+
+int
+fsd_disturb_omni_off(fsd_handle_t *handle)
+{
+	return (ioctl_set_handle(handle, ioctl(handle->fd,
+	    FSD_DISTURB_OMNI_OFF)));
+}
+
+int
+fsd_get_param(fsd_handle_t *handle, const char *mnt_path, fsd_t *param)
+{
+	fsd_ioc_t ioc;
+	int error;
+	int mntfd;
+
+	if ((ioc.fsdioc_mnt = open(mnt_path, O_RDONLY)) == -1) {
+		handle->fsd_errno = EFSD_CANT_OPEN_MOUNTPOINT;
+		handle->errno = errno;
+		return (-1);
+	}
+	mntfd = ioc.fsdioc_mnt;
+
+	error = ioctl(handle->fd, FSD_GET_PARAM, &ioc);
+	if (error == 0)
+		(void) memcpy(param, &ioc.fsdioc_param, sizeof (*param));
+
+	(void) close(mntfd);
+
+	return (ioctl_set_handle(handle, error));
+}
+
+int
+fsd_get_info(fsd_handle_t *handle, fsd_info_t *info)
+{
+	fsd_ioc_t ioc;
+	int error;
+
+	error = ioctl(handle->fd, FSD_GET_INFO, &ioc);
+	if (error == 0)
+		(void) memcpy(info, &ioc.fsdioc_info, sizeof (*info));
+
+	return (ioctl_set_handle(handle, error));
+}
+
+int
+fsd_get_list(fsd_handle_t *handle, fsd_fs_t *fslist, int *count)
+{
+	fsd_ioc_t ioc;
+	int error;
+
+	ioc.fsdioc_list.listp = (uintptr_t)fslist;
+	ioc.fsdioc_list.count = *count;
+
+	error = ioctl(handle->fd, FSD_GET_LIST, &ioc);
+	*count = ioc.fsdioc_list.count;
+	return (ioctl_set_handle(handle, error));
+}
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/common/libfsd.h	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBFSD_H
+#define	_LIBFSD_H
+
+#include <sys/fsd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct fsd_handle {
+	int fd;
+	int fsd_errno;
+	int errno;
+} fsd_handle_t;
+
+#define	EFSD_NOERROR			0
+#define	EFSD_BAD_PARAM			1
+#define	EFSD_CANT_OPEN_DRIVER		2
+#define	EFSD_CANT_OPEN_MOUNTPOINT	3
+#define	EFSD_ENTRY_NOT_FOUND		4
+#define	EFSD_FAULT			5
+#define	EFSD_NOT_ENABLED		6
+#define	EFSD_TOO_MANY_HOOKS		7
+#define	EFSD_INTERNAL			8
+#define	EFSD_UNKNOWN_ERROR		9
+
+extern const char *fsd_strerr(int e);
+
+extern int fsd_open(fsd_handle_t *handle);
+extern void fsd_close(fsd_handle_t *handle);
+
+extern int fsd_enable(fsd_handle_t *handle);
+extern int fsd_disable(fsd_handle_t *handle);
+
+extern int fsd_get_param(fsd_handle_t *handle, const char *mnt_path,
+		fsd_t *param);
+extern int fsd_disturb(fsd_handle_t *handle, const char *mnt_path,
+		fsd_t *param);
+extern int fsd_disturb_off(fsd_handle_t *handle, const char *mnt_path);
+
+extern int fsd_disturb_omni(fsd_handle_t *handle, fsd_t *param);
+extern int fsd_disturb_omni_off(fsd_handle_t *handle);
+
+
+extern int fsd_get_info(fsd_handle_t *handle, fsd_info_t *info);
+extern int fsd_get_list(fsd_handle_t *handle, fsd_fs_t *fslist, int *count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFSD_H */
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/common/llib-lfsd	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+ /* LINTLIBRARY */
+ /* PROTOLIB1 */
+
+#include <libfsd.h>
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/common/mapfile-vers	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION ILLUMOSprivate {
+	global:
+		fsd_close;
+		fsd_disable;
+		fsd_disturb;
+		fsd_disturb_off;
+		fsd_enable;
+		fsd_get_param;
+		fsd_disturb_omni;
+		fsd_disturb_omni_off;
+		fsd_open;
+		fsd_get_list;
+		fsd_get_info;
+		fsd_strerr;
+	local:
+		*;
+};
--- /dev/null	Tue Sep  3 18:09:40 2013
+++ new/usr/src/lib/libfsd/i386/Makefile	Tue Sep  3 18:09:40 2013
@@ -0,0 +1,18 @@
+#
+# 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 ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
+
--- old/usr/src/uts/common/Makefile.files	Tue Sep  3 18:09:40 2013
+++ new/usr/src/uts/common/Makefile.files	Tue Sep  3 18:09:40 2013
@@ -178,6 +178,7 @@
 		flock.o		\
 		fm.o		\
 		fork.o		\
+		fsh.o		\
 		vpm.o		\
 		fs_reparse.o	\
 		fs_subr.o	\
@@ -933,6 +934,8 @@
 PMCS8001FW_C_OBJS +=	pmcs_fw_hdr.o
 PMCS8001FW_OBJS +=		$(PMCS8001FW_C_OBJS) SPCBoot.o ila.o firmware.o
 
+FSD_OBJS +=	fsd.o
+
 #
 #	Build up defines and paths.
 
--- old/usr/src/uts/common/Makefile.rules	Tue Sep  3 18:09:40 2013
+++ new/usr/src/uts/common/Makefile.rules	Tue Sep  3 18:09:40 2013
@@ -729,6 +729,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/fsd/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/hme/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -2091,6 +2095,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/fcoe/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/fsd/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/hme/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Tue Sep  3 18:09:41 2013
+++ new/usr/src/uts/common/fs/fsh.c	Tue Sep  3 18:09:41 2013
@@ -0,0 +1,1057 @@
+/*
+ * 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/debug.h>
+#include <sys/errno.h>
+#include <sys/fsh.h>
+#include <sys/fsh_impl.h>
+#include <sys/id_space.h>
+#include <sys/kmem.h>
+#include <sys/ksynch.h>
+#include <sys/list.h>
+#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+
+/*
+ * Filesystem hook framework (fsh)
+ *
+ * 1. Abstract.
+ * The main goal of the filesystem hook framework is to provide an easy way to
+ * inject client-defined behaviour into vfs/vnode calls. fsh works on
+ * vfs_t granularity.
+ *
+ *
+ * 2. Overview.
+ * fsh_t is the main object in the fsh. An fsh_t is a structure containing:
+ * 	- pointers to hooking functions (named after corresponding
+ *	vnodeops/vfsops)
+ * 	- a pointer to an argument to pass (this is shared for all the
+ * 	hooks in a given fsh_t)
+ *	- a pointer to the *hook remove callback* - it's being fired after a
+ *	hook is removed and the hook has stopped executing. It's safe to destroy
+ *	any data associated with this hook.
+ *
+ * The information from fsh_t is copied by the fsh and an fsh_handle_t
+ * is returned. It should be used for further removing.
+ *
+ *
+ * 3. Usage.
+ * It is expected that vfs_t/vnode_t that are passed to fsh_foo() functions
+ * are held by the caller when needed. fsh does no vfs_t/vnode_t locking.
+ *
+ * fsh_t is a structure filled out by the client. If a client does not want
+ * to add/remove a hook for function foo(), he should fill the foo field of
+ * fsh_t with NULL. Every hook has a type of corresponding vfsop/vnodeop with
+ * two additional arguments:
+ *	- fsh_int_t *fsh_int - this argument MUST be passed to
+ *	hook_next_foo(). fsh wouldn't know which hook to execute next
+ *	without it
+ *	- void *arg - this is the argument passed with fsh_t during
+ *	installation
+ *	- void (*remove_cb)(void *, fsh_handle_t) - hook remove callback
+ *	(mentioned earlier); it's first argument is arg, the second is the
+ *	handle
+ *
+ * After installation, an fsh_handle_t is returned to the caller.
+ *
+ * Every hook function is responsible for passing the control to the next
+ * hook associated with a particular call. In order to provide an easy way to
+ * modify the behaviour of a function call both before and after the
+ * underlying vfsop/vnodeop (or next hook) execution, a hook has to call
+ * fsh_next_foo() at some point. This function does necessary internal
+ * operations and calls the next hook, until there's no hook left, then it
+ * calls the underlying vfsop/vnodeop.
+ * Example:
+ * my_freefs(fsh_int_t *fsh_int, void *arg, vfs_t *vfsp) {
+ * 	cmn_err(CE_NOTE, "freefs called!\n");
+ *	return (fsh_next_freefs(fsh_int, vfsp));
+ * }
+ *
+ *
+ * A client might want to fire callbacks when vfs_t's are being mounted
+ * or freed. There's an fsh_callback_t structure provided to install such
+ * callbacks along with the API.
+ * It is legal to call fsh_hook_{install,remove}() inside a mount callback
+ * WITHOUT holding the vfs_t.
+ *
+ * After vfs_t's free callback returns, all the handles associated with the
+ * hooks installed on this vfs_t are invalid and must not be used.
+ *
+ *
+ * 4. API
+ * None of the APIs should be called during interrupt context above lock
+ * level. The only exceptions are fsh_next_foo() functions, which do not use
+ * locks.
+ *
+ * a) fsh.h
+ * Any of these functions could be called inside a hook or a hook remove
+ * callback.
+ * fsh_callback_{install,remove}() must not be called inside a {mount,free}
+ * callback. Doing so will cause a deadlock. Other functions can be called
+ * inside {mount,free} callbacks.
+ *
+ * fsh_fs_enable(vfs_t *vfsp)
+ * fsh_fs_disable(vfs_t *vfsp)
+ * 	Enables/disables fsh for a given vfs_t.
+ *
+ * fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
+ * 	Installs hooks on vfsp filesystem.
+ *	It's important that hooks are executed in LIFO installation order,
+ *	which means that if there are hooks A and B installed in this order, B
+ *	is going to be executed before A.
+ *	It returns a correct handle, or (-1) if hook/callback limit exceeded.
+ *	The handle is valid until a free callback returns or an explicit call
+ *	to fsh_hook_remove().
+ *
+ * fsh_hook_remove(fsh_handle_t handle)
+ * 	Removes a hook and invalidates the handle.
+ *	It is guaranteed that after this funcion returns, calls to
+ *	vnodeops/vfsops won't go through this hook, although there might be
+ *	some threads still executing this hook. When hook remove callback is
+ *	fired, it is guaranteed that the hook won't be executed anymore. It is
+ *	safe to remove all the internal data associated with this hook inside
+ *	the hook remove callback. The hook remove callback could be called
+ *	inside fsh_hook_remove().
+ *
+ * fsh_next_foo(fsh_int_t *fsh_int, void *arg, ARGUMENTS)
+ * 	This is the function which should be called once in every hook. It
+ * 	does the necessary internal operations and passes control to the
+ * 	next hook or, if there's no hook left, to the underlying
+ * 	vfsop/vnodeop.
+ *
+ * fsh_callback_install(fsh_callback_t *callback)
+ * fsh_callback_remove(fsh_callback_handle_t handle)
+ * 	Installs/removes callbacks for vfs_t mount/free. The mount callback
+ * 	is executed right before domount() returns. The free callback is
+ * 	called right before VFS_FREEVFS() is called.
+ *	The fsh_callback_install() returns a correct handle, or (-1) if
+ *	hook/callback limit exceeded.
+ *
+ * b) fsh_impl.h (for vfs.c and vnode.c only)
+ * fsh_init()
+ * 	This call has to be done in vfsinit(). It initialises the fsh. It
+ * 	is absolutely necessary that this call is made before any other fsh
+ * 	operation.
+ *
+ * fsh_exec_mount_callbacks(vfs_t *vfsp)
+ * fsh_exec_free_callbacks(vfs_t *vfsp)
+ * 	Used to execute all fsh callbacks for {mount,free} of a vfs_t.
+ *
+ * fsh_fsrec_destroy(struct fsh_fsrecord *fsrecp)
+ * 	Destroys an fsh_fsrecord structure. All the hooks installed on this
+ * 	vfs_t are then destroyed. free callback is called before this function.
+ *
+ * fsh_foo(ARGUMENTS)
+ * 	Function used to start executing the hook chain for a given call.
+ *
+ *
+ * 5. Internals.
+ * fsh_int_t is an internal hook structure. It is reference counted.
+ * fshi_hold() and fshi_rele() should be used whenever needed.
+ * fsh_int_t entries are elements of both fsh_map (global) and fshfsr_list
+ * (local to vfs_t). All entries are unique and are identified by fshi_handle.
+ *
+ * fsh_int_t properties:
+ *	- fsh_hook_install() sets the ref. counter to 1 and adds it to both
+ *	fsh_map and fshfsr_list
+ *	- fsh_hook_remove() decreases the ref. counter by 1, removes the hook
+ *	from fsh_map and marks the hook as *doomed*
+ *	- if fsh_int_t is on the fshfsr_list, it's alive and there is a thread
+ *	executing it
+ *	- if fsh_int_t is marked as *doomed*, the reference counter is not
+ *	be increased and thus no thread can acquire this fsh_int_t
+ *	- ref. counter can drop to 0 only after an fsh_hook_remove() call; this
+ *	also means that the fsh_int_t is *doomed* and isn't a part of fsh_map
+ *	- fsh_int_t could be also destroyed without fsh_hook_remove() call,
+ *	that happens only inside fsh_fsrec_destroy() where it is guaranteed
+ *	that there is no thread executing the hook
+ *
+ *
+ * fsh_fsrecord_t is a structure which lives inside a vfs_t.
+ * fsh_fsrecord_t contains:
+ *	- an rw-lock that protects the structure
+ *	- a list of hooks installed on this vfs_t
+ * 	- a flag which tells whether fsh is enabled on this vfs_t
+ *
+ *
+ * fsh_prepare_fsrec rule:
+ * Every function that needs vfsp->vfs_fshrecord has to call
+ * fsh_prepare_fsrec() first. If and only if the call is made, it is safe to
+ * use vfsp->vfs_fshrecord.
+ *
+ * Unfortunately, because of unexpected behaviour of some filesystems (no use
+ * of vfs_alloc()/vfs_init()) there's no good place to initialise the
+ * fsh_fshrecord_t structure. The approach being used here is to check if it's
+ * initialised in every call. Because of the fact that no lock could be used
+ * here (the same problem with initialisation), a spinlock is used.  This is
+ * explained in more detail in a comment before fsh_prepare_fsrec(). After
+ * calling fsh_preapre_fsrec() it's completely safe to keep the vfs_fshrecord
+ * pointer locally, because it won't be changed until vfs_free() is called.
+ *
+ * The only exception from the fsh_prepare_fsrec() rule is vfs_free(),
+ * where there is expected that no other fsh calls would be made for the
+ * vfs_t that's being freed. That's why vfs_fshrecord could be only NULL or a
+ * valid pointer and could not be concurrently accessed.
+ *
+ * When there are no fsh functions (that use a particular fsh_fsrecord_t)
+ * executing, the vfs_fshrecord pointer won't be equal to fsh_res_ptr. It
+ * would be NULL or a pointer to an initialised fsh_fsrecord_t.
+ *
+ *
+ * Callbacks:
+ * Mount callbacks are executed by a call to fsh_exec_mount_callbacks() right
+ * before returning from domount()@vfs.c.
+ *
+ * Free callbacks are executed by a call to fsh_exec_free_callbacks() right
+ * before calling VFS_FREEVFS(), after vfs_t's reference count drops to 0.
+ *
+ *
+ * fsh_next_foo(fsh_int_t *fshi, ARGUMENTS)
+ * 	This function is quite simple. It takes the fsh_int_t and passes control
+ * 	to the next hook or to the underlying vnodeop/vfsop.
+ *
+ *
+ * 6. Locking
+ * a) public
+ * fsh does no vfs_t nor vnode_t locking. It is expected that whenever it is
+ * needed, the client does that.
+ *
+ * fsh_callback_{install,remove} must not be called inside a callback, because
+ * it will cause a deadlock.
+ *
+ * b) internal
+ * Locking diagram:
+ *
+ *     fsh_hook_install()    fsh_hook_remove()   fsh_fsrec_destroy()
+ *           |                     |                |
+ *           |                     |                |
+ *           +------------------+  |   +------------+
+ *                              |  |   |
+ *                              V  V   V
+ *                              fsh_lock
+ *                                 |   |
+ *                                 |   +----- fshfsr_lock, RW_WRITER ---+
+ *                                 |                                    |
+ *                                 V                                    |
+ *               +---------------------------------------+              |
+ *               |               fsh_map                 |              |
+ *               |                                       |              |
+ *          +----|-> vfsp->vfs_fshrecord->fshfsr_list <--|--------------+
+ *          |    +------------------------------^--------+
+ *          |                                   |
+ *          |                                   |
+ * fshfsr_lock, RW_READER              fshfsr_lock, RW_WRITER
+ *          |                                   |
+ *          |                                   |
+ *   fsh_read(),                            fshi_rele()
+ *   fsh_write(),
+ *   ...,                               Might be called from:
+ *   fsh_next_read(),                    fsh_hook_remove()
+ *   fsh_next_write(),                   fsh_read(), fsh_write(), ...
+ *   ...                                 fsh_next_read(), fsh_next_write(), ...
+ *
+ * fsh_lock is a global lock for adminsitrative path (fsh_hook_install,
+ * fsh_hook_remove) and fsh_fsrec_destroy() (which is semi-administrative, since
+ * it destroys the unremoved hooks). It is used only when fsh_map needs to be
+ * locked. The usage of this lock guarantees that the data in fsh_map and
+ * fshfsr_lists is consistent.
+ */
+
+
+/* Internals */
+struct fsh_int {
+	fsh_handle_t	fshi_handle;
+	fsh_t		fshi_hooks;
+	vfs_t		*fshi_vfsp;
+
+	kmutex_t	fshi_lock;
+	uint64_t	fshi_ref;
+	uint64_t	fshi_doomed;	/* changed inside fsh_lock */
+
+	/* next node in fshfsr_list */
+	list_node_t	fshi_next;
+
+	/* next node in fsh_map */
+	list_node_t	fshi_global;
+};
+
+typedef struct fsh_callback_int {
+	fsh_callback_t	fshci_cb;
+	fsh_callback_handle_t fshci_handle;
+	list_node_t	fshci_next;
+} fsh_callback_int_t;
+
+
+static kmutex_t fsh_lock;
+
+/*
+ * fsh_fsrecord_t is the main internal structure. It's content is protected
+ * by fshfsr_lock. The fshfsr_list is a list of fsh_int_t hook entries for
+ * the vfs_t that contains the fsh_fsrecord_t.
+ */
+struct fsh_fsrecord {
+	krwlock_t	fshfsr_lock;
+	int		fshfsr_enabled;
+	list_t		fshfsr_list;
+};
+
+/*
+ * Global list of fsh_int_t. Protected by fsh_lock.
+ */
+static list_t fsh_map;
+
+/*
+ * Global list of fsh_callback_int_t.
+ */
+static krwlock_t fsh_cblist_lock;
+static list_t fsh_cblist;
+
+/*
+ * A reserved pointer for fsh purposes. It is used because of the method
+ * chosen for solving concurrency issues with vfs_fshrecord. The full
+ * explanation is in the big theory statement at the beginning of this
+ * file and above fsh_fsrec_prepare(). It is initialised in fsh_init().
+ */
+static void *fsh_res_ptr;
+
+static fsh_fsrecord_t *fsh_fsrec_create();
+
+int fsh_limit = INT_MAX;
+static id_space_t *fsh_idspace;
+
+/*
+ * fsh_prepare_fsrec()
+ *
+ * Important note:
+ * Before using this function, fsh_init() MUST be called. We do that in
+ * vfsinit()@vfs.c.
+ *
+ * One would ask, why isn't the vfsp->vfs_fshrecord initialised when the
+ * vfs_t is created. Unfortunately, some filesystems (e.g. fifofs) do not
+ * call vfs_init() or even vfs_alloc(), It's possible that some unbundled
+ * filesystems could do the same thing. That's why this solution is
+ * introduced. It should be called before any code that needs access to
+ * vfs_fshrecord.
+ *
+ * Locking:
+ * There are no locks here, because there's no good place to initialise
+ * the lock. Concurrency issues are solved by using atomic instructions
+ * and a spinlock, which is spinning only once for a given vfs_t. Because
+ * of that, the usage of the spinlock isn't bad at all.
+ *
+ * How it works:
+ * a) if vfsp->vfs_fshrecord equals NULL, atomic_cas_ptr() changes it to
+ *	fsh_res_ptr. That's a signal for other threads, that the structure
+ *	is being initialised.
+ * b) if vfsp->vfs_fshrecord equals fsh_res_ptr, that means we have to wait,
+ *	because vfs_fshrecord is being initialised by another call.
+ * c) other cases:
+ *	vfs_fshrecord is already initialised, so we can use it. It won't change
+ *	until vfs_free() is called. It can't happen when someone is holding
+ *	the vfs_t, which is expected from the caller of fsh API.
+ */
+static void
+fsh_prepare_fsrec(vfs_t *vfsp)
+{
+	fsh_fsrecord_t *fsrec;
+
+	while ((fsrec = atomic_cas_ptr(&vfsp->vfs_fshrecord, NULL,
+	    fsh_res_ptr)) == fsh_res_ptr)
+		;
+
+	if (fsrec == NULL)
+		atomic_swap_ptr(&vfsp->vfs_fshrecord, fsh_fsrec_create());
+}
+
+/*
+ * API for enabling/disabling fsh per vfs_t.
+ *
+ * A newly created vfs_t has fsh enabled by default. If one would want to change
+ * this behaviour, mount callbacks could be used.
+ *
+ * The caller is expected to hold the vfs_t.
+ *
+ * These functions must NOT be called in a hook.
+ */
+void
+fsh_fs_enable(vfs_t *vfsp)
+{
+	fsh_prepare_fsrec(vfsp);
+
+	rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
+	vfsp->vfs_fshrecord->fshfsr_enabled = 1;
+	rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
+}
+
+void
+fsh_fs_disable(vfs_t *vfsp)
+{
+	fsh_prepare_fsrec(vfsp);
+
+	rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
+	vfsp->vfs_fshrecord->fshfsr_enabled = 0;
+	rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
+}
+
+/*
+ * API used for installing hooks. fsh_handle_t is returned for further
+ * actions (currently just removing) on this set of hooks.
+ *
+ * fsh_t fields:
+ * - arg - argument passed to every hook
+ * - remove_cb - remove callback, called after a hook is removed and all the
+ *	threads stops executing it
+ * - read, write, ... - pointers to hooks for corresponding vnodeops/vfsops;
+ *	if there is no hook desired for an operation, it should be set to
+ *	NULL
+ *
+ * It's important that the hooks are executed in LIFO installation order (they
+ * are added to the head of the hook list).
+ *
+ * The caller is expected to hold the vfs_t.
+ *
+ * Returns (-1) if hook/callback limit exceeded, handle otherwise.
+ */
+fsh_handle_t
+fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
+{
+	fsh_handle_t	handle;
+	fsh_int_t	*fshi;
+
+	fsh_prepare_fsrec(vfsp);
+
+	if ((handle = id_alloc(fsh_idspace)) == -1)
+		return (-1);
+
+	fshi = kmem_alloc(sizeof (*fshi), KM_SLEEP);
+	mutex_init(&fshi->fshi_lock, NULL, MUTEX_DRIVER, NULL);
+	(void) memcpy(&fshi->fshi_hooks, hooks, sizeof (fshi->fshi_hooks));
+	fshi->fshi_handle = handle;
+	fshi->fshi_doomed = 0;
+	fshi->fshi_ref = 1;
+	fshi->fshi_vfsp = vfsp;
+
+	mutex_enter(&fsh_lock);
+	rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
+	list_insert_head(&vfsp->vfs_fshrecord->fshfsr_list, fshi);
+	rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
+
+	list_insert_head(&fsh_map, fshi);
+	mutex_exit(&fsh_lock);
+
+	return (handle);
+}
+
+static int
+fshi_hold(fsh_int_t *fshi)
+{
+	int can_hold;
+
+	mutex_enter(&fshi->fshi_lock);
+	if (fshi->fshi_doomed == 1) {
+		can_hold = 0;
+	} else {
+		fshi->fshi_ref++;
+		can_hold = 1;
+	}
+	mutex_exit(&fshi->fshi_lock);
+
+	return (can_hold);
+}
+
+/*
+ * This function must not be called while fshfsr_lock is held. Doing so could
+ * cause a deadlock.
+ */
+static void
+fshi_rele(fsh_int_t *fshi)
+{
+	int destroy;
+
+	mutex_enter(&fshi->fshi_lock);
+	ASSERT(fshi->fshi_ref > 0);
+	fshi->fshi_ref--;
+	if (fshi->fshi_ref == 0) {
+		ASSERT(fshi->fshi_doomed == 1);
+		destroy = 1;
+	} else {
+		destroy = 0;
+	}
+	mutex_exit(&fshi->fshi_lock);
+
+	if (destroy) {
+		/*
+		 * At this point, we are sure that fsh_hook_remove() has been
+		 * called, that's why we don't remove the fshi from fsh_map.
+		 * fsh_hook_remove() did that already.
+		 */
+		fsh_fsrecord_t *fsrecp;
+
+		if (fshi->fshi_hooks.remove_cb != NULL)
+			(*fshi->fshi_hooks.remove_cb)(
+			    fshi->fshi_hooks.arg, fshi->fshi_handle);
+		/*
+		 * We don't have to call fsh_prepare_fsrec() here.
+		 * fsh_fsrecord_t is already initialised, because we've found a
+		 * mapping for the given handle.
+		 */
+		fsrecp = fshi->fshi_vfsp->vfs_fshrecord;
+		ASSERT(fsrecp != NULL);
+		ASSERT(fsrecp != fsh_res_ptr);
+
+		rw_enter(&fsrecp->fshfsr_lock, RW_WRITER);
+		list_remove(&fsrecp->fshfsr_list, fshi);
+		rw_exit(&fsrecp->fshfsr_lock);
+
+		id_free(fsh_idspace, fshi->fshi_handle);
+		mutex_destroy(&fshi->fshi_lock);
+		kmem_free(fshi, sizeof (*fshi));
+	}
+}
+
+/*
+ * Used for removing a hook set.
+ *
+ * fsh_hook_remove() invalidates the given handle.
+ *
+ * It is guaranteed, that after successful return from fsh_hook_remove(),
+ * calls to vnodeops/vfsops, on the vfs_t on which the hook is installed, won't
+ * go through this hook.
+ *
+ * There is no guarantee that after fsh_hook_remove() returns, the hook
+ * associated with the handle won't be executing. Instead, it is guaranteed that
+ * when remove_cb() is called, the hook finished it's execution in all threads.
+ * It is safe to destroy all internal data associated with this hook inside
+ * remove_cb().
+ *
+ * It is possible that remove_cb() would be called before fsh_hook_remove()
+ * returns.
+ *
+ * Returns (-1) if hook wasn't found, 0 otherwise.
+ */
+int
+fsh_hook_remove(fsh_handle_t handle)
+{
+	fsh_int_t	*fshi;
+
+	mutex_enter(&fsh_lock);
+	for (fshi = list_head(&fsh_map); fshi != NULL;
+	    fshi = list_next(&fsh_map, fshi)) {
+		if (fshi->fshi_handle == handle) {
+			list_remove(&fsh_map, fshi);
+			break;
+		}
+	}
+
+	if (fshi == NULL)
+		return (-1);
+
+	mutex_enter(&fshi->fshi_lock);
+	ASSERT(fshi->fshi_doomed == 0);
+	fshi->fshi_doomed = 1;
+	mutex_exit(&fshi->fshi_lock);
+	mutex_exit(&fsh_lock);
+
+	fshi_rele(fshi);
+
+	return (0);
+}
+
+/*
+ * API for installing global mount/free callbacks.
+ *
+ * fsh_callback_t fields:
+ * fshc_arg - argument passed to the callbacks
+ * fshc_free - callback fired before VFS_FREEVFS() is called, after vfs_count
+ *	drops to 0
+ * fshc_mount - callback fired right before returning from domount()
+ * The first argument of these callbacks is the vfs_t that is mounted/freed.
+ * The second one is the fshc_arg.
+ *
+ * fsh_callback_handle_t is filled out by this function.
+ *
+ * This function must NOT be called in a callback, because it will cause
+ * a deadlock.
+ *
+ * Returns (-1) if hook/callback limit exceeded.
+ */
+fsh_callback_handle_t
+fsh_callback_install(fsh_callback_t *callback)
+{
+	fsh_callback_int_t *fshci;
+	fsh_callback_handle_t handle;
+
+	if ((handle = id_alloc(fsh_idspace)) == -1)
+		return (-1);
+
+	fshci = (fsh_callback_int_t *)kmem_alloc(sizeof (*fshci), KM_SLEEP);
+	(void) memcpy(&fshci->fshci_cb, callback, sizeof (fshci->fshci_cb));
+	fshci->fshci_handle = handle;
+
+	/* If it is called in a {mount,free} callback, causes deadlock. */
+	rw_enter(&fsh_cblist_lock, RW_WRITER);
+	list_insert_head(&fsh_cblist, fshci);
+	rw_exit(&fsh_cblist_lock);
+
+	return (handle);
+}
+
+/*
+ * API for removing global mount/free callbacks.
+ *
+ * This function must NOT be called in a callback, because it will cause
+ * a deadlock.
+ *
+ * Returns (-1) if callback wasn't found, 0 otherwise.
+ */
+int
+fsh_callback_remove(fsh_callback_handle_t handle)
+{
+	fsh_callback_int_t *fshci;
+
+	/* If it is called in a {mount,free} callback, causes deadlock. */
+	rw_enter(&fsh_cblist_lock, RW_WRITER);
+	for (fshci = list_head(&fsh_cblist); fshci != NULL;
+	    fshci = list_next(&fsh_cblist, fshci)) {
+		if (fshci->fshci_handle == handle) {
+			list_remove(&fsh_cblist, fshci);
+			break;
+		}
+	}
+	rw_exit(&fsh_cblist_lock);
+
+	if (fshci == NULL)
+		return (-1);
+
+	kmem_free(fshci, sizeof (*fshci));
+	id_free(fsh_idspace, handle);
+
+	return (0);
+}
+
+/*
+ * This function is executed right before returning from domount()@vfs.c.
+ * We are sure that it's called only after fsh_init().
+ * It executes all the mount callbacks installed in the fsh.
+ *
+ * Since fsh_exec_mount_callbacks() is called only inside domount(), it is legal
+ * to call fsh_hook_{install,remove}() inside a mount callback WITHOUT holding
+ * this vfs_t. This guarantee should be preserved, because it's in the "Usage"
+ * section in the big theory statement at the top of this file.
+ */
+void
+fsh_exec_mount_callbacks(vfs_t *vfsp)
+{
+	fsh_callback_int_t *fshci;
+	fsh_callback_t *cb;
+
+	rw_enter(&fsh_cblist_lock, RW_READER);
+	for (fshci = list_head(&fsh_cblist); fshci != NULL;
+	    fshci = list_next(&fsh_cblist, fshci)) {
+		cb = &fshci->fshci_cb;
+		if (cb->fshc_mount != NULL)
+			(*(cb->fshc_mount))(vfsp, cb->fshc_arg);
+	}
+	rw_exit(&fsh_cblist_lock);
+}
+
+/*
+ * This function is executed right before VFS_FREEVFS() is called in
+ * vfs_rele()@vfs.c. We are sure that it's called only after fsh_init().
+ * It executes all the free callbacks installed in the fsh.
+ *
+ * free() callback is the point after the handles associated with the hooks
+ * installed on this vfs_t become invalid
+ */
+void
+fsh_exec_free_callbacks(vfs_t *vfsp)
+{
+	fsh_callback_int_t *fshci;
+	fsh_callback_t *cb;
+
+	rw_enter(&fsh_cblist_lock, RW_READER);
+	for (fshci = list_head(&fsh_cblist); fshci != NULL;
+	    fshci = list_next(&fsh_cblist, fshci)) {
+		cb = &fshci->fshci_cb;
+		if (cb->fshc_free != NULL)
+			(*(cb->fshc_free))(vfsp, cb->fshc_arg);
+	}
+	rw_exit(&fsh_cblist_lock);
+}
+
+/*
+ * API for vnode.c/vfs.c to start executing the fsh for a given operation.
+ *
+ * fsh_xxx() tries to find the first non-NULL xxx hook on the fshfsr_list. If it
+ * does, it executes it. If not, underlying vnodeop/vfsop is called.
+ *
+ * These interfaces are using fsh_res_ptr (in fsh_prepare_fsrec()), so it's
+ * absolutely necessary to call fsh_init() before using them. That's done in
+ * vfsinit().
+ *
+ * While these functions are executing, it's expected that necessary vfs_t's
+ * are held so that vfs_free() isn't called. vfs_free() expects that noone
+ * accesses vfs_fshrecord of a given vfs_t.
+ * It's also the caller's responsibility to keep vnode_t passed to fsh_foo()
+ * alive and valid.
+ * All these expectations are met because these functions are used only in
+ * correspondng {fop,fsop}_foo() functions.
+ */
+int
+fsh_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+	caller_context_t *ct)
+{
+	int ret;
+	fsh_fsrecord_t *fsrecp;
+	fsh_int_t *fshi;
+
+	fsh_prepare_fsrec(vp->v_vfsp);
+	fsrecp = vp->v_vfsp->vfs_fshrecord;
+
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	if (!(fsrecp->fshfsr_enabled)) {
+		rw_exit(&fsrecp->fshfsr_lock);
+		return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
+	}
+
+	for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.read != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
+
+	ret = (*fshi->fshi_hooks.read)(fshi, fshi->fshi_hooks.arg,
+	    vp, uiop, ioflag, cr, ct);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+int
+fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+	caller_context_t *ct)
+{
+	fsh_int_t *fshi;
+	int ret;
+	fsh_fsrecord_t *fsrecp;
+
+	fsh_prepare_fsrec(vp->v_vfsp);
+	fsrecp = vp->v_vfsp->vfs_fshrecord;
+
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	if (!(vp->v_vfsp->vfs_fshrecord->fshfsr_enabled)) {
+		rw_exit(&fsrecp->fshfsr_lock);
+		return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
+	}
+
+	for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.write != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
+
+	ret = (*fshi->fshi_hooks.write)(fshi, fshi->fshi_hooks.arg,
+	    vp, uiop, ioflag, cr, ct);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+int
+fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+	fsh_fsrecord_t *fsrecp;
+	fsh_int_t *fshi;
+	int ret;
+
+	fsh_prepare_fsrec(vfsp);
+	fsrecp = vfsp->vfs_fshrecord;
+
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	if (!(fsrecp->fshfsr_enabled)) {
+		rw_exit(&fsrecp->fshfsr_lock);
+		return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
+	}
+
+	for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.mount != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
+
+	ret = (*fshi->fshi_hooks.mount)(fshi, fshi->fshi_hooks.arg,
+	    vfsp, mvp, uap, cr);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+int
+fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+	fsh_fsrecord_t *fsrecp;
+	fsh_int_t *fshi;
+	int ret;
+
+	fsh_prepare_fsrec(vfsp);
+	fsrecp = vfsp->vfs_fshrecord;
+
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	if (!(fsrecp->fshfsr_enabled)) {
+		rw_exit(&fsrecp->fshfsr_lock);
+		return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
+	}
+
+	for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.unmount != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
+
+	ret = (*fshi->fshi_hooks.unmount)(fshi, fshi->fshi_hooks.arg,
+	    vfsp, flag, cr);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+/*
+ * This is the funtion used by fsh_prepare_fsrec() to allocate a new
+ * fsh_fsrecord. This function is called by the first function which
+ * access the vfs_fshrecord and finds out it's NULL.
+ */
+static fsh_fsrecord_t *
+fsh_fsrec_create()
+{
+	fsh_fsrecord_t *fsrecp;
+
+	fsrecp = (fsh_fsrecord_t *)kmem_zalloc(sizeof (*fsrecp), KM_SLEEP);
+	list_create(&fsrecp->fshfsr_list, sizeof (fsh_int_t),
+	    offsetof(fsh_int_t, fshi_next));
+	rw_init(&fsrecp->fshfsr_lock, NULL, RW_DRIVER, NULL);
+	fsrecp->fshfsr_enabled = 1;
+	return (fsrecp);
+}
+
+
+/*
+ * This call can be used ONLY in vfs_free(). It's assumed that no other
+ * fsh calls using the vfs_t that owns the fsh_fsrecord to be destroyed
+ * are executing while a call to fsh_fsrec_destroy() is made. With this
+ * assumptions, no concurrency issues occur.
+ *
+ * Before calling this function outside the fsh, it's sufficient and
+ * required to check if the passed fsh_fsrecord * is not NULL. We don't
+ * have to check if it is not equal to fsh_res_ptr, because all the fsh API
+ * calls involving this vfs_t should end before vfs_free() is called
+ * (outside the fsh, fsh_fsrecord is never equal to fsh_res_ptr). That is
+ * guaranteed by the explicit requirement that the caller of fsh API holds
+ * the vfs_t when needed.
+ *
+ * All the remaining hooks are being removed.
+ */
+void
+fsh_fsrec_destroy(struct fsh_fsrecord *volatile fsrecp)
+{
+	fsh_int_t *fshi;
+
+	VERIFY(fsrecp != NULL);
+
+	_NOTE(CONSTCOND)
+	while (1) {
+		mutex_enter(&fsh_lock);
+		/* No need here to hold fshfsr_lock */
+		fshi = list_remove_head(&fsrecp->fshfsr_list);
+		if (fshi == NULL) {
+			mutex_exit(&fsh_lock);
+			break;
+		}
+		ASSERT(fshi->fshi_doomed == 0);
+		list_remove(&fsh_map, fshi);
+		mutex_exit(&fsh_lock);
+
+		if (fshi->fshi_hooks.remove_cb != NULL)
+			(*fshi->fshi_hooks.remove_cb)(fshi->fshi_hooks.arg,
+			    fshi->fshi_handle);
+		id_free(fsh_idspace, fshi->fshi_handle);
+		mutex_destroy(&fshi->fshi_lock);
+		kmem_free(fshi, sizeof (*fshi));
+
+	}
+
+	list_destroy(&fsrecp->fshfsr_list);
+	rw_destroy(&fsrecp->fshfsr_lock);
+	kmem_free(fsrecp, sizeof (*fsrecp));
+}
+
+/*
+ * fsh_init() is called in vfsinit()@vfs.c. This function MUST be called
+ * before every other fsh call.
+ */
+void
+fsh_init(void)
+{
+	rw_init(&fsh_cblist_lock, NULL, RW_DRIVER, NULL);
+	list_create(&fsh_cblist, sizeof (fsh_callback_int_t),
+	    offsetof(fsh_callback_int_t, fshci_next));
+
+	mutex_init(&fsh_lock, NULL, MUTEX_DRIVER, NULL);
+
+	list_create(&fsh_map, sizeof (fsh_int_t), offsetof(fsh_int_t,
+	    fshi_global));
+
+	/* See comment above fsh_prepare_fsrec() */
+	fsh_res_ptr = (void *)-1;
+
+	fsh_idspace = id_space_create("fsh", 0, fsh_limit);
+}
+
+/*
+ * These functions are used to pass control to the next hook or underlying
+ * vop or vfsop. It's client doesn't have to worry about any locking.
+ */
+int
+fsh_next_read(fsh_int_t *fshi, vnode_t *vp, uio_t *uiop, int ioflag,
+	cred_t *cr, caller_context_t *ct)
+{
+	int ret;
+	fsh_fsrecord_t *fsrecp = vp->v_vfsp->vfs_fshrecord;
+
+	/*
+	 * The passed fshi is the previous hook (the one from which we've been
+	 * called). We need to find the next one.
+	 */
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.read != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*vp->v_op->vop_read)(vp, uiop, ioflag, cr, ct));
+
+	ret = (*fshi->fshi_hooks.read)(fshi, fshi->fshi_hooks.arg,
+	    vp, uiop, ioflag, cr, ct);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+int
+fsh_next_write(fsh_int_t *fshi, vnode_t *vp, uio_t *uiop, int ioflag,
+	cred_t *cr, caller_context_t *ct)
+{
+	fsh_fsrecord_t *fsrecp = vp->v_vfsp->vfs_fshrecord;
+	int ret;
+
+	/*
+	 * The passed fshi is the previous hook (the one from which we've been
+	 * called). We need to find the next one.
+	 */
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.write != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*vp->v_op->vop_write)(vp, uiop, ioflag, cr, ct));
+
+	ret = (*fshi->fshi_hooks.write)(fshi, fshi->fshi_hooks.arg,
+	    vp, uiop, ioflag, cr, ct);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+int
+fsh_next_mount(fsh_int_t *fshi, vfs_t *vfsp, vnode_t *mvp, struct mounta *uap,
+	cred_t *cr)
+{
+	fsh_fsrecord_t *fsrecp = vfsp->vfs_fshrecord;
+	int ret;
+
+	/*
+	 * The passed fshi is the previous hook (the one from which we've been
+	 * called). We need to find the next one.
+	 */
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.mount != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
+
+	ret = (*fshi->fshi_hooks.mount)(fshi, fshi->fshi_hooks.arg,
+	    vfsp, mvp, uap, cr);
+	fshi_rele(fshi);
+	return (ret);
+}
+
+int
+fsh_next_unmount(fsh_int_t *fshi, vfs_t *vfsp, int flag, cred_t *cr)
+{
+	fsh_fsrecord_t *fsrecp = vfsp->vfs_fshrecord;
+	int ret;
+
+	/*
+	 * The passed fshi is the previous hook (the one from which we've been
+	 * called). We need to find the next one.
+	 */
+	rw_enter(&fsrecp->fshfsr_lock, RW_READER);
+	for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
+	    fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
+		if (fshi->fshi_hooks.unmount != NULL)
+			if (fshi_hold(fshi))
+				break;
+	}
+	rw_exit(&fsrecp->fshfsr_lock);
+
+	if (fshi == NULL)
+		return ((*vfsp->vfs_op->vfs_unmount)(vfsp, flag, cr));
+
+	ret = (*fshi->fshi_hooks.unmount)(fshi, fshi->fshi_hooks.arg,
+	    vfsp, flag, cr);
+	fshi_rele(fshi);
+	return (ret);
+}
--- old/usr/src/uts/common/fs/vfs.c	Tue Sep  3 18:09:41 2013
+++ new/usr/src/uts/common/fs/vfs.c	Tue Sep  3 18:09:41 2013
@@ -84,6 +84,8 @@
 #include <sys/spa.h>
 #include <sys/lofi.h>
 #include <sys/bootprops.h>
+#include <sys/fsh.h>
+#include <sys/fsh_impl.h>
 
 #include <vm/page.h>
 
@@ -216,13 +218,13 @@
 int
 fsop_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 {
-	return (*(vfsp)->vfs_op->vfs_mount)(vfsp, mvp, uap, cr);
+	return (fsh_mount(vfsp, mvp, uap, cr));
 }
 
 int
 fsop_unmount(vfs_t *vfsp, int flag, cred_t *cr)
 {
-	return (*(vfsp)->vfs_op->vfs_unmount)(vfsp, flag, cr);
+	return (fsh_unmount(vfsp, flag, cr));
 }
 
 int
@@ -1785,6 +1787,7 @@
 		}
 		/* Return vfsp to caller. */
 		*vfspp = vfsp;
+		fsh_exec_mount_callbacks(vfsp);
 	}
 errout:
 	vfs_freeopttbl(&mnt_mntopts);
@@ -4235,6 +4238,9 @@
 	/* Setup event monitor framework */
 	fem_init();
 
+	/* Setup filesystem hook framework */
+	fsh_init();
+
 	/* Initialize the dummy stray file system type. */
 	error = vfs_setfsops(0, stray_vfsops_template, NULL);
 
@@ -4319,6 +4325,15 @@
 		vfsp->vfs_femhead = NULL;
 	}
 
+	/*
+	 * fsh cleanup
+	 * There's no need here to use atomic operations on vfs_fshrecord.
+	 */
+	if (vfsp->vfs_fshrecord != NULL) {
+		fsh_fsrec_destroy(vfsp->vfs_fshrecord);
+		vfsp->vfs_fshrecord = NULL;
+	}
+
 	if (vfsp->vfs_implp)
 		vfsimpl_teardown(vfsp);
 	sema_destroy(&vfsp->vfs_reflock);
@@ -4345,6 +4360,7 @@
 {
 	ASSERT(vfsp->vfs_count != 0);
 	if (atomic_add_32_nv(&vfsp->vfs_count, -1) == 0) {
+		fsh_exec_free_callbacks(vfsp);
 		VFS_FREEVFS(vfsp);
 		lofi_remove(vfsp);
 		if (vfsp->vfs_zone)
--- old/usr/src/uts/common/fs/vnode.c	Tue Sep  3 18:09:41 2013
+++ new/usr/src/uts/common/fs/vnode.c	Tue Sep  3 18:09:41 2013
@@ -65,6 +65,7 @@
 #include <fs/fs_subr.h>
 #include <sys/taskq.h>
 #include <fs/fs_reparse.h>
+#include <sys/fsh_impl.h>
 
 /* Determine if this vnode is a file that is read-only */
 #define	ISROFILE(vp)	\
@@ -3225,7 +3226,7 @@
 
 	VOPXID_MAP_CR(vp, cr);
 
-	err = (*(vp)->v_op->vop_read)(vp, uiop, ioflag, cr, ct);
+	err = fsh_read(vp, uiop, ioflag, cr, ct);
 	VOPSTATS_UPDATE_IO(vp, read,
 	    read_bytes, (resid_start - uiop->uio_resid));
 	return (err);
@@ -3244,7 +3245,7 @@
 
 	VOPXID_MAP_CR(vp, cr);
 
-	err = (*(vp)->v_op->vop_write)(vp, uiop, ioflag, cr, ct);
+	err = fsh_write(vp, uiop, ioflag, cr, ct);
 	VOPSTATS_UPDATE_IO(vp, write,
 	    write_bytes, (resid_start - uiop->uio_resid));
 	return (err);
--- /dev/null	Tue Sep  3 18:09:41 2013
+++ new/usr/src/uts/common/io/fsd/fsd.c	Tue Sep  3 18:09:41 2013
@@ -0,0 +1,935 @@
+/*
+ * 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.
+ */
+
+/*
+ * Filesystem disturber pseudo-device driver.
+ */
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/file.h>
+#include <sys/fsd.h>
+#include <sys/fsh.h>
+#include <sys/kmem.h>
+#include <sys/ksynch.h>
+#include <sys/list.h>
+#include <sys/mkdev.h>
+#include <sys/refstr.h>
+#include <sys/stat.h>
+#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+/*
+ * TODO:
+ * - add checking if a file descriptor passed by the client is indeed
+ *	a mountpoint (we'd like to avoid disturbing / instead of an
+ *	unmounted filesystem)
+ */
+/*
+ * fsd - filesystem disturber
+ *
+ * 1. Abstract
+ * Filesystem disturber is a pseudo-device driver used to inject pathological
+ * behaviour into vfs calls. It is NOT a fuzzer. That kind of behaviour
+ * should be expected and correctly handled by software. A simple example of
+ * such behaviour is read() reading less bytes than it was requested. It's
+ * well documented and every read() caller should check the return value of
+ * this function before proceeding.
+ *
+ * 2. Features
+ * * per-vfs injections
+ * * injection installing on every newly mounted vfs (that's called an
+ *   omnipresent disturber)
+ *
+ * 3. Usage
+ * fsd_t is a structure which contains all the parameters for the disturbers.
+ * This structure is shared by all hooks on a vfs_t.
+ *
+ * fsd_info_t is filled out by a call to ioctl() and it provides basic
+ * information about fsd's current status.
+ *
+ * fsd_dis_t is passed to ioctl() when a request to disturb a filesystem is
+ * made. It's just a descriptor of a representative file and an fsd_t structure.
+ *
+ * fsd_fs_t is a structure filled out by ioctl() call when the client requests a
+ * full list of disturbers installed in the system.
+ *
+ * fsd_ioc_t is an union for different ioctl() commands.
+ *
+ * ioctl() commands:
+ * FSD_ENABLE:
+ *	ioctl(fd, FSD_ENABLE);
+ *	Enables the fsd. When fsd is enabled, any attemps to detach the driver
+ *	will fail.
+ *
+ * FSD_DISABLE:
+ *	ioctl(fd, FSD_DISABLE);
+ *	Disables the fsd.
+ *
+ * FSD_GET_PARAM:
+ *	ioctl(fd, FSD_GET_PARAM, ioc);
+ *	Get's fsd_t associated with a given filesystem. ioc is fsdioc_mnt when
+ *	passed to ioctl(). fsdioc_param is the output.
+ *	Errors:
+ *		ENOENT - the filesystem is not being disturbed
+ *
+ * FSD_DISTURB:
+ *	ioctl(fd, FSD_DISTURB, ioc);
+ *	Installs a disturber on a given filesystem. If a disturber is already
+ *	installed on this filesystem, it overwrites it. ioc is fsdioc_dis.
+ *	Errors:
+ *		EAGAIN - hook limit exceeded
+ *		EBADFD - cannot open the file descriptor
+ *		EINVAL - parameters are invalid
+ *
+ * FSD_DISTURB_OFF:
+ *	ioctl(fd, FSD_DISTURB_OFF, ioc);
+ *	Removes a disturber from a given filesystem. ioc is fsdioc_mnt
+ *	Errors:
+ *		EBADFD - cannot open the file descriptor
+ *		ENOENT - the filesystem is not being disturbed
+ *
+ * FSD_DISTURB_OMNI:
+ *	ioctl(fd, FSD_DISTURB_OMNI, ioc);
+ *	Install an omnipresent disturber. It means that whenever a new vfs_t is
+ *	being created, this disturber is installed on it. If an omnipresent
+ *	disturber is already installed, it overwrites it. ioc is fsdioc_param
+ *	Errors:
+ *		EINVAL - parameters are invalid
+ *
+ * FSD_DISTURB_OMNI_OFF:
+ *	ioctl(fd, FSD_DISTURB_OMNI_OFF);
+ *	Removes the omnipresent disturber. That does NOT mean that filesystems
+ *	which are disturbed because of the omnipresent disturber presence in the
+ *	past are going to stop being disturbed after this call.
+ *
+ * FSD_GET_LIST:
+ *	ioctl(fd, FSD_GET_LIST, ioc);
+ *	Get's a full list of disturbers installed in the system. ioc is
+ *	fsdioc_list here. This is a structure with two fields, count and listp.
+ *	The count is the number of fsd_fs_t's allocated on the address that
+ *	listp is pointing to. There would be at most count fsd_fs_t entries
+ *	copied out to the caller. Also, count is set to the number of entries
+ *	copied out.
+ *
+ * FSD_GET_INFO:
+ *	ioctl(fd, FSD_GET_INFO, ioc);
+ *	Get's current information about fsd. ioc is fsdioc_info here.
+ *
+ * At most one hook is installed per vfs_t, and fsd_t describes all possible
+ * disturbance methods. Multiple commands using the fsd should somehow cooperate
+ * in order not to destroy each other efforts in installing disturbers.
+ *
+ * 4. Internals
+ * When fsd_enabled is nonzero, fsd_detach() fails.
+ *
+ * These mount callback is used for installing injections on newly mounted
+ * vfs_t's (omnipresent).
+ *
+ * The list of currently installed hooks is kept in fsd_list.
+ *
+ * fsd installs at most one hook on a vfs_t.
+ *
+ * Inside fsd_detach, we go through fsd_hooks list. There is no guarantee that
+ * a hook remove callback (fsd_remove_cb) wouldn't execute inside
+ * fsh_hook_remove(), thus we can't assume that while walking through fsd_hooks,
+ * our iterator will be valid, because fsh_hook_remove() could invalidate it.
+ * That's why fsd_detaching flag is introduced.
+ *
+ * 5. Locking
+ * Every modification of fsd_enable, fsd_hooks, fsd_omni_param and fsd_list is
+ * protected by fsd_lock.
+ *
+ * Hooks use only the elements of fsd_list, nothing else. Before an element of
+ * fsd_list is destroyed, a hook which uses it is removed. Elements from
+ * fsd_lists are removed and destroyed in the hook remove callback
+ * (fsd_remove_cb).
+ *
+ * Because of the fact that fsd_remove_cb() could be called both in the context
+ * of the thread that executes fsh_hook_remove() or outside the fsd, we need to
+ * use fsd_rem_thread in order not to cause a deadlock. fsh_hook_remove() could
+ * be called by at most one thread inside fsd (fsd_remove_disturber() holds
+ * fsd_lock). We just have to check inside fsd_remove_cb() if it was called
+ * from fsh_hook_remove() or not. We use fsd_rem_thread to determine that.
+ *
+ * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock.
+ */
+
+/*
+ * Once a set of hooks is installed on a filesystem, there's no need
+ * to bother fsh if we want to change the parameters of disturbance.
+ * Intead, we use fsd_lock to protect the fsd_int_t when it's being
+ * used or changed.
+ */
+typedef struct fsd_int {
+	krwlock_t	fsdi_lock;	/* protects fsd_param */
+	fsd_t		fsdi_param;
+	fsh_handle_t	fsdi_handle;	/* we use fsh's handle in fsd */
+	vfs_t		*fsdi_vfsp;
+	int		fsdi_doomed;
+	list_node_t	fsdi_next;
+} fsd_int_t;
+
+static dev_info_t *fsd_devi;
+
+
+/*
+ * fsd_lock protects: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle,
+ * fsd_detaching
+ */
+static kmutex_t fsd_lock;
+
+static kthread_t *fsd_rem_thread;
+static kmutex_t fsd_rem_thread_lock;
+
+static fsd_t *fsd_omni_param;	/* Argument used by fsd's mount callback. */
+static fsh_callback_handle_t fsd_cb_handle;
+static int fsd_enabled;
+static int fsd_detaching;
+
+/*
+ * List of fsd_int_t. For every vfs_t on which fsd has installed a set of hooks
+ * there exist exactly one fsd_int_t with fsdi_vfsp pointing to this vfs_t.
+ */
+static list_t fsd_list;
+static int fsd_list_count;
+static kcondvar_t fsd_cv_empty;
+
+
+/*
+ * Although it's safe to use this kind of pseudo-random number generator,
+ * it behaves very regular when it comes to parity. Every fsd_rand() call
+ * changes the parity of the seed. That's why when a range of width 2 is set
+ * as a parameter, it's highly possible that the random value will always be
+ * the same, because fsd_rand() could be called the same number of times in a
+ * hook.
+ */
+static long	fsd_rand_seed;
+
+static int
+fsd_rand()
+{
+	fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345;
+	return (fsd_rand_seed & 0x7ffffffff);
+}
+
+/* vnode hooks */
+/*
+ * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx()
+ * call, because it's valid until the hooks associated with it are removed.
+ * If a hook is removed, it cannot be executing.
+ */
+static int
+fsd_hook_read(fsh_int_t *fshi, void *arg, vnode_t *vp, uio_t *uiop,
+	int ioflag, cred_t *cr, caller_context_t *ct)
+{
+	fsd_int_t *fsdi = (fsd_int_t *)arg;
+	uint64_t count, less, less_chance;
+
+	/*
+	 * It is used to keep an odd number of fsd_rand() calls in every
+	 * fsd_hook_read() call. That is desired because when a range of width
+	 * 2 is set as a parameter, we don't want to make it a constant.
+	 * The pseudo-random number generator returns a number with different
+	 * parity with every call. If this function is called in every
+	 * fsd_hook_read() execution even number of times, it would always be
+	 * the same % 2.
+	 */
+	(void) fsd_rand();
+
+	ASSERT(vp->v_vfsp == fsdi->fsdi_vfsp);
+
+	rw_enter(&fsdi->fsdi_lock, RW_READER);
+	less_chance = fsdi->fsdi_param.read_less_chance;
+	less = (uint64_t)fsd_rand() %
+	    (fsdi->fsdi_param.read_less_r[1] + 1 -
+	    fsdi->fsdi_param.read_less_r[0]) + fsdi->fsdi_param.read_less_r[0];
+	rw_exit(&fsdi->fsdi_lock);
+
+	count = uiop->uio_iov->iov_len;
+	if ((uint64_t)fsd_rand() % 100 < less_chance) {
+		extern size_t copyout_max_cached;
+		int ret;
+
+		if (count > less)
+			count -= less;
+		else
+			less = 0;
+
+		uiop->uio_iov->iov_len = count;
+		uiop->uio_resid = count;
+		if (count <= copyout_max_cached)
+			uiop->uio_extflg = UIO_COPY_CACHED;
+		else
+			uiop->uio_extflg = UIO_COPY_DEFAULT;
+
+		ret = fsh_next_read(fshi, vp, uiop, ioflag, cr, ct);
+		uiop->uio_resid += less;
+		return (ret);
+	}
+
+	return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct));
+}
+
+
+static void
+fsd_remove_cb(void *arg, fsh_handle_t handle)
+{
+	_NOTE(ARGUNUSED(handle));
+
+	fsd_int_t *fsdi = (fsd_int_t *)arg;
+	int fsd_context;
+
+	mutex_enter(&fsd_rem_thread_lock);
+	fsd_context = fsd_rem_thread == curthread;
+	mutex_exit(&fsd_rem_thread_lock);
+
+	if (!fsd_context)
+		mutex_enter(&fsd_lock);
+
+	ASSERT(MUTEX_HELD(&fsd_lock));
+
+	if (!fsd_detaching)
+		list_remove(&fsd_list, fsdi);
+
+	rw_destroy(&fsdi->fsdi_lock);
+	kmem_free(fsdi, sizeof (*fsdi));
+
+	fsd_list_count--;
+	if (fsd_list_count == 0)
+		cv_signal(&fsd_cv_empty);
+
+	if (!fsd_context)
+		mutex_exit(&fsd_lock);
+}
+
+/*
+ * Installs a set of hook with given parameters on a vfs_t.
+ *
+ * It is expected that fsd_lock is being held.
+ *
+ * Returns 0 on success and non-zero if hook limit exceeded.
+ */
+static int
+fsd_install_disturber(vfs_t *vfsp, fsd_t *fsd)
+{
+	fsd_int_t *fsdi;
+
+	ASSERT(MUTEX_HELD(&fsd_lock));
+
+	for (fsdi = list_head(&fsd_list); fsdi != NULL;
+	    fsdi = list_next(&fsd_list, fsdi)) {
+		if (fsdi->fsdi_vfsp == vfsp)
+			break;
+	}
+
+	if (fsdi != NULL) {
+		/* Just change the existing fsd_int_t */
+		rw_enter(&fsdi->fsdi_lock, RW_WRITER);
+		(void) memcpy(&fsdi->fsdi_param, fsd,
+		    sizeof (fsdi->fsdi_param));
+		rw_exit(&fsdi->fsdi_lock);
+	} else {
+		fsh_t hook = { 0 };
+
+		fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP);
+		fsdi->fsdi_vfsp = vfsp;
+		(void) memcpy(&fsdi->fsdi_param, fsd,
+		    sizeof (fsdi->fsdi_param));
+		rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL);
+
+		hook.arg = fsdi;
+		hook.read = fsd_hook_read;
+		hook.remove_cb = fsd_remove_cb;
+
+		/*
+		 * It is safe to do so, because none of the hooks installed
+		 * by fsd uses fsdi_handle nor the fsd_list.
+		 */
+		fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook);
+		if (fsdi->fsdi_handle == -1) {
+			kmem_free(fsdi, sizeof (*fsdi));
+			rw_destroy(&fsdi->fsdi_lock);
+			return (-1);
+		}
+		list_insert_head(&fsd_list, fsdi);
+		fsd_list_count++;
+	}
+	return (0);
+}
+
+static int
+fsd_remove_disturber(vfs_t *vfsp)
+{
+	fsd_int_t *fsdi;
+
+	ASSERT(MUTEX_HELD(&fsd_lock));
+
+	for (fsdi = list_head(&fsd_list); fsdi != NULL;
+	    fsdi = list_next(&fsd_list, fsdi)) {
+		if (fsdi->fsdi_vfsp == vfsp)
+			break;
+	}
+	if (fsdi == NULL || fsdi->fsdi_doomed)
+		return (ENOENT);
+
+	fsdi->fsdi_doomed = 1;
+
+	mutex_enter(&fsd_rem_thread_lock);
+	fsd_rem_thread = curthread;
+	mutex_exit(&fsd_rem_thread_lock);
+
+	ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
+
+	mutex_enter(&fsd_rem_thread_lock);
+	fsd_rem_thread = NULL;
+	mutex_exit(&fsd_rem_thread_lock);
+
+	return (0);
+}
+
+static void
+fsd_callback_mount(vfs_t *vfsp, void *arg)
+{
+	_NOTE(ARGUNUSED(arg));
+
+	int error = 0;
+
+	mutex_enter(&fsd_lock);
+	if (fsd_omni_param != NULL)
+		error = fsd_install_disturber(vfsp, fsd_omni_param);
+	mutex_exit(&fsd_lock);
+
+	if (error != 0) {
+		refstr_t *mntref;
+
+		mntref = vfs_getmntpoint(vfsp);
+		(void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n",
+		    refstr_value(mntref));
+		refstr_rele(mntref);
+	}
+}
+
+static void
+fsd_enable()
+{
+	mutex_enter(&fsd_lock);
+	fsd_enabled = 1;
+	mutex_exit(&fsd_lock);
+}
+
+static void
+fsd_disable()
+{
+	mutex_enter(&fsd_lock);
+	fsd_enabled = 0;
+	mutex_exit(&fsd_lock);
+}
+
+
+/* Entry points */
+static int
+fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	minor_t instance;
+	fsh_callback_t cb = { 0 };
+
+	if (cmd != DDI_ATTACH)
+		return (DDI_FAILURE);
+
+	if (fsd_devi != NULL)
+		return (DDI_FAILURE);
+
+	instance = ddi_get_instance(dip);
+	if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
+	    DDI_PSEUDO, 0) == DDI_FAILURE)
+		return (DDI_FAILURE);
+	fsd_devi = dip;
+	ddi_report_dev(fsd_devi);
+
+	list_create(&fsd_list, sizeof (fsd_int_t),
+	    offsetof(fsd_int_t, fsdi_next));
+
+	fsd_rand_seed = gethrtime();
+
+	mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
+	mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL);
+
+	cb.fshc_mount = fsd_callback_mount;
+	cb.fshc_arg = fsd_omni_param;
+	fsd_cb_handle = fsh_callback_install(&cb);
+	if (fsd_cb_handle == -1) {
+		/* Cleanup */
+		list_destroy(&fsd_list);
+		cv_destroy(&fsd_cv_empty);
+		mutex_destroy(&fsd_rem_thread_lock);
+		mutex_destroy(&fsd_lock);
+		ddi_remove_minor_node(fsd_devi, NULL);
+		fsd_devi = NULL;
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * If fsd_enable() was called and there was no subsequent fsd_disable() call,
+ * detach will fail.
+ */
+static int
+fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	fsd_int_t *fsdi;
+
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	ASSERT(dip == fsd_devi);
+
+	/*
+	 * No need to hold fsd_lock here. Since only the hooks and callbacks
+	 * might be running at this point.
+	 */
+	if (fsd_enabled)
+		return (DDI_FAILURE);
+
+	ddi_remove_minor_node(dip, NULL);
+	fsd_devi = NULL;
+
+	mutex_enter(&fsd_lock);
+	fsd_detaching = 1;
+	while ((fsdi = list_remove_head(&fsd_list)) != NULL)
+		if (fsdi->fsdi_doomed == 0) {
+			fsdi->fsdi_doomed = 1;
+
+			mutex_enter(&fsd_rem_thread_lock);
+			fsd_rem_thread = curthread;
+			mutex_exit(&fsd_rem_thread_lock);
+			
+			/*
+			 * fsd_lock is held, so no other thread could have
+			 * removed this hook.
+			 */
+			ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
+
+			mutex_enter(&fsd_rem_thread_lock);
+			fsd_rem_thread = NULL;
+			mutex_exit(&fsd_rem_thread_lock);
+		}
+
+	while (fsd_list_count > 0)
+		cv_wait(&fsd_cv_empty, &fsd_lock);
+	mutex_exit(&fsd_lock);
+	cv_destroy(&fsd_cv_empty);
+
+	ASSERT(fsh_callback_remove(fsd_cb_handle) == 0);
+	if (fsd_omni_param != NULL) {
+		kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
+		fsd_omni_param = NULL;
+	}
+
+	/* After removing the callback and hooks, it is safe to remove these */
+	list_destroy(&fsd_list);
+	mutex_destroy(&fsd_rem_thread_lock);
+	mutex_destroy(&fsd_lock);
+
+	return (DDI_SUCCESS);
+}
+
+static int
+fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
+{
+	_NOTE(ARGUNUSED(dip));
+
+	switch (infocmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		*resultp = fsd_devi;
+		return (DDI_SUCCESS);
+	case DDI_INFO_DEVT2INSTANCE:
+		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
+		return (DDI_SUCCESS);
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+static int
+fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+	_NOTE(ARGUNUSED(devp));
+
+	if (flag & FEXCL || flag & FNDELAY)
+		return (EINVAL);
+
+	if (otyp != OTYP_CHR)
+		return (EINVAL);
+
+	if (!(flag & FREAD && flag & FWRITE))
+		return (EINVAL);
+
+	if (drv_priv(credp) == EPERM)
+		return (EPERM);
+
+	return (0);
+}
+
+static int
+fsd_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+	_NOTE(ARGUNUSED(dev));
+	_NOTE(ARGUNUSED(flag));
+	_NOTE(ARGUNUSED(otyp));
+	_NOTE(ARGUNUSED(credp));
+
+	return (0);
+}
+
+
+/* ioctl(9E) and it's support functions */
+static int
+fsd_check_param(fsd_t *fsd)
+{
+	if (fsd->read_less_chance > 100 ||
+	    fsd->read_less_r[0] > fsd->read_less_r[1])
+		return (EINVAL);
+	return (0);
+}
+
+static int
+fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp)
+{
+	file_t *file;
+	fsd_dis_t dis;
+	int rv;
+
+	if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode))
+		return (EFAULT);
+
+	if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) {
+		*rvalp = rv;
+		return (0);
+	}
+
+	if ((file = getf((int)dis.fsdd_mnt)) == NULL) {
+		*rvalp = EBADFD;
+		return (0);
+	}
+
+	mutex_enter(&fsd_lock);
+	rv = fsd_install_disturber(file->f_vnode->v_vfsp, &dis.fsdd_param);
+	mutex_exit(&fsd_lock);
+
+	releasef((int)dis.fsdd_mnt);
+
+	if (rv != 0)
+		*rvalp = EAGAIN;
+	else
+		*rvalp = 0;
+
+	return (0);
+}
+
+static int
+fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp)
+{
+	file_t *file;
+	fsd_int_t *fsdi;
+	int error = 0;
+	int64_t fd;
+	vfs_t *vfsp;
+
+	if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
+		return (EFAULT);
+
+	if ((file = getf((int)fd)) == NULL) {
+		*rvalp = EBADFD;
+		return (0);
+	}
+	vfsp = file->f_vnode->v_vfsp;
+	releasef((int)fd);
+
+
+	mutex_enter(&fsd_lock);
+
+	for (fsdi = list_head(&fsd_list); fsdi != NULL;
+	    fsdi = list_next(&fsd_list, fsdi)) {
+		if (fsdi->fsdi_vfsp == vfsp)
+			break;
+	}
+	if (fsdi == NULL) {
+		*rvalp = ENOENT;
+		mutex_exit(&fsd_lock);
+		return (0);
+	}
+	rw_enter(&fsdi->fsdi_lock, RW_READER);
+	error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param,
+	    sizeof (fsdi->fsdi_param), mode);
+	rw_exit(&fsdi->fsdi_lock);
+
+	mutex_exit(&fsd_lock);
+
+
+	if (error) {
+		return (EFAULT);
+	} else {
+		*rvalp = 0;
+		return (0);
+	}
+}
+
+static int
+fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp)
+{
+	fsd_info_t info;
+
+	mutex_enter(&fsd_lock);
+	info.fsdinf_enabled = fsd_enabled;
+	info.fsdinf_count = fsd_list_count;
+	info.fsdinf_omni_on = fsd_omni_param != NULL;
+	if (info.fsdinf_omni_on)
+		(void) memcpy(&info.fsdinf_omni_param, fsd_omni_param,
+		    sizeof (info.fsdinf_omni_param));
+	mutex_exit(&fsd_lock);
+
+	if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode))
+		return (EFAULT);
+
+	*rvalp = 0;
+	return (0);
+}
+
+static int
+fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp)
+{
+	fsd_int_t *fsdi;
+	fsd_fs_t *fsdfs_list;
+	int i;
+	int ret = 0;
+	int64_t ioc_list_count;
+
+	*rvalp = 0;
+
+	/* Get data */
+	if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count,
+	    sizeof (ioc_list_count), mode))
+		return (EFAULT);
+	if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list,
+	    sizeof (fsdfs_list), mode))
+		return (EFAULT);
+
+
+	mutex_enter(&fsd_lock);
+	if (ioc_list_count > fsd_list_count)
+		ioc_list_count = fsd_list_count;
+
+	/* Copyout */
+	if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count,
+	    sizeof (ioc_list_count), mode)) {
+		ret = EFAULT;
+		goto out;
+	}
+	for (fsdi = list_head(&fsd_list), i = 0;
+	    fsdi != NULL && i < ioc_list_count;
+	    fsdi = list_next(&fsd_list, fsdi), i++) {
+		refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp);
+		int len = strlen(refstr_value(mntstr));
+
+		rw_enter(&fsdi->fsdi_lock, RW_READER);
+		if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name,
+		    len + 1, mode) ||
+		    ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param,
+		    sizeof (fsdi->fsdi_param), mode)) {
+			ret = EFAULT;
+		}
+		rw_exit(&fsdi->fsdi_lock);
+		refstr_rele(mntstr);
+
+		if (ret != 0)
+			break;
+	}
+
+
+out:
+	mutex_exit(&fsd_lock);
+	return (ret);
+}
+
+static int
+fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp)
+{
+	file_t *file;
+	int64_t fd;
+
+	if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
+		return (EFAULT);
+
+	if ((file = getf((int)fd)) == NULL) {
+		*rvalp = EBADFD;
+		return (0);
+	}
+
+	mutex_enter(&fsd_lock);
+	*rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp);
+	releasef((int)fd);
+	mutex_exit(&fsd_lock);
+
+	return (0);
+}
+
+static int
+fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp)
+{
+	fsd_t fsd;
+	int rv;
+
+	if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode))
+		return (EFAULT);
+
+	if ((rv = fsd_check_param(&fsd)) != 0) {
+		*rvalp = rv;
+		return (0);
+	}
+
+	mutex_enter(&fsd_lock);
+	if (fsd_omni_param == NULL)
+		fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param),
+		    KM_SLEEP);
+	(void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param));
+	mutex_exit(&fsd_lock);
+
+	*rvalp = 0;
+	return (0);
+}
+
+
+static int
+fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+	int *rvalp)
+{
+	_NOTE(ARGUNUSED(dev));
+	_NOTE(ARGUNUSED(credp));
+
+	if (!fsd_enabled && cmd != FSD_ENABLE) {
+		*rvalp = ENOTACTIVE;
+		return (0);
+	}
+
+	switch (cmd) {
+	case FSD_ENABLE:
+		fsd_enable();
+		*rvalp = 0;
+		return (0);
+
+	case FSD_DISABLE:
+		fsd_disable();
+		*rvalp = 0;
+		return (0);
+
+	case FSD_GET_PARAM:
+		return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp));
+
+	case FSD_DISTURB:
+		return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp));
+
+	case FSD_DISTURB_OFF:
+		return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp));
+
+	case FSD_DISTURB_OMNI:
+		return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp));
+
+	case FSD_DISTURB_OMNI_OFF:
+		mutex_enter(&fsd_lock);
+		if (fsd_omni_param != NULL)
+			kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
+		fsd_omni_param = NULL;
+		mutex_exit(&fsd_lock);
+
+		*rvalp = 0;
+		return (0);
+
+	case FSD_GET_LIST:
+		return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp));
+
+	case FSD_GET_INFO:
+		return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp));
+
+	default:
+		return (ENOTTY);
+	}
+}
+
+static struct cb_ops cb_ops = {
+	fsd_open,	/* open(9E) */
+	fsd_close,	/* close(9E) */
+	nodev,		/* strategy(9E) */
+	nodev,		/* print(9E) */
+	nodev,		/* dump(9E) */
+	nodev,		/* read(9E) */
+	nodev,		/* write(9E) */
+	fsd_ioctl,	/* ioctl(9E) */
+	nodev,		/* devmap(9E) */
+	nodev,		/* mmap(9E) */
+	nodev,		/* segmap(9E) */
+	nochpoll,	/* chpoll(9E) */
+	ddi_prop_op,	/* prop_op(9E) */
+	NULL,		/* streamtab(9E) */
+	D_MP | D_64BIT,	/* cb_flag(9E) */
+	CB_REV,		/* cb_rev(9E) */
+	nodev,		/* aread(9E) */
+	nodev,		/* awrite(9E) */
+};
+
+static struct dev_ops dev_ops = {
+	DEVO_REV,		/* driver build version */
+	0,			/* reference count */
+	fsd_getinfo,		/* getinfo */
+	nulldev,
+	nulldev,		/* probe */
+	fsd_attach,		/* attach */
+	fsd_detach,		/* detach */
+	nodev,
+	&cb_ops,		/* cb_ops */
+	NULL,			/* bus_ops */
+	NULL,			/* power */
+	ddi_quiesce_not_needed,	/* quiesce */
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops,	"Filesystem disturber", &dev_ops
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modldrv, NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&modlinkage));
+}
--- /dev/null	Tue Sep  3 18:09:41 2013
+++ new/usr/src/uts/common/io/fsd/fsd.conf	Tue Sep  3 18:09:41 2013
@@ -0,0 +1,15 @@
+#
+# 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.
+#
+name="fsd" parent="pseudo" instance=0;
--- old/usr/src/uts/common/sys/Makefile	Tue Sep  3 18:09:41 2013
+++ new/usr/src/uts/common/sys/Makefile	Tue Sep  3 18:09:41 2013
@@ -241,6 +241,9 @@
 	flock.h			\
 	flock_impl.h		\
 	fork.h			\
+	fsd.h			\
+	fsh.h			\
+	fsh_impl.h		\
 	fss.h			\
 	fsspriocntl.h		\
 	fsid.h			\
--- /dev/null	Tue Sep  3 18:09:42 2013
+++ new/usr/src/uts/common/sys/fsd.h	Tue Sep  3 18:09:42 2013
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#ifndef _FSD_H
+#define	_FSD_H
+
+/*
+ * filesystem disturber header file
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/errno.h>
+#include <sys/ksynch.h>
+#include <sys/list.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+
+
+#define	FSD_DEV_PATH	"/dev/fsd"
+
+/*
+ * Commands for ioctl().
+ */
+#define	FSDIOC	(('f' << 24) | ('s' << 16) | ('d' << 8))
+#define	FSD_GET_PARAM		(FSDIOC | 1)
+#define	FSD_ENABLE		(FSDIOC | 2)
+#define	FSD_DISABLE		(FSDIOC | 3)
+#define	FSD_DISTURB		(FSDIOC | 4)
+#define	FSD_DISTURB_OFF		(FSDIOC | 5)
+#define	FSD_DISTURB_OMNI	(FSDIOC | 6)
+#define	FSD_DISTURB_OMNI_OFF	(FSDIOC | 7)
+#define	FSD_GET_LIST		(FSDIOC | 8)
+#define	FSD_GET_INFO		(FSDIOC | 9)
+
+
+/*
+ * Parameters description:
+ * "read less"
+ *	Makes a VOP_READ() call read n (from a given range) bytes less than it
+ *	was requested.
+ */
+typedef struct fsd {
+	uint64_t	read_less_chance;
+	uint64_t	read_less_r[2];	/* range */
+} fsd_t;
+
+typedef struct fsd_info {
+	uint64_t	fsdinf_enabled;		/* fsd enabled */
+	uint64_t	fsdinf_count;		/* disturbers installed */
+	uint64_t	fsdinf_omni_on;		/* omnipresent disturber on */
+	fsd_t		fsdinf_omni_param;	/* omnipresent dist. params */
+} fsd_info_t;
+
+typedef struct fsd_dis {
+	int64_t	fsdd_mnt;
+	fsd_t	fsdd_param;
+} fsd_dis_t;
+
+typedef struct fsd_fs {
+	fsd_t	fsdf_param;
+	uint8_t	fsdf_name[MAXPATHLEN];
+} fsd_fs_t;
+
+typedef union fsd_ioc {		/* Used with:				*/
+	fsd_info_t fsdioc_info;	/* _GET_INFO 				*/
+	fsd_dis_t fsdioc_dis;	/* _DISTURB 				*/
+	fsd_t	fsdioc_param;	/* _GET_PARAM out, _DISTURB_OMNI	*/
+	int64_t	fsdioc_mnt;	/* _DISTURB_OFF, _GET_PARAM in		*/
+	struct {
+		int64_t	count;
+		uint64_t listp;
+	} fsdioc_list;		/* _GET_LIST				*/
+} fsd_ioc_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FSD_H */
--- /dev/null	Tue Sep  3 18:09:42 2013
+++ new/usr/src/uts/common/sys/fsh.h	Tue Sep  3 18:09:42 2013
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef _FSH_H
+#define	_FSH_H
+
+#include <sys/id_space.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef id_t fsh_handle_t;
+typedef id_t fsh_callback_handle_t;
+
+struct fsh_int;
+typedef struct fsh_int fsh_int_t;
+
+typedef struct fsh {
+	void *arg;
+	void (*remove_cb)(void *, fsh_handle_t);
+
+	/* vnode */
+	int (*read)(fsh_int_t *, void *, vnode_t *, uio_t *, int, cred_t *,
+		caller_context_t *);
+	int (*write)(fsh_int_t *, void *, vnode_t *, uio_t *, int, cred_t *,
+		caller_context_t *);
+
+	/* vfs */
+	int (*mount)(fsh_int_t *, void *, vfs_t *, vnode_t *, struct mounta *,
+		cred_t *);
+	int (*unmount)(fsh_int_t *, void *, vfs_t *, int, cred_t *);
+} fsh_t;
+
+typedef struct fsh_callback {
+	void	*fshc_arg;
+	void	(*fshc_free)(vfs_t *, void *);
+	void	(*fshc_mount)(vfs_t *, void *);
+} fsh_callback_t;
+
+/* API */
+extern fsh_handle_t fsh_hook_install(vfs_t *, fsh_t *);
+extern int fsh_hook_remove(fsh_handle_t);
+
+extern fsh_callback_handle_t fsh_callback_install(fsh_callback_t *);
+extern int fsh_callback_remove(fsh_callback_handle_t);
+
+extern void fsh_fs_enable(vfs_t *);
+extern void fsh_fs_disable(vfs_t *);
+
+/* fsh control passing */
+extern int fsh_next_read(fsh_int_t *, vnode_t *, uio_t *, int, cred_t *,
+		caller_context_t *);
+extern int fsh_next_write(fsh_int_t *, vnode_t *, uio_t *, int, cred_t *,
+		caller_context_t *);
+
+extern int fsh_next_mount(fsh_int_t *, vfs_t *, vnode_t *, struct mounta *uap,
+		cred_t *);
+extern int fsh_next_unmount(fsh_int_t *, vfs_t *, int, cred_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FSH_H */
--- /dev/null	Tue Sep  3 18:09:42 2013
+++ new/usr/src/uts/common/sys/fsh_impl.h	Tue Sep  3 18:09:42 2013
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file includes all the necessary declarations for vfs.c and vnode.c.
+ */
+
+#ifndef _FSH_IMPL_H
+#define	_FSH_IMPL_H
+
+#include <sys/fsh.h>
+#include <sys/list.h>
+#include <sys/pathname.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct fsh_fshrecord;
+typedef struct fsh_fsrecord fsh_fsrecord_t;
+
+/* API for vnode.c and vfs.c only */
+/* vnode.c */
+extern int fsh_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+extern int fsh_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+
+/* vfs.c */
+extern void fsh_init(void);
+extern void fsh_exec_mount_callbacks(vfs_t *);
+extern void fsh_exec_free_callbacks(vfs_t *);
+extern void fsh_fsrec_destroy(fsh_fsrecord_t *volatile);
+
+extern int fsh_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+extern int fsh_unmount(vfs_t *, int, cred_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FSH_IMPL_H */
--- old/usr/src/uts/common/sys/vfs.h	Tue Sep  3 18:09:42 2013
+++ new/usr/src/uts/common/sys/vfs.h	Tue Sep  3 18:09:42 2013
@@ -198,6 +198,7 @@
  */
 struct zone;		/* from zone.h */
 struct fem_head;	/* from fem.h */
+struct fsh_fsrecord;	/* from fsh_impl.h */
 
 typedef struct vfs {
 	struct vfs	*vfs_next;		/* next VFS in VFS list */
@@ -235,6 +236,9 @@
 
 	struct fem_head	*vfs_femhead;		/* fs monitoring */
 	minor_t		vfs_lofi_minor;		/* minor if lofi mount */
+
+	struct fsh_fsrecord *volatile
+		vfs_fshrecord;			/* fs hooking */
 } vfs_t;
 
 #define	vfs_featureset	vfs_implp->vi_featureset
--- old/usr/src/uts/intel/Makefile.intel.shared	Tue Sep  3 18:09:42 2013
+++ new/usr/src/uts/intel/Makefile.intel.shared	Tue Sep  3 18:09:42 2013
@@ -239,6 +239,7 @@
 DRV_KMODS	+= fd
 DRV_KMODS	+= fdc
 DRV_KMODS	+= fm
+DRV_KMODS	+= fsd
 DRV_KMODS	+= fssnap
 DRV_KMODS	+= hxge
 DRV_KMODS	+= i8042
--- /dev/null	Tue Sep  3 18:09:42 2013
+++ new/usr/src/uts/intel/fsd/Makefile	Tue Sep  3 18:09:42 2013
@@ -0,0 +1,65 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2012 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# Copyright 2013 Damian Bogel. All rights reserved.
+#
+
+UTSBASE	= ../..
+
+MODULE	= fsd
+OBJECTS	= $(FSD_OBJS:%=$(OBJS_DIR)/%)
+LINTS	= $(FSD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/fsd
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+LDFLAGS += -dy
+
+.KEEP_STATE:
+
+def:	$(DEF_DEPS)
+
+all:	$(ALL_DEPS)
+
+clean:	$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:	$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
