--- old/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Aug  5 17:27:10 2019
+++ new/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Aug  5 17:27:10 2019
@@ -21,15 +21,18 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
  * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
  * Portions Copyright 2011 Martin Matuska
  * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved.
  * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
  * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  * Copyright (c) 2014 Integros [integros.com]
+ * Copyright 2018 Nexenta Systems, Inc.
  * Copyright 2016 Toomas Soome <tsoome@me.com>
  * Copyright 2017 RackTop Systems.
  * Copyright (c) 2017 Datto Inc.
@@ -156,11 +159,13 @@
 #include <sys/spa_impl.h>
 #include <sys/vdev.h>
 #include <sys/priv_impl.h>
+#include <sys/autosnap.h>
 #include <sys/dmu.h>
 #include <sys/dsl_dir.h>
 #include <sys/dsl_dataset.h>
 #include <sys/dsl_prop.h>
 #include <sys/dsl_deleg.h>
+#include <sys/dsl_synctask.h>
 #include <sys/dmu_objset.h>
 #include <sys/dmu_impl.h>
 #include <sys/dmu_tx.h>
@@ -186,14 +191,19 @@
 #include <sys/dsl_bookmark.h>
 #include <sys/dsl_userhold.h>
 #include <sys/zfeature.h>
+#include <sys/cos.h>
+#include <sys/cos_impl.h>
+#include <sys/zfeature.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent_impl.h>
 #include <sys/zcp.h>
 #include <sys/zio_checksum.h>
-#include <sys/vdev_removal.h>
 
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
 #include "zfs_deleg.h"
 #include "zfs_comutil.h"
+#include "zfs_errno.h"
 
 #include "lua.h"
 #include "lauxlib.h"
@@ -257,6 +267,39 @@
 
 static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature);
 
+static int
+zfs_is_wormed_ds(dsl_dataset_t *ds)
+{
+	char worminfo[13] = {0};
+
+	if (dsl_prop_get_ds(ds, "nms:worm", 1, 12, &worminfo, NULL) == 0 &&
+	    worminfo[0] && strcmp(worminfo, "0") != 0 &&
+	    strcmp(worminfo, "off") != 0 && strcmp(worminfo, "-") != 0) {
+		return (1);
+	}
+	return (0);
+}
+
+static int
+zfs_is_wormed(const char *name)
+{
+	char worminfo[13] = {0};
+	char cname[MAXNAMELEN];
+	char *end;
+
+	(void) strlcpy(cname, name, MAXNAMELEN);
+	end = strchr(cname, '@');
+	if (end)
+		*end = 0;
+
+	if (dsl_prop_get(cname, "nms:worm", 1, 12, &worminfo, NULL) == 0 &&
+	    worminfo[0] && strcmp(worminfo, "0") != 0 &&
+	    strcmp(worminfo, "off") != 0 && strcmp(worminfo, "-") != 0) {
+		return (1);
+	}
+	return (0);
+}
+
 /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
 void
 __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
@@ -772,9 +815,6 @@
 int
 zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 {
-	if (!INGLOBALZONE(curproc))
-		return (SET_ERROR(EPERM));
-
 	if (secpolicy_nfs(cr) == 0) {
 		return (0);
 	} else {
@@ -785,9 +825,6 @@
 int
 zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 {
-	if (!INGLOBALZONE(curproc))
-		return (SET_ERROR(EPERM));
-
 	if (secpolicy_smb(cr) == 0) {
 		return (0);
 	} else {
@@ -1043,14 +1080,6 @@
 
 /* ARGSUSED */
 static int
-zfs_secpolicy_remap(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
-{
-	return (zfs_secpolicy_write_perms(zc->zc_name,
-	    ZFS_DELEG_PERM_REMAP, cr));
-}
-
-/* ARGSUSED */
-static int
 zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 {
 	nvpair_t *pair, *nextpair;
@@ -1383,6 +1412,13 @@
 	return (0);
 }
 
+/*
+ * Callers will know whether there's anything to unpack based on ret non-0/errno
+ * set to ENOMEM, but observers (e.g truss) need the message properly marked to
+ * know if it should be unpacked and displayed. Don't marked as filled unless
+ * completely successful. If there's a non-empty nvlist, set size to its nvl
+ * size as resize hint.
+ */
 static int
 put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
 {
@@ -1392,6 +1428,7 @@
 
 	size = fnvlist_size(nvl);
 
+	zc->zc_nvlist_dst_filled = B_FALSE;
 	if (size > zc->zc_nvlist_dst_size) {
 		error = SET_ERROR(ENOMEM);
 	} else {
@@ -1399,14 +1436,51 @@
 		if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
 		    size, zc->zc_iflags) != 0)
 			error = SET_ERROR(EFAULT);
+		else
+			zc->zc_nvlist_dst_filled = B_TRUE;
 		fnvlist_pack_free(packed, size);
 	}
 
 	zc->zc_nvlist_dst_size = size;
-	zc->zc_nvlist_dst_filled = B_TRUE;
 	return (error);
 }
 
+static int
+getzfsvfs_from_ds(dsl_dataset_t *ds, zfsvfs_t **zfvp)
+{
+	objset_t *os;
+	int error;
+	dsl_pool_t *dp;
+
+	dp = ds->ds_dir->dd_pool;
+	dsl_pool_config_enter(dp, FTAG);
+
+	/*
+	 * IU:	we probably need to hold dataset here.
+	 *	For now let's assume we do.
+	 *	May need revision later.
+	 */
+	dsl_dataset_long_hold(ds, FTAG);
+	error = dmu_objset_from_ds(ds, &os);
+	if (dmu_objset_type(os) != DMU_OST_ZFS) {
+		dsl_dataset_long_rele(ds, FTAG);
+		dsl_pool_config_exit(dp, FTAG);
+		return (EINVAL);
+	}
+
+	mutex_enter(&os->os_user_ptr_lock);
+	*zfvp = dmu_objset_get_user(os);
+	if (*zfvp) {
+		VFS_HOLD((*zfvp)->z_vfs);
+	} else {
+		error = ESRCH;
+	}
+	mutex_exit(&os->os_user_ptr_lock);
+	dsl_dataset_long_rele(ds, FTAG);
+	dsl_pool_config_exit(dp, FTAG);
+	return (error);
+}
+
 int
 getzfsvfs_impl(objset_t *os, zfsvfs_t **zfvp)
 {
@@ -1483,6 +1557,29 @@
 	}
 }
 
+
+/*
+ * Publish events using GPEC subsystem
+ */
+
+static evchan_t *zfs_channel = NULL;
+
+void
+zfs_event_post(const char *subclass, const char *operation, nvlist_t *ev_data)
+{
+
+	if (zfs_channel == NULL)
+		goto out;
+
+	fnvlist_add_string(ev_data, "operation", operation);
+
+	(void) sysevent_evc_publish(zfs_channel, subclass, operation,
+	    "com.nexenta", "zfs-kernel", ev_data, EVCH_NOSLEEP);
+
+out:
+	fnvlist_free(ev_data);
+}
+
 static int
 zfs_ioc_pool_create(zfs_cmd_t *zc)
 {
@@ -1490,6 +1587,7 @@
 	nvlist_t *config, *props = NULL;
 	nvlist_t *rootprops = NULL;
 	nvlist_t *zplprops = NULL;
+	nvlist_t *event;
 
 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
 	    zc->zc_iflags, &config))
@@ -1538,6 +1636,15 @@
 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
 		(void) spa_destroy(zc->zc_name);
 
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "name", zc->zc_name);
+		fnvlist_add_nvlist(event, "config", config);
+		if (props != NULL)
+			fnvlist_add_nvlist(event, "props", props);
+		zfs_event_post(ZPOOL_EC_STATUS, "create", event);
+	}
+
 pool_props_bad:
 	nvlist_free(rootprops);
 	nvlist_free(zplprops);
@@ -1551,10 +1658,15 @@
 zfs_ioc_pool_destroy(zfs_cmd_t *zc)
 {
 	int error;
+	nvlist_t *event;
 	zfs_log_history(zc);
 	error = spa_destroy(zc->zc_name);
-	if (error == 0)
+	if (error == 0) {
 		zvol_remove_minors(zc->zc_name);
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "pool", zc->zc_name);
+		zfs_event_post(ZPOOL_EC_STATUS, "destroy", event);
+	}
 	return (error);
 }
 
@@ -1564,6 +1676,7 @@
 	nvlist_t *config, *props = NULL;
 	uint64_t guid;
 	int error;
+	nvlist_t *event;
 
 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
 	    zc->zc_iflags, &config)) != 0)
@@ -1582,6 +1695,16 @@
 	else
 		error = spa_import(zc->zc_name, config, props, zc->zc_cookie);
 
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "pool", zc->zc_name);
+		fnvlist_add_uint64(event, "guid", zc->zc_guid);
+		fnvlist_add_nvlist(event, "config", config);
+		if (props != NULL)
+			fnvlist_add_nvlist(event, "props", props);
+		zfs_event_post(ZPOOL_EC_STATUS, "import", event);
+	}
+
 	if (zc->zc_nvlist_dst != 0) {
 		int err;
 
@@ -1602,11 +1725,17 @@
 	int error;
 	boolean_t force = (boolean_t)zc->zc_cookie;
 	boolean_t hardforce = (boolean_t)zc->zc_guid;
+	boolean_t saveconfig = (boolean_t)zc->zc_obj;
+	nvlist_t *event;
 
 	zfs_log_history(zc);
-	error = spa_export(zc->zc_name, NULL, force, hardforce);
-	if (error == 0)
+	error = spa_export(zc->zc_name, NULL, force, hardforce, saveconfig);
+	if (error == 0) {
 		zvol_remove_minors(zc->zc_name);
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "pool", zc->zc_name);
+		zfs_event_post(ZPOOL_EC_STATUS, "export", event);
+	}
 	return (error);
 }
 
@@ -1719,7 +1848,37 @@
 	return (error);
 }
 
+/*
+ * inputs:
+ * zc_name              name of the pool
+ * zc_cookie            trim_cmd_info_t
+ */
 static int
+zfs_ioc_pool_trim(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	int error;
+	trim_cmd_info_t	tci;
+
+	if (ddi_copyin((void *)(uintptr_t)zc->zc_cookie, &tci,
+	    sizeof (tci), 0) == -1)
+		return (EFAULT);
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+		return (error);
+
+	if (tci.tci_start) {
+		spa_man_trim(spa, tci.tci_rate);
+	} else {
+		spa_man_trim_stop(spa);
+	}
+
+	spa_close(spa, FTAG);
+
+	return (error);
+}
+
+static int
 zfs_ioc_pool_freeze(zfs_cmd_t *zc)
 {
 	spa_t *spa;
@@ -1870,6 +2029,7 @@
 	int error;
 	nvlist_t *config, **l2cache, **spares;
 	uint_t nl2cache = 0, nspares = 0;
+	nvlist_t *event;
 
 	error = spa_open(zc->zc_name, &spa, FTAG);
 	if (error != 0)
@@ -1901,6 +2061,13 @@
 
 	if (error == 0) {
 		error = spa_vdev_add(spa, config);
+		if (error == 0) {
+			event = fnvlist_alloc();
+			fnvlist_add_string(event, "pool", zc->zc_name);
+			fnvlist_add_nvlist(event, "config", config);
+			zfs_event_post(ZPOOL_EC_STATUS, "add", event);
+
+		}
 		nvlist_free(config);
 	}
 	spa_close(spa, FTAG);
@@ -1910,8 +2077,8 @@
 /*
  * inputs:
  * zc_name		name of the pool
- * zc_guid		guid of vdev to remove
- * zc_cookie		cancel removal
+ * zc_nvlist_conf	nvlist of devices to remove
+ * zc_cookie		to stop the remove?
  */
 static int
 zfs_ioc_vdev_remove(zfs_cmd_t *zc)
@@ -1918,15 +2085,19 @@
 {
 	spa_t *spa;
 	int error;
+	nvlist_t *event;
 
 	error = spa_open(zc->zc_name, &spa, FTAG);
 	if (error != 0)
 		return (error);
-	if (zc->zc_cookie != 0) {
-		error = spa_vdev_remove_cancel(spa);
-	} else {
-		error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
+	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "pool", zc->zc_name);
+		fnvlist_add_uint64(event, "guid", zc->zc_guid);
+		zfs_event_post(ZPOOL_EC_STATUS, "remove", event);
 	}
+
 	spa_close(spa, FTAG);
 	return (error);
 }
@@ -1951,7 +2122,8 @@
 
 	case VDEV_STATE_FAULTED:
 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
-		    zc->zc_obj != VDEV_AUX_EXTERNAL)
+		    zc->zc_obj != VDEV_AUX_EXTERNAL &&
+		    zc->zc_obj != VDEV_AUX_OPEN_FAILED)
 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
 
 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
@@ -1979,6 +2151,7 @@
 	spa_t *spa;
 	int replacing = zc->zc_cookie;
 	nvlist_t *config;
+	nvlist_t *event;
 	int error;
 
 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
@@ -1987,6 +2160,13 @@
 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
 	    zc->zc_iflags, &config)) == 0) {
 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
+		if (error == 0) {
+			event = fnvlist_alloc();
+			fnvlist_add_string(event, "pool", zc->zc_name);
+			fnvlist_add_nvlist(event, "config", config);
+			fnvlist_add_int32(event, "replacing", replacing);
+			zfs_event_post(ZPOOL_EC_STATUS, "attach", event);
+		}
 		nvlist_free(config);
 	}
 
@@ -1999,12 +2179,18 @@
 {
 	spa_t *spa;
 	int error;
+	nvlist_t *event;
 
 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
 		return (error);
 
 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
-
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "pool", zc->zc_name);
+		fnvlist_add_uint64(event, "guid", zc->zc_guid);
+		zfs_event_post(ZPOOL_EC_STATUS, "detach", event);
+	}
 	spa_close(spa, FTAG);
 	return (error);
 }
@@ -2045,6 +2231,24 @@
 }
 
 static int
+zfs_ioc_vdev_setl2adddt(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	int error;
+	uint64_t guid = zc->zc_guid;
+	char *l2ad_ddt = zc->zc_value;
+
+	error = spa_open(zc->zc_name, &spa, FTAG);
+	if (error != 0)
+		return (error);
+
+	error = spa_vdev_setl2adddt(spa, guid, l2ad_ddt);
+	spa_close(spa, FTAG);
+	return (error);
+}
+
+
+static int
 zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
 {
 	spa_t *spa;
@@ -2123,7 +2327,7 @@
 static int
 zfs_ioc_objset_stats(zfs_cmd_t *zc)
 {
-	objset_t *os;
+	objset_t *os = NULL;
 	int error;
 
 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
@@ -2232,23 +2436,6 @@
 	return (err);
 }
 
-static boolean_t
-dataset_name_hidden(const char *name)
-{
-	/*
-	 * Skip over datasets that are not visible in this zone,
-	 * internal datasets (which have a $ in their name), and
-	 * temporary datasets (which have a % in their name).
-	 */
-	if (strchr(name, '$') != NULL)
-		return (B_TRUE);
-	if (strchr(name, '%') != NULL)
-		return (B_TRUE);
-	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
-		return (B_TRUE);
-	return (B_FALSE);
-}
-
 /*
  * inputs:
  * zc_name		name of filesystem
@@ -2521,6 +2708,7 @@
 zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
     nvlist_t *errlist)
 {
+	spa_t *spa = NULL;
 	nvpair_t *pair;
 	nvpair_t *propval;
 	int rv = 0;
@@ -2528,7 +2716,15 @@
 	char *strval;
 	nvlist_t *genericnvl = fnvlist_alloc();
 	nvlist_t *retrynvl = fnvlist_alloc();
+	zfsvfs_t *zfsvfs;
+	boolean_t set_worm = B_FALSE;
+	boolean_t set_wbc_mode = B_FALSE;
+	boolean_t wbc_walk_locked = B_FALSE;
+	boolean_t set_dedup = B_FALSE;
 
+	if ((rv = spa_open(dsname, &spa, FTAG)) != 0)
+		return (rv);
+
 retry:
 	pair = NULL;
 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
@@ -2536,6 +2732,23 @@
 		zfs_prop_t prop = zfs_name_to_prop(propname);
 		int err = 0;
 
+		if (!set_worm && (strcmp(propname, "nms:worm") == 0)) {
+			set_worm = B_TRUE;
+		}
+
+		/*
+		 * If 'wbc_mode' is going to be changed, then we need to
+		 * do some actions before 'set'
+		 */
+		if (prop == ZFS_PROP_WBC_MODE)
+			set_wbc_mode = B_TRUE;
+
+		/*
+		 *
+		 */
+		if (prop == ZFS_PROP_DEDUP)
+			set_dedup = B_TRUE;
+
 		/* decode the property value */
 		propval = pair;
 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
@@ -2621,6 +2834,31 @@
 		goto retry;
 	}
 
+	/*
+	 * Deduplication and WBC cannot be used together
+	 * This code returns error also for case when
+	 * WBC is ON, DEDUP is off and a user tries
+	 * to do DEDUP=off, because in this case the code
+	 * will be more complex, but benefit is too small
+	 */
+	if (set_wbc_mode && set_dedup) {
+		nvlist_free(genericnvl);
+		nvlist_free(retrynvl);
+		spa_close(spa, FTAG);
+
+		return (SET_ERROR(EKZFS_WBCCONFLICT));
+	}
+
+	/*
+	 * Additional actions before set wbc_mode:
+	 * - first need to try to lock WBC-walking, to stop migration and
+	 *   avoid the openning of new migration window
+	 * - second step (from sync-context): if migration window
+	 *   is active it will be purged, to correctly add/remove WBC-instance
+	 */
+	if (set_wbc_mode && wbc_walk_lock(spa) == 0)
+		wbc_walk_locked = B_TRUE;
+
 	if (!nvlist_empty(genericnvl) &&
 	    dsl_props_set(dsname, source, genericnvl) != 0) {
 		/*
@@ -2662,6 +2900,23 @@
 	nvlist_free(genericnvl);
 	nvlist_free(retrynvl);
 
+	if (wbc_walk_locked)
+		wbc_walk_unlock(spa);
+
+	if (set_worm && getzfsvfs(dsname, &zfsvfs) == 0) {
+		if (zfs_is_wormed(dsname)) {
+			zfsvfs->z_isworm = B_TRUE;
+		} else {
+			zfsvfs->z_isworm = B_FALSE;
+		}
+		VFS_RELE(zfsvfs->z_vfs);
+	}
+
+	if (rv == 0)
+		autosnap_force_snap_by_name(dsname, NULL, B_FALSE);
+
+	spa_close(spa, FTAG);
+
 	return (rv);
 }
 
@@ -2689,7 +2944,7 @@
 			return (SET_ERROR(ENAMETOOLONG));
 
 		if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN)
-			return (E2BIG);
+			return (SET_ERROR(E2BIG));
 	}
 	return (0);
 }
@@ -2730,8 +2985,130 @@
 	return (err);
 }
 
+int
+zfs_ioc_set_prop_impl(char *name, nvlist_t *props,
+    boolean_t received, nvlist_t **out_errors)
+{
+	int error = 0;
+	nvlist_t *errors, *event;
+	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
+	    ZPROP_SRC_LOCAL);
+
+	ASSERT(props != NULL);
+
+	if (received) {
+		nvlist_t *origprops;
+
+		if (dsl_prop_get_received(name, &origprops) == 0) {
+			(void) clear_received_props(name, origprops, props);
+			nvlist_free(origprops);
+		}
+
+		error = dsl_prop_set_hasrecvd(name);
+	}
+
+	errors = fnvlist_alloc();
+	if (error == 0)
+		error = zfs_set_prop_nvlist(name, source, props, errors);
+
+	event = fnvlist_alloc();
+	fnvlist_add_string(event, "fsname", name);
+	fnvlist_add_nvlist(event, "properties", props);
+	fnvlist_add_nvlist(event, "errors", errors);
+	zfs_event_post(ZFS_EC_STATUS, "set", event);
+
+	if (out_errors != NULL)
+		*out_errors = fnvlist_dup(errors);
+
+	fnvlist_free(errors);
+
+	return (error);
+}
+
 /*
+ * XXX This functionality will be removed after integration of
+ * functionality, that does the same via zfs-channel programm.
+ * The zfs-channel programm implementation is being developed
+ * by Delphix.
+ *
+ * This functions sets provided props for provided datasets
+ * in one sync-round. There are some requirements:
+ *  - all datasets should belong to the same pool
+ *  - only user-properties
+ *
+ * This function does all or nothing.
+ *
  * inputs:
+ * zc_nvlist_src{_size}	nvlist of datasets and properties to apply
+ *
+ * outputs:
+ * zc_nvlist_dst{_size} error for each unapplied property
+ */
+/* ARGSUSED */
+static int
+zfs_ioc_set_prop_mds(const char *pool_name, nvlist_t *dss_props,
+    nvlist_t *outnvl)
+{
+	int error = 0;
+	spa_t *spa = NULL;
+	nvpair_t *pair = NULL;
+	size_t pool_name_len;
+	size_t total_num_props = 0;
+
+	ASSERT(dss_props != NULL);
+
+	if (nvlist_empty(dss_props))
+		return (SET_ERROR(ENODATA));
+
+	pool_name_len = strlen(pool_name);
+	while ((pair = nvlist_next_nvpair(dss_props, pair)) != NULL) {
+		nvlist_t *props;
+		nvpair_t *prop_nvp = NULL;
+		const char *ds_name;
+
+		ds_name = nvpair_name(pair);
+		if (strncmp(pool_name, ds_name, pool_name_len) == 0) {
+			char c = ds_name[pool_name_len];
+			if (c != '\0' && c != '/' && c != '@')
+				return (SET_ERROR(EXDEV));
+		}
+
+		if (nvpair_type(pair) != DATA_TYPE_NVLIST)
+			return (SET_ERROR(EINVAL));
+
+		props = fnvpair_value_nvlist(pair);
+		while ((prop_nvp = nvlist_next_nvpair(props,
+		    prop_nvp)) != NULL) {
+			const char *propname = nvpair_name(prop_nvp);
+			/* Only user-props */
+			if (!zfs_prop_user(propname) ||
+			    nvpair_type(prop_nvp) != DATA_TYPE_STRING)
+				return (SET_ERROR(EINVAL));
+
+			/*
+			 * We count the number to use it
+			 * later to check for ENOSPC
+			 */
+			total_num_props++;
+		}
+	}
+
+	if ((error = spa_open(pool_name, &spa, FTAG)) != 0)
+		return (error);
+
+	error = dsl_props_set_mds(pool_name, dss_props, total_num_props);
+	spa_close(spa, FTAG);
+	if (error == 0) {
+		nvlist_t *event = fnvlist_alloc();
+		fnvlist_add_nvlist(event, "properties", dss_props);
+		zfs_event_post(ZFS_EC_STATUS, "set-mds", event);
+	}
+
+	return (error);
+}
+
+/*
+ * inputs:
  * zc_name		name of filesystem
  * zc_value		name of property to set
  * zc_nvlist_src{_size}	nvlist of properties to apply
@@ -2745,9 +3122,7 @@
 {
 	nvlist_t *nvl;
 	boolean_t received = zc->zc_cookie;
-	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
-	    ZPROP_SRC_LOCAL);
-	nvlist_t *errors;
+	nvlist_t *errors = NULL;
 	int error;
 
 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
@@ -2754,22 +3129,8 @@
 	    zc->zc_iflags, &nvl)) != 0)
 		return (error);
 
-	if (received) {
-		nvlist_t *origprops;
+	error = zfs_ioc_set_prop_impl(zc->zc_name, nvl, received, &errors);
 
-		if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) {
-			(void) clear_received_props(zc->zc_name,
-			    origprops, nvl);
-			nvlist_free(origprops);
-		}
-
-		error = dsl_prop_set_hasrecvd(zc->zc_name);
-	}
-
-	errors = fnvlist_alloc();
-	if (error == 0)
-		error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors);
-
 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
 		(void) put_nvlist(zc, errors);
 	}
@@ -2861,7 +3222,7 @@
 	spa_t *spa;
 	int error;
 	nvpair_t *pair;
-
+	nvlist_t *event;
 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
 	    zc->zc_iflags, &props))
 		return (error);
@@ -2877,7 +3238,7 @@
 		mutex_enter(&spa_namespace_lock);
 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
 			spa_configfile_set(spa, props, B_FALSE);
-			spa_write_cachefile(spa, B_FALSE, B_TRUE);
+			spa_config_sync(spa, B_FALSE, B_TRUE);
 		}
 		mutex_exit(&spa_namespace_lock);
 		if (spa != NULL) {
@@ -2893,6 +3254,13 @@
 
 	error = spa_prop_set(spa, props);
 
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "pool", zc->zc_name);
+		fnvlist_add_nvlist(event, "props", props);
+		zfs_event_post(ZPOOL_EC_STATUS, "set", event);
+	}
+
 	nvlist_free(props);
 	spa_close(spa, FTAG);
 
@@ -3014,6 +3382,9 @@
 
 /*
  * inputs:
+ * createprops		list of properties requested by creator
+ * default_zplver	zpl version to use if unspecified in createprops
+ * fuids_ok		fuids allowed in this version of the spa?
  * os			parent objset pointer (NULL if root fs)
  * fuids_ok		fuids allowed in this version of the spa?
  * sa_ok		SAs allowed in this version of the spa?
@@ -3091,6 +3462,11 @@
 	VERIFY(nvlist_add_uint64(zplprops,
 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
 
+	if (os) {
+		if (zfs_is_wormed_ds(dmu_objset_ds(os)))
+			return (SET_ERROR(EPERM));
+	}
+
 	/*
 	 * If we're normalizing, names must always be valid UTF-8 strings.
 	 */
@@ -3188,6 +3564,8 @@
 	int32_t type32;
 	dmu_objset_type_t type;
 	boolean_t is_insensitive = B_FALSE;
+	char parent[MAXNAMELEN];
+	nvlist_t *event;
 
 	if (nvlist_lookup_int32(innvl, "type", &type32) != 0)
 		return (SET_ERROR(EINVAL));
@@ -3216,6 +3594,11 @@
 	if (cbfunc == NULL)
 		return (SET_ERROR(EINVAL));
 
+	if (zfs_get_parent(fsname, parent, MAXNAMELEN) == 0 &&
+	    zfs_is_wormed(parent)) {
+		return (SET_ERROR(EPERM));
+	}
+
 	if (type == DMU_OST_ZVOL) {
 		uint64_t volsize, volblocksize;
 
@@ -3240,8 +3623,6 @@
 		    volblocksize)) != 0)
 			return (error);
 	} else if (type == DMU_OST_ZFS) {
-		int error;
-
 		/*
 		 * We have to have normalization and
 		 * case-folding flags correct when we do the
@@ -3271,6 +3652,16 @@
 		if (error != 0)
 			(void) dsl_destroy_head(fsname);
 	}
+
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "fsname", fsname);
+		fnvlist_add_int32(event, "type", type);
+		if (nvprops != NULL)
+			fnvlist_add_nvlist(event, "properties", nvprops);
+		zfs_event_post(ZFS_EC_STATUS, "create", event);
+	}
+
 	return (error);
 }
 
@@ -3287,10 +3678,19 @@
 {
 	int error = 0;
 	nvlist_t *nvprops = NULL;
-	char *origin_name;
+	char *origin_name, *origin_snap;
+	nvlist_t *event;
 
 	if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0)
 		return (SET_ERROR(EINVAL));
+
+	origin_snap = strchr(origin_name, '@');
+	if (!origin_snap)
+		return (SET_ERROR(EINVAL));
+
+	if (autosnap_check_name(origin_snap))
+		return (SET_ERROR(EPERM));
+
 	(void) nvlist_lookup_nvlist(innvl, "props", &nvprops);
 
 	if (strchr(fsname, '@') ||
@@ -3299,6 +3699,7 @@
 
 	if (dataset_namecheck(origin_name, NULL, NULL) != 0)
 		return (SET_ERROR(EINVAL));
+
 	error = dmu_objset_clone(fsname, origin_name);
 	if (error != 0)
 		return (error);
@@ -3312,18 +3713,17 @@
 		if (error != 0)
 			(void) dsl_destroy_head(fsname);
 	}
-	return (error);
-}
 
-/* ARGSUSED */
-static int
-zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
-{
-	if (strchr(fsname, '@') ||
-	    strchr(fsname, '%'))
-		return (SET_ERROR(EINVAL));
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "origin", origin_name);
+		fnvlist_add_string(event, "fsname", fsname);
+		if (nvprops != NULL)
+			fnvlist_add_nvlist(event, "properties", nvprops);
+		zfs_event_post(ZFS_EC_STATUS, "clone", event);
+	}
 
-	return (dmu_objset_remap_indirects(fsname));
+	return (error);
 }
 
 /*
@@ -3341,6 +3741,7 @@
 	nvlist_t *props = NULL;
 	int error, poollen;
 	nvpair_t *pair;
+	nvlist_t *event;
 
 	(void) nvlist_lookup_nvlist(innvl, "props", &props);
 	if ((error = zfs_check_userprops(poolname, props)) != 0)
@@ -3366,6 +3767,9 @@
 		    zfs_component_namecheck(cp + 1, NULL, NULL) != 0)
 			return (SET_ERROR(EINVAL));
 
+		if (autosnap_check_name(cp))
+			return (EINVAL);
+
 		/*
 		 * The snap must be in the specified pool.
 		 */
@@ -3384,6 +3788,13 @@
 	}
 
 	error = dsl_dataset_snapshot(snaps, props, outnvl);
+
+	event = fnvlist_alloc();
+	fnvlist_add_nvlist(event, "snaps", snaps);
+	fnvlist_add_nvlist(event, "errors", outnvl);
+	fnvlist_add_string(event, "pool", poolname);
+	zfs_event_post(ZFS_EC_STATUS, "snapshot", event);
+
 	return (error);
 }
 
@@ -3501,6 +3912,25 @@
 	}
 }
 
+static int
+zfs_destroy_check_autosnap(spa_t *spa, const char *name)
+{
+	const char *snap = strchr(name, '@');
+
+	if (snap == NULL)
+		return (EINVAL);
+
+	if (autosnap_check_name(snap)) {
+		int err = autosnap_check_for_destroy(
+		    spa_get_autosnap(spa), name);
+
+		if (err != 0)
+			return (EBUSY);
+	}
+
+	return (0);
+}
+
 /*
  * innvl: {
  *     "snaps" -> { snapshot1, snapshot2 }
@@ -3517,17 +3947,48 @@
 	nvlist_t *snaps;
 	nvpair_t *pair;
 	boolean_t defer;
+	int error = 0;
+	nvlist_t *event;
+	spa_t *spa;
 
+	if (zfs_is_wormed(poolname))
+		return (SET_ERROR(EPERM));
+
 	if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0)
 		return (SET_ERROR(EINVAL));
 	defer = nvlist_exists(innvl, "defer");
 
+	error = spa_open(poolname, &spa, FTAG);
+	if (spa == NULL)
+		return (error);
+
+	for (pair = nvlist_next_nvpair(snaps, NULL);
+	    pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) {
+		error = zfs_destroy_check_autosnap(spa, nvpair_name(pair));
+		if (error)
+			fnvlist_add_int32(outnvl, nvpair_name(pair), error);
+	}
+
+	spa_close(spa, FTAG);
+
+	if (error)
+		return (error);
+
 	for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
 	    pair = nvlist_next_nvpair(snaps, pair)) {
 		zfs_unmount_snap(nvpair_name(pair));
 	}
 
-	return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
+	error = dsl_destroy_snapshots_nvl(snaps, defer, outnvl);
+
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_nvlist(event, "snaps", snaps);
+		fnvlist_add_nvlist(event, "errors", outnvl);
+		zfs_event_post(ZFS_EC_STATUS, "destroy_snaps", event);
+	}
+
+	return (error);
 }
 
 /*
@@ -3663,6 +4124,7 @@
  * zc_name		name of dataset to destroy
  * zc_objset_type	type of objset
  * zc_defer_destroy	mark for deferred destroy
+ * zc_guid		if set, do atomical recursive destroy
  *
  * outputs:		none
  */
@@ -3670,16 +4132,97 @@
 zfs_ioc_destroy(zfs_cmd_t *zc)
 {
 	int err;
+	nvlist_t *event;
 
+	if (zfs_is_wormed(zc->zc_name))
+		return (SET_ERROR(EPERM));
+
 	if (zc->zc_objset_type == DMU_OST_ZFS)
 		zfs_unmount_snap(zc->zc_name);
 
-	if (strchr(zc->zc_name, '@'))
-		err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy);
-	else
-		err = dsl_destroy_head(zc->zc_name);
+	if (zc->zc_guid) {
+		spa_t *spa;
+
+		if ((err = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+			return (err);
+
+		err = autosnap_lock(spa, RW_WRITER);
+		if (err == 0) {
+			err = wbc_walk_lock(spa);
+			if (err != 0)
+				autosnap_unlock(spa);
+		}
+
+		if (err == 0) {
+			err = dsl_destroy_atomically(zc->zc_name,
+			    zc->zc_defer_destroy);
+			wbc_walk_unlock(spa);
+			autosnap_unlock(spa);
+		}
+
+		spa_close(spa, FTAG);
+	} else {
+		if (strchr(zc->zc_name, '@')) {
+			spa_t *spa = NULL;
+
+			err = spa_open(zc->zc_name, &spa, FTAG);
+			if (err != 0)
+				return (err);
+
+			err = zfs_destroy_check_autosnap(spa, zc->zc_name);
+			if (err == 0) {
+				err = dsl_destroy_snapshot(zc->zc_name,
+				    zc->zc_defer_destroy);
+			}
+
+			spa_close(spa, FTAG);
+		} else {
+			err = dsl_destroy_head(zc->zc_name);
+			if (err == EEXIST) {
+				/*
+				 * It is possible that the given DS may have
+				 * hidden child (%recv) datasets - "leftovers"
+				 * resulting from the previously interrupted
+				 * 'zfs receive'.
+				 */
+				char namebuf[ZFS_MAX_DATASET_NAME_LEN];
+
+				if (snprintf(namebuf, sizeof (namebuf),
+				    "%s/%%recv", zc->zc_name) >=
+				    sizeof (namebuf))
+					return (err);
+
+				/* Try to remove the hidden child (%recv) */
+				err = dsl_destroy_head(namebuf);
+				if (err == 0) {
+					/*
+					 * Now the given DS should not have
+					 * children, so we can try to remove
+					 * it again
+					 */
+					err = dsl_destroy_head(zc->zc_name);
+				} else if (err == ENOENT) {
+					/*
+					 * The hidden child (%recv) does not
+					 * exist, so need to restore original
+					 * error
+					 */
+					err = EEXIST;
+				}
+
+			}
+		}
+	}
 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
 		(void) zvol_remove_minor(zc->zc_name);
+
+	if (err == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "fsname", zc->zc_name);
+		fnvlist_add_int32(event, "type", zc->zc_objset_type);
+		zfs_event_post(ZFS_EC_STATUS, "destroy", event);
+	}
+
 	return (err);
 }
 
@@ -3698,7 +4241,12 @@
 	zfsvfs_t *zfsvfs;
 	char *target = NULL;
 	int error;
+	nvlist_t *event;
+	int resume_err = 0;
 
+	if (zfs_is_wormed(fsname))
+		return (SET_ERROR(EPERM));
+
 	(void) nvlist_lookup_string(innvl, "target", &target);
 	if (target != NULL) {
 		const char *cp = strchr(target, '@');
@@ -3718,17 +4266,24 @@
 		ds = dmu_objset_ds(zfsvfs->z_os);
 		error = zfs_suspend_fs(zfsvfs);
 		if (error == 0) {
-			int resume_err;
-
 			error = dsl_dataset_rollback(fsname, target, zfsvfs,
 			    outnvl);
 			resume_err = zfs_resume_fs(zfsvfs, ds);
-			error = error ? error : resume_err;
 		}
 		VFS_RELE(zfsvfs->z_vfs);
 	} else {
 		error = dsl_dataset_rollback(fsname, target, NULL, outnvl);
 	}
+
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "target", (target != NULL) ? target : "");
+		fnvlist_add_string(event, "fsname", fsname);
+		fnvlist_add_int32(event, "resume_err", resume_err);
+		zfs_event_post(ZFS_EC_STATUS, "rollback", event);
+	}
+
+	error = (error != 0) ? error : resume_err;
 	return (error);
 }
 
@@ -3757,19 +4312,20 @@
 {
 	boolean_t recursive = zc->zc_cookie & 1;
 	char *at;
+	nvlist_t *event;
+	int error;
 
-	/* "zfs rename" from and to ...%recv datasets should both fail */
-	zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
+	if (zfs_is_wormed(zc->zc_name))
+		return (SET_ERROR(EPERM));
+
 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
-	if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 ||
-	    dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
-	    strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%'))
+	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
+	    strchr(zc->zc_value, '%'))
 		return (SET_ERROR(EINVAL));
 
 	at = strchr(zc->zc_name, '@');
 	if (at != NULL) {
 		/* snaps must be in same fs */
-		int error;
 
 		if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1))
 			return (SET_ERROR(EXDEV));
@@ -3787,12 +4343,21 @@
 		    at + 1, strchr(zc->zc_value, '@') + 1, recursive);
 		*at = '@';
 
-		return (error);
 	} else {
 		if (zc->zc_objset_type == DMU_OST_ZVOL)
 			(void) zvol_remove_minor(zc->zc_name);
-		return (dsl_dir_rename(zc->zc_name, zc->zc_value));
+		error = dsl_dir_rename(zc->zc_name, zc->zc_value);
 	}
+
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "origin", zc->zc_name);
+		fnvlist_add_string(event, "fsname", zc->zc_value);
+		fnvlist_add_int32(event, "type", zc->zc_objset_type);
+		zfs_event_post(ZFS_EC_STATUS, "rename", event);
+	}
+
+	return (error);
 }
 
 static int
@@ -3949,6 +4514,32 @@
 		}
 		break;
 
+	case ZFS_PROP_WBC_MODE:
+		{
+			spa_t *spa;
+			boolean_t wbc_feature_enabled;
+
+			if ((err = spa_open(dsname, &spa, FTAG)) != 0)
+				return (err);
+
+			wbc_feature_enabled =
+			    spa_feature_is_enabled(spa, SPA_FEATURE_WBC);
+			spa_close(spa, FTAG);
+
+			/* WBC cannot be used without special-vdev */
+			if (!wbc_feature_enabled || !spa_has_special(spa))
+				return (SET_ERROR(EKZFS_WBCNOTSUP));
+
+			/*
+			 * We do not want to have races, because on
+			 * import or after reboot WBC does registration
+			 * asynchronously.
+			 */
+			if (!spa->spa_wbc.wbc_ready_to_use)
+				return (SET_ERROR(EBUSY));
+		}
+		break;
+
 	case ZFS_PROP_CHECKSUM:
 	case ZFS_PROP_DEDUP:
 	{
@@ -4224,75 +4815,33 @@
 static boolean_t zfs_ioc_recv_inject_err;
 #endif
 
-/*
- * inputs:
- * zc_name		name of containing filesystem
- * zc_nvlist_src{_size}	nvlist of properties to apply
- * zc_value		name of snapshot to create
- * zc_string		name of clone origin (if DRR_FLAG_CLONE)
- * zc_cookie		file descriptor to recv from
- * zc_begin_record	the BEGIN record of the stream (not byteswapped)
- * zc_guid		force flag
- * zc_cleanup_fd	cleanup-on-exit file descriptor
- * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
- * zc_resumable		if data is incomplete assume sender will resume
- *
- * outputs:
- * zc_cookie		number of bytes read
- * zc_nvlist_dst{_size} error for each unapplied received property
- * zc_obj		zprop_errflags_t
- * zc_action_handle	handle for this guid/ds mapping
- */
-static int
-zfs_ioc_recv(zfs_cmd_t *zc)
+int
+dmu_recv_impl(int fd, char *tofs, char *tosnap, char *origin,
+    dmu_replay_record_t *drr_begin, boolean_t is_resumable, nvlist_t *props,
+    nvlist_t *errors, uint64_t *errf, int cfd, uint64_t *ahdl, uint64_t *sz,
+    boolean_t force, dmu_krrp_task_t *krrp_task)
 {
-	file_t *fp;
+	file_t *fp = getf(fd);
 	dmu_recv_cookie_t drc;
-	boolean_t force = (boolean_t)zc->zc_guid;
-	int fd;
 	int error = 0;
 	int props_error = 0;
-	nvlist_t *errors;
 	offset_t off;
-	nvlist_t *props = NULL; /* sent properties */
 	nvlist_t *origprops = NULL; /* existing properties */
 	nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
-	char *origin = NULL;
-	char *tosnap;
-	char tofs[ZFS_MAX_DATASET_NAME_LEN];
 	boolean_t first_recvd_props = B_FALSE;
+	nvlist_t *event;
+	boolean_t force_cksum =
+	    !krrp_task || krrp_task->buffer_args.force_cksum;
 
-	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
-	    strchr(zc->zc_value, '@') == NULL ||
-	    strchr(zc->zc_value, '%'))
-		return (SET_ERROR(EINVAL));
+	ASSERT(fp || krrp_task);
 
-	(void) strcpy(tofs, zc->zc_value);
-	tosnap = strchr(tofs, '@');
-	*tosnap++ = '\0';
-
-	if (zc->zc_nvlist_src != NULL &&
-	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
-	    zc->zc_iflags, &props)) != 0)
-		return (error);
-
-	fd = zc->zc_cookie;
-	fp = getf(fd);
-	if (fp == NULL) {
-		nvlist_free(props);
-		return (SET_ERROR(EBADF));
-	}
-
-	errors = fnvlist_alloc();
-
-	if (zc->zc_string[0])
-		origin = zc->zc_string;
-
 	error = dmu_recv_begin(tofs, tosnap,
-	    &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc);
+	    drr_begin, force, is_resumable, force_cksum, origin, &drc);
+
 	if (error != 0)
 		goto out;
 
+	drc.drc_krrp_task = krrp_task;
 	/*
 	 * Set properties before we receive the stream so that they are applied
 	 * to the new data. Note that we must call dmu_recv_stream() if
@@ -4327,9 +4876,9 @@
 
 			if (clear_received_props(tofs, origprops,
 			    first_recvd_props ? NULL : props) != 0)
-				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
+				*errf |= ZPROP_ERR_NOCLEAR;
 		} else {
-			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
+			*errf |= ZPROP_ERR_NOCLEAR;
 		}
 	}
 
@@ -4343,14 +4892,19 @@
 		}
 	}
 
-	off = fp->f_offset;
-	error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
-	    &zc->zc_action_handle);
+	if (fp) {
+		off = fp->f_offset;
+	} else {
+		off = 0;
+	}
+	error = dmu_recv_stream(&drc, fp ? fp->f_vnode : NULL,
+	    &off, cfd, ahdl, krrp_task);
 
 	if (error == 0) {
 		zfsvfs_t *zfsvfs = NULL;
 
-		if (getzfsvfs(tofs, &zfsvfs) == 0) {
+		error = getzfsvfs(tofs, &zfsvfs);
+		if (error == 0) {
 			/* online recv */
 			dsl_dataset_t *ds;
 			int end_err;
@@ -4391,24 +4945,30 @@
 		nvlist_free(delayprops);
 	}
 
-	/*
-	 * Now that all props, initial and delayed, are set, report the prop
-	 * errors to the caller.
-	 */
-	if (zc->zc_nvlist_dst_size != 0 &&
-	    (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
-	    put_nvlist(zc, errors) != 0)) {
-		/*
-		 * Caller made zc->zc_nvlist_dst less than the minimum expected
-		 * size or supplied an invalid address.
-		 */
-		props_error = SET_ERROR(EINVAL);
+	if (fp) {
+		*sz = off - fp->f_offset;
+		if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
+			fp->f_offset = off;
+	} else {
+		*sz = off;
 	}
+	if (error == 0) {
+		char val[MAXNAMELEN];
 
-	zc->zc_cookie = off - fp->f_offset;
-	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
-		fp->f_offset = off;
+		(void) strcpy(val, tofs);
+		(void) strcat(val, "@");
+		(void) strcat(val, tosnap);
 
+		event = fnvlist_alloc();
+		if (props != NULL)
+			fnvlist_add_nvlist(event, "props", props);
+		fnvlist_add_string(event, "origin", tofs);
+		fnvlist_add_string(event, "tosnap", val);
+		fnvlist_add_uint64(event, "bytes", *sz);
+		fnvlist_add_boolean_value(event, "newds", drc.drc_newfs);
+		zfs_event_post(ZFS_EC_STATUS, "recv", event);
+	}
+
 #ifdef	DEBUG
 	if (zfs_ioc_recv_inject_err) {
 		zfs_ioc_recv_inject_err = B_FALSE;
@@ -4425,7 +4985,7 @@
 			 * Since we may have left a $recvd value on the
 			 * system, we can't clear the $hasrecvd flag.
 			 */
-			zc->zc_obj |= ZPROP_ERR_NORESTORE;
+			*errf |= ZPROP_ERR_NORESTORE;
 		} else if (first_recvd_props) {
 			dsl_prop_unset_hasrecvd(tofs);
 		}
@@ -4432,7 +4992,7 @@
 
 		if (origprops == NULL && !drc.drc_newfs) {
 			/* We failed to stash the original properties. */
-			zc->zc_obj |= ZPROP_ERR_NORESTORE;
+			*errf |= ZPROP_ERR_NORESTORE;
 		}
 
 		/*
@@ -4449,14 +5009,13 @@
 			 * We stashed the original properties but failed to
 			 * restore them.
 			 */
-			zc->zc_obj |= ZPROP_ERR_NORESTORE;
+			*errf |= ZPROP_ERR_NORESTORE;
 		}
 	}
 out:
-	nvlist_free(props);
 	nvlist_free(origprops);
-	nvlist_free(errors);
-	releasef(fd);
+	if (fp)
+		releasef(fd);
 
 	if (error == 0)
 		error = props_error;
@@ -4466,6 +5025,81 @@
 
 /*
  * inputs:
+ * zc_name		name of containing filesystem
+ * zc_nvlist_src{_size}	nvlist of properties to apply
+ * zc_value		name of snapshot to create
+ * zc_string		name of clone origin (if DRR_FLAG_CLONE)
+ * zc_cookie		file descriptor to recv from
+ * zc_begin_record	the BEGIN record of the stream (not byteswapped)
+ * zc_guid		force flag
+ * zc_cleanup_fd	cleanup-on-exit file descriptor
+ * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
+ * zc_resumable		if data is incomplete assume sender will resume
+ *
+ * outputs:
+ * zc_cookie		number of bytes read
+ * zc_nvlist_dst{_size} error for each unapplied received property
+ * zc_obj		zprop_errflags_t
+ * zc_action_handle	handle for this guid/ds mapping
+ */
+static int
+zfs_ioc_recv(zfs_cmd_t *zc)
+{
+	int fd = zc->zc_cookie;
+	char tofs[ZFS_MAX_DATASET_NAME_LEN];
+	char *tosnap;
+	char *origin = NULL;
+	nvlist_t *errors;
+	nvlist_t *props = NULL; /* sent properties */
+	boolean_t force = (boolean_t)zc->zc_guid;
+	int err;
+
+	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
+	    strchr(zc->zc_value, '@') == NULL ||
+	    strchr(zc->zc_value, '%'))
+		return (SET_ERROR(EINVAL));
+
+	(void) strcpy(tofs, zc->zc_value);
+	tosnap = strchr(tofs, '@');
+	*tosnap++ = '\0';
+
+	if (zc->zc_string[0])
+		origin = zc->zc_string;
+
+	if (zc->zc_nvlist_src != NULL &&
+	    (err = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+	    zc->zc_iflags, &props)) != 0)
+		return (err);
+
+	errors = fnvlist_alloc();
+
+	err = dmu_recv_impl(fd, tofs, tosnap, origin,
+	    &zc->zc_begin_record, zc->zc_resumable, props, errors, &zc->zc_obj,
+	    zc->zc_cleanup_fd, &zc->zc_action_handle, &zc->zc_cookie,
+	    force, NULL);
+
+	/*
+	 * Now that all props, initial and delayed, are set, report the prop
+	 * errors to the caller.
+	 */
+	if (zc->zc_nvlist_dst_size != 0 &&
+	    (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
+	    put_nvlist(zc, errors) != 0)) {
+		/*
+		 * Caller made zc->zc_nvlist_dst less than the minimum expected
+		 * size or supplied an invalid address.
+		 */
+		err = SET_ERROR(EINVAL);
+	}
+
+	nvlist_free(errors);
+	nvlist_free(props);
+	return (err);
+
+}
+
+/*
+ * inputs:
  * zc_name	name of snapshot to send
  * zc_cookie	file descriptor to send stream to
  * zc_obj	fromorigin flag (mutually exclusive with zc_fromobj)
@@ -4542,15 +5176,17 @@
 		dsl_dataset_rele(tosnap, FTAG);
 		dsl_pool_rele(dp, FTAG);
 	} else {
+		offset_t off_starting;
 		file_t *fp = getf(zc->zc_cookie);
 		if (fp == NULL)
 			return (SET_ERROR(EBADF));
 
-		off = fp->f_offset;
+		off_starting = off = fp->f_offset;
 		error = dmu_send_obj(zc->zc_name, zc->zc_sendobj,
 		    zc->zc_fromobj, embedok, large_block_ok, compressok,
-		    zc->zc_cookie, fp->f_vnode, &off);
+		    zc->zc_cookie, fp->f_vnode, &off, zc->zc_sendsize);
 
+		zc->zc_sendcounter = off - off_starting;
 		if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
 			fp->f_offset = off;
 		releasef(zc->zc_cookie);
@@ -4785,12 +5421,8 @@
 	char origin[ZFS_MAX_DATASET_NAME_LEN];
 	char *cp;
 	int error;
+	nvlist_t *event;
 
-	zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
-	if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 ||
-	    strchr(zc->zc_name, '%'))
-		return (SET_ERROR(EINVAL));
-
 	error = dsl_pool_hold(zc->zc_name, FTAG, &dp);
 	if (error != 0)
 		return (error);
@@ -4829,7 +5461,16 @@
 		*cp = '\0';
 	(void) dmu_objset_find(origin,
 	    zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS);
-	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
+	error = dsl_dataset_promote(zc->zc_name, zc->zc_string);
+
+	if (error == 0) {
+		event = fnvlist_alloc();
+		fnvlist_add_string(event, "fsname", zc->zc_name);
+		fnvlist_add_string(event, "origin", zc->zc_value);
+		zfs_event_post(ZFS_EC_STATUS, "promote", event);
+	}
+
+	return (error);
 }
 
 /*
@@ -5510,6 +6151,213 @@
 	return (error);
 }
 
+static int
+zfs_ioc_vdev_set_props(zfs_cmd_t *zc)
+{
+	nvlist_t *props;
+	spa_t *spa;
+	int error;
+	uint64_t vdev_guid = zc->zc_guid;
+
+	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+	    zc->zc_iflags, &props))
+		return (error);
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+		nvlist_free(props);
+		return (error);
+	}
+
+	error = spa_vdev_prop_set(spa, vdev_guid, props);
+
+	nvlist_free(props);
+	spa_close(spa, FTAG);
+
+	return (error);
+}
+
+static int
+zfs_ioc_vdev_get_props(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	uint64_t vdev_guid = zc->zc_guid;
+	nvlist_t *nvp = NULL;
+	int error;
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+		/*
+		 * If the pool is faulted, there may be properties we can still
+		 * get (such as altroot and cachefile), so attempt to get them
+		 * anyway.
+		 */
+		mutex_enter(&spa_namespace_lock);
+		if ((spa = spa_lookup(zc->zc_name)) != NULL)
+			error = spa_vdev_prop_get(spa, vdev_guid, &nvp);
+		mutex_exit(&spa_namespace_lock);
+	} else {
+		error = spa_vdev_prop_get(spa, vdev_guid, &nvp);
+		spa_close(spa, FTAG);
+	}
+
+	if (error == 0 && zc->zc_nvlist_dst != NULL)
+		error = put_nvlist(zc, nvp);
+	else
+		error = EFAULT;
+
+	nvlist_free(nvp);
+	return (error);
+}
+
+static int
+zfs_ioc_cos_alloc(zfs_cmd_t *zc)
+{
+	nvlist_t *props;
+	spa_t *spa;
+	int error;
+
+	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+	    zc->zc_iflags, &props))
+		return (error);
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+		nvlist_free(props);
+		return (error);
+	}
+
+	error = spa_alloc_cos(spa, zc->zc_string, 0);
+	if (!error)
+		error = spa_cos_prop_set(spa, zc->zc_string, props);
+
+	spa_close(spa, FTAG);
+	nvlist_free(props);
+
+	return (error);
+}
+
+static int
+zfs_ioc_cos_free(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	int error = 0;
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+		return (error);
+
+	error = spa_free_cos(spa, zc->zc_string, zc->zc_cookie);
+
+	spa_close(spa, FTAG);
+
+	return (error);
+}
+
+static int
+zfs_ioc_cos_list(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	nvlist_t *nvl;
+	int error = 0;
+
+	VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+		nvlist_free(nvl);
+		return (error);
+	}
+
+	error = spa_list_cos(spa, nvl);
+
+	if (error == 0 && zc->zc_nvlist_dst != NULL)
+		error = put_nvlist(zc, nvl);
+
+	spa_close(spa, FTAG);
+	nvlist_free(nvl);
+
+	return (error);
+}
+
+static int
+zfs_ioc_cos_set_props(zfs_cmd_t *zc)
+{
+	nvlist_t *props;
+	spa_t *spa;
+	cos_t *cos;
+	const char *cosname = NULL;
+	int error = 0;
+
+	if ((zc->zc_string == NULL || zc->zc_string[0] == '\0') &&
+	    zc->zc_guid == 0)
+		return (SET_ERROR(EINVAL));
+
+	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+	    zc->zc_iflags, &props))
+		return (error);
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+		nvlist_free(props);
+		return (error);
+	}
+
+	if (zc->zc_guid == 0) {
+		cosname = zc->zc_string;
+	} else {
+		spa_cos_enter(spa);
+		cos = spa_lookup_cos_by_guid(spa, zc->zc_guid);
+		if (cos != NULL)
+			cosname = cos->cos_name;
+		else
+			error = SET_ERROR(ENOENT);
+		spa_cos_exit(spa);
+	}
+
+	if (error == 0)
+		error = spa_cos_prop_set(spa, cosname, props);
+
+	spa_close(spa, FTAG);
+	nvlist_free(props);
+
+	return (error);
+}
+
+static int
+zfs_ioc_cos_get_props(zfs_cmd_t *zc)
+{
+	spa_t *spa;
+	cos_t *cos;
+	nvlist_t *nvp = NULL;
+	const char *cosname = NULL;
+	int error = 0;
+
+	if ((zc->zc_string == NULL || zc->zc_string[0] == '\0') &&
+	    zc->zc_guid == 0)
+		return (SET_ERROR(EINVAL));
+
+	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+		return (error);
+
+	if (zc->zc_guid == 0) {
+		cosname = zc->zc_string;
+	} else {
+		spa_cos_enter(spa);
+		cos = spa_lookup_cos_by_guid(spa, zc->zc_guid);
+		if (cos != NULL)
+			cosname = cos->cos_name;
+		spa_cos_exit(spa);
+	}
+
+	if (error == 0)
+		error = spa_cos_prop_get(spa, cosname, &nvp);
+
+	spa_close(spa, FTAG);
+
+	if (error == 0 && zc->zc_nvlist_dst != NULL)
+		error = put_nvlist(zc, nvp);
+	else
+		error = EFAULT;
+
+	nvlist_free(nvp);
+	return (error);
+}
+
 /*
  * innvl: {
  *     "fd" -> file descriptor to write stream to (int32)
@@ -5660,6 +6508,220 @@
 	return (error);
 }
 
+typedef struct dp_cursor_cb_arg {
+	nvlist_t *outnvl;
+	uint64_t offset;
+	uint32_t count;
+	uint32_t skip;
+	boolean_t verbose;
+	boolean_t snaps;
+} dp_cursor_cb_arg_t;
+
+/* ARGSUSED */
+int
+ds_cursor_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
+{
+	int error;
+	char dsname[MAXNAMELEN];
+	objset_t *osp;
+
+	dp_cursor_cb_arg_t *cb = (dp_cursor_cb_arg_t *)arg;
+
+	dsl_dataset_name(ds, dsname);
+	nvlist_t *nv = fnvlist_alloc();
+
+	fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_GUID),
+	    dsl_dataset_phys(ds)->ds_guid);
+
+	if (cb->verbose) {
+		uint64_t refd, avail, uobjs, aobjs;
+		dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs);
+
+		fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_AVAILABLE),
+		    avail);
+		fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_REFERENCED),
+		    refd);
+		fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_USED),
+		    dsl_dir_phys(ds->ds_dir)->dd_used_bytes);
+	}
+
+	error = dmu_objset_from_ds(ds, &osp);
+
+	if (error == 0)
+		fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_TYPE),
+		    dmu_objset_type(osp));
+
+	fnvlist_add_nvlist(cb->outnvl, dsname, nv);
+	nvlist_free(nv);
+	return (error);
+}
+
+int
+dmu_objset_find_dp_cursor(dsl_pool_t *dp, uint64_t ddobj,
+    int func(dsl_pool_t *, dsl_dataset_t *, void *), void *arg)
+{
+	dsl_dir_t *dd;
+	dsl_dataset_t *ds;
+	zap_cursor_t zc;
+	zap_attribute_t *attr;
+	uint64_t thisobj;
+	int error, i;
+
+	dp_cursor_cb_arg_t *cb = (dp_cursor_cb_arg_t *)arg;
+
+	ASSERT(dsl_pool_config_held(dp));
+	error = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd);
+	thisobj = dsl_dir_phys(dd)->dd_head_dataset_obj;
+
+	if (error != 0)
+		return (error);
+
+	attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
+
+	/* we are interrestd in filesytems and volumes */
+	if (!cb->snaps) {
+
+		/* init the cursor at given offset */
+		zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
+		    dsl_dir_phys(dd)->dd_child_dir_zapobj, cb->offset);
+
+
+		for (i = 0; i < cb->skip; i++) {
+			zap_cursor_advance(&zc);
+			if ((zap_cursor_retrieve(&zc, attr) != 0)) {
+				error = ENOENT;
+				goto out;
+			}
+		}
+
+		for (i = 0; i < cb->count; i++) {
+			zap_cursor_advance(&zc);
+			if ((zap_cursor_retrieve(&zc, attr) != 0)) {
+				error = ENOENT;
+				goto out;
+			}
+
+			ASSERT3U(attr->za_integer_length, ==,
+			    sizeof (uint64_t));
+			ASSERT3U(attr->za_num_integers, ==, 1);
+			/* recursivly walk objects skipping $MOS and $ORIGIN */
+			error = dmu_objset_find_dp(dp, attr->za_first_integer,
+			    func, arg, 0);
+			if (error != 0)
+				break;
+		}
+	} else {
+
+		dsl_dataset_t *ds;
+
+		error = dsl_dataset_hold_obj(dp, thisobj, FTAG, &ds);
+
+		if (error == 0) {
+
+			dsl_dataset_rele(ds, FTAG);
+			zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
+			    dsl_dataset_phys(ds)->ds_snapnames_zapobj,
+			    cb->offset);
+
+			for (i = 0; i < cb->skip; i++) {
+				zap_cursor_advance(&zc);
+					if ((zap_cursor_retrieve(&zc,
+					    attr) != 0)) {
+					error = ENOENT;
+					goto out;
+				}
+			}
+
+			for (i = 0; i < cb->count; i++) {
+				zap_cursor_advance(&zc);
+				if ((zap_cursor_retrieve(&zc, attr) != 0)) {
+					error = ENOENT;
+					goto out;
+
+				}
+
+				ASSERT3U(attr->za_integer_length, ==,
+				    sizeof (uint64_t));
+				ASSERT3U(attr->za_num_integers, ==, 1);
+
+				error = dsl_dataset_hold_obj(dp,
+				    attr->za_first_integer, FTAG, &ds);
+				if (error != 0)
+					break;
+				error = func(dp, ds, arg);
+				dsl_dataset_rele(ds, FTAG);
+				if (error != 0)
+					break;
+			}
+		}
+	}
+out:
+	cb->offset = zap_cursor_serialize(&zc);
+	zap_cursor_fini(&zc);
+	dsl_dir_rele(dd, FTAG);
+	kmem_free(attr, sizeof (zap_attribute_t));
+
+	/* return self as the last dataset */
+	if (error == ENOENT) {
+		if ((error = dsl_dataset_hold_obj(dp, thisobj, FTAG, &ds)) != 0)
+			return (error);
+		error = func(dp, ds, arg);
+		dsl_dataset_rele(ds, FTAG);
+		if (error)
+			return (error);
+		error = ENOENT;
+	}
+
+	return (error);
+}
+
+
+/*
+ * We want to list all dataset under the given name. Optionally, we advance the
+ * ZAP cursor "skip" times and retrieve "count" datasets. We return the offset
+ * so the user can start the next invocation where he left off.
+ */
+
+static int
+zfs_ioc_list_from_cursor(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
+{
+
+	dsl_pool_t *dp;
+	dsl_dataset_t *ds;
+
+	int error;
+
+	dp_cursor_cb_arg_t cb_args;
+
+	if ((strchr(name, '@') != NULL))
+		return (EINVAL);
+
+	if ((error = dsl_pool_hold(name, FTAG, &dp)) != 0)
+		return (error);
+
+	if ((error = dsl_dataset_hold(dp, name, FTAG, &ds)) != 0) {
+		dsl_pool_rele(dp, FTAG);
+		return (error);
+	}
+
+	(void) nvlist_lookup_uint32(innvl, "count", &cb_args.count);
+	(void) nvlist_lookup_uint32(innvl, "skip", &cb_args.skip);
+	(void) nvlist_lookup_uint64(innvl, "offset", &cb_args.offset);
+	(void) nvlist_lookup_boolean_value(innvl, "verbose", &cb_args.verbose);
+	(void) nvlist_lookup_boolean_value(innvl, "snaps", &cb_args.snaps);
+
+	cb_args.outnvl = outnvl;
+	error = dmu_objset_find_dp_cursor(dp, ds->ds_dir->dd_object,
+	    &ds_cursor_cb, &cb_args);
+
+	fnvlist_add_uint64(outnvl, "offset", cb_args.offset);
+	dsl_dataset_rele(ds, FTAG);
+	dsl_pool_rele(dp, FTAG);
+
+	return (error);
+}
+
+
 static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST];
 
 static void
@@ -5765,9 +6827,368 @@
 	    DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
 }
 
+/*
+ * Appearing to take poolname as a parameter is a concession to the ioctl
+ * handler. Leading underbar for generation idea nvpair exists only on output to
+ * avoid pool name conflict.
+ */
+/* ARGSUSED */
+static int
+zfs_ioc_pool_configs_nvl(const char *poolname, nvlist_t *innvl,
+    nvlist_t *outnvl)
+{
+	nvlist_t *configs;
+	uint64_t generation;
+
+	if (nvlist_lookup_uint64(innvl, "generation", &generation) != 0)
+		return (SET_ERROR(EINVAL));
+
+	if ((configs = spa_all_configs(&generation)) == NULL)
+		return (SET_ERROR(EEXIST));
+
+	fnvlist_merge(outnvl, configs);
+	nvlist_free(configs);
+	fnvlist_add_uint64(outnvl, "_generation", generation);
+
+	return (0);
+}
+
+/*
+ * Ask spa for pool statistics. If we get a non-NULL config but a non-zero
+ * return from spa, we return EAGAIN to hint to callers that we've retrieved
+ * a config for a faulted pool. We take no arguments but declare otherwise to
+ * suit the ioctl handler's pattern. Similar considerations apply to outnvl as a
+ * single pointer that has to be merged with config allocated or nulled by spa.
+ */
+static int
+zfs_ioc_pool_stats_nvl(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
+{
+	nvlist_t *config;
+	int error;
+	int ret = 0;
+
+	ASSERT3P(innvl, ==, NULL);
+	error = spa_get_stats(poolname, &config, NULL, 0);
+	ASSERT3U(error, !=, EAGAIN);
+
+	if (config != NULL) {
+		fnvlist_merge(outnvl, config);
+		nvlist_free(config);
+		if (error)
+			ret = SET_ERROR(EAGAIN);
+	} else {
+		ret = error;
+	}
+
+	return (ret);
+}
+
+static nvlist_t *
+objset_stats2nv(dmu_objset_stats_t *stat)
+{
+	nvlist_t *statlist  = fnvlist_alloc();
+
+	fnvlist_add_uint64(statlist, "dds_num_clones", stat->dds_num_clones);
+	fnvlist_add_uint64(statlist, "dds_creation_txg",
+	    stat->dds_creation_txg);
+	fnvlist_add_uint64(statlist, "dds_guid", stat->dds_guid);
+	fnvlist_add_uint8(statlist, "dds_type", (uint8_t)stat->dds_type);
+	fnvlist_add_uint8(statlist, "dds_is_snapshot", stat->dds_is_snapshot);
+	fnvlist_add_uint8(statlist, "dds_inconsistent",
+	    stat->dds_inconsistent);
+	fnvlist_add_string(statlist, "dds_origin", stat->dds_origin);
+
+	return (statlist);
+}
+
+/* Given an objset, retrieve stats and props by adding them to the output nvl */
+static int
+objset_render(objset_t *os, nvlist_t *outnvl)
+{
+	int error = 0;
+	nvlist_t *props = NULL, *statlist = NULL;
+	dmu_objset_stats_t stats;
+
+	dmu_objset_fast_stat(os, &stats);
+
+	if ((error = dsl_prop_get_all(os, &props)) == 0) {
+		dmu_objset_stats(os, props);
+		/*
+		 * NB: zvol_get_stats() will read the objset contents,
+		 * which we aren't supposed to do with a
+		 * DS_MODE_USER hold, because it could be
+		 * inconsistent.  So this is a bit of a workaround...
+		 * XXX reading with out owning
+		 */
+		if (!stats.dds_inconsistent &&
+		    dmu_objset_type(os) == DMU_OST_ZVOL) {
+			error = zvol_get_stats(os, props);
+			if (error == EIO)
+				goto out;
+			VERIFY0(error);
+		}
+		fnvlist_add_nvlist(outnvl, "props", props);
+		statlist = objset_stats2nv(&stats);
+		fnvlist_add_nvlist(outnvl, "stats", statlist);
+		nvlist_free(statlist);
+	}
+
+out:
+	nvlist_free(props);
+	return (error);
+}
+
+/*
+ * Note: this IOC can be called internally by other IOCs as an existence
+ * check against race conditions. Given a dataset name, return its stats
+ * and props. Optionally we can verify type, which simplifies things for
+ * callers that may not want to parse stats for themselves (and may discard
+ * the outnvl in handlers).
+ */
+static int
+zfs_ioc_objset_stats_nvl(const char *data, nvlist_t *innvl, nvlist_t *outnvl)
+{
+	objset_t *os;
+	int error;
+	dmu_objset_type_t checktype = DMU_OST_ANY;
+	boolean_t gettype = B_FALSE;
+
+	if (innvl != NULL) {
+		if (nvlist_lookup_uint8(innvl, "type", (uint8_t *)&checktype)
+		    == 0)
+			gettype = B_TRUE;
+	}
+	if ((error = dmu_objset_hold(data, FTAG, &os)) == 0) {
+		error = objset_render(os, outnvl);
+		dmu_objset_rele(os, FTAG);
+
+		if (error == 0) {
+			nvlist_t *statlist;
+			dmu_objset_type_t type;
+			statlist = fnvlist_lookup_nvlist(outnvl, "stats");
+			type = fnvlist_lookup_uint8_t(statlist, "dds_type");
+			if (checktype != DMU_OST_ANY && type != checktype) {
+				error = EEXIST;
+				fnvlist_remove(outnvl, "stats");
+				fnvlist_remove(outnvl, "props");
+			}
+			if (gettype)
+				fnvlist_add_uint8(outnvl, "type", type);
+		}
+	}
+
+	return (error);
+}
+
+/*
+ * Given a dataset name and an innvl containing a DMU cursor offset, find the
+ * next child dataset, and return its name, stats, and props and an updated
+ * cursor.
+ */
+static int
+zfs_ioc_dataset_list_next_nvl(const char *data, nvlist_t *innvl,
+    nvlist_t *outnvl)
+{
+	objset_t *os;
+	int error;
+	uint64_t off;
+	char *p, *nextds;
+	char name[MAXNAMELEN];
+	size_t len;
+	size_t orig_len = strlen(data);
+
+	if (innvl == NULL ||
+	    nvlist_lookup_uint64(innvl, "offset", &off) != 0)
+		return (SET_ERROR(EINVAL));
+
+	(void) strlcpy(name, data, sizeof (name));
+top:
+	if (error = dmu_objset_hold(name, FTAG, &os)) {
+		if (error == ENOENT)
+			error = SET_ERROR(ESRCH);
+		return (error);
+	}
+
+	p = strrchr(name, '/');
+	if (p == NULL || p[1] != '\0') {
+		if ((len = strlcat(name, "/", sizeof (name))) >= MAXNAMELEN) {
+			dmu_objset_rele(os, FTAG);
+			return (SET_ERROR(ESRCH));
+		}
+	} else {
+		len = orig_len;
+	}
+	p = name + len;
+
+	do {
+		error = dmu_dir_list_next(os, sizeof (name) - len, p, NULL,
+		    &off);
+		if (error == ENOENT)
+			error = ESRCH;
+	} while (error == 0 && dataset_name_hidden(name));
+	dmu_objset_rele(os, FTAG);
+
+	/*
+	 * If it's an internal dataset (ie. with a '$' in its name),
+	 * don't try to get stats for it, otherwise we'll return ENOENT.
+	 */
+	if (error == 0 && strchr(name, '$') == NULL) {
+		error = zfs_ioc_objset_stats_nvl(name, NULL, outnvl);
+		if (error == ENOENT) {
+			/* We lost a race with destroy, get the next one. */
+			name[orig_len] = '\0';
+			goto top;
+		}
+		len = strlen(name) + 1;
+		nextds = kmem_alloc(len, KM_SLEEP);
+		(void) strlcpy(nextds, name, len);
+		fnvlist_add_string(outnvl, "nextds", (const char *)nextds);
+		fnvlist_add_uint64(outnvl, "offset", off);
+	}
+
+	return (error);
+}
+
+/*
+ * Given a dataset name and a DMU cursor offset, find its next snapshot, and
+ * return its name, props, and stats and an updated cursor offset.
+ */
+static int
+zfs_ioc_snapshot_list_next_nvl(const char *data, nvlist_t *innvl,
+    nvlist_t *outnvl)
+{
+	objset_t *os;
+	int error;
+	uint64_t off, obj;
+	char name[MAXNAMELEN], *nextsnap;
+	size_t len;
+
+	if (innvl == NULL ||
+	    nvlist_lookup_uint64(innvl, "offset", &off) != 0)
+		return (SET_ERROR(EINVAL));
+
+	error = dmu_objset_hold(data, FTAG, &os);
+	if (error != 0) {
+		return (error == ENOENT ? ESRCH : error);
+	}
+
+	/*
+	 * A dataset name of maximum length cannot have any snapshots,
+	 * so exit immediately.
+	 */
+	(void) strlcpy(name, data, sizeof (name));
+	if ((len = strlcat(name, "@", sizeof (name))) >= MAXNAMELEN) {
+		dmu_objset_rele(os, FTAG);
+		return (SET_ERROR(ESRCH));
+	}
+
+	/* Rest of name buffer is passed so snap ID can be appended. */
+	error = dmu_snapshot_list_next(os, sizeof (name) - len, name + len,
+	    &obj, &off, NULL);
+
+	if (error == 0) {
+		dsl_dataset_t *ds;
+		dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
+
+		error = dsl_dataset_hold_obj(dp, obj, FTAG, &ds);
+		if (error == 0) {
+			objset_t *ossnap;
+
+			error = dmu_objset_from_ds(ds, &ossnap);
+			if (error == 0)
+				error = objset_render(ossnap, outnvl);
+			dsl_dataset_rele(ds, FTAG);
+		}
+	} else if (error == ENOENT) {
+		error = ESRCH;
+	}
+
+	dmu_objset_rele(os, FTAG);
+
+	if (error == 0) {
+		len = strlen(name) + 1;
+		nextsnap = kmem_alloc(len, KM_SLEEP);
+		(void) strlcpy(nextsnap, name, len);
+		fnvlist_add_string(outnvl, "nextsnap", (const char *)nextsnap);
+		fnvlist_add_uint64(outnvl, "offset", off);
+	}
+	return (error);
+}
+
+static int
+zfs_ioc_pool_get_props_nvl(const char *poolname, nvlist_t *innvl,
+    nvlist_t *outnvl)
+{
+	spa_t *spa;
+	int error;
+	nvlist_t *props = NULL;
+
+	ASSERT3P(innvl, ==, NULL);
+	if ((error = spa_open(poolname, &spa, FTAG)) != 0) {
+		/*
+		 * If the pool is faulted, there may be properties we can still
+		 * get (such as altroot and cachefile), so attempt to get them
+		 * anyway.
+		 */
+		mutex_enter(&spa_namespace_lock);
+		if ((spa = spa_lookup(poolname)) != NULL)
+			error = spa_prop_get(spa, &props);
+		mutex_exit(&spa_namespace_lock);
+	} else {
+		error = spa_prop_get(spa, &props);
+		spa_close(spa, FTAG);
+	}
+
+	if (props != NULL) {
+		fnvlist_merge(outnvl, props);
+		nvlist_free(props);
+	} else {
+		ASSERT3S(error, !=, 0);
+	}
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+zfs_ioc_check_krrp(const char *dataset, nvlist_t *innvl, nvlist_t *outnvl)
+{
+	spa_t *spa;
+	int err;
+
+	/*
+	 * Here we use different way to open spa for the given pool,
+	 * because the pool maybe faulted
+	 */
+
+	mutex_enter(&spa_namespace_lock);
+	if ((spa = spa_lookup(dataset)) == NULL) {
+		mutex_exit(&spa_namespace_lock);
+		/* From KRRP side everything nice */
+		return (0);
+	}
+
+	spa_open_ref(spa, FTAG);
+	mutex_exit(&spa_namespace_lock);
+
+	err = autosnap_check_for_destroy(spa_get_autosnap(spa), dataset);
+	if (err == 0)
+		err = ENOTSUP;
+
+	mutex_enter(&spa_namespace_lock);
+	spa_close(spa, FTAG);
+	mutex_exit(&spa_namespace_lock);
+
+	return (err != 0 ? SET_ERROR(err) : 0);
+}
+
 static void
 zfs_ioctl_init(void)
 {
+	zfs_ioctl_register("bulk_list", ZFS_IOC_BULK_LIST,
+	    zfs_ioc_list_from_cursor, zfs_secpolicy_read,
+	    DATASET_NAME, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE);
+
 	zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT,
 	    zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME,
 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
@@ -5796,14 +7217,40 @@
 	    zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME,
 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
 
-	zfs_ioctl_register("remap", ZFS_IOC_REMAP,
-	    zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME,
-	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE);
-
 	zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS,
 	    zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME,
 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
 
+	zfs_ioctl_register("check_krrp", ZFS_IOC_CHECK_KRRP,
+	    zfs_ioc_check_krrp, zfs_secpolicy_read, DATASET_NAME,
+	    POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+	zfs_ioctl_register("pool_stats_nvl", ZFS_IOC_POOL_STATS_NVL,
+	    zfs_ioc_pool_stats_nvl, zfs_secpolicy_read, POOL_NAME,
+	    POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+	zfs_ioctl_register("pool_configs_nvl", ZFS_IOC_POOL_CONFIGS_NVL,
+	    zfs_ioc_pool_configs_nvl, zfs_secpolicy_none, NO_NAME,
+	    POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+	zfs_ioctl_register("pool_get_props_nvl", ZFS_IOC_POOL_GET_PROPS_NVL,
+	    zfs_ioc_pool_get_props_nvl, zfs_secpolicy_read, POOL_NAME,
+	    POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+	zfs_ioctl_register("objset_stats_nvl", ZFS_IOC_OBJSET_STATS_NVL,
+	    zfs_ioc_objset_stats_nvl, zfs_secpolicy_read, DATASET_NAME,
+	    POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE);
+
+	zfs_ioctl_register("dataset_list_next_nvl",
+	    ZFS_IOC_DATASET_LIST_NEXT_NVL, zfs_ioc_dataset_list_next_nvl,
+	    zfs_secpolicy_read, DATASET_NAME, POOL_CHECK_SUSPENDED, B_FALSE,
+	    B_FALSE);
+
+	zfs_ioctl_register("snapshot_list_next_nvl",
+	    ZFS_IOC_SNAPSHOT_LIST_NEXT_NVL, zfs_ioc_snapshot_list_next_nvl,
+	    zfs_secpolicy_read, DATASET_NAME, POOL_CHECK_SUSPENDED, B_FALSE,
+	    B_FALSE);
+
 	zfs_ioctl_register("hold", ZFS_IOC_HOLD,
 	    zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME,
 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
@@ -5837,6 +7284,11 @@
 	    POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE,
 	    B_TRUE);
 
+	zfs_ioctl_register("set_props_mds", ZFS_IOC_SET_PROPS_MDS,
+	    zfs_ioc_set_prop_mds, zfs_secpolicy_config,
+	    POOL_NAME,
+	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
+
 	/* IOCTLS that use the legacy function signature */
 
 	zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
@@ -5846,6 +7298,8 @@
 	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
 	zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN,
 	    zfs_ioc_pool_scan);
+	zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_TRIM,
+	    zfs_ioc_pool_trim);
 	zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE,
 	    zfs_ioc_pool_upgrade);
 	zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD,
@@ -5858,6 +7312,8 @@
 	    zfs_ioc_vdev_attach);
 	zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH,
 	    zfs_ioc_vdev_detach);
+	zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETL2ADDDT,
+	    zfs_ioc_vdev_setl2adddt);
 	zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH,
 	    zfs_ioc_vdev_setpath);
 	zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU,
@@ -5908,10 +7364,23 @@
 	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
 
 	zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear,
-	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_READONLY);
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
 	zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen,
 	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
-
+	zfs_ioctl_register_pool(ZFS_IOC_VDEV_SET_PROPS, zfs_ioc_vdev_set_props,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+	zfs_ioctl_register_pool(ZFS_IOC_VDEV_GET_PROPS, zfs_ioc_vdev_get_props,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+	zfs_ioctl_register_pool(ZFS_IOC_COS_ALLOC, zfs_ioc_cos_alloc,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+	zfs_ioctl_register_pool(ZFS_IOC_COS_FREE, zfs_ioc_cos_free,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+	zfs_ioctl_register_pool(ZFS_IOC_COS_LIST, zfs_ioc_cos_list,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+	zfs_ioctl_register_pool(ZFS_IOC_COS_SET_PROPS, zfs_ioc_cos_set_props,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+	zfs_ioctl_register_pool(ZFS_IOC_COS_GET_PROPS, zfs_ioc_cos_get_props,
+	    zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
 	zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN,
 	    zfs_ioc_space_written);
 	zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS,
@@ -6403,6 +7872,10 @@
 	ASSERT(error == 0);
 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
 
+	if (sysevent_evc_bind(ZFS_EVENT_CHANNEL, &zfs_channel,
+	    EVCH_HOLD_PEND | EVCH_CREAT) != 0)
+		cmn_err(CE_NOTE, "Failed to bind to zfs event channel");
+
 	return (0);
 }
 
@@ -6432,6 +7905,9 @@
 	zfs_li = NULL;
 	mutex_destroy(&zfs_share_lock);
 
+	if (zfs_channel)
+		(void) sysevent_evc_unbind(zfs_channel);
+
 	return (error);
 }
 
