summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sys/dsl_dataset.h9
-rw-r--r--include/sys/zcp.h4
-rw-r--r--lib/libzfs_core/libzfs_core.c11
-rw-r--r--man/man8/zfs-program.813
-rw-r--r--module/zfs/dsl_dataset.c11
-rw-r--r--module/zfs/zcp.c9
-rw-r--r--module/zfs/zcp_global.c7
-rw-r--r--module/zfs/zcp_synctask.c71
-rw-r--r--module/zfs/zfs_ioctl.c2
-rw-r--r--tests/runfiles/linux.run9
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am2
-rwxr-xr-xtests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh54
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp24
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am10
-rwxr-xr-xtests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh39
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp24
-rwxr-xr-xtests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh44
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp35
-rwxr-xr-xtests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh61
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp28
-rwxr-xr-xtests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh40
-rw-r--r--tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp26
22 files changed, 500 insertions, 33 deletions
diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h
index ee822a201..0030fca24 100644
--- a/include/sys/dsl_dataset.h
+++ b/include/sys/dsl_dataset.h
@@ -252,6 +252,13 @@ typedef struct dsl_dataset_rollback_arg {
nvlist_t *ddra_result;
} dsl_dataset_rollback_arg_t;
+typedef struct dsl_dataset_snapshot_arg {
+ nvlist_t *ddsa_snaps;
+ nvlist_t *ddsa_props;
+ nvlist_t *ddsa_errors;
+ cred_t *ddsa_cr;
+} dsl_dataset_snapshot_arg_t;
+
/*
* The max length of a temporary tag prefix is the number of hex digits
* required to express UINT64_MAX plus one for the hyphen.
@@ -296,6 +303,8 @@ uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname,
struct dsl_crypto_params *, dmu_tx_t *);
uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin,
struct dsl_crypto_params *dcp, uint64_t flags, dmu_tx_t *tx);
+void dsl_dataset_snapshot_sync(void *arg, dmu_tx_t *tx);
+int dsl_dataset_snapshot_check(void *arg, dmu_tx_t *tx);
int dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors);
void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx);
int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx);
diff --git a/include/sys/zcp.h b/include/sys/zcp.h
index ca9719ecf..df13bf230 100644
--- a/include/sys/zcp.h
+++ b/include/sys/zcp.h
@@ -14,7 +14,7 @@
*/
/*
- * Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright (c) 2016, 2017 by Delphix. All rights reserved.
*/
#ifndef _SYS_ZCP_H
@@ -137,8 +137,6 @@ typedef struct zcp_lib_info {
const zcp_arg_t kwargs[2];
} zcp_lib_info_t;
-int zcp_nvlist_to_lua(lua_State *, nvlist_t *, char *, int);
-
#ifdef __cplusplus
}
#endif
diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c
index 2fa035e67..ca0b3cfb5 100644
--- a/lib/libzfs_core/libzfs_core.c
+++ b/lib/libzfs_core/libzfs_core.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2017 Datto Inc.
* Copyright 2017 RackTop Systems.
@@ -146,7 +146,12 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
if (resultp != NULL) {
*resultp = NULL;
- zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
+ if (ioc == ZFS_IOC_CHANNEL_PROGRAM) {
+ zc.zc_nvlist_dst_size = fnvlist_lookup_uint64(source,
+ ZCP_ARG_MEMLIMIT);
+ } else {
+ zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
+ }
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
malloc(zc.zc_nvlist_dst_size);
if (zc.zc_nvlist_dst == (uint64_t)0) {
@@ -160,7 +165,7 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
* If ioctl exited with ENOMEM, we retry the ioctl after
* increasing the size of the destination nvlist.
*
- * Channel programs that exit with ENOMEM probably ran over the
+ * Channel programs that exit with ENOMEM ran over the
* lua memory sandbox; they should not be retried.
*/
if (errno == ENOMEM && resultp != NULL &&
diff --git a/man/man8/zfs-program.8 b/man/man8/zfs-program.8
index 4c7d86ef1..b1d937b6e 100644
--- a/man/man8/zfs-program.8
+++ b/man/man8/zfs-program.8
@@ -372,6 +372,18 @@ filesystem (string)
.Bd -ragged -compact -offset "xxxx"
Filesystem to rollback.
.Ed
+.It Em zfs.sync.snapshot(dataset)
+Create a snapshot of a filesystem.
+Returns 0 if the snapshot was successfully created,
+and a nonzero error code otherwise.
+.Pp
+Note: Taking a snapshot will fail on any pool older than legacy version 27.
+To enable taking snapshots from ZCP scripts, the pool must be upgraded.
+.Pp
+dataset (string)
+.Bd -ragged -compact -offset "xxxx"
+Name of snapshot to create.
+.Ed
.El
.It Sy zfs.check submodule
For each function in the zfs.sync submodule, there is a corresponding zfs.check
@@ -392,6 +404,7 @@ The available zfs.check functions are:
.It Em zfs.check.destroy(dataset, [defer=true|false])
.It Em zfs.check.promote(dataset)
.It Em zfs.check.rollback(filesystem)
+.It Em zfs.check.snapshot(dataset)
.El
.It Sy zfs.list submodule
The zfs.list submodule provides functions for iterating over datasets and
diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c
index a35ba1f7e..0bfc4cd7a 100644
--- a/module/zfs/dsl_dataset.c
+++ b/module/zfs/dsl_dataset.c
@@ -1140,13 +1140,6 @@ dsl_dataset_snapshot_reserve_space(dsl_dataset_t *ds, dmu_tx_t *tx)
return (0);
}
-typedef struct dsl_dataset_snapshot_arg {
- nvlist_t *ddsa_snaps;
- nvlist_t *ddsa_props;
- nvlist_t *ddsa_errors;
- cred_t *ddsa_cr;
-} dsl_dataset_snapshot_arg_t;
-
int
dsl_dataset_snapshot_check_impl(dsl_dataset_t *ds, const char *snapname,
dmu_tx_t *tx, boolean_t recv, uint64_t cnt, cred_t *cr)
@@ -1206,7 +1199,7 @@ dsl_dataset_snapshot_check_impl(dsl_dataset_t *ds, const char *snapname,
return (0);
}
-static int
+int
dsl_dataset_snapshot_check(void *arg, dmu_tx_t *tx)
{
dsl_dataset_snapshot_arg_t *ddsa = arg;
@@ -1486,7 +1479,7 @@ dsl_dataset_snapshot_sync_impl(dsl_dataset_t *ds, const char *snapname,
spa_history_log_internal_ds(ds->ds_prev, "snapshot", tx, "");
}
-static void
+void
dsl_dataset_snapshot_sync(void *arg, dmu_tx_t *tx)
{
dsl_dataset_snapshot_arg_t *ddsa = arg;
diff --git a/module/zfs/zcp.c b/module/zfs/zcp.c
index 83560309b..1a39330d3 100644
--- a/module/zfs/zcp.c
+++ b/module/zfs/zcp.c
@@ -14,7 +14,7 @@
*/
/*
- * Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright (c) 2016, 2017 by Delphix. All rights reserved.
*/
/*
@@ -106,10 +106,15 @@
#define KM_NORMALPRI 0
#endif
+#define ZCP_NVLIST_MAX_DEPTH 20
+
uint64_t zfs_lua_check_instrlimit_interval = 100;
uint64_t zfs_lua_max_instrlimit = ZCP_MAX_INSTRLIMIT;
uint64_t zfs_lua_max_memlimit = ZCP_MAX_MEMLIMIT;
+/*
+ * Forward declarations for mutually recursive functions
+ */
static int zcp_nvpair_value_to_lua(lua_State *, nvpair_t *, char *, int);
static int zcp_lua_to_nvlist_impl(lua_State *, int, nvlist_t *, const char *,
int);
@@ -217,8 +222,6 @@ zcp_cleanup(lua_State *state)
}
}
-#define ZCP_NVLIST_MAX_DEPTH 20
-
/*
* Convert the lua table at the given index on the Lua stack to an nvlist
* and return it.
diff --git a/module/zfs/zcp_global.c b/module/zfs/zcp_global.c
index b6c3c3a4f..8e166e073 100644
--- a/module/zfs/zcp_global.c
+++ b/module/zfs/zcp_global.c
@@ -14,7 +14,7 @@
*/
/*
- * Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright (c) 2016, 2017 by Delphix. All rights reserved.
*/
#include <sys/zcp_global.h>
@@ -62,7 +62,12 @@ static const zcp_errno_global_t errno_globals[] = {
{"EPIPE", EPIPE},
{"EDOM", EDOM},
{"ERANGE", ERANGE},
+ {"EDEADLK", EDEADLK},
+ {"ENOLCK", ENOLCK},
+ {"ECANCELED", ECANCELED},
+ {"ENOTSUP", ENOTSUP},
{"EDQUOT", EDQUOT},
+ {"ENAMETOOLONG", ENAMETOOLONG},
{0, 0}
};
diff --git a/module/zfs/zcp_synctask.c b/module/zfs/zcp_synctask.c
index 93797e9f3..ad9bffacb 100644
--- a/module/zfs/zcp_synctask.c
+++ b/module/zfs/zcp_synctask.c
@@ -39,10 +39,10 @@ typedef int (zcp_synctask_func_t)(lua_State *, boolean_t, nvlist_t *);
typedef struct zcp_synctask_info {
const char *name;
zcp_synctask_func_t *func;
- zfs_space_check_t space_check;
- int blocks_modified;
const zcp_arg_t pargs[4];
const zcp_arg_t kwargs[2];
+ zfs_space_check_t space_check;
+ int blocks_modified;
} zcp_synctask_info_t;
/*
@@ -91,8 +91,6 @@ static int zcp_synctask_destroy(lua_State *, boolean_t, nvlist_t *);
static zcp_synctask_info_t zcp_synctask_destroy_info = {
.name = "destroy",
.func = zcp_synctask_destroy,
- .space_check = ZFS_SPACE_CHECK_NONE,
- .blocks_modified = 0,
.pargs = {
{.za_name = "filesystem | snapshot", .za_lua_type = LUA_TSTRING},
{NULL, 0}
@@ -100,7 +98,9 @@ static zcp_synctask_info_t zcp_synctask_destroy_info = {
.kwargs = {
{.za_name = "defer", .za_lua_type = LUA_TBOOLEAN},
{NULL, 0}
- }
+ },
+ .space_check = ZFS_SPACE_CHECK_NONE,
+ .blocks_modified = 0
};
/* ARGSUSED */
@@ -140,19 +140,19 @@ zcp_synctask_destroy(lua_State *state, boolean_t sync, nvlist_t *err_details)
return (err);
}
-static int zcp_synctask_promote(lua_State *, boolean_t, nvlist_t *err_details);
+static int zcp_synctask_promote(lua_State *, boolean_t, nvlist_t *);
static zcp_synctask_info_t zcp_synctask_promote_info = {
.name = "promote",
.func = zcp_synctask_promote,
- .space_check = ZFS_SPACE_CHECK_RESERVED,
- .blocks_modified = 3,
.pargs = {
{.za_name = "clone", .za_lua_type = LUA_TSTRING},
{NULL, 0}
},
.kwargs = {
{NULL, 0}
- }
+ },
+ .space_check = ZFS_SPACE_CHECK_RESERVED,
+ .blocks_modified = 3
};
static int
@@ -208,6 +208,58 @@ zcp_synctask_rollback(lua_State *state, boolean_t sync, nvlist_t *err_details)
return (err);
}
+static int zcp_synctask_snapshot(lua_State *, boolean_t, nvlist_t *);
+static zcp_synctask_info_t zcp_synctask_snapshot_info = {
+ .name = "snapshot",
+ .func = zcp_synctask_snapshot,
+ .pargs = {
+ {.za_name = "filesystem@snapname | volume@snapname",
+ .za_lua_type = LUA_TSTRING},
+ {NULL, 0}
+ },
+ .kwargs = {
+ {NULL, 0}
+ },
+ .space_check = ZFS_SPACE_CHECK_NORMAL,
+ .blocks_modified = 3
+};
+
+/* ARGSUSED */
+static int
+zcp_synctask_snapshot(lua_State *state, boolean_t sync, nvlist_t *err_details)
+{
+ int err;
+ dsl_dataset_snapshot_arg_t ddsa = { 0 };
+ const char *dsname = lua_tostring(state, 1);
+ zcp_run_info_t *ri = zcp_run_info(state);
+
+ /*
+ * We only allow for a single snapshot rather than a list, so the
+ * error list output is unnecessary.
+ */
+ ddsa.ddsa_errors = NULL;
+ ddsa.ddsa_props = NULL;
+ ddsa.ddsa_cr = ri->zri_cred;
+ ddsa.ddsa_snaps = fnvlist_alloc();
+ fnvlist_add_boolean(ddsa.ddsa_snaps, dsname);
+
+ /*
+ * On old pools, the ZIL must not be active when a snapshot is created,
+ * but we can't suspend the ZIL because we're already in syncing
+ * context.
+ */
+ if (spa_version(ri->zri_pool->dp_spa) < SPA_VERSION_FAST_SNAP) {
+ return (ENOTSUP);
+ }
+
+ err = zcp_sync_task(state, dsl_dataset_snapshot_check,
+ dsl_dataset_snapshot_sync, &ddsa, sync, dsname);
+
+ fnvlist_free(ddsa.ddsa_snaps);
+
+ return (err);
+}
+
void
zcp_synctask_wrapper_cleanup(void *arg)
{
@@ -279,6 +331,7 @@ zcp_load_synctask_lib(lua_State *state, boolean_t sync)
&zcp_synctask_destroy_info,
&zcp_synctask_promote_info,
&zcp_synctask_rollback_info,
+ &zcp_synctask_snapshot_info,
NULL
};
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index a8f37fe84..8b0cbb40b 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -3695,7 +3695,7 @@ zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl,
if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit)
return (EINVAL);
- if (memlimit == 0 || memlimit > ZCP_MAX_MEMLIMIT)
+ if (memlimit == 0 || memlimit > zfs_lua_max_memlimit)
return (EINVAL);
return (zcp_eval(poolname, program, instrlimit, memlimit,
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 0320301d2..a844d6b1b 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -67,8 +67,9 @@ tests = ['tst.args_to_lua', 'tst.divide_by_zero', 'tst.exists',
'tst.integer_illegal', 'tst.integer_overflow', 'tst.language_functions_neg',
'tst.language_functions_pos', 'tst.large_prog', 'tst.memory_limit',
'tst.nested_neg', 'tst.nested_pos', 'tst.nvlist_to_lua',
- 'tst.recursive_neg', 'tst.recursive_pos', 'tst.return_nvlist_neg',
- 'tst.return_nvlist_pos', 'tst.return_recursive_table', 'tst.timeout']
+ 'tst.recursive_neg', 'tst.recursive_pos', 'tst.return_large',
+ 'tst.return_nvlist_neg', 'tst.return_nvlist_pos',
+ 'tst.return_recursive_table', 'tst.timeout']
tags = ['functional', 'channel_program', 'lua_core']
[tests/functional/channel_program/synctask_core]
@@ -78,7 +79,9 @@ tests = ['tst.destroy_fs', 'tst.destroy_snap', 'tst.get_count_and_limit',
'tst.get_userquota', 'tst.get_written', 'tst.list_children',
'tst.list_clones', 'tst.list_snapshots', 'tst.list_system_props',
'tst.parse_args_neg', 'tst.promote_conflict', 'tst.promote_multiple',
- 'tst.promote_simple', 'tst.rollback_mult', 'tst.rollback_one']
+ 'tst.promote_simple', 'tst.rollback_mult', 'tst.rollback_one',
+ 'tst.snapshot_destroy', 'tst.snapshot_neg', 'tst.snapshot_recursive',
+ 'tst.snapshot_simple']
tags = ['functional', 'channel_program', 'synctask_core']
[tests/functional/chattr]
diff --git a/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am b/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am
index d733acb0b..dba3da0f1 100644
--- a/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am
+++ b/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am
@@ -26,6 +26,8 @@ dist_pkgdata_SCRIPTS = \
tst.recursive_neg.ksh \
tst.recursive_pos.ksh \
tst.recursive.zcp \
+ tst.return_large.ksh \
+ tst.return_large.zcp \
tst.return_nvlist_neg.ksh \
tst.return_nvlist_pos.ksh \
tst.return_recursive_table.ksh \
diff --git a/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh b/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh
new file mode 100755
index 000000000..369ad846a
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh
@@ -0,0 +1,54 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Returning very large (up to the memory limit) lists should
+# function correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+
+function cleanup
+{
+ datasetexists $fs && log_must zfs destroy -R $fs
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+#
+# Actually checking in the ~500kb expected result of this program would be
+# awful, so we just make sure it was as long as we expected.
+#
+output_lines=$(log_must zfs program $TESTPOOL \
+ $ZCP_ROOT/lua_core/tst.return_large.zcp | wc -l)
+
+[[ $output_lines -lt 5000 ]] &&
+ log_fail "Expected return of full list but only got $output_lines lines"
+
+#
+# Make sure we fail if the return is over the memory limit
+#
+log_mustnot_program $TESTPOOL -m 10000 \
+ $ZCP_ROOT/lua_core/tst.return_large.zcp
+
+log_pass "Large return values work properly"
+
diff --git a/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp b/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp
new file mode 100644
index 000000000..0ea9f8930
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp
@@ -0,0 +1,24 @@
+--
+-- This file and its contents are supplied under the terms of the
+-- Common Development and Distribution License ("CDDL"), version 1.0.
+-- You may only use this file in accordance with the terms of version
+-- 1.0 of the CDDL.
+--
+-- A full copy of the text of the CDDL should have accompanied this
+-- source. A copy of the CDDL is also available via the Internet at
+-- http://www.illumos.org/license/CDDL.
+--
+
+--
+-- Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+--
+
+basestring = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ..
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+ret = {}
+for i=1,5000 do
+ table.insert(ret, basestring)
+end
+
+return ret
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am b/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am
index 29bd68eee..48b89b578 100644
--- a/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am
@@ -29,4 +29,12 @@ dist_pkgdata_SCRIPTS = \
tst.promote_multiple.ksh \
tst.promote_simple.ksh \
tst.rollback_mult.ksh \
- tst.rollback_one.ksh
+ tst.rollback_one.ksh \
+ tst.snapshot_destroy.ksh \
+ tst.snapshot_destroy.zcp \
+ tst.snapshot_neg.ksh \
+ tst.snapshot_neg.zcp \
+ tst.snapshot_recursive.ksh \
+ tst.snapshot_recursive.zcp \
+ tst.snapshot_simple.ksh \
+ tst.snapshot_simple.zcp
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh
new file mode 100755
index 000000000..98317db6e
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh
@@ -0,0 +1,39 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Creating and destroying snapshots in the same txg should work.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+
+function cleanup
+{
+ datasetexists $fs && log_must zfs destroy -R $fs
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must_program $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.snapshot_destroy.zcp $fs
+
+log_pass "Creating/destroying snapshots in one channel program works"
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp
new file mode 100644
index 000000000..6fbfb06ad
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp
@@ -0,0 +1,24 @@
+--
+-- This file and its contents are supplied under the terms of the
+-- Common Development and Distribution License ("CDDL"), version 1.0.
+-- You may only use this file in accordance with the terms of version
+-- 1.0 of the CDDL.
+--
+-- A full copy of the text of the CDDL should have accompanied this
+-- source. A copy of the CDDL is also available via the Internet at
+-- http://www.illumos.org/license/CDDL.
+--
+
+--
+-- Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+--
+
+args = ...
+argv = args["argv"]
+
+assert(zfs.sync.snapshot(argv[1] .. "@snap1") == 0)
+assert(zfs.sync.destroy(argv[1] .. "@snap1") == 0)
+
+for s in zfs.list.snapshots(argv[1]) do
+ assert(false)
+end
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
new file mode 100755
index 000000000..c3585c938
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
@@ -0,0 +1,44 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Check various invalid snapshot error cases
+#
+
+verify_runnable "global"
+
+fs1=$TESTPOOL/$TESTFS/testchild1
+fs2=$TESTPOOL/$TESTFS/testchild2
+
+function cleanup
+{
+ for fs in $fs1 $fs2; do
+ datasetexists $fs && log_must zfs destroy -R $fs
+ done
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs1
+log_must zfs create $fs2
+log_must zfs snapshot $fs1@snap1
+
+log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.snapshot_neg.zcp $fs1 $fs2
+
+log_pass "zfs.sync.snapshot returns correct errors on invalid input"
+
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp
new file mode 100644
index 000000000..5cae324bc
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp
@@ -0,0 +1,35 @@
+--
+-- This file and its contents are supplied under the terms of the
+-- Common Development and Distribution License ("CDDL"), version 1.0.
+-- You may only use this file in accordance with the terms of version
+-- 1.0 of the CDDL.
+--
+-- A full copy of the text of the CDDL should have accompanied this
+-- source. A copy of the CDDL is also available via the Internet at
+-- http://www.illumos.org/license/CDDL.
+--
+
+--
+-- Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+--
+
+args = ...
+argv = args["argv"]
+fs1 = argv[1]
+fs2 = argv[2]
+
+longstring = "a"
+for i=1,9 do
+ longstring = longstring .. longstring
+end
+
+-- invalid snapshot names
+assert(zfs.sync.snapshot("ceci_nest_pas_une_dataset") == EINVAL);
+assert(zfs.sync.snapshot(fs1) == EINVAL)
+assert(zfs.sync.snapshot(fs1 .. "@" .. longstring) == ENAMETOOLONG)
+
+assert(zfs.sync.snapshot(fs2 .. "@snap1") == 0)
+-- only one snapshot of a filesystem is allowed per TXG.
+assert(zfs.sync.snapshot(fs2 .. "@snap2") == EAGAIN)
+-- snapshot already exists
+assert(zfs.sync.snapshot(fs1 .. "@snap1") == EEXIST)
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh
new file mode 100755
index 000000000..c0180d90c
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh
@@ -0,0 +1,61 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Construct a set of nested filesystems, then recursively snapshot
+# all of them.
+#
+
+verify_runnable "global"
+
+rootfs=$TESTPOOL/$TESTFS/root
+snapname=snap
+
+function cleanup
+{
+ datasetexists $rootfs && log_must zfs destroy -R $rootfs
+}
+
+log_onexit cleanup
+
+filesystems="$rootfs \
+$rootfs/child1 \
+$rootfs/child1/subchild1 \
+$rootfs/child1/subchild2 \
+$rootfs/child1/subchild3 \
+$rootfs/child2 \
+$rootfs/child2/subchild4 \
+$rootfs/child2/subchild5"
+
+for fs in $filesystems; do
+ log_must zfs create $fs
+done
+
+log_must_program $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.snapshot_recursive.zcp $rootfs $snapname
+
+#
+# Make sure all of the snapshots we expect were created.
+#
+for fs in $filesystems; do
+ log_must snapexists $fs@$snapname
+done
+
+log_pass "Recursively snapshotting multiple filesystems works."
+
+
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp
new file mode 100644
index 000000000..097940d71
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp
@@ -0,0 +1,28 @@
+--
+-- This file and its contents are supplied under the terms of the
+-- Common Development and Distribution License ("CDDL"), version 1.0.
+-- You may only use this file in accordance with the terms of version
+-- 1.0 of the CDDL.
+--
+-- A full copy of the text of the CDDL should have accompanied this
+-- source. A copy of the CDDL is also available via the Internet at
+-- http://www.illumos.org/license/CDDL.
+--
+
+--
+-- Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+--
+
+args = ...
+argv = args["argv"]
+fs = argv[1]
+snap = argv[2]
+
+function snapshot_recursive(root)
+ assert(zfs.sync.snapshot(root .. "@" .. snap) == 0)
+ for child in zfs.list.children(root) do
+ snapshot_recursive(child)
+ end
+end
+
+snapshot_recursive(fs)
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh
new file mode 100755
index 000000000..e818ce9fc
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh
@@ -0,0 +1,40 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Make sure basic snapshot functionality works in channel programs
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+snapname=testsnap
+
+function cleanup
+{
+ datasetexists $fs && log_must zfs destroy -R $fs
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must_program $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.snapshot_simple.zcp $fs $snapname
+
+log_pass "Simple snapshotting works"
diff --git a/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp
new file mode 100644
index 000000000..215e013df
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp
@@ -0,0 +1,26 @@
+--
+-- This file and its contents are supplied under the terms of the
+-- Common Development and Distribution License ("CDDL"), version 1.0.
+-- You may only use this file in accordance with the terms of version
+-- 1.0 of the CDDL.
+--
+-- A full copy of the text of the CDDL should have accompanied this
+-- source. A copy of the CDDL is also available via the Internet at
+-- http://www.illumos.org/license/CDDL.
+--
+
+--
+-- Copyright (c) 2016, 2017 by Delphix. All rights reserved.
+--
+
+-- This program should be invoked as "zfs program <pool> <prog> <fs> <snap>"
+
+args = ...
+argv = args["argv"]
+assert(zfs.sync.snapshot(argv[1] .. "@" .. argv[2]) == 0)
+snaps = {}
+for s in zfs.list.snapshots(argv[1]) do
+ table.insert(snaps, s)
+end
+assert(#snaps == 1)
+assert(snaps[1] == (argv[1] .. "@" .. argv[2]))