Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 1 Jan 2014 01:15:55 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-vendor@freebsd.org
Subject:   svn commit: r260154 - vendor-sys/illumos/dist/common/zfs vendor-sys/illumos/dist/uts/common/fs/zfs vendor-sys/illumos/dist/uts/common/fs/zfs/sys vendor-sys/illumos/dist/uts/common/sys/fs vendor/ill...
Message-ID:  <201401010115.s011FtqV078441@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Wed Jan  1 01:15:53 2014
New Revision: 260154
URL: http://svnweb.freebsd.org/changeset/base/260154

Log:
  4369 implement zfs bookmarks
  4368 zfs send filesystems from readonly pools
  
  llumos/illumos-gate@78f171005391b928aaf1642b3206c534ed644332

Added:
  vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_bookmark.c   (contents, props changed)
  vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dsl_bookmark.h   (contents, props changed)
Modified:
  vendor-sys/illumos/dist/common/zfs/zfeature_common.c
  vendor-sys/illumos/dist/common/zfs/zfeature_common.h
  vendor-sys/illumos/dist/common/zfs/zfs_deleg.c
  vendor-sys/illumos/dist/common/zfs/zfs_deleg.h
  vendor-sys/illumos/dist/common/zfs/zfs_namecheck.c
  vendor-sys/illumos/dist/common/zfs/zfs_namecheck.h
  vendor-sys/illumos/dist/common/zfs/zfs_prop.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_diff.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_send.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_dataset.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_destroy.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/spa_misc.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dsl_dataset.h
  vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dsl_deleg.h
  vendor-sys/illumos/dist/uts/common/fs/zfs/zfs_ctldir.c
  vendor-sys/illumos/dist/uts/common/fs/zfs/zfs_ioctl.c
  vendor-sys/illumos/dist/uts/common/sys/fs/zfs.h

Changes in other areas also in this revision:
Modified:
  vendor/illumos/dist/cmd/zfs/zfs_iter.c
  vendor/illumos/dist/cmd/zfs/zfs_main.c
  vendor/illumos/dist/lib/libzfs/common/libzfs.h
  vendor/illumos/dist/lib/libzfs/common/libzfs_dataset.c
  vendor/illumos/dist/lib/libzfs/common/libzfs_impl.h
  vendor/illumos/dist/lib/libzfs/common/libzfs_iter.c
  vendor/illumos/dist/lib/libzfs/common/libzfs_sendrecv.c
  vendor/illumos/dist/lib/libzfs_core/common/libzfs_core.c
  vendor/illumos/dist/lib/libzfs_core/common/libzfs_core.h
  vendor/illumos/dist/lib/pyzfs/common/allow.py
  vendor/illumos/dist/man/man1m/zfs.1m
  vendor/illumos/dist/man/man5/zpool-features.5

Modified: vendor-sys/illumos/dist/common/zfs/zfeature_common.c
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfeature_common.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfeature_common.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -197,4 +197,13 @@ zpool_feature_init(void)
 	    "com.delphix:extensible_dataset", "extensible_dataset",
 	    "Enhanced dataset functionality, used by other features.",
 	    B_FALSE, B_FALSE, B_FALSE, NULL);
+
+	static const spa_feature_t bookmarks_deps[] = {
+		SPA_FEATURE_EXTENSIBLE_DATASET,
+		SPA_FEATURE_NONE
+	};
+	zfeature_register(SPA_FEATURE_BOOKMARKS,
+	    "com.delphix:bookmarks", "bookmarks",
+	    "\"zfs bookmark\" command",
+	    B_TRUE, B_FALSE, B_FALSE, bookmarks_deps);
 }

Modified: vendor-sys/illumos/dist/common/zfs/zfeature_common.h
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfeature_common.h	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfeature_common.h	Wed Jan  1 01:15:53 2014	(r260154)
@@ -48,6 +48,7 @@ typedef enum spa_feature {
 	SPA_FEATURE_ENABLED_TXG,
 	SPA_FEATURE_HOLE_BIRTH,
 	SPA_FEATURE_EXTENSIBLE_DATASET,
+	SPA_FEATURE_BOOKMARKS,
 	SPA_FEATURES
 } spa_feature_t;
 

Modified: vendor-sys/illumos/dist/common/zfs/zfs_deleg.c
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfs_deleg.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfs_deleg.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -21,8 +21,11 @@
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
+#include <sys/zfs_context.h>
+
 #if defined(_KERNEL)
 #include <sys/systm.h>
 #include <sys/sunddi.h>
@@ -34,43 +37,34 @@
 #include <libnvpair.h>
 #include <ctype.h>
 #endif
-/* XXX includes zfs_context.h, so why bother with the above? */
 #include <sys/dsl_deleg.h>
 #include "zfs_prop.h"
 #include "zfs_deleg.h"
 #include "zfs_namecheck.h"
 
-/*
- * permission table
- *
- * Keep this table in sorted order
- *
- * This table is used for displaying all permissions for
- * zfs allow
- */
-
 zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
-	{ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW},
-	{ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
-	{ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
-	{ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
-	{ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
-	{ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
-	{ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
-	{ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
-	{ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
-	{ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
-	{ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
-	{ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
-	{ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
-	{ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
-	{ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
-	{ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
-	{ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
-	{ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
-	{ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
-	{ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
-	{NULL, ZFS_DELEG_NOTE_NONE }
+	{ZFS_DELEG_PERM_ALLOW},
+	{ZFS_DELEG_PERM_BOOKMARK},
+	{ZFS_DELEG_PERM_CLONE},
+	{ZFS_DELEG_PERM_CREATE},
+	{ZFS_DELEG_PERM_DESTROY},
+	{ZFS_DELEG_PERM_DIFF},
+	{ZFS_DELEG_PERM_MOUNT},
+	{ZFS_DELEG_PERM_PROMOTE},
+	{ZFS_DELEG_PERM_RECEIVE},
+	{ZFS_DELEG_PERM_RENAME},
+	{ZFS_DELEG_PERM_ROLLBACK},
+	{ZFS_DELEG_PERM_SNAPSHOT},
+	{ZFS_DELEG_PERM_SHARE},
+	{ZFS_DELEG_PERM_SEND},
+	{ZFS_DELEG_PERM_USERPROP},
+	{ZFS_DELEG_PERM_USERQUOTA},
+	{ZFS_DELEG_PERM_GROUPQUOTA},
+	{ZFS_DELEG_PERM_USERUSED},
+	{ZFS_DELEG_PERM_GROUPUSED},
+	{ZFS_DELEG_PERM_HOLD},
+	{ZFS_DELEG_PERM_RELEASE},
+	{NULL}
 };
 
 static int

Modified: vendor-sys/illumos/dist/common/zfs/zfs_deleg.h
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfs_deleg.h	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfs_deleg.h	Wed Jan  1 01:15:53 2014	(r260154)
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
 #ifndef	_ZFS_DELEG_H
@@ -65,6 +66,7 @@ typedef enum {
 	ZFS_DELEG_NOTE_HOLD,
 	ZFS_DELEG_NOTE_RELEASE,
 	ZFS_DELEG_NOTE_DIFF,
+	ZFS_DELEG_NOTE_BOOKMARK,
 	ZFS_DELEG_NOTE_NONE
 } zfs_deleg_note_t;
 

Modified: vendor-sys/illumos/dist/common/zfs/zfs_namecheck.c
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfs_namecheck.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfs_namecheck.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -22,6 +22,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
 
 /*
  * Common name validation routines for ZFS.  These routines are shared by the
@@ -62,7 +65,7 @@ valid_char(char c)
  * 	[-_.: ]
  */
 int
-snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
+zfs_component_namecheck(const char *path, namecheck_err_t *why, char *what)
 {
 	const char *loc;
 
@@ -113,7 +116,7 @@ permset_namecheck(const char *path, name
 		return (-1);
 	}
 
-	return (snapshot_namecheck(&path[1], why, what));
+	return (zfs_component_namecheck(&path[1], why, what));
 }
 
 /*

Modified: vendor-sys/illumos/dist/common/zfs/zfs_namecheck.h
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfs_namecheck.h	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfs_namecheck.h	Wed Jan  1 01:15:53 2014	(r260154)
@@ -22,6 +22,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
 
 #ifndef	_ZFS_NAMECHECK_H
 #define	_ZFS_NAMECHECK_H
@@ -48,7 +51,7 @@ typedef enum {
 int pool_namecheck(const char *, namecheck_err_t *, char *);
 int dataset_namecheck(const char *, namecheck_err_t *, char *);
 int mountpoint_namecheck(const char *, namecheck_err_t *);
-int snapshot_namecheck(const char *, namecheck_err_t *, char *);
+int zfs_component_namecheck(const char *, namecheck_err_t *, char *);
 int permset_namecheck(const char *, namecheck_err_t *, char *);
 
 #ifdef	__cplusplus

Modified: vendor-sys/illumos/dist/common/zfs/zfs_prop.c
==============================================================================
--- vendor-sys/illumos/dist/common/zfs/zfs_prop.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/common/zfs/zfs_prop.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  */
@@ -312,7 +312,8 @@ zfs_prop_init(void)
 	    PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off | share(1M) options",
 	    "SHARENFS");
 	zprop_register_string(ZFS_PROP_TYPE, "type", NULL, PROP_READONLY,
-	    ZFS_TYPE_DATASET, "filesystem | volume | snapshot", "TYPE");
+	    ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK,
+	    "filesystem | volume | snapshot | bookmark", "TYPE");
 	zprop_register_string(ZFS_PROP_SHARESMB, "sharesmb", "off",
 	    PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
 	    "on | off | sharemgr(1M) options", "SHARESMB");
@@ -378,18 +379,18 @@ zfs_prop_init(void)
 
 	/* hidden properties */
 	zprop_register_hidden(ZFS_PROP_CREATETXG, "createtxg", PROP_TYPE_NUMBER,
-	    PROP_READONLY, ZFS_TYPE_DATASET, "CREATETXG");
+	    PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "CREATETXG");
 	zprop_register_hidden(ZFS_PROP_NUMCLONES, "numclones", PROP_TYPE_NUMBER,
 	    PROP_READONLY, ZFS_TYPE_SNAPSHOT, "NUMCLONES");
 	zprop_register_hidden(ZFS_PROP_NAME, "name", PROP_TYPE_STRING,
-	    PROP_READONLY, ZFS_TYPE_DATASET, "NAME");
+	    PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "NAME");
 	zprop_register_hidden(ZFS_PROP_ISCSIOPTIONS, "iscsioptions",
 	    PROP_TYPE_STRING, PROP_INHERIT, ZFS_TYPE_VOLUME, "ISCSIOPTIONS");
 	zprop_register_hidden(ZFS_PROP_STMF_SHAREINFO, "stmf_sbd_lu",
 	    PROP_TYPE_STRING, PROP_INHERIT, ZFS_TYPE_VOLUME,
 	    "STMF_SBD_LU");
 	zprop_register_hidden(ZFS_PROP_GUID, "guid", PROP_TYPE_NUMBER,
-	    PROP_READONLY, ZFS_TYPE_DATASET, "GUID");
+	    PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "GUID");
 	zprop_register_hidden(ZFS_PROP_USERACCOUNTING, "useraccounting",
 	    PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET,
 	    "USERACCOUNTING");
@@ -402,7 +403,7 @@ zfs_prop_init(void)
 
 	/* oddball properties */
 	zprop_register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0,
-	    NULL, PROP_READONLY, ZFS_TYPE_DATASET,
+	    NULL, PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK,
 	    "<date>", "CREATION", B_FALSE, B_TRUE, NULL);
 }
 

Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_diff.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_diff.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_diff.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -187,7 +187,7 @@ dmu_diff(const char *tosnap_name, const 
 		return (error);
 	}
 
-	if (!dsl_dataset_is_before(tosnap, fromsnap)) {
+	if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) {
 		dsl_dataset_rele(fromsnap, FTAG);
 		dsl_dataset_rele(tosnap, FTAG);
 		dsl_pool_rele(dp, FTAG);

Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_send.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_send.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dmu_send.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -48,6 +48,7 @@
 #include <sys/zfs_onexit.h>
 #include <sys/dmu_send.h>
 #include <sys/dsl_destroy.h>
+#include <sys/dsl_bookmark.h>
 
 /* Set this tunable to TRUE to replace corrupt data with 0x2f5baddb10c */
 int zfs_send_corrupt_data = B_FALSE;
@@ -356,6 +357,12 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
 	if (zb->zb_object != DMU_META_DNODE_OBJECT &&
 	    DMU_OBJECT_IS_SPECIAL(zb->zb_object)) {
 		return (0);
+	} else if (zb->zb_level == ZB_ZIL_LEVEL) {
+		/*
+		 * If we are sending a non-snapshot (which is allowed on
+		 * read-only pools), it may have a ZIL, which must be ignored.
+		 */
+		return (0);
 	} else if (BP_IS_HOLE(bp) &&
 	    zb->zb_object == DMU_META_DNODE_OBJECT) {
 		uint64_t span = BP_SPAN(dnp, zb->zb_level);
@@ -404,6 +411,7 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
 		arc_buf_t *abuf;
 		int blksz = BP_GET_LSIZE(bp);
 
+		ASSERT0(zb->zb_level);
 		if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf,
 		    ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL,
 		    &aflags, zb) != 0) {
@@ -431,11 +439,12 @@ backup_cb(spa_t *spa, zilog_t *zilog, co
 }
 
 /*
- * Releases dp, ds, and fromds, using the specified tag.
+ * Releases dp using the specified tag.
  */
 static int
 dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
-    dsl_dataset_t *fromds, int outfd, vnode_t *vp, offset_t *off)
+    zfs_bookmark_phys_t *fromzb, boolean_t is_clone, int outfd,
+    vnode_t *vp, offset_t *off)
 {
 	objset_t *os;
 	dmu_replay_record_t *drr;
@@ -443,18 +452,8 @@ dmu_send_impl(void *tag, dsl_pool_t *dp,
 	int err;
 	uint64_t fromtxg = 0;
 
-	if (fromds != NULL && !dsl_dataset_is_before(ds, fromds)) {
-		dsl_dataset_rele(fromds, tag);
-		dsl_dataset_rele(ds, tag);
-		dsl_pool_rele(dp, tag);
-		return (SET_ERROR(EXDEV));
-	}
-
 	err = dmu_objset_from_ds(ds, &os);
 	if (err != 0) {
-		if (fromds != NULL)
-			dsl_dataset_rele(fromds, tag);
-		dsl_dataset_rele(ds, tag);
 		dsl_pool_rele(dp, tag);
 		return (err);
 	}
@@ -470,9 +469,6 @@ dmu_send_impl(void *tag, dsl_pool_t *dp,
 		uint64_t version;
 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &version) != 0) {
 			kmem_free(drr, sizeof (dmu_replay_record_t));
-			if (fromds != NULL)
-				dsl_dataset_rele(fromds, tag);
-			dsl_dataset_rele(ds, tag);
 			dsl_pool_rele(dp, tag);
 			return (SET_ERROR(EINVAL));
 		}
@@ -487,20 +483,20 @@ dmu_send_impl(void *tag, dsl_pool_t *dp,
 	drr->drr_u.drr_begin.drr_creation_time =
 	    ds->ds_phys->ds_creation_time;
 	drr->drr_u.drr_begin.drr_type = dmu_objset_type(os);
-	if (fromds != NULL && ds->ds_dir != fromds->ds_dir)
+	if (is_clone)
 		drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CLONE;
 	drr->drr_u.drr_begin.drr_toguid = ds->ds_phys->ds_guid;
 	if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
 		drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CI_DATA;
 
-	if (fromds != NULL)
-		drr->drr_u.drr_begin.drr_fromguid = fromds->ds_phys->ds_guid;
+	if (fromzb != NULL) {
+		drr->drr_u.drr_begin.drr_fromguid = fromzb->zbm_guid;
+		fromtxg = fromzb->zbm_creation_txg;
+	}
 	dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname);
-
-	if (fromds != NULL) {
-		fromtxg = fromds->ds_phys->ds_creation_txg;
-		dsl_dataset_rele(fromds, tag);
-		fromds = NULL;
+	if (!dsl_dataset_is_snapshot(ds)) {
+		(void) strlcat(drr->drr_u.drr_begin.drr_toname, "@--head--",
+		    sizeof (drr->drr_u.drr_begin.drr_toname));
 	}
 
 	dsp = kmem_zalloc(sizeof (dmu_sendarg_t), KM_SLEEP);
@@ -514,7 +510,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp,
 	dsp->dsa_toguid = ds->ds_phys->ds_guid;
 	ZIO_SET_CHECKSUM(&dsp->dsa_zc, 0, 0, 0, 0);
 	dsp->dsa_pending_op = PENDING_NONE;
-	dsp->dsa_incremental = (fromtxg != 0);
+	dsp->dsa_incremental = (fromzb != NULL);
 
 	mutex_enter(&ds->ds_sendstream_lock);
 	list_insert_head(&ds->ds_sendstreams, dsp);
@@ -560,7 +556,6 @@ out:
 	kmem_free(dsp, sizeof (dmu_sendarg_t));
 
 	dsl_dataset_long_rele(ds, FTAG);
-	dsl_dataset_rele(ds, tag);
 
 	return (err);
 }
@@ -585,15 +580,30 @@ dmu_send_obj(const char *pool, uint64_t 
 	}
 
 	if (fromsnap != 0) {
+		zfs_bookmark_phys_t zb;
+		boolean_t is_clone;
+
 		err = dsl_dataset_hold_obj(dp, fromsnap, FTAG, &fromds);
 		if (err != 0) {
 			dsl_dataset_rele(ds, FTAG);
 			dsl_pool_rele(dp, FTAG);
 			return (err);
 		}
+		if (!dsl_dataset_is_before(ds, fromds, 0))
+			err = SET_ERROR(EXDEV);
+		zb.zbm_creation_time = fromds->ds_phys->ds_creation_time;
+		zb.zbm_creation_txg = fromds->ds_phys->ds_creation_txg;
+		zb.zbm_guid = fromds->ds_phys->ds_guid;
+		is_clone = (fromds->ds_dir != ds->ds_dir);
+		dsl_dataset_rele(fromds, FTAG);
+		err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone,
+		    outfd, vp, off);
+	} else {
+		err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE,
+		    outfd, vp, off);
 	}
-
-	return (dmu_send_impl(FTAG, dp, ds, fromds, outfd, vp, off));
+	dsl_dataset_rele(ds, FTAG);
+	return (err);
 }
 
 int
@@ -602,33 +612,79 @@ dmu_send(const char *tosnap, const char 
 {
 	dsl_pool_t *dp;
 	dsl_dataset_t *ds;
-	dsl_dataset_t *fromds = NULL;
 	int err;
+	boolean_t owned = B_FALSE;
 
-	if (strchr(tosnap, '@') == NULL)
-		return (SET_ERROR(EINVAL));
-	if (fromsnap != NULL && strchr(fromsnap, '@') == NULL)
+	if (fromsnap != NULL && strpbrk(fromsnap, "@#") == NULL)
 		return (SET_ERROR(EINVAL));
 
 	err = dsl_pool_hold(tosnap, FTAG, &dp);
 	if (err != 0)
 		return (err);
 
-	err = dsl_dataset_hold(dp, tosnap, FTAG, &ds);
+	if (strchr(tosnap, '@') == NULL && spa_writeable(dp->dp_spa)) {
+		/*
+		 * We are sending a filesystem or volume.  Ensure
+		 * that it doesn't change by owning the dataset.
+		 */
+		err = dsl_dataset_own(dp, tosnap, FTAG, &ds);
+		owned = B_TRUE;
+	} else {
+		err = dsl_dataset_hold(dp, tosnap, FTAG, &ds);
+	}
 	if (err != 0) {
 		dsl_pool_rele(dp, FTAG);
 		return (err);
 	}
 
 	if (fromsnap != NULL) {
-		err = dsl_dataset_hold(dp, fromsnap, FTAG, &fromds);
+		zfs_bookmark_phys_t zb;
+		boolean_t is_clone = B_FALSE;
+		int fsnamelen = strchr(tosnap, '@') - tosnap;
+
+		/*
+		 * If the fromsnap is in a different filesystem, then
+		 * mark the send stream as a clone.
+		 */
+		if (strncmp(tosnap, fromsnap, fsnamelen) != 0 ||
+		    (fromsnap[fsnamelen] != '@' &&
+		    fromsnap[fsnamelen] != '#')) {
+			is_clone = B_TRUE;
+		}
+
+		if (strchr(fromsnap, '@')) {
+			dsl_dataset_t *fromds;
+			err = dsl_dataset_hold(dp, fromsnap, FTAG, &fromds);
+			if (err == 0) {
+				if (!dsl_dataset_is_before(ds, fromds, 0))
+					err = SET_ERROR(EXDEV);
+				zb.zbm_creation_time =
+				    fromds->ds_phys->ds_creation_time;
+				zb.zbm_creation_txg =
+				    fromds->ds_phys->ds_creation_txg;
+				zb.zbm_guid = fromds->ds_phys->ds_guid;
+				is_clone = (ds->ds_dir != fromds->ds_dir);
+				dsl_dataset_rele(fromds, FTAG);
+			}
+		} else {
+			err = dsl_bookmark_lookup(dp, fromsnap, ds, &zb);
+		}
 		if (err != 0) {
 			dsl_dataset_rele(ds, FTAG);
 			dsl_pool_rele(dp, FTAG);
 			return (err);
 		}
+		err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone,
+		    outfd, vp, off);
+	} else {
+		err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE,
+		    outfd, vp, off);
 	}
-	return (dmu_send_impl(FTAG, dp, ds, fromds, outfd, vp, off));
+	if (owned)
+		dsl_dataset_disown(ds, FTAG);
+	else
+		dsl_dataset_rele(ds, FTAG);
+	return (err);
 }
 
 int
@@ -648,7 +704,7 @@ dmu_send_estimate(dsl_dataset_t *ds, dsl
 	 * fromsnap must be an earlier snapshot from the same fs as tosnap,
 	 * or the origin's fs.
 	 */
-	if (fromds != NULL && !dsl_dataset_is_before(ds, fromds))
+	if (fromds != NULL && !dsl_dataset_is_before(ds, fromds, 0))
 		return (SET_ERROR(EXDEV));
 
 	/* Get uncompressed size estimate of changed data. */

Added: vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_bookmark.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_bookmark.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -0,0 +1,454 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
+
+#include <sys/zfs_context.h>
+#include <sys/dsl_dataset.h>
+#include <sys/dsl_dir.h>
+#include <sys/dsl_prop.h>
+#include <sys/dsl_synctask.h>
+#include <sys/dmu_impl.h>
+#include <sys/dmu_tx.h>
+#include <sys/arc.h>
+#include <sys/zap.h>
+#include <sys/zfeature.h>
+#include <sys/spa.h>
+#include <sys/dsl_bookmark.h>
+#include <zfs_namecheck.h>
+
+static int
+dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname,
+    dsl_dataset_t **dsp, void *tag, char **shortnamep)
+{
+	char buf[MAXNAMELEN];
+	char *hashp;
+
+	if (strlen(fullname) >= MAXNAMELEN)
+		return (SET_ERROR(ENAMETOOLONG));
+	hashp = strchr(fullname, '#');
+	if (hashp == NULL)
+		return (SET_ERROR(EINVAL));
+
+	*shortnamep = hashp + 1;
+	if (zfs_component_namecheck(*shortnamep, NULL, NULL))
+		return (SET_ERROR(EINVAL));
+	(void) strlcpy(buf, fullname, hashp - fullname + 1);
+	return (dsl_dataset_hold(dp, buf, tag, dsp));
+}
+
+/*
+ * Returns ESRCH if bookmark is not found.
+ */
+static int
+dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname,
+    zfs_bookmark_phys_t *bmark_phys)
+{
+	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+	uint64_t bmark_zapobj = ds->ds_bookmarks;
+	matchtype_t mt;
+	int err;
+
+	if (bmark_zapobj == 0)
+		return (SET_ERROR(ESRCH));
+
+	if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
+		mt = MT_FIRST;
+	else
+		mt = MT_EXACT;
+
+	err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
+	    sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt,
+	    NULL, 0, NULL);
+
+	return (err == ENOENT ? ESRCH : err);
+}
+
+/*
+ * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark
+ * does not represents an earlier point in later_ds's timeline.
+ *
+ * Returns ENOENT if the dataset containing the bookmark does not exist.
+ * Returns ESRCH if the dataset exists but the bookmark was not found in it.
+ */
+int
+dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname,
+    dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp)
+{
+	char *shortname;
+	dsl_dataset_t *ds;
+	int error;
+
+	error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname);
+	if (error != 0)
+		return (error);
+
+	error = dsl_dataset_bmark_lookup(ds, shortname, bmp);
+	if (error == 0 && later_ds != NULL) {
+		if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg))
+			error = SET_ERROR(EXDEV);
+	}
+	dsl_dataset_rele(ds, FTAG);
+	return (error);
+}
+
+typedef struct dsl_bookmark_create_arg {
+	nvlist_t *dbca_bmarks;
+	nvlist_t *dbca_errors;
+} dsl_bookmark_create_arg_t;
+
+static int
+dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
+    dmu_tx_t *tx)
+{
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	dsl_dataset_t *bmark_fs;
+	char *shortname;
+	int error;
+	zfs_bookmark_phys_t bmark_phys;
+
+	if (!dsl_dataset_is_snapshot(snapds))
+		return (SET_ERROR(EINVAL));
+
+	error = dsl_bookmark_hold_ds(dp, bookmark_name,
+	    &bmark_fs, FTAG, &shortname);
+	if (error != 0)
+		return (error);
+
+	if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) {
+		dsl_dataset_rele(bmark_fs, FTAG);
+		return (SET_ERROR(EINVAL));
+	}
+
+	error = dsl_dataset_bmark_lookup(bmark_fs, shortname,
+	    &bmark_phys);
+	dsl_dataset_rele(bmark_fs, FTAG);
+	if (error == 0)
+		return (SET_ERROR(EEXIST));
+	if (error == ESRCH)
+		return (0);
+	return (error);
+}
+
+static int
+dsl_bookmark_create_check(void *arg, dmu_tx_t *tx)
+{
+	dsl_bookmark_create_arg_t *dbca = arg;
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	int rv = 0;
+
+	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
+		return (SET_ERROR(ENOTSUP));
+
+	for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
+	    pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
+		dsl_dataset_t *snapds;
+		int error;
+
+		/* note: validity of nvlist checked by ioctl layer */
+		error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
+		    FTAG, &snapds);
+		if (error == 0) {
+			error = dsl_bookmark_create_check_impl(snapds,
+			    nvpair_name(pair), tx);
+			dsl_dataset_rele(snapds, FTAG);
+		}
+		if (error != 0) {
+			fnvlist_add_int32(dbca->dbca_errors,
+			    nvpair_name(pair), error);
+			rv = error;
+		}
+	}
+
+	return (rv);
+}
+
+static void
+dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
+{
+	dsl_bookmark_create_arg_t *dbca = arg;
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	objset_t *mos = dp->dp_meta_objset;
+
+	ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
+
+	for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
+	    pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
+		dsl_dataset_t *snapds, *bmark_fs;
+		zfs_bookmark_phys_t bmark_phys;
+		char *shortname;
+
+		VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
+		    FTAG, &snapds));
+		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
+		    &bmark_fs, FTAG, &shortname));
+		if (bmark_fs->ds_bookmarks == 0) {
+			bmark_fs->ds_bookmarks =
+			    zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
+			    DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
+			spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
+
+			dsl_dataset_zapify(bmark_fs, tx);
+			VERIFY0(zap_add(mos, bmark_fs->ds_object,
+			    DS_FIELD_BOOKMARK_NAMES,
+			    sizeof (bmark_fs->ds_bookmarks), 1,
+			    &bmark_fs->ds_bookmarks, tx));
+		}
+
+		bmark_phys.zbm_guid = snapds->ds_phys->ds_guid;
+		bmark_phys.zbm_creation_txg = snapds->ds_phys->ds_creation_txg;
+		bmark_phys.zbm_creation_time =
+		    snapds->ds_phys->ds_creation_time;
+
+		VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
+		    shortname, sizeof (uint64_t),
+		    sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t),
+		    &bmark_phys, tx));
+
+		spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
+		    "name=%s creation_txg=%llu target_snap=%llu",
+		    shortname,
+		    (longlong_t)bmark_phys.zbm_creation_txg,
+		    (longlong_t)snapds->ds_object);
+
+		dsl_dataset_rele(bmark_fs, FTAG);
+		dsl_dataset_rele(snapds, FTAG);
+	}
+}
+
+/*
+ * The bookmarks must all be in the same pool.
+ */
+int
+dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
+{
+	nvpair_t *pair;
+	dsl_bookmark_create_arg_t dbca;
+
+	pair = nvlist_next_nvpair(bmarks, NULL);
+	if (pair == NULL)
+		return (0);
+
+	dbca.dbca_bmarks = bmarks;
+	dbca.dbca_errors = errors;
+
+	return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
+	    dsl_bookmark_create_sync, &dbca, fnvlist_num_pairs(bmarks)));
+}
+
+int
+dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
+{
+	int err = 0;
+	zap_cursor_t zc;
+	zap_attribute_t attr;
+	dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+	uint64_t bmark_zapobj = ds->ds_bookmarks;
+	if (bmark_zapobj == 0)
+		return (0);
+
+	for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
+	    zap_cursor_retrieve(&zc, &attr) == 0;
+	    zap_cursor_advance(&zc)) {
+		char *bmark_name = attr.za_name;
+		zfs_bookmark_phys_t bmark_phys;
+
+		err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
+		ASSERT3U(err, !=, ENOENT);
+		if (err != 0)
+			break;
+
+		nvlist_t *out_props = fnvlist_alloc();
+		if (nvlist_exists(props,
+		    zfs_prop_to_name(ZFS_PROP_GUID))) {
+			dsl_prop_nvlist_add_uint64(out_props,
+			    ZFS_PROP_GUID, bmark_phys.zbm_guid);
+		}
+		if (nvlist_exists(props,
+		    zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
+			dsl_prop_nvlist_add_uint64(out_props,
+			    ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
+		}
+		if (nvlist_exists(props,
+		    zfs_prop_to_name(ZFS_PROP_CREATION))) {
+			dsl_prop_nvlist_add_uint64(out_props,
+			    ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
+		}
+
+		fnvlist_add_nvlist(outnvl, bmark_name, out_props);
+		fnvlist_free(out_props);
+	}
+	zap_cursor_fini(&zc);
+	return (err);
+}
+
+/*
+ * Retrieve the bookmarks that exist in the specified dataset, and the
+ * requested properties of each bookmark.
+ *
+ * The "props" nvlist specifies which properties are requested.
+ * See lzc_get_bookmarks() for the list of valid properties.
+ */
+int
+dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
+{
+	dsl_pool_t *dp;
+	dsl_dataset_t *ds;
+	int err;
+
+	err = dsl_pool_hold(dsname, FTAG, &dp);
+	if (err != 0)
+		return (err);
+	err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
+	if (err != 0) {
+		dsl_pool_rele(dp, FTAG);
+		return (err);
+	}
+
+	err = dsl_get_bookmarks_impl(ds, props, outnvl);
+
+	dsl_dataset_rele(ds, FTAG);
+	dsl_pool_rele(dp, FTAG);
+	return (err);
+}
+
+typedef struct dsl_bookmark_destroy_arg {
+	nvlist_t *dbda_bmarks;
+	nvlist_t *dbda_success;
+	nvlist_t *dbda_errors;
+} dsl_bookmark_destroy_arg_t;
+
+static int
+dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
+{
+	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+	uint64_t bmark_zapobj = ds->ds_bookmarks;
+	matchtype_t mt;
+
+	if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
+		mt = MT_FIRST;
+	else
+		mt = MT_EXACT;
+
+	return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
+}
+
+static int
+dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
+{
+	dsl_bookmark_destroy_arg_t *dbda = arg;
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	int rv = 0;
+
+	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
+		return (0);
+
+	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
+	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
+		const char *fullname = nvpair_name(pair);
+		dsl_dataset_t *ds;
+		zfs_bookmark_phys_t bm;
+		int error;
+		char *shortname;
+
+		error = dsl_bookmark_hold_ds(dp, fullname, &ds,
+		    FTAG, &shortname);
+		if (error == ENOENT) {
+			/* ignore it; the bookmark is "already destroyed" */
+			continue;
+		}
+		if (error == 0) {
+			error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
+			dsl_dataset_rele(ds, FTAG);
+			if (error == ESRCH) {
+				/*
+				 * ignore it; the bookmark is
+				 * "already destroyed"
+				 */
+				continue;
+			}
+		}
+		if (error == 0) {
+			fnvlist_add_boolean(dbda->dbda_success, fullname);
+		} else {
+			fnvlist_add_int32(dbda->dbda_errors, fullname, error);
+			rv = error;
+		}
+	}
+	return (rv);
+}
+
+static void
+dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
+{
+	dsl_bookmark_destroy_arg_t *dbda = arg;
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	objset_t *mos = dp->dp_meta_objset;
+
+	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
+	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
+		dsl_dataset_t *ds;
+		char *shortname;
+		uint64_t zap_cnt;
+
+		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
+		    &ds, FTAG, &shortname));
+		VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
+
+		/*
+		 * If all of this dataset's bookmarks have been destroyed,
+		 * free the zap object and decrement the feature's use count.
+		 */
+		VERIFY0(zap_count(mos, ds->ds_bookmarks,
+		    &zap_cnt));
+		if (zap_cnt == 0) {
+			dmu_buf_will_dirty(ds->ds_dbuf, tx);
+			VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
+			ds->ds_bookmarks = 0;
+			spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
+			VERIFY0(zap_remove(mos, ds->ds_object,
+			    DS_FIELD_BOOKMARK_NAMES, tx));
+		}
+
+		spa_history_log_internal_ds(ds, "remove bookmark", tx,
+		    "name=%s", shortname);
+
+		dsl_dataset_rele(ds, FTAG);
+	}
+}
+
+/*
+ * The bookmarks must all be in the same pool.
+ */
+int
+dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
+{
+	int rv;
+	dsl_bookmark_destroy_arg_t dbda;
+	nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
+	if (pair == NULL)
+		return (0);
+
+	dbda.dbda_bmarks = bmarks;
+	dbda.dbda_errors = errors;
+	dbda.dbda_success = fnvlist_alloc();
+
+	rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
+	    dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks));
+	fnvlist_free(dbda.dbda_success);
+	return (rv);
+}

Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_dataset.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_dataset.c	Wed Jan  1 01:05:38 2014	(r260153)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dsl_dataset.c	Wed Jan  1 01:15:53 2014	(r260154)
@@ -47,6 +47,7 @@
 #include <sys/dsl_deadlist.h>
 #include <sys/dsl_destroy.h>
 #include <sys/dsl_userhold.h>
+#include <sys/dsl_bookmark.h>
 
 #define	SWITCH64(x, y) \
 	{ \
@@ -400,6 +401,14 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uin
 				    ds->ds_phys->ds_prev_snap_obj,
 				    ds, &ds->ds_prev);
 			}
+			if (doi.doi_type == DMU_OTN_ZAP_METADATA) {
+				int zaperr = zap_lookup(mos, ds->ds_object,
+				    DS_FIELD_BOOKMARK_NAMES,
+				    sizeof (ds->ds_bookmarks), 1,
+				    &ds->ds_bookmarks);
+				if (zaperr != ENOENT)
+					VERIFY0(zaperr);
+			}
 		} else {
 			if (zfs_flags & ZFS_DEBUG_SNAPNAMES)
 				err = dsl_dataset_get_snapname(ds);
@@ -1721,6 +1730,28 @@ dsl_dataset_rollback_check(void *arg, dm
 		return (SET_ERROR(EINVAL));
 	}
 
+	/* must not have any bookmarks after the most recent snapshot */
+	nvlist_t *proprequest = fnvlist_alloc();
+	fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG));
+	nvlist_t *bookmarks = fnvlist_alloc();
+	error = dsl_get_bookmarks_impl(ds, proprequest, bookmarks);
+	fnvlist_free(proprequest);
+	if (error != 0)
+		return (error);
+	for (nvpair_t *pair = nvlist_next_nvpair(bookmarks, NULL);
+	    pair != NULL; pair = nvlist_next_nvpair(bookmarks, pair)) {
+		nvlist_t *valuenv =
+		    fnvlist_lookup_nvlist(fnvpair_value_nvlist(pair),
+		    zfs_prop_to_name(ZFS_PROP_CREATETXG));
+		uint64_t createtxg = fnvlist_lookup_uint64(valuenv, "value");
+		if (createtxg > ds->ds_phys->ds_prev_snap_txg) {
+			fnvlist_free(bookmarks);
+			dsl_dataset_rele(ds, FTAG);
+			return (SET_ERROR(EEXIST));
+		}
+	}
+	fnvlist_free(bookmarks);
+
 	error = dsl_dataset_handoff_check(ds, ddra->ddra_owner, tx);
 	if (error != 0) {
 		dsl_dataset_rele(ds, FTAG);
@@ -2942,18 +2973,25 @@ dsl_dataset_space_wouldfree(dsl_dataset_

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201401010115.s011FtqV078441>