Print this page
8520 lzc_rollback_to should support rolling back to origin
7198 libzfs should gracefully handle EINVAL from lzc_rollback
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Dan McDonald <danmcd@joyent.com>
NEX-16623 Ability to set properties for multiple datasets/snapshots during single sync-round
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-16502 libshare needs to support SMB in a zone
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-8600 Destroying a KRRP session takes too long
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5736 implement autoreplace matching based on FRU slot number
NEX-6200 hot spares are not reactivated after reinserting into enclosure
NEX-9403 need to update FRU for spare and l2cache devices
NEX-9404 remove lofi autoreplace support from syseventd
NEX-9409 hotsparing doesn't work for vdevs without FRU
NEX-9424 zfs`vdev_online() needs better notification about state changes
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-7822 40Gb Intel XL710 NIC performance data
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-8827 AUTOSNAP: can't register recursive zone when there is a child under autosnappool/volumegrou
Reviewed by: Alex Deiter <alex.deiter@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-8106 async ZFS event notifications don't need to announce publish failure
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-6490 Unable to destroy filesystem with receive_resume_token
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-6213 KRRP: System panics if recv-stream does not have snap-properties
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-5553 ZFS auto-trim, manual-trim and scrub can race and deadlock
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5795 Rename 'wrc' as 'wbc' in the source and in the tech docs
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-5272 KRRP: replicate snapshot properties
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5268 WBC: add sysevent migration-done
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5060 WBC: Writecache and deduplication should not be used together
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-4830 writecache=off leaks data on special vdev (the data will never migrate)
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-4608 WRC: Possible deadlock during the disabling of WRC for a dataset
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
4986 receiving replication stream fails if any snapshot exceeds refquota
Reviewed by: John Kennedy <john.kennedy@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Gordon Ross <gordon.ross@nexenta.com>
6388 Failure of userland copy should return EFAULT
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Dan Kimmel <dan.kimmel@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
6328 Fix cstyle errors in zfs codebase (fix studio)
6328 Fix cstyle errors in zfs codebase
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Alex Reece <alex@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Jorgen Lundman <lundman@lundman.net>
Approved by: Robert Mustacchi <rm@joyent.com>
2605 want to resume interrupted zfs send
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Xin Li <delphij@freebsd.org>
Reviewed by: Arne Jansen <sensille@gmx.net>
Approved by: Dan McDonald <danmcd@omniti.com>
6286 ZFS internal error when set large block on bootfs
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Approved by: Robert Mustacchi <rm@joyent.com>
4185 add new cryptographic checksums to ZFS: SHA-512, Skein, Edon-R (fix studio build)
4185 add new cryptographic checksums to ZFS: SHA-512, Skein, Edon-R
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Garrett D'Amore <garrett@damore.org>
6096 ZFS_SMB_ACL_RENAME needs to cleanup better
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: George Wilson <gwilson@zfsmail.com>
Approved by: Robert Mustacchi <rm@joyent.com>
5946 zfs_ioc_space_snaps must check that firstsnap and lastsnap refer to snapshots
5945 zfs_ioc_send_space must ensure that fromsnap refers to a snapshot
Reviewed by: Steven Hartland <killing@multiplay.co.uk>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Gordon Ross <gordon.ross@nexenta.com>
5515 dataset user hold doesn't reject empty tags
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Approved by: Matthew Ahrens <mahrens@delphix.com>
5765 add support for estimating send stream size with lzc_send_space when source is a bookmark
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Christopher Siden <christopher.siden@delphix.com>
Reviewed by: Steven Hartland <killing@multiplay.co.uk>
Reviewed by: Bayard Bell <buffer.g.overflow@gmail.com>
Approved by: Albert Lee <trisk@nexenta.com>
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Revert "NEX-4476 WRC: Allow to use write back cache per tree of datasets"
This reverts commit fe97b74444278a6f36fec93179133641296312da.
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-4194 WRC does not migrate data when idle
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-4206 Event 'recv' should contain information that received ds is new
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
NEX-4044 remove sha1crc32 in preparation with upstream merge of edon-r and skien
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Conflicts:
        usr/src/uts/common/fs/zfs/sys/zio_checksum.h
NEX-3984 On-demand TRIM
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Conflicts:
        usr/src/common/zfs/zpool_prop.c
        usr/src/uts/common/sys/fs/zfs.h
NEX-3964 It should not be allowed to rename a snapshot that its new name is matched to the prefix of in-kernel autosnapshots
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3878 Bogus address kernel panic in nvlist_copy_pairs from snmpd whilst exporting a zpool
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3921 Memory leaks in ZFS_IOC_OBJSET_STATS_NVL implementation
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-3010 e1000g use after free on start failure
Reviewed by: Marcel Telka <marcel.telka@nexenta.com>
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3731 fixed incorrect behavior of zpool export/destroy for faulted pool that is caused by krrp-check (lint)
NEX-3731 fixed incorrect behavior of zpool export/destroy for faulted pool that is caused by krrp-check
 Reviewed by: Alek Pinchuk <alek@nexenta.com>
 Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-3723 Want ZFS dataset bulk listing support
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
NEX-2195 zfs panic assertion failed: strcmp(zn->zn_key_orig, mze->mze_name) != 0
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-3726 CBC decrypt returns encrypted data
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3709 Fixed KRRP/WRC code style and KRRP related warnings that are showed by 'git nits'
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3558 KRRP Integration
NEX-3165 need some dedup improvements
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-3070 Panic in zpool_create_features_002_pos test
NEX-2935 want async notification for certain ZFS changes (fix)
NEX-2935 want async notification for certain ZFS changes (lint)
NEX-2935 want async notification for certain ZFS changes
OS-103 handle CoS descriptor persistent references across vdev operations
OS-102 add man page info and tests for vdev/CoS properties and ZFS meta features
OS-80 support for vdev and CoS properties for the new I/O scheduler
OS-95 lint warning introduced by OS-61
Remaining fixes for the illumos merge
Moved closed ZFS files to open repo, changed Makefiles accordingly
Removed unneeded weak symbols
Issue #34: Add feature flag for the compount checksum - sha1crc32
           Contributors: Boris Protopopov
Fixup merge results
OS-15 ensure that truss continues to work
OS-15 revert changes to put_nvlist
re #13850 Refactor ZFS config discovery IOCs to libzfs_core patterns
re 13748 added zpool export -c option
zpool export -c command exports specified pool while keeping its latest
configuration in the cache file for subsequent zpool import -c.
re #12619 rb4429 More dp->dp_config_rwlock holds
re #13204 rb4280 zfs receive/rollback deadlock
re #12585 rb4049 ZFS++ work port - refactoring to improve separation of open/closed code, bug fixes, performance improvements - open code
Bug 11205: add missing libzfs_closed_stubs.c to fix opensource-only build.
ZFS plus work: special vdevs, cos, cos/vdev properties
Bug 10481 - Dry run option in 'zfs send' isn't the same as in NexentaStor 3.1
re #7550 rb2134 lint-clean nza-kernel
re #6815 rb1758 need WORM in nza-kernel (4.0)

@@ -19,19 +19,22 @@
  * CDDL HEADER END
  */
 
 /*
  * 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.
  */
 

@@ -154,15 +157,17 @@
 #include <sys/zap.h>
 #include <sys/spa.h>
 #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>
 #include <sys/ddi.h>
 #include <sys/sunddi.h>

@@ -184,18 +189,23 @@
 #include <sys/dmu_send.h>
 #include <sys/dsl_destroy.h>
 #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"
 
 extern struct modlfs zfs_modlfs;

@@ -255,10 +265,43 @@
 int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *);
 static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp);
 
 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, ...)
 {
         const char *newfile;

@@ -770,13 +813,10 @@
 }
 
 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 {
                 return (zfs_secpolicy_deleg_share(zc, innvl, cr));
         }

@@ -783,13 +823,10 @@
 }
 
 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 {
                 return (zfs_secpolicy_deleg_share(zc, innvl, cr));
         }

@@ -1041,18 +1078,10 @@
         return (error);
 }
 
 /* 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;
         int error = 0;
 

@@ -1381,34 +1410,79 @@
         }
 
         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)
 {
         char *packed = NULL;
         int error = 0;
         size_t size;
 
         size = fnvlist_size(nvl);
 
+        zc->zc_nvlist_dst_filled = B_FALSE;
         if (size > zc->zc_nvlist_dst_size) {
                 error = SET_ERROR(ENOMEM);
         } else {
                 packed = fnvlist_pack(nvl, &size);
                 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)
 {
         int error = 0;
         if (dmu_objset_type(os) != DMU_OST_ZFS) {

@@ -1481,17 +1555,41 @@
                 dmu_objset_disown(zfsvfs->z_os, zfsvfs);
                 zfsvfs_free(zfsvfs);
         }
 }
 
+
+/*
+ * 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)
 {
         int error;
         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))
                 return (error);
 

@@ -1536,10 +1634,19 @@
          */
         if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
             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);
         nvlist_free(config);
         nvlist_free(props);

@@ -1549,23 +1656,29 @@
 
 static int
 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);
 }
 
 static int
 zfs_ioc_pool_import(zfs_cmd_t *zc)
 {
         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)
                 return (error);
 

@@ -1580,10 +1693,20 @@
             guid != zc->zc_guid)
                 error = SET_ERROR(EINVAL);
         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;
 
                 if ((err = put_nvlist(zc, config)) != 0)
                         error = err;

@@ -1600,15 +1723,21 @@
 zfs_ioc_pool_export(zfs_cmd_t *zc)
 {
         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);
 }
 
 static int
 zfs_ioc_pool_configs(zfs_cmd_t *zc)

@@ -1717,11 +1846,41 @@
         spa_close(spa, FTAG);
 
         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;
         int error;
 

@@ -1868,10 +2027,11 @@
 {
         spa_t *spa;
         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)
                 return (error);
 

@@ -1899,36 +2059,47 @@
                 return (SET_ERROR(EDOM));
         }
 
         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);
         return (error);
 }
 
 /*
  * 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)
 {
         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);
+        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);
 }
 
 static int

@@ -1949,11 +2120,12 @@
                 error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
                 break;
 
         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);
                 break;
 

@@ -1977,18 +2149,26 @@
 zfs_ioc_vdev_attach(zfs_cmd_t *zc)
 {
         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)
                 return (error);
 
         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);
         }
 
         spa_close(spa, FTAG);
         return (error);

@@ -1997,16 +2177,22 @@
 static int
 zfs_ioc_vdev_detach(zfs_cmd_t *zc)
 {
         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);
 }
 
 static int

@@ -2043,10 +2229,28 @@
 
         return (error);
 }
 
 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;
         char *path = zc->zc_value;
         uint64_t guid = zc->zc_guid;

@@ -2121,11 +2325,11 @@
  * zc_nvlist_dst_size   size of property nvlist
  */
 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);
         if (error == 0) {
                 error = zfs_ioc_objset_stats_impl(zc, os);

@@ -2230,27 +2434,10 @@
         }
         dmu_objset_rele(os, FTAG);
         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
  * zc_cookie            zap cursor
  * zc_nvlist_dst_size   size of buffer for property nvlist

@@ -2519,25 +2706,51 @@
  */
 int
 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;
         uint64_t intval;
         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) {
                 const char *propname = nvpair_name(pair);
                 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) {
                         nvlist_t *attrs;
                         attrs = fnvpair_value_nvlist(pair);

@@ -2619,10 +2832,35 @@
         if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
                 nvl = retrynvl;
                 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) {
                 /*
                  * If this fails, we still want to set as many properties as we
                  * can, so try setting them individually.

@@ -2660,10 +2898,27 @@
                 }
         }
         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);
 }
 
 /*
  * Check that all the properties are valid user properties.

@@ -2687,11 +2942,11 @@
 
                 if (strlen(propname) >= ZAP_MAXNAMELEN)
                         return (SET_ERROR(ENAMETOOLONG));
 
                 if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN)
-                        return (E2BIG);
+                        return (SET_ERROR(E2BIG));
         }
         return (0);
 }
 
 static void

@@ -2728,12 +2983,134 @@
         }
         nvlist_free(cleared_props);
         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
  * zc_cookie            received properties flag
  *

@@ -2743,35 +3120,19 @@
 static int
 zfs_ioc_set_prop(zfs_cmd_t *zc)
 {
         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,
             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);
         }
 
         nvlist_free(errors);

@@ -2859,11 +3220,11 @@
 {
         nvlist_t *props;
         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);
 
         /*

@@ -2875,11 +3236,11 @@
             zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
             nvlist_next_nvpair(props, pair) == NULL) {
                 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) {
                         nvlist_free(props);
                         return (0);

@@ -2891,10 +3252,17 @@
                 return (error);
         }
 
         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);
 
         return (error);
 }

@@ -3012,10 +3380,13 @@
 
 #define ZFS_PROP_UNDEFINED      ((uint64_t)-1)
 
 /*
  * 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?
  * createprops          list of properties requested by creator
  *

@@ -3089,10 +3460,15 @@
         if (norm == ZFS_PROP_UNDEFINED)
                 VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
         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.
          */
         if (norm)
                 u8 = 1;

@@ -3186,10 +3562,12 @@
         nvlist_t *nvprops = NULL;
         void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
         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));
         type = type32;
         (void) nvlist_lookup_nvlist(innvl, "props", &nvprops);

@@ -3214,10 +3592,15 @@
         zct.zct_props = nvprops;
 
         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;
 
                 if (nvprops == NULL)
                         return (SET_ERROR(EINVAL));

@@ -3238,12 +3621,10 @@
                     volblocksize)) != 0 ||
                     (error = zvol_check_volsize(volsize,
                     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
                  * file system creation, so go figure them out
                  * now.

@@ -3269,10 +3650,20 @@
                 error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL,
                     nvprops, outnvl);
                 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);
 }
 
 /*
  * innvl: {

@@ -3285,22 +3676,32 @@
 static int
 zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 {
         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, '@') ||
             strchr(fsname, '%'))
                 return (SET_ERROR(EINVAL));
 
         if (dataset_namecheck(origin_name, NULL, NULL) != 0)
                 return (SET_ERROR(EINVAL));
+
         error = dmu_objset_clone(fsname, origin_name);
         if (error != 0)
                 return (error);
 
         /*

@@ -3310,22 +3711,21 @@
                 error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL,
                     nvprops, outnvl);
                 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);
 }
 
 /*
  * innvl: {
  *     "snaps" -> { snapshot1, snapshot2 }

@@ -3339,10 +3739,11 @@
 {
         nvlist_t *snaps;
         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)
                 return (error);
 

@@ -3364,10 +3765,13 @@
                  */
                 if (cp == NULL ||
                     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.
                  */
                 if (strncmp(name, poolname, poollen) != 0 ||
                     (name[poollen] != '/' && name[poollen] != '@'))

@@ -3382,10 +3786,17 @@
                         }
                 }
         }
 
         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);
 }
 
 /*
  * innvl: "message" -> string

@@ -3499,10 +3910,29 @@
         } else {
                 dmu_objset_rele(os, FTAG);
         }
 }
 
+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 }
  *     (optional boolean) "defer"
  * }

@@ -3515,21 +3945,52 @@
 zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
 {
         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);
 }
 
 /*
  * Create bookmarks.  Bookmark names are of the form <fs>#<bmark>.
  * All bookmarks must be in the same pool.

@@ -3661,27 +4122,109 @@
 /*
  * inputs:
  * 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
  */
 static int
 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
+        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);
 }
 
 /*
  * fsname is name of dataset to rollback (to most recent snapshot)

@@ -3696,11 +4239,16 @@
 zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 {
         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, '@');
 
                 /*

@@ -3716,21 +4264,28 @@
                 dsl_dataset_t *ds;
 
                 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);
 }
 
 static int
 recursive_unmount(const char *fsname, void *arg)

@@ -3755,23 +4310,24 @@
 static int
 zfs_ioc_rename(zfs_cmd_t *zc)
 {
         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));
                 *at = '\0';
                 if (zc->zc_objset_type == DMU_OST_ZFS) {

@@ -3785,16 +4341,25 @@
                 }
                 error = dsl_dataset_rename_snapshot(zc->zc_name,
                     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
 zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
 {

@@ -3947,10 +4512,36 @@
                             SPA_VERSION_PASSTHROUGH_X))
                                 return (SET_ERROR(ENOTSUP));
                 }
                 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:
         {
                 spa_feature_t feature;
                 spa_t *spa;

@@ -4222,79 +4813,37 @@
 
 #ifdef  DEBUG
 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
          * dmu_recv_begin() succeeds.
          */

@@ -4325,13 +4874,13 @@
                                 (void) nvlist_merge(errors, errlist, 0);
                         nvlist_free(errlist);
 
                         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;
                 }
         }
 
         if (props != NULL) {
                 props_error = dsl_prop_set_hasrecvd(tofs);

@@ -4341,18 +4890,23 @@
                         (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
                             props, errors);
                 }
         }
 
+        if (fp) {
         off = fp->f_offset;
-        error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
-            &zc->zc_action_handle);
+        } 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;
 
                         ds = dmu_objset_ds(zfsvfs->z_os);

@@ -4389,28 +4943,34 @@
                  */
                 ASSERT(nvlist_merge(props, delayprops, 0) == 0);
                 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);
-        }
-
-        zc->zc_cookie = off - fp->f_offset;
+        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];
 
+                (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;
                 error = 1;
         }

@@ -4423,18 +4983,18 @@
                         /*
                          * We failed to clear the received properties.
                          * 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);
                 }
 
                 if (origprops == NULL && !drc.drc_newfs) {
                         /* We failed to stash the original properties. */
-                        zc->zc_obj |= ZPROP_ERR_NORESTORE;
+                        *errf |= ZPROP_ERR_NORESTORE;
                 }
 
                 /*
                  * dsl_props_set() will not convert RECEIVED to LOCAL on or
                  * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL

@@ -4447,17 +5007,16 @@
                     origprops, NULL) != 0) {
                         /*
                          * 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);
+        if (fp)
         releasef(fd);
 
         if (error == 0)
                 error = props_error;
 

@@ -4464,10 +5023,85 @@
         return (error);
 }
 
 /*
  * 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)
  * zc_sendobj   objsetid of snapshot to send
  * zc_fromobj   objsetid of incremental fromsnap (may be zero)

@@ -4540,19 +5174,21 @@
                 if (fromsnap != NULL)
                         dsl_dataset_rele(fromsnap, FTAG);
                 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);
         }
         return (error);

@@ -4783,16 +5419,12 @@
         dsl_pool_t *dp;
         dsl_dataset_t *ds, *ods;
         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);
 
         error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds);

@@ -4827,11 +5459,20 @@
         cp = strchr(origin, '@');
         if (cp)
                 *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);
 }
 
 /*
  * Retrieve a single {user|group}{used|quota}@... property.
  *

@@ -5508,10 +6149,217 @@
         fnvlist_add_uint64(outnvl, "compressed", comp);
         fnvlist_add_uint64(outnvl, "uncompressed", uncomp);
         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)
  *     (optional) "fromsnap" -> full snap name to send an incremental from
  *     (optional) "largeblockok" -> (value ignored)

@@ -5658,10 +6506,224 @@
         dsl_dataset_rele(tosnap, FTAG);
         dsl_pool_rele(dp, FTAG);
         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
 zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
     zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck,

@@ -5763,13 +6825,372 @@
 {
         zfs_ioctl_register_legacy(ioc, func, secpolicy,
             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);
 
         zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY,

@@ -5794,18 +7215,44 @@
 
         zfs_ioctl_register("clone", ZFS_IOC_CLONE,
             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);
         zfs_ioctl_register("release", ZFS_IOC_RELEASE,
             zfs_ioc_release, zfs_secpolicy_release, POOL_NAME,

@@ -5835,19 +7282,26 @@
         zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM,
             zfs_ioc_channel_program, zfs_secpolicy_config,
             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,
             zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY);
 
         zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create,
             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,
             zfs_ioc_vdev_add);
         zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE,

@@ -5856,10 +7310,12 @@
             zfs_ioc_vdev_set_state);
         zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH,
             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,
             zfs_ioc_vdev_setfru);
         zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS,

@@ -5906,14 +7362,27 @@
 
         zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import,
             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,
             zfs_ioc_objset_recvd_props);
         zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ,

@@ -6401,10 +7870,14 @@
 
         error = ldi_ident_from_mod(&modlinkage, &zfs_li);
         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);
 }
 
 int
 _fini(void)

@@ -6430,10 +7903,13 @@
         tsd_destroy(&zfs_fsyncer_key);
         ldi_ident_release(zfs_li);
         zfs_li = NULL;
         mutex_destroy(&zfs_share_lock);
 
+        if (zfs_channel)
+                (void) sysevent_evc_unbind(zfs_channel);
+
         return (error);
 }
 
 int
 _info(struct modinfo *modinfop)