aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program')
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/channel_common.kshlib230
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am46
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh19
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh21
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh30
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp25
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err4
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh30
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp16
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.ksh45
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.zcp26
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh41
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh32
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh52
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh42
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh30
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp280
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_base.lua469
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_coroutine.lua362
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_strings.lua241
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_table.lua252
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.libraries.ksh31
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh77
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh30
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp770
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh23
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp71
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh30
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp31
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh24
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh23
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh54
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp24
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh63
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh57
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh31
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp21
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.err18
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.ksh33
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.zcp20
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh55
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp22
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am53
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh22
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh25
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.ksh45
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.zcp32
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.ksh43
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.zcp26
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh45
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh44
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh89
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh41
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out4
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp77
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh88
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh43
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh52
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out5
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp101
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh45
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out5
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp73
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.ksh54
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh80
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh57
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.inherit.ksh39
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_bookmarks.ksh120
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh125
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh116
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_holds.ksh121
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh112
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.ksh54
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_user_props.ksh147
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh50
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh55
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp23
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh71
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh47
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh60
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh49
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.ksh39
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.zcp109
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh39
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp24
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh45
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp35
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh61
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp28
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh40
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp26
-rwxr-xr-xsys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.terminate_by_signal.ksh98
95 files changed, 6691 insertions, 0 deletions
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/Makefile.am
new file mode 100644
index 000000000000..3886863d1dfb
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = \
+ lua_core \
+ synctask_core
+
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/channel_program
+dist_pkgdata_DATA = channel_common.kshlib
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/channel_common.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/channel_common.kshlib
new file mode 100644
index 000000000000..a828ba29065e
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/channel_common.kshlib
@@ -0,0 +1,230 @@
+#
+# 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/include/libtest.shlib
+
+ZCP_ROOT=$STF_SUITE/tests/functional/channel_program
+
+#
+# Note: In case of failure (log_fail) in this function
+# we delete the file passed as <input file> so the
+# test suite doesn't leak temp files on failures. So it
+# is expected that <input file> is a temp file and not
+# an installed file.
+#
+# <exitcode> <expected error string> <input file> <zfs program args>
+# e.g. log_program 0 "" tmp.7a12V $POOL foo.zcp arg1 arg2
+function log_program
+{
+ typeset expectexit=$1
+ shift
+ typeset expecterror=$1
+ shift
+ typeset tmpin=$1
+ shift
+ typeset cmdargs=$@ tmpout=$(mktemp) tmperr=$(mktemp)
+
+ # Expected output/error filename is the same as the .zcp name
+ typeset basename
+ if [[ $2 != "-" ]]; then
+ basename=${2%.*}
+ fi
+
+ log_note "running: zfs program $cmdargs:"
+
+ zfs program $cmdargs >$tmpout 2>$tmperr
+ typeset ret=$?
+
+ log_note "input:\n$(cat $tmpin)"
+ log_note "output:\n$(cat $tmpout)"
+ log_note "error:\n$(cat $tmperr)"
+
+ #
+ # Verify correct return value
+ #
+ if [[ $ret -ne $expectexit ]]; then
+ rm $tmpout $tmperr $tmpin
+ log_fail "return mismatch: expected $expectexit, got $ret"
+ fi
+
+ #
+ # Check the output or reported error for successful or error returns,
+ # respectively.
+ #
+ if [[ -f "$basename.out" ]] && [[ $expectexit -eq 0 ]]; then
+
+ outdiff=$(diff "$basename.out" "$tmpout")
+ if [[ $? -ne 0 ]]; then
+ output=$(<$tmpout)
+ rm $tmpout $tmperr $tmpin
+ log_fail "Output mismatch. Expected:\n" \
+ "$(<$basename.out)\nBut got:\n$output\n" \
+ "Diff:\n$outdiff"
+ fi
+
+ elif [[ -f "$basename.err" ]] && [[ $expectexit -ne 0 ]]; then
+
+ outdiff=$(diff "$basename.err" "$tmperr")
+ if [[ $? -ne 0 ]]; then
+ outputerror=$(<$tmperr)
+ rm $tmpout $tmperr $tmpin
+ log_fail "Error mismatch. Expected:\n" \
+ "$(<$basename.err)\nBut got:\n$outputerror\n" \
+ "Diff:\n$outdiff"
+ fi
+
+ elif [[ -n $expecterror ]] && [[ $expectexit -ne 0 ]]; then
+
+ grep -q "$expecterror" $tmperr
+ if [[ $? -ne 0 ]]; then
+ outputerror=$(<$tmperr)
+ rm $tmpout $tmperr $tmpin
+ log_fail "Error mismatch. Expected to contain:\n" \
+ "$expecterror\nBut got:\n$outputerror\n"
+ fi
+
+ elif [[ $expectexit -ne 0 ]]; then
+ #
+ # If there's no expected output, error reporting is allowed to
+ # vary, but ensure that we didn't fail silently.
+ #
+ if [[ -z "$(<$tmperr)" ]]; then
+ rm $tmpout $tmperr $tmpin
+ log_fail "error with no stderr output"
+ fi
+ fi
+
+ #
+ # Clean up all temp files except $tmpin which is
+ # reused for the second invocation of log_program.
+ #
+ rm $tmpout $tmperr
+}
+
+#
+# Even though the command's arguments are passed correctly
+# to the log_must_program family of wrappers the majority
+# of the time, zcp scripts passed as HERE documents can
+# make things trickier (see comment within the function
+# below) in the ordering of the commands arguments and how
+# they are passed. Thus, with this function we reconstruct
+# them to ensure that they are passed properly.
+#
+function log_program_construct_args
+{
+ typeset tmpin=$1
+ shift
+
+ args=""
+ i=0
+ while getopts "nt:m:" opt; do
+ case $opt in
+ t) args="$args -t $OPTARG"; i=$(($i + 2)) ;;
+ m) args="$args -m $OPTARG"; i=$(($i + 2)) ;;
+ n) args="$args -n"; i=$(($i + 1)) ;;
+ esac
+ done
+ shift $i
+
+ pool=$1
+ shift
+
+ infile=$1
+ shift
+
+ #
+ # Copy the contents of the original channel program to $tmpin.
+ #
+ # If $infile currently holds "-" (a dash) it means that we consume a
+ # HERE doc from stdin, otherwise $infile is a file path.
+ #
+ cat $infile > $tmpin
+
+ lua_args=$@
+
+ echo "$args $pool $tmpin $lua_args"
+}
+
+#
+# Program should complete successfully
+# when run in either context.
+#
+function log_must_program
+{
+ typeset tmpin=$(mktemp)
+
+ program_args=$(log_program_construct_args $tmpin $@)
+
+ log_program 0 "" $tmpin "-n $program_args"
+ log_program 0 "" $tmpin "$program_args"
+
+ rm $tmpin
+}
+#
+# Program should error as expected in
+# the same way in both contexts.
+#
+function log_mustnot_checkerror_program
+{
+ typeset expecterror=$1
+ shift
+ typeset tmpin=$(mktemp)
+
+ program_args=$(log_program_construct_args $tmpin $@)
+
+ log_program 1 "$expecterror" $tmpin "-n $program_args"
+ log_program 1 "$expecterror" $tmpin "$program_args"
+
+ rm $tmpin
+}
+
+#
+# Program should fail when run in either
+# context.
+#
+function log_mustnot_program
+{
+ log_mustnot_checkerror_program "" $@
+}
+
+
+#
+# Program should error as expected in
+# open context but complete successfully
+# in syncing context.
+#
+function log_mustnot_checkerror_program_open
+{
+ typeset expecterror=$1
+ shift
+ typeset tmpin=$(mktemp)
+
+ program_args=$(log_program_construct_args $tmpin $@)
+
+ log_program 1 "$expecterror" $tmpin "-n $program_args"
+ log_program 0 "" $tmpin "$program_args"
+
+ rm $tmpin
+}
+
+#
+# Program should complete successfully
+# when run in syncing context but fail
+# when attempted to run in open context.
+#
+function log_must_program_sync
+{
+ log_mustnot_checkerror_program_open "requires passing sync=TRUE" $@
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am
new file mode 100644
index 000000000000..fb352081190c
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/Makefile.am
@@ -0,0 +1,46 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/channel_program/lua_core
+dist_pkgdata_SCRIPTS = \
+ cleanup.ksh \
+ setup.ksh \
+ tst.args_to_lua.ksh \
+ tst.divide_by_zero.ksh \
+ tst.exists.ksh \
+ tst.integer_illegal.ksh \
+ tst.integer_overflow.ksh \
+ tst.language_functions_neg.ksh \
+ tst.language_functions_pos.ksh \
+ tst.large_prog.ksh \
+ tst.libraries.ksh \
+ tst.memory_limit.ksh \
+ tst.nested_neg.ksh \
+ tst.nested_pos.ksh \
+ tst.nvlist_to_lua.ksh \
+ tst.recursive_neg.ksh \
+ tst.recursive_pos.ksh \
+ tst.return_large.ksh \
+ tst.return_nvlist_neg.ksh \
+ tst.return_nvlist_pos.ksh \
+ tst.return_recursive_table.ksh \
+ tst.stack_gsub.ksh \
+ tst.timeout.ksh
+
+dist_pkgdata_DATA = \
+ tst.args_to_lua.out \
+ tst.args_to_lua.zcp \
+ tst.divide_by_zero.err \
+ tst.divide_by_zero.zcp \
+ tst.exists.zcp \
+ tst.large_prog.out \
+ tst.large_prog.zcp \
+ tst.lib_base.lua \
+ tst.lib_coroutine.lua \
+ tst.lib_strings.lua \
+ tst.lib_table.lua \
+ tst.nested_neg.zcp \
+ tst.nested_pos.zcp \
+ tst.recursive.zcp \
+ tst.return_large.zcp \
+ tst.return_recursive_table.zcp \
+ tst.stack_gsub.err \
+ tst.stack_gsub.zcp \
+ tst.timeout.zcp
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh
new file mode 100755
index 000000000000..281f639a4276
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/cleanup.ksh
@@ -0,0 +1,19 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh
new file mode 100755
index 000000000000..2516b6b8ad9e
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/setup.ksh
@@ -0,0 +1,21 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+DISK=${DISKS%% *}
+
+default_setup ${DISK}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh
new file mode 100755
index 000000000000..6ec5610639de
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.ksh
@@ -0,0 +1,30 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Passing arguments to lua programs should work correctly.
+#
+
+verify_runnable "global"
+
+log_assert "Passing arguments to lua programs should work correctly."
+
+log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.args_to_lua.zcp foo bar
+
+log_pass "Passing arguments to lua programs should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out
new file mode 100644
index 000000000000..84191f8f803c
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.out
@@ -0,0 +1 @@
+Channel program fully executed with no return value.
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp
new file mode 100644
index 000000000000..f6f14e5222f9
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.args_to_lua.zcp
@@ -0,0 +1,25 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- This program should be invoked as "zfs program <pool> <prog> foo bar"
+
+arg = ...
+argv = arg["argv"]
+
+assert(#argv == 2)
+assert(argv[1] == "foo")
+assert(argv[2] == "bar")
+
+return
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err
new file mode 100644
index 000000000000..fd67d09f7309
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.err
@@ -0,0 +1,4 @@
+Channel program execution failed:
+[string "channel program"]:16: attempt to divide by zero
+stack traceback:
+ [string "channel program"]:16: in main chunk
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh
new file mode 100755
index 000000000000..2587c594da0d
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.ksh
@@ -0,0 +1,30 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Divide by zero should fail gracefully.
+#
+
+verify_runnable "global"
+
+log_assert "Divide by zero should fail gracefully."
+
+log_mustnot_program $TESTPOOL $ZCP_ROOT/lua_core/tst.divide_by_zero.zcp
+
+log_pass "Divide by zero should fail gracefully."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp
new file mode 100644
index 000000000000..bd882d493b63
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.divide_by_zero.zcp
@@ -0,0 +1,16 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+return 1 / 0
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.ksh
new file mode 100755
index 000000000000..d486c25f4487
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.ksh
@@ -0,0 +1,45 @@
+#!/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) 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# zfs.exists should accurately report whether a dataset exists, and
+# report an error if a dataset is in another pool.
+
+verify_runnable "global"
+
+# create $TESTSNAP and $TESTCLONE
+create_snapshot
+create_clone
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \
+ log_must zfs destroy -R $TESTPOOL/$TESTFS@$TESTSNAP
+}
+
+log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.exists.zcp \
+ $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTFS@$TESTSNAP \
+ $TESTPOOL/$TESTCLONE
+
+log_mustnot_checkerror_program "not in the target pool" \
+ $TESTPOOL - <<-EOF
+ return zfs.exists('rpool')
+EOF
+
+log_pass "zfs.exists() gives correct results"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.zcp
new file mode 100644
index 000000000000..e44cf4560502
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.exists.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) 2017 by Delphix. All rights reserved.
+--
+
+-- ensure zfs.exists works as expected.
+
+args = ...
+argv = args['argv']
+pool = argv[1]
+
+for i = 1,4 do
+ assert(zfs.exists(argv[i]))
+end
+
+assert(not zfs.exists(pool .. '/notadataset'))
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh
new file mode 100755
index 000000000000..c34f2afd9e18
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_illegal.ksh
@@ -0,0 +1,41 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Constructing integers that are doubles, too large, or too
+# small should fail gracefully.
+#
+
+verify_runnable "global"
+
+log_assert "constructing illegal integer values should fail gracefully"
+
+set -A args "1.0" \
+ "1.5" \
+ "-1.5"
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot_checkerror_program "malformed number" $TESTPOOL - <<-EOF
+ return ${args[i]}
+ EOF
+ ((i = i + 1))
+done
+
+log_pass "constructing illegal integer values should fail gracefully"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh
new file mode 100755
index 000000000000..c129bae51b04
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.integer_overflow.ksh
@@ -0,0 +1,32 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Overflowing a 64-bit integer should wrap around.
+#
+
+verify_runnable "global"
+
+log_assert "overflowing a 64-bit integer should wrap around"
+
+log_must_program $TESTPOOL - <<-EOF
+ assert(18446744073709551615 + 1 == (-18446744073709551616))
+EOF
+
+log_pass "overflowing a 64-bit integer should wrap around"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh
new file mode 100755
index 000000000000..0125d76c7036
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_neg.ksh
@@ -0,0 +1,52 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Try channel programs with various lua runtime issues.
+# the program should fail, but the system should not crash.
+# Issues include:
+# * syntax errors
+# * misuse of language constructs (e.g. indexing non-tables)
+# * the error() function
+# * the assert() function
+#
+
+verify_runnable "global"
+
+set -A args "{]" \
+ "retrn 1" \
+ "abc = nil; abc.deref" \
+ "abc = nil; abc()" \
+ "error(0)" \
+ "error(\"string\")" \
+ "error(true)" \
+ "error({})" \
+ "assert(false)"
+
+log_assert "Runtime errors in lua scripts fail as expected."
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot_checkerror_program "execution failed" $TESTPOOL - <<-EOF
+ ${args[i]}
+ EOF
+ ((i = i + 1))
+done
+
+log_pass "Runtime errors in lua scripts fail as expected."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh
new file mode 100755
index 000000000000..924d8e2c6944
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.language_functions_pos.ksh
@@ -0,0 +1,42 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Try very simple programs that interact with the core lua
+# runtime rather than ZFS functions, just to make sure the
+# runtime is hooked up correctly.
+#
+
+verify_runnable "global"
+
+set -A args "" \
+ "assert(true)" \
+ "x = 1 + 1"
+
+log_assert "Simple lua scripts pass."
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_must_program $TESTPOOL - <<-EOF
+ ${args[i]}
+ EOF
+ ((i = i + 1))
+done
+
+log_pass "Simple lua scripts pass."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh
new file mode 100755
index 000000000000..21ab69adb1c7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.ksh
@@ -0,0 +1,30 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Running a large program file should work correctly.
+#
+
+verify_runnable "global"
+
+log_assert "Running a large program file should work correctly."
+
+log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.large_prog.zcp
+
+log_pass "Running a large program file should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out
new file mode 100644
index 000000000000..84191f8f803c
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.out
@@ -0,0 +1 @@
+Channel program fully executed with no return value.
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp
new file mode 100644
index 000000000000..add68973349e
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.large_prog.zcp
@@ -0,0 +1,280 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- /sbin/zfs initially allocates 1KB for the channel program file. This
+-- program file is larger than that, to test the reallocation and
+-- passing of a large channel string to the kernel.
+-- This file is at least 16KB long.
+
+--[[
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+1234567890123456789012345678901234567890123456789012345678901234
+]]
+
+return
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_base.lua b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_base.lua
new file mode 100644
index 000000000000..c39144959a3f
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_base.lua
@@ -0,0 +1,469 @@
+--[[
+--*****************************************************************************
+--* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
+--*
+--* Permission is hereby granted, free of charge, to any person obtaining
+--* a copy of this software and associated documentation files (the
+--* "Software"), to deal in the Software without restriction, including
+--* without limitation the rights to use, copy, modify, merge, publish,
+--* distribute, sublicense, and/or sell copies of the Software, and to
+--* permit persons to whom the Software is furnished to do so, subject to
+--* the following conditions:
+--*
+--* The above copyright notice and this permission notice shall be
+--* included in all copies or substantial portions of the Software.
+--*
+--* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+--* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+--* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+--* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+--* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+--* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+--* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--*****************************************************************************
+--]]
+
+-- testing metatables
+
+X = 20; B = 30
+
+_ENV = setmetatable({}, {__index=_G})
+
+collectgarbage()
+
+X = X+10
+assert(X == 30 and _G.X == 20)
+B = false
+assert(B == false)
+B = nil
+assert(B == 30)
+
+assert(getmetatable{} == nil)
+assert(getmetatable(4) == nil)
+assert(getmetatable(nil) == nil)
+a={name = "NAME"}; setmetatable(a, {__metatable = "xuxu",
+ __tostring=function(x) return x.name end})
+assert(getmetatable(a) == "xuxu")
+assert(tostring(a) == "NAME")
+
+local a, t = {10,20,30; x="10", y="20"}, {}
+assert(setmetatable(a,t) == a)
+assert(getmetatable(a) == t)
+assert(setmetatable(a,nil) == a)
+assert(getmetatable(a) == nil)
+assert(setmetatable(a,t) == a)
+
+
+function f (t, i, e)
+ assert(not e)
+ local p = rawget(t, "parent")
+ return (p and p[i]+3), "dummy return"
+end
+
+t.__index = f
+
+a.parent = {z=25, x=12, [4] = 24}
+assert(a[1] == 10 and a.z == 28 and a[4] == 27 and a.x == "10")
+
+collectgarbage()
+
+a = setmetatable({}, t)
+function f(t, i, v) rawset(t, i, v-3) end
+setmetatable(t, t) -- causes a bug in 5.1 !
+t.__newindex = f
+a[1] = 30; a.x = "101"; a[5] = 200
+assert(a[1] == 27 and a.x == 98 and a[5] == 197)
+
+
+local c = {}
+a = setmetatable({}, t)
+t.__newindex = c
+a[1] = 10; a[2] = 20; a[3] = 90
+assert(c[1] == 10 and c[2] == 20 and c[3] == 90)
+
+
+do
+ local a;
+ a = setmetatable({}, {__index = setmetatable({},
+ {__index = setmetatable({},
+ {__index = function (_,n) return a[n-3]+4, "lixo" end})})})
+ a[0] = 20
+ for i=0,10 do
+ assert(a[i*3] == 20 + i*4)
+ end
+end
+
+
+do -- newindex
+ local foi
+ local a = {}
+ for i=1,10 do a[i] = 0; a['a'..i] = 0; end
+ setmetatable(a, {__newindex = function (t,k,v) foi=true; rawset(t,k,v) end})
+ foi = false; a[1]=0; assert(not foi)
+ foi = false; a['a1']=0; assert(not foi)
+ foi = false; a['a11']=0; assert(foi)
+ foi = false; a[11]=0; assert(foi)
+ foi = false; a[1]=nil; assert(not foi)
+ foi = false; a[1]=nil; assert(foi)
+end
+
+
+setmetatable(t, nil)
+function f (t, ...) return t, {...} end
+t.__call = f
+
+do
+ local x,y = a(table.unpack{'a', 1})
+ assert(x==a and y[1]=='a' and y[2]==1 and y[3]==nil)
+ x,y = a()
+ assert(x==a and y[1]==nil)
+end
+
+
+local b = setmetatable({}, t)
+setmetatable(b,t)
+
+function f(op)
+ return function (...) cap = {[0] = op, ...} ; return (...) end
+end
+t.__add = f("add")
+t.__sub = f("sub")
+t.__mul = f("mul")
+t.__div = f("div")
+t.__mod = f("mod")
+t.__unm = f("unm")
+t.__pow = f("pow")
+t.__len = f("len")
+
+assert(b+5 == b)
+assert(cap[0] == "add" and cap[1] == b and cap[2] == 5 and cap[3]==nil)
+assert(b+'5' == b)
+assert(cap[0] == "add" and cap[1] == b and cap[2] == '5' and cap[3]==nil)
+assert(5+b == 5)
+assert(cap[0] == "add" and cap[1] == 5 and cap[2] == b and cap[3]==nil)
+assert('5'+b == '5')
+assert(cap[0] == "add" and cap[1] == '5' and cap[2] == b and cap[3]==nil)
+b=b-3; assert(getmetatable(b) == t)
+assert(5-a == 5)
+assert(cap[0] == "sub" and cap[1] == 5 and cap[2] == a and cap[3]==nil)
+assert('5'-a == '5')
+assert(cap[0] == "sub" and cap[1] == '5' and cap[2] == a and cap[3]==nil)
+assert(a*a == a)
+assert(cap[0] == "mul" and cap[1] == a and cap[2] == a and cap[3]==nil)
+assert(a/0 == a)
+assert(cap[0] == "div" and cap[1] == a and cap[2] == 0 and cap[3]==nil)
+assert(a%2 == a)
+assert(cap[0] == "mod" and cap[1] == a and cap[2] == 2 and cap[3]==nil)
+assert(-a == a)
+assert(cap[0] == "unm" and cap[1] == a)
+assert(a^4 == a)
+assert(cap[0] == "pow" and cap[1] == a and cap[2] == 4 and cap[3]==nil)
+assert(a^'4' == a)
+assert(cap[0] == "pow" and cap[1] == a and cap[2] == '4' and cap[3]==nil)
+assert(4^a == 4)
+assert(cap[0] == "pow" and cap[1] == 4 and cap[2] == a and cap[3]==nil)
+assert('4'^a == '4')
+assert(cap[0] == "pow" and cap[1] == '4' and cap[2] == a and cap[3]==nil)
+assert(#a == a)
+assert(cap[0] == "len" and cap[1] == a)
+
+
+-- test for rawlen
+t = setmetatable({1,2,3}, {__len = function () return 10 end})
+assert(#t == 10 and rawlen(t) == 3)
+assert(rawlen"abc" == 3)
+assert(rawlen(string.rep('a', 1000)) == 1000)
+
+t = {}
+t.__lt = function (a,b,c)
+ collectgarbage()
+ assert(c == nil)
+ if type(a) == 'table' then a = a.x end
+ if type(b) == 'table' then b = b.x end
+ return a<b, "dummy"
+end
+
+function Op(x) return setmetatable({x=x}, t) end
+
+local function test ()
+ assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
+ assert(not(1 < Op(1)) and (Op(1) < 2) and not(2 < Op(1)))
+ assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
+ assert(not('a' < Op('a')) and (Op('a') < 'b') and not(Op('b') < Op('a')))
+ assert((Op(1)<=Op(1)) and (Op(1)<=Op(2)) and not(Op(2)<=Op(1)))
+ assert((Op('a')<=Op('a')) and (Op('a')<=Op('b')) and not(Op('b')<=Op('a')))
+ assert(not(Op(1)>Op(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1)))
+ assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a')))
+ assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1)))
+ assert((1 >= Op(1)) and not(1 >= Op(2)) and (Op(2) >= 1))
+ assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
+ assert(('a' >= Op('a')) and not(Op('a') >= 'b') and (Op('b') >= Op('a')))
+end
+
+test()
+
+t.__le = function (a,b,c)
+ assert(c == nil)
+ if type(a) == 'table' then a = a.x end
+ if type(b) == 'table' then b = b.x end
+ return a<=b, "dummy"
+end
+
+test() -- retest comparisons, now using both `lt' and `le'
+
+
+-- test `partial order'
+
+local function Set(x)
+ local y = {}
+ for _,k in pairs(x) do y[k] = 1 end
+ return setmetatable(y, t)
+end
+
+t.__lt = function (a,b)
+ for k in pairs(a) do
+ if not b[k] then return false end
+ b[k] = nil
+ end
+ return next(b) ~= nil
+end
+
+t.__le = nil
+
+assert(Set{1,2,3} < Set{1,2,3,4})
+assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
+assert((Set{1,2,3,4} <= Set{1,2,3,4}))
+assert((Set{1,2,3,4} >= Set{1,2,3,4}))
+assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-)
+
+t.__le = function (a,b)
+ for k in pairs(a) do
+ if not b[k] then return false end
+ end
+ return true
+end
+
+assert(not (Set{1,3} <= Set{3,5})) -- now its OK!
+assert(not(Set{1,3} <= Set{3,5}))
+assert(not(Set{1,3} >= Set{3,5}))
+
+t.__eq = function (a,b)
+ for k in pairs(a) do
+ if not b[k] then return false end
+ b[k] = nil
+ end
+ return next(b) == nil
+end
+
+local s = Set{1,3,5}
+assert(s == Set{3,5,1})
+assert(not rawequal(s, Set{3,5,1}))
+assert(rawequal(s, s))
+assert(Set{1,3,5,1} == Set{3,5,1})
+assert(Set{1,3,5} ~= Set{3,5,1,6})
+t[Set{1,3,5}] = 1
+assert(t[Set{1,3,5}] == nil) -- `__eq' is not valid for table accesses
+
+
+t.__concat = function (a,b,c)
+ assert(c == nil)
+ if type(a) == 'table' then a = a.val end
+ if type(b) == 'table' then b = b.val end
+ if A then return a..b
+ else
+ return setmetatable({val=a..b}, t)
+ end
+end
+
+c = {val="c"}; setmetatable(c, t)
+d = {val="d"}; setmetatable(d, t)
+
+A = true
+assert(c..d == 'cd')
+assert(0 .."a".."b"..c..d.."e".."f"..(5+3).."g" == "0abcdef8g")
+
+A = false
+assert((c..d..c..d).val == 'cdcd')
+x = c..d
+assert(getmetatable(x) == t and x.val == 'cd')
+x = 0 .."a".."b"..c..d.."e".."f".."g"
+assert(x.val == "0abcdefg")
+
+
+-- concat metamethod x numbers (bug in 5.1.1)
+c = {}
+local x
+setmetatable(c, {__concat = function (a,b)
+ assert(type(a) == "number" and b == c or type(b) == "number" and a == c)
+ return c
+end})
+assert(c..5 == c and 5 .. c == c)
+assert(4 .. c .. 5 == c and 4 .. 5 .. 6 .. 7 .. c == c)
+
+
+-- test comparison compatibilities
+local t1, t2, c, d
+t1 = {}; c = {}; setmetatable(c, t1)
+d = {}
+t1.__eq = function () return true end
+t1.__lt = function () return true end
+setmetatable(d, t1)
+assert(c == d and c < d and not(d <= c))
+t2 = {}
+t2.__eq = t1.__eq
+t2.__lt = t1.__lt
+setmetatable(d, t2)
+assert(c == d and c < d and not(d <= c))
+
+
+
+-- test for several levels of calls
+local i
+local tt = {
+ __call = function (t, ...)
+ i = i+1
+ if t.f then return t.f(...)
+ else return {...}
+ end
+ end
+}
+
+local a = setmetatable({}, tt)
+local b = setmetatable({f=a}, tt)
+local c = setmetatable({f=b}, tt)
+
+i = 0
+x = c(3,4,5)
+assert(i == 3 and x[1] == 3 and x[3] == 5)
+
+
+assert(_G.X == 20)
+
+
+local _g = _G
+_ENV = setmetatable({}, {__index=function (_,k) return _g[k] end})
+
+
+a = {}
+rawset(a, "x", 1, 2, 3)
+assert(a.x == 1 and rawget(a, "x", 3) == 1)
+
+
+-- bug in 5.1
+T, K, V = nil
+grandparent = {}
+grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end
+
+parent = {}
+parent.__newindex = parent
+setmetatable(parent, grandparent)
+
+child = setmetatable({}, parent)
+child.foo = 10 --> CRASH (on some machines)
+assert(T == parent and K == "foo" and V == 10)
+
+
+-- testing 'tonumber'
+assert(tonumber{} == nil)
+assert(tonumber('-012') == -010-2)
+assert(tonumber("0xffffffffffff") == 2^(4*12) - 1)
+assert(tonumber("0x"..string.rep("f", 150)) == 2^(4*150) - 1)
+
+-- testing 'tonumber' with base
+assert(tonumber(' 001010 ', 2) == 10)
+assert(tonumber(' 001010 ', 10) == 1010)
+assert(tonumber(' -1010 ', 2) == -10)
+assert(tonumber('10', 36) == 36)
+assert(tonumber(' -10 ', 36) == -36)
+assert(tonumber(' +1Z ', 36) == 36 + 35)
+assert(tonumber(' -1z ', 36) == -36 + -35)
+assert(tonumber('-fFfa', 16) == -(10+(16*(15+(16*(15+(16*15)))))))
+assert(tonumber(string.rep('1', 42), 2) + 1 == 2^42)
+assert(tonumber(string.rep('1', 34), 2) + 1 == 2^34)
+assert(tonumber('ffffFFFF', 16)+1 == 2^32)
+assert(tonumber('0ffffFFFF', 16)+1 == 2^32)
+assert(tonumber('-0ffffffFFFF', 16) - 1 == -2^40)
+for i = 2,36 do
+ assert(tonumber('\t10000000000\t', i) == i^10)
+end
+
+-- testing 'tonumber' for invalid formats
+function f(...)
+ if select('#', ...) == 1 then
+ return (...)
+ else
+ return "***"
+ end
+end
+
+assert(f(tonumber('fFfa', 15)) == nil)
+assert(f(tonumber('099', 8)) == nil)
+assert(f(tonumber('1\0', 2)) == nil)
+assert(f(tonumber('', 8)) == nil)
+assert(f(tonumber(' ', 9)) == nil)
+assert(f(tonumber('0xf', 10)) == nil)
+
+assert(f(tonumber('inf')) == nil)
+assert(f(tonumber(' INF ')) == nil)
+assert(f(tonumber('Nan')) == nil)
+assert(f(tonumber('nan')) == nil)
+
+assert(f(tonumber('')) == nil)
+assert(f(tonumber('1 a')) == nil)
+assert(f(tonumber('1\0')) == nil)
+assert(f(tonumber('1 \0')) == nil)
+assert(f(tonumber('1\0 ')) == nil)
+assert(f(tonumber('e1')) == nil)
+assert(f(tonumber('e 1')) == nil)
+
+
+-- testing 'tonumber' for invalid hexadecimal formats
+assert(tonumber('0x') == nil)
+assert(tonumber('x') == nil)
+assert(tonumber('x3') == nil)
+assert(tonumber('00x2') == nil)
+assert(tonumber('0x 2') == nil)
+assert(tonumber('0 x2') == nil)
+assert(tonumber('23x') == nil)
+assert(tonumber('- 0xaa') == nil)
+
+
+-- testing hexadecimal numerals
+assert(tonumber('+0x2') == 2)
+assert(tonumber('-0xaA') == -170)
+assert(tonumber('-0xffFFFfff') == -2^32 + 1)
+
+
+-- testing 'tostring'
+assert(tostring("alo") == "alo")
+assert(tostring(12) == "12")
+assert(tostring(1234567890123) == '1234567890123')
+assert(type(tostring("hello")) == "string")
+assert(tostring(true) == "true")
+assert(tostring(false) == "false")
+assert(string.find(tostring{}, 'table:'))
+assert(string.find(tostring(select), 'function:'))
+assert(#tostring('\0') == 1)
+
+
+-- testing ipairs
+local x = 0
+for k,v in ipairs{10,20,30;x=12} do
+ x = x + 1
+ assert(k == x and v == x * 10)
+end
+
+for _ in ipairs{x=12, y=24} do assert(nil) end
+
+-- test for 'false' x ipair
+x = false
+local i = 0
+for k,v in ipairs{true,false,true,false} do
+ i = i + 1
+ x = not x
+ assert(x == v)
+end
+assert(i == 4)
+
+
+return "OK"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_coroutine.lua b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_coroutine.lua
new file mode 100644
index 000000000000..e0e9e2a64fbc
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_coroutine.lua
@@ -0,0 +1,362 @@
+--[[
+--*****************************************************************************
+--* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
+--*
+--* Permission is hereby granted, free of charge, to any person obtaining
+--* a copy of this software and associated documentation files (the
+--* "Software"), to deal in the Software without restriction, including
+--* without limitation the rights to use, copy, modify, merge, publish,
+--* distribute, sublicense, and/or sell copies of the Software, and to
+--* permit persons to whom the Software is furnished to do so, subject to
+--* the following conditions:
+--*
+--* The above copyright notice and this permission notice shall be
+--* included in all copies or substantial portions of the Software.
+--*
+--* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+--* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+--* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+--* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+--* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+--* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+--* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--*****************************************************************************
+--]]
+
+local f
+
+local main, ismain = coroutine.running()
+assert(type(main) == "thread" and ismain)
+assert(not coroutine.resume(main))
+
+
+-- tests for multiple yield/resume arguments
+
+local function eqtab (t1, t2)
+ assert(#t1 == #t2)
+ for i = 1, #t1 do
+ local v = t1[i]
+ assert(t2[i] == v)
+ end
+end
+
+_G.x = nil -- declare x
+function foo (a, ...)
+ local x, y = coroutine.running()
+ assert(x == f and y == false)
+ -- next call should not corrupt coroutine (but must fail,
+ -- as it attempts to resume the running coroutine)
+ assert(coroutine.resume(f) == false)
+ assert(coroutine.status(f) == "running")
+ local arg = {...}
+ for i=1,#arg do
+ _G.x = {coroutine.yield(table.unpack(arg[i]))}
+ end
+ return table.unpack(a)
+end
+
+f = coroutine.create(foo)
+assert(type(f) == "thread" and coroutine.status(f) == "suspended")
+assert(string.find(tostring(f), "thread"))
+local s,a,b,c,d
+s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
+assert(s and a == nil and coroutine.status(f) == "suspended")
+s,a,b,c,d = coroutine.resume(f)
+eqtab(_G.x, {})
+assert(s and a == 1 and b == nil)
+s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
+eqtab(_G.x, {1, 2, 3})
+assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
+s,a,b,c,d = coroutine.resume(f, "xuxu")
+eqtab(_G.x, {"xuxu"})
+assert(s and a == 1 and b == 2 and c == 3 and d == nil)
+assert(coroutine.status(f) == "dead")
+s, a = coroutine.resume(f, "xuxu")
+assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
+
+
+-- yields in tail calls
+local function foo (i) return coroutine.yield(i) end
+f = coroutine.wrap(function ()
+ for i=1,10 do
+ assert(foo(i) == _G.x)
+ end
+ return 'a'
+end)
+for i=1,10 do _G.x = i; assert(f(i) == i) end
+_G.x = 'xuxu'; assert(f('xuxu') == 'a')
+
+-- recursive
+function pf (n, i)
+ coroutine.yield(n)
+ pf(n*i, i+1)
+end
+
+f = coroutine.wrap(pf)
+local s=1
+for i=1,10 do
+ assert(f(1, 1) == s)
+ s = s*i
+end
+
+-- sieve implemented with co-routines
+
+-- generate all the numbers from 2 to n
+function gen (n)
+ return coroutine.wrap(function ()
+ for i=2,n do coroutine.yield(i) end
+ end)
+end
+
+-- filter the numbers generated by 'g', removing multiples of 'p'
+function filter (p, g)
+ return coroutine.wrap(function ()
+ for n in g do
+ if n%p ~= 0 then coroutine.yield(n) end
+ end
+ end)
+end
+
+-- generate primes up to 20
+local x = gen(20)
+local a = {}
+while 1 do
+ local n = x()
+ if n == nil then break end
+ table.insert(a, n)
+ x = filter(n, x)
+end
+
+-- expect 8 primes and last one is 19
+assert(#a == 8 and a[#a] == 19)
+x, a = nil
+
+
+-- yielding across C boundaries
+
+co = coroutine.wrap(function()
+ coroutine.yield(20)
+ return 30
+ end)
+
+assert(co() == 20)
+assert(co() == 30)
+
+
+local f = function (s, i) return coroutine.yield(i) end
+function f (a, b) a = coroutine.yield(a); error{a + b} end
+function g(x) return x[1]*2 end
+
+
+-- unyieldable C call
+do
+ local function f (c)
+ return c .. c
+ end
+
+ local co = coroutine.wrap(function (c)
+ local s = string.gsub("a", ".", f)
+ return s
+ end)
+ assert(co() == "aa")
+end
+
+
+-- errors in coroutines
+function foo ()
+ coroutine.yield(3)
+ error(foo)
+end
+
+function goo() foo() end
+x = coroutine.wrap(goo)
+assert(x() == 3)
+x = coroutine.create(goo)
+a,b = coroutine.resume(x)
+assert(a and b == 3)
+a,b = coroutine.resume(x)
+assert(not a and b == foo and coroutine.status(x) == "dead")
+a,b = coroutine.resume(x)
+assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
+
+
+-- co-routines x for loop
+function all (a, n, k)
+ if k == 0 then coroutine.yield(a)
+ else
+ for i=1,n do
+ a[k] = i
+ all(a, n, k-1)
+ end
+ end
+end
+
+local a = 0
+for t in coroutine.wrap(function () all({}, 5, 4) end) do
+ a = a+1
+end
+assert(a == 5^4)
+
+
+-- access to locals of collected corroutines
+local C = {}; setmetatable(C, {__mode = "kv"})
+local x = coroutine.wrap (function ()
+ local a = 10
+ local function f () a = a+10; return a end
+ while true do
+ a = a+1
+ coroutine.yield(f)
+ end
+ end)
+
+C[1] = x;
+
+local f = x()
+assert(f() == 21 and x()() == 32 and x() == f)
+x = nil
+collectgarbage()
+assert(C[1] == nil)
+assert(f() == 43 and f() == 53)
+
+
+-- old bug: attempt to resume itself
+
+function co_func (current_co)
+ assert(coroutine.running() == current_co)
+ assert(coroutine.resume(current_co) == false)
+ coroutine.yield(10, 20)
+ assert(coroutine.resume(current_co) == false)
+ coroutine.yield(23)
+ return 10
+end
+
+local co = coroutine.create(co_func)
+local a,b,c = coroutine.resume(co, co)
+assert(a == true and b == 10 and c == 20)
+a,b = coroutine.resume(co, co)
+assert(a == true and b == 23)
+a,b = coroutine.resume(co, co)
+assert(a == true and b == 10)
+assert(coroutine.resume(co, co) == false)
+assert(coroutine.resume(co, co) == false)
+
+
+-- attempt to resume 'normal' coroutine
+local co1, co2
+co1 = coroutine.create(function () return co2() end)
+co2 = coroutine.wrap(function ()
+ assert(coroutine.status(co1) == 'normal')
+ assert(not coroutine.resume(co1))
+ coroutine.yield(3)
+ end)
+
+a,b = coroutine.resume(co1)
+assert(a and b == 3)
+assert(coroutine.status(co1) == 'dead')
+
+
+-- access to locals of erroneous coroutines
+local x = coroutine.create (function ()
+ local a = 10
+ _G.f = function () a=a+1; return a end
+ error('x')
+ end)
+
+assert(not coroutine.resume(x))
+-- overwrite previous position of local `a'
+assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
+assert(_G.f() == 11)
+assert(_G.f() == 12)
+
+
+-- leaving a pending coroutine open
+_X = coroutine.wrap(function ()
+ local a = 10
+ local x = function () a = a+1 end
+ coroutine.yield()
+ end)
+
+_X()
+
+assert(coroutine.running() == main)
+
+
+
+-- testing yields inside metamethods
+
+local mt = {
+ __eq = function(a,b) coroutine.yield(nil, "eq"); return a.x == b.x end,
+ __lt = function(a,b) coroutine.yield(nil, "lt"); return a.x < b.x end,
+ __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
+ __add = function(a,b) coroutine.yield(nil, "add"); return a.x + b.x end,
+ __sub = function(a,b) coroutine.yield(nil, "sub"); return a.x - b.x end,
+ __mod = function(a,b) coroutine.yield(nil, "mod"); return a.x % b.x end,
+ __unm = function(a,b) coroutine.yield(nil, "unm"); return -a.x end,
+
+ __concat = function(a,b)
+ coroutine.yield(nil, "concat");
+ a = type(a) == "table" and a.x or a
+ b = type(b) == "table" and b.x or b
+ return a .. b
+ end,
+ __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
+ __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
+}
+
+
+local function new (x)
+ return setmetatable({x = x, k = {}}, mt)
+end
+
+
+local a = new(10)
+local b = new(12)
+local c = new"hello"
+
+local function run (f, t)
+ local i = 1
+ local c = coroutine.wrap(f)
+ while true do
+ local res, stat = c()
+ if res then assert(t[i] == nil); return res, t end
+ assert(stat == t[i])
+ i = i + 1
+ end
+end
+
+
+assert(run(function () if (a>=b) then return '>=' else return '<' end end,
+ {"le", "sub"}) == "<")
+-- '<=' using '<'
+mt.__le = nil
+assert(run(function () if (a<=b) then return '<=' else return '>' end end,
+ {"lt"}) == "<=")
+assert(run(function () if (a==b) then return '==' else return '~=' end end,
+ {"eq"}) == "~=")
+
+assert(run(function () return a % b end, {"mod"}) == 10)
+
+assert(run(function () return a..b end, {"concat"}) == "1012")
+
+assert(run(function() return a .. b .. c .. a end,
+ {"concat", "concat", "concat"}) == "1012hello10")
+
+assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
+ {"concat", "concat", "concat"}) == "ab10chello12x")
+
+
+-- testing yields inside 'for' iterators
+
+local f = function (s, i)
+ if i%2 == 0 then coroutine.yield(nil, "for") end
+ if i < s then return i + 1 end
+ end
+
+assert(run(function ()
+ local s = 0
+ for i in f, 4, 0 do s = s + i end
+ return s
+ end, {"for", "for", "for"}) == 10)
+
+
+return "OK"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_strings.lua b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_strings.lua
new file mode 100644
index 000000000000..1725fd123796
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_strings.lua
@@ -0,0 +1,241 @@
+--[[
+--*****************************************************************************
+--* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
+--*
+--* Permission is hereby granted, free of charge, to any person obtaining
+--* a copy of this software and associated documentation files (the
+--* "Software"), to deal in the Software without restriction, including
+--* without limitation the rights to use, copy, modify, merge, publish,
+--* distribute, sublicense, and/or sell copies of the Software, and to
+--* permit persons to whom the Software is furnished to do so, subject to
+--* the following conditions:
+--*
+--* The above copyright notice and this permission notice shall be
+--* included in all copies or substantial portions of the Software.
+--*
+--* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+--* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+--* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+--* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+--* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+--* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+--* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--*****************************************************************************
+--]]
+
+-- testing string library
+
+local maxi, mini = 0x7fffffffffffffff, 0x8000000000000000
+
+-- testing string.sub
+assert(string.sub("123456789",2,4) == "234")
+assert(string.sub("123456789",7) == "789")
+assert(string.sub("123456789",7,6) == "")
+assert(string.sub("123456789",7,7) == "7")
+assert(string.sub("123456789",0,0) == "")
+assert(string.sub("123456789",-10,10) == "123456789")
+assert(string.sub("123456789",1,9) == "123456789")
+assert(string.sub("123456789",-10,-20) == "")
+assert(string.sub("123456789",-1) == "9")
+assert(string.sub("123456789",-4) == "6789")
+assert(string.sub("123456789",-6, -4) == "456")
+assert(string.sub("123456789", mini, -4) == "123456")
+assert(string.sub("123456789", mini, maxi) == "123456789")
+assert(string.sub("123456789", mini, mini) == "")
+assert(string.sub("\000123456789",3,5) == "234")
+assert(("\000123456789"):sub(8) == "789")
+
+-- testing string.find
+assert(string.find("123456789", "345") == 3)
+a,b = string.find("123456789", "345")
+assert(string.sub("123456789", a, b) == "345")
+assert(string.find("1234567890123456789", "345", 3) == 3)
+assert(string.find("1234567890123456789", "345", 4) == 13)
+assert(string.find("1234567890123456789", "346", 4) == nil)
+assert(string.find("1234567890123456789", ".45", -9) == 13)
+assert(string.find("abcdefg", "\0", 5, 1) == nil)
+assert(string.find("", "") == 1)
+assert(string.find("", "", 1) == 1)
+assert(not string.find("", "", 2))
+assert(string.find('', 'aaa', 1) == nil)
+assert(('alo(.)alo'):find('(.)', 1, 1) == 4)
+
+assert(string.len("") == 0)
+assert(string.len("\0\0\0") == 3)
+assert(string.len("1234567890") == 10)
+
+assert(#"" == 0)
+assert(#"\0\0\0" == 3)
+assert(#"1234567890" == 10)
+
+-- testing string.byte/string.char
+assert(string.byte("a") == 97)
+assert(string.byte("\xe4") > 127)
+assert(string.byte(string.char(255)) == 255)
+assert(string.byte(string.char(0)) == 0)
+assert(string.byte("\0") == 0)
+assert(string.byte("\0\0alo\0x", -1) == string.byte('x'))
+assert(string.byte("ba", 2) == 97)
+assert(string.byte("\n\n", 2, -1) == 10)
+assert(string.byte("\n\n", 2, 2) == 10)
+assert(string.byte("") == nil)
+assert(string.byte("hi", -3) == nil)
+assert(string.byte("hi", 3) == nil)
+assert(string.byte("hi", 9, 10) == nil)
+assert(string.byte("hi", 2, 1) == nil)
+assert(string.char() == "")
+assert(string.char(0, 255, 0) == "\0\255\0")
+assert(string.char(0, string.byte("\xe4"), 0) == "\0\xe4\0")
+assert(string.char(string.byte("\xe4l\0\195\179u", 1, -1)) == "\xe4l\0\195\179u")
+assert(string.char(string.byte("\xe4l\0\195\179u", 1, 0)) == "")
+assert(string.char(string.byte("\xe4l\0\195\179u", -10, 100)) == "\xe4l\0\195\179u")
+
+assert(string.upper("ab\0c") == "AB\0C")
+assert(string.lower("\0ABCc%$") == "\0abcc%$")
+assert(string.rep('teste', 0) == '')
+assert(string.rep('t\195\169s\00t\195\170', 2) == 't\195\169s\0t\195\170t\195\169s\000t\195\170')
+assert(string.rep('', 10) == '')
+
+-- repetitions with separator
+assert(string.rep('teste', 0, 'xuxu') == '')
+assert(string.rep('teste', 1, 'xuxu') == 'teste')
+assert(string.rep('\1\0\1', 2, '\0\0') == '\1\0\1\0\0\1\0\1')
+assert(string.rep('', 10, '.') == string.rep('.', 9))
+
+assert(string.reverse"" == "")
+assert(string.reverse"\0\1\2\3" == "\3\2\1\0")
+assert(string.reverse"\0001234" == "4321\0")
+
+for i=0,30 do assert(string.len(string.rep('a', i)) == i) end
+
+
+x = '"\195\174lo"\n\\'
+assert(string.format('%q%s', x, x) == '"\\"\195\174lo\\"\\\n\\\\""\195\174lo"\n\\')
+assert(string.format('%q', "\0") == [["\0"]])
+x = "\0\1\0023\5\0009"
+assert(string.format("\0%c\0%c%x\0", string.byte("\xe4"), string.byte("b"), 140) ==
+ "\0\xe4\0b8c\0")
+assert(string.format('') == "")
+assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) ==
+ string.format("%c%c%c%c", 34, 48, 90, 100))
+assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be')
+assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023")
+x = string.format('"%-50s"', 'a')
+assert(#x == 52)
+assert(string.sub(x, 1, 4) == '"a ')
+
+assert(string.format("-%.20s.20s", string.rep("%", 2000)) ==
+ "-"..string.rep("%", 20)..".20s")
+assert(string.format('"-%20s.20s"', string.rep("%", 2000)) ==
+ string.format("%q", "-"..string.rep("%", 2000)..".20s"))
+
+-- format x tostring
+assert(string.format("%s %s", nil, true) == "nil true")
+assert(string.format("%s %.4s", false, true) == "false true")
+assert(string.format("%.3s %.3s", false, true) == "fal tru")
+
+
+-- testing large numbers for format
+do
+ local max, min = 0x7fffffff, -0x80000000 -- "large" for 32 bits
+ assert(string.sub(string.format("%8x", -1), -8) == "ffffffff")
+ assert(string.format("%x", max) == "7fffffff")
+ assert(string.sub(string.format("%x", min), -8) == "80000000")
+ assert(string.format("%d", max) == "2147483647")
+ assert(string.format("%d", min) == "-2147483648")
+ assert(string.format("%u", 0xffffffff) == "4294967295")
+ assert(string.format("%o", 0xABCD) == "125715")
+
+ max, min = 0x7fffffffffffffff, -0x8000000000000000
+ assert(string.format("0x%8X", 0x8f000003) == "0x8F000003")
+ assert(string.format("%d", 2^53) == "9007199254740992")
+ assert(string.format("%x", max) == "7fffffffffffffff")
+ assert(string.format("%x", min) == "8000000000000000")
+ assert(string.format("%d", max) == "9223372036854775807")
+ assert(string.format("%d", min) == "-9223372036854775808")
+end
+
+
+assert(table.concat{} == "")
+assert(table.concat({}, 'x') == "")
+assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")
+local a = {}; for i=1,300 do a[i] = "xuxu" end
+assert(table.concat(a, "123").."123" == string.rep("xuxu123", 300))
+assert(table.concat(a, "b", 20, 20) == "xuxu")
+assert(table.concat(a, "", 20, 21) == "xuxuxuxu")
+assert(table.concat(a, "x", 22, 21) == "")
+assert(table.concat(a, "3", 299) == "xuxu3xuxu")
+assert(table.concat({}, "x", 2^31-1, 2^31-2) == "")
+assert(table.concat({}, "x", -2^31+1, -2^31) == "")
+assert(table.concat({}, "x", 2^31-1, -2^31) == "")
+assert(table.concat({[2^31-1] = "alo"}, "x", 2^31-1, 2^31-1) == "alo")
+
+a = {"a","b","c"}
+assert(table.concat(a, ",", 1, 0) == "")
+assert(table.concat(a, ",", 1, 1) == "a")
+assert(table.concat(a, ",", 1, 2) == "a,b")
+assert(table.concat(a, ",", 2) == "b,c")
+assert(table.concat(a, ",", 3) == "c")
+assert(table.concat(a, ",", 4) == "")
+
+
+-- tests for gmatch
+local a = 0
+for i in string.gmatch('abcde', '()') do assert(i == a+1); a=i end
+assert(a==6)
+
+t = {n=0}
+for w in string.gmatch("first second word", "%w+") do
+ t.n=t.n+1; t[t.n] = w
+end
+assert(t[1] == "first" and t[2] == "second" and t[3] == "word")
+
+t = {3, 6, 9}
+for i in string.gmatch ("xuxx uu ppar r", "()(.)%2") do
+ assert(i == table.remove(t, 1))
+end
+assert(#t == 0)
+
+t = {}
+for i,j in string.gmatch("13 14 10 = 11, 15= 16, 22=23", "(%d+)%s*=%s*(%d+)") do
+ t[i] = j
+end
+a = 0
+for k,v in pairs(t) do assert(k+1 == v+0); a=a+1 end
+assert(a == 3)
+
+
+-- tests for gsub
+function f1(s, p)
+ p = string.gsub(p, "%%([0-9])", function (s) return "%" .. (s+1) end)
+ p = string.gsub(p, "^(^?)", "%1()", 1)
+ p = string.gsub(p, "($?)$", "()%1", 1)
+ local t = {string.match(s, p)}
+ return string.sub(s, t[1], t[#t] - 1)
+end
+
+assert(f1('alo alx 123 b\0o b\0o', '(..*) %1') == "b\0o b\0o")
+assert(f1('axz123= 4= 4 34', '(.+)=(.*)=%2 %1') == '3= 4= 4 3')
+assert(f1('=======', '^(=*)=%1$') == '=======')
+
+-- gsub with tables
+assert(string.gsub("alo alo", ".", {}) == "alo alo")
+assert(string.gsub("alo alo", "(.)", {a="AA", l=""}) == "AAo AAo")
+assert(string.gsub("alo alo", "(.).", {a="AA", l="K"}) == "AAo AAo")
+assert(string.gsub("alo alo", "((.)(.?))", {al="AA", o=false}) == "AAo AAo")
+
+assert(string.gsub("alo alo", "().", {2,5,6}) == "256 alo")
+
+t = {}; setmetatable(t, {__index = function (t,s) return string.upper(s) end})
+assert(string.gsub("a alo b hi", "%w%w+", t) == "a ALO b HI")
+
+
+-- tests for match
+assert(string.match('==========', '^([=]*)=%1$') == nil)
+assert(string.match("alo xyzK", "(%w+)K") == "xyz")
+assert(string.match("254 K", "(%d*)K") == "")
+assert(string.match("alo ", "(%w*)$") == "")
+assert(string.match("alo ", "(%w+)$") == nil)
+assert(string.match("ab\0\1\2c", "[\0-\2]+") == "\0\1\2")
+
+return "OK"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_table.lua b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_table.lua
new file mode 100644
index 000000000000..500117b19d90
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.lib_table.lua
@@ -0,0 +1,252 @@
+--[[
+--*****************************************************************************
+--* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
+--*
+--* Permission is hereby granted, free of charge, to any person obtaining
+--* a copy of this software and associated documentation files (the
+--* "Software"), to deal in the Software without restriction, including
+--* without limitation the rights to use, copy, modify, merge, publish,
+--* distribute, sublicense, and/or sell copies of the Software, and to
+--* permit persons to whom the Software is furnished to do so, subject to
+--* the following conditions:
+--*
+--* The above copyright notice and this permission notice shall be
+--* included in all copies or substantial portions of the Software.
+--*
+--* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+--* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+--* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+--* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+--* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+--* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+--* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--*****************************************************************************
+--]]
+
+-- testing table library
+
+-- workaround missing pcall in zfs lua implementation
+local function tuple(...)
+ return {n=select('#', ...), ...}
+end
+
+function pcall(f, ...)
+ local co = coroutine.create(f)
+ local res = tuple(coroutine.resume(co, ...))
+ if res[1] and coroutine.status(co) == "suspended" then
+ res[1] = false
+ end
+ return table.unpack(res, 1, res.n)
+end
+
+
+-- workaround missing math lib in zfs lua implementation
+local A1, A2 = 727595, 798405 -- 5^17=D20*A1+A2
+local D20, D40 = 1048576, 1099511627776 -- 2^20, 2^40
+local X1, X2 = 0, 1
+function rand()
+ local U = X2*A2
+ local V = (X1*A2 + X2*A1) % D20
+ V = (V*D20 + U) % D40
+ X1 = V/D20
+ X2 = V - X1*D20
+ return V*100/D40
+end
+
+
+-- testing unpack
+
+local unpack = table.unpack
+
+local x,y,z,a,n
+a = {}; lim = 2000
+for i=1, lim do a[i]=i end
+assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim)
+x = unpack(a)
+assert(x == 1)
+x = {unpack(a)}
+assert(#x == lim and x[1] == 1 and x[lim] == lim)
+x = {unpack(a, lim-2)}
+assert(#x == 3 and x[1] == lim-2 and x[3] == lim)
+x = {unpack(a, 10, 6)}
+assert(next(x) == nil) -- no elements
+x = {unpack(a, 11, 10)}
+assert(next(x) == nil) -- no elements
+x,y = unpack(a, 10, 10)
+assert(x == 10 and y == nil)
+x,y,z = unpack(a, 10, 11)
+assert(x == 10 and y == 11 and z == nil)
+a,x = unpack{1}
+assert(a==1 and x==nil)
+a,x = unpack({1,2}, 1, 1)
+assert(a==1 and x==nil)
+
+if not _no32 then
+ assert(not pcall(unpack, {}, 0, 2^31-1))
+ assert(not pcall(unpack, {}, 1, 2^31-1))
+ assert(not pcall(unpack, {}, -(2^31), 2^31-1))
+ assert(not pcall(unpack, {}, -(2^31 - 1), 2^31-1))
+ assert(pcall(unpack, {}, 2^31-1, 0))
+ assert(pcall(unpack, {}, 2^31-1, 1))
+ pcall(unpack, {}, 1, 2^31)
+ a, b = unpack({[2^31-1] = 20}, 2^31-1, 2^31-1)
+ assert(a == 20 and b == nil)
+ a, b = unpack({[2^31-1] = 20}, 2^31-2, 2^31-1)
+ assert(a == nil and b == 20)
+end
+
+-- testing pack
+
+a = table.pack()
+assert(a[1] == nil and a.n == 0)
+
+a = table.pack(table)
+assert(a[1] == table and a.n == 1)
+
+a = table.pack(nil, nil, nil, nil)
+assert(a[1] == nil and a.n == 4)
+
+
+-- testing sort
+
+
+-- test checks for invalid order functions
+local function check (t)
+ local function f(a, b) assert(a and b); return true end
+ local s, e = pcall(table.sort, t, f)
+ assert(not s and e:find("invalid order function"))
+end
+
+check{1,2,3,4}
+check{1,2,3,4,5}
+check{1,2,3,4,5,6}
+
+
+function check (a, f)
+ f = f or function (x,y) return x<y end;
+ for n = #a, 2, -1 do
+ assert(not f(a[n], a[n-1]))
+ end
+end
+
+a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"}
+
+table.sort(a)
+check(a)
+
+function perm (s, n)
+ n = n or #s
+ if n == 1 then
+ local t = {unpack(s)}
+ table.sort(t)
+ check(t)
+ else
+ for i = 1, n do
+ s[i], s[n] = s[n], s[i]
+ perm(s, n - 1)
+ s[i], s[n] = s[n], s[i]
+ end
+ end
+end
+
+perm{}
+perm{1}
+perm{1,2}
+perm{1,2,3}
+perm{1,2,3,4}
+perm{2,2,3,4}
+perm{1,2,3,4,5}
+perm{1,2,3,3,5}
+perm{1,2,3,4,5,6}
+perm{2,2,3,3,5,6}
+
+limit = 5000
+
+a = {}
+for i=1,limit do
+ a[i] = rand()
+end
+
+table.sort(a)
+check(a)
+
+table.sort(a)
+check(a)
+
+a = {}
+for i=1,limit do
+ a[i] = rand()
+end
+
+i=0
+table.sort(a, function(x,y) i=i+1; return y<x end)
+check(a, function(x,y) return y<x end)
+
+
+table.sort{} -- empty array
+
+for i=1,limit do a[i] = false end
+table.sort(a, function(x,y) return nil end)
+check(a, function(x,y) return nil end)
+for i,v in pairs(a) do assert(not v or i=='n' and v==limit) end
+
+A = {"álo", "\0first :-)", "alo", "then this one", "45", "and a new"}
+table.sort(A)
+check(A)
+
+tt = {__lt = function (a,b) return a.val < b.val end}
+a = {}
+for i=1,10 do a[i] = {val=rand(100)}; setmetatable(a[i], tt); end
+table.sort(a)
+check(a, tt.__lt)
+check(a)
+
+
+-- test remove
+local function test (a)
+ table.insert(a, 10); table.insert(a, 2, 20);
+ table.insert(a, 1, -1); table.insert(a, 40);
+ table.insert(a, #a+1, 50)
+ table.insert(a, 2, -2)
+ assert(table.remove(a,1) == -1)
+ assert(table.remove(a,1) == -2)
+ assert(table.remove(a,1) == 10)
+ assert(table.remove(a,1) == 20)
+ assert(table.remove(a,1) == 40)
+ assert(table.remove(a,1) == 50)
+ assert(table.remove(a,1) == nil)
+end
+
+a = {n=0, [-7] = "ban"}
+test(a)
+assert(a.n == 0 and a[-7] == "ban")
+
+a = {[-7] = "ban"};
+test(a)
+assert(a.n == nil and #a == 0 and a[-7] == "ban")
+
+
+table.insert(a, 1, 10); table.insert(a, 1, 20); table.insert(a, 1, -1)
+assert(table.remove(a) == 10)
+assert(table.remove(a) == 20)
+assert(table.remove(a) == -1)
+
+a = {'c', 'd'}
+table.insert(a, 3, 'a')
+table.insert(a, 'b')
+assert(table.remove(a, 1) == 'c')
+assert(table.remove(a, 1) == 'd')
+assert(table.remove(a, 1) == 'a')
+assert(table.remove(a, 1) == 'b')
+assert(#a == 0 and a.n == nil)
+
+a = {10,20,30,40}
+assert(a[#a] == 40)
+assert(table.remove(a, #a) == 40)
+assert(a[#a] == 30)
+assert(table.remove(a, 2) == 20)
+assert(a[#a] == 30 and #a == 2)
+
+
+return "OK"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.libraries.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.libraries.ksh
new file mode 100755
index 000000000000..71afabdbe2d3
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.libraries.ksh
@@ -0,0 +1,31 @@
+#!/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) 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+verify_runnable "global"
+arch=$(uname -m)
+
+if [[ "$arch" == "sparc64" ]]; then
+ log_note "Skipping lib_base and lib_coroutine on sparc64 to avoid stack overflow"
+else
+ log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.lib_base.lua
+ log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.lib_coroutine.lua
+fi
+log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.lib_strings.lua
+log_must_program -m 40000000 $TESTPOOL $ZCP_ROOT/lua_core/tst.lib_table.lua
+
+log_pass "lua libraries work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh
new file mode 100755
index 000000000000..0533b8fa306f
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.memory_limit.ksh
@@ -0,0 +1,77 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+
+#
+# DESCRIPTION:
+# Passing memory limit options to channel programs should work correctly.
+# Programs that exceed these limits should fail gracefully.
+
+
+verify_runnable "global"
+
+log_mustnot_checkerror_program "Memory limit exhausted" \
+ -t 100000000 $TESTPOOL - <<-EOF
+ a = {};
+ i = 0;
+ while true do
+ i = i + 1
+ a[i] = "Here is the " .. i .. "th entry of a"
+ end;
+ return a
+EOF
+
+log_assert "memory limit options work"
+log_mustnot_checkerror_program "Memory limit exhausted" \
+ -m 100000 -t 100000000 $TESTPOOL - <<-EOF
+ a = {};
+ i = 0;
+ while true do
+ i = i + 1
+ a[i] = "Here is the " .. i .. "th entry of a"
+ end;
+ return a
+EOF
+
+log_must_program -m 100000 $TESTPOOL - <<-EOF
+ s = "teststring"
+ s = s .. s .. s .. s
+ return s
+EOF
+
+log_assert "very small memory limits fail correctly"
+log_mustnot_checkerror_program "Memory limit exhausted" -m 1 $TESTPOOL - <<-EOF
+ s = "teststring"
+ s = s .. s .. s .. s
+ return s
+EOF
+
+# Set the memlimit, in case it is a non-default value
+log_must set_tunable32 LUA_MAX_MEMLIMIT 100000000
+
+log_mustnot_checkerror_program "Invalid instruction or memory limit" \
+ -m 200000000 $TESTPOOL - <<-EOF
+ return 1;
+EOF
+
+log_mustnot_checkerror_program "Return value too large" \
+ -m 9223372036854775808 $TESTPOOL - <<-EOF
+ return 1;
+EOF
+
+log_pass "Memory limits work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh
new file mode 100755
index 000000000000..2be9150c34fa
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.ksh
@@ -0,0 +1,30 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+verify_runnable "global"
+
+arch=$(uname -m)
+
+if [[ "$arch" == "sparc64" ]]; then
+ log_unsupported "May cause stack overflow on sparc64 due to recursion"
+else
+ log_mustnot_checkerror_program "too many C levels" \
+ $TESTPOOL $ZCP_ROOT/lua_core/tst.nested_neg.zcp
+
+ log_pass "Too many nested lua statements fail cleanly."
+fi
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp
new file mode 100644
index 000000000000..afe3aa8f4f58
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_neg.zcp
@@ -0,0 +1,770 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- This program contains deeply nested LUA statements, such that the LUA
+-- parser can not process it, given the limited size of the C stack.
+-- This program contains 243 levels, well beyond the LUAI_MAXCCALLS limit
+
+i = 0
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+return i
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh
new file mode 100755
index 000000000000..0c8b8dd8d0ca
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.ksh
@@ -0,0 +1,23 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+verify_runnable "global"
+
+log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.nested_pos.zcp
+
+log_pass "Nested lua statements work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp
new file mode 100644
index 000000000000..02af95d99d18
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nested_pos.zcp
@@ -0,0 +1,71 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- This program should work with LUAI_MAXCCALLS=20
+
+i = 0
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+if true then
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+i = i + 1
+end
+
+return i
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh
new file mode 100755
index 000000000000..eaba6427c96c
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.nvlist_to_lua.ksh
@@ -0,0 +1,30 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# run C program which tests passing different nvlists to lua
+#
+
+verify_runnable "global"
+
+log_assert "nvlist arguments can be passed to LUA."
+
+log_must nvlist_to_lua $TESTPOOL
+
+log_pass "nvlist arguments can be passed to LUA."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp
new file mode 100644
index 000000000000..9a13c1e7dc29
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive.zcp
@@ -0,0 +1,31 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- Test recursion in LUA. Deep recursion should work because it does
+-- not use the C stack. The LUA stack is is allocated from the kernel's
+-- heap, up to the specified memory limit.
+
+arg = ...
+argv = arg["argv"]
+
+function f (x)
+ if (x == 0) then
+ return x
+ end
+ return f(x-1) + 1
+end
+
+r = f(argv[1])
+return r
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh
new file mode 100755
index 000000000000..c75048ae7d66
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_neg.ksh
@@ -0,0 +1,24 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+verify_runnable "global"
+
+log_mustnot_checkerror_program "Memory limit exhausted" \
+ $TESTPOOL $ZCP_ROOT/lua_core/tst.recursive.zcp 10000000
+
+log_pass "Nearly-infinite recursive LUA calls fail cleanly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh
new file mode 100755
index 000000000000..18d5d0f1ee47
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.recursive_pos.ksh
@@ -0,0 +1,23 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+verify_runnable "global"
+
+log_must_program $TESTPOOL $ZCP_ROOT/lua_core/tst.recursive.zcp 10000
+
+log_pass "Deeply recursive LUA calls succeed."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh
new file mode 100755
index 000000000000..ba9c40739471
--- /dev/null
+++ b/sys/contrib/openzfs/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 -m 10000 $TESTPOOL \
+ $ZCP_ROOT/lua_core/tst.return_large.zcp
+
+log_pass "Large return values work properly"
+
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.zcp
new file mode 100644
index 000000000000..0ea9f8930eac
--- /dev/null
+++ b/sys/contrib/openzfs/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/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh
new file mode 100755
index 000000000000..10afa6727847
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_neg.ksh
@@ -0,0 +1,63 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Try returning various values that lua allows you to construct,
+# but that cannot be represented as nvlists and therefore should
+# cause the script to fail (but not panic). Try sending the values
+# back to userland from both "return" and "error()".
+#
+
+verify_runnable "both"
+
+set -A args 'function() return 1 end' \
+ '{[{}]=true}' \
+ '{[function() return 1 end]=0}' \
+ 'assert' \
+ '0, assert' \
+ 'true, {[{}]=0}' \
+ '{val=true}, {val=false}' \
+ '{1, 2, 3}, {[4]=5}' \
+ 'nil, true, 1, "test", {}, {val=true}' \
+ '{[false]=true, ["false"]=false}' \
+ '{[true]=false, ["true"]=true}' \
+ '{[0]=true, ["0"]=false}' \
+ '{0,0,0,["1"]=0}' \
+ '{0,0,0,["2"]=0}' \
+ '{0,0,0,["3"]=0}'
+
+typeset -i last_index=$((${#args[*]} - 1))
+for i in $(seq 0 $last_index); do
+ log_note "running program: ${args[i]}"
+ log_mustnot_checkerror_program "execution failed" $TESTPOOL - <<-EOF
+ return ${args[i]}
+ EOF
+ ((i = i + 1))
+done
+
+for i in $(seq 0 $last_index); do
+ log_note "running program: ${args[i]}"
+ log_mustnot_checkerror_program "execution failed" $TESTPOOL - <<-EOF
+ error(${args[i]})
+ EOF
+ ((i = i + 1))
+done
+
+log_pass "Returning lua constructs that cannot be converted to " \
+ "nvlists fails as expected."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh
new file mode 100755
index 000000000000..8b4ef6e760b0
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_nvlist_pos.ksh
@@ -0,0 +1,57 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Try returning various lua values that should be converted
+# to nvlists. Also, try to pass them to error().
+#
+
+verify_runnable "global"
+
+set -A args "" \
+ "nil" \
+ "true" \
+ "1" \
+ "\"test\"" \
+ "{}" \
+ "{val=0}" \
+ "{{1, {2, 3}, {val1={val2=true}}}, {test=\"yes\"}}" \
+ "EINVAL"
+
+log_assert "Returning valid lua constructs works."
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_note "running program: return ${args[i]}"
+ log_must_program $TESTPOOL - <<-EOF
+ return ${args[i]}
+ EOF
+ ((i = i + 1))
+done
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_note "running program: error(${args[i]})"
+ log_mustnot_checkerror_program "in function 'error'" $TESTPOOL - <<-EOF
+ error(${args[i]})
+ EOF
+ ((i = i + 1))
+done
+
+log_pass "Returning valid lua constructs works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh
new file mode 100755
index 000000000000..18c035e0464b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.ksh
@@ -0,0 +1,31 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Returning a recursive table should fail gracefully
+#
+
+verify_runnable "both"
+
+log_assert "Returning a recursive table should fail gracefully."
+
+log_mustnot_checkerror_program "Maximum table depth" \
+ $TESTPOOL $ZCP_ROOT/lua_core/tst.return_recursive_table.zcp
+
+log_pass "Returning a recursive table should fail gracefully."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp
new file mode 100644
index 000000000000..21e6c60d8e26
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.return_recursive_table.zcp
@@ -0,0 +1,21 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- This program returns a self-referential data structure.
+
+a = {}
+a["key"] = "val"
+a["self"] = a
+return a
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.err b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.err
new file mode 100644
index 000000000000..45f2d9ef0ea6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.err
@@ -0,0 +1,18 @@
+Channel program execution failed:
+C stack overflow
+stack traceback:
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ [C]: in function 'gsub'
+ [string "channel program"]:17: in function <[string "channel program"]:16>
+ (...tail calls...) \ No newline at end of file
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.ksh
new file mode 100755
index 000000000000..ecabf3a3fec7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.ksh
@@ -0,0 +1,33 @@
+#!/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) 2020 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Overflowing the C stack using recursive gsub() should be handled
+# gracefully. gsub() uses more stack space than typical, so it relies
+# on LUAI_MINCSTACK to ensure that we don't overflow the Linux kernel's
+# stack.
+#
+
+verify_runnable "global"
+
+log_assert "recursive gsub() should be handled gracefully"
+
+log_mustnot_program $TESTPOOL $ZCP_ROOT/lua_core/tst.stack_gsub.zcp
+
+log_pass "recursive gsub() should be handled gracefully"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.zcp
new file mode 100644
index 000000000000..a493363ca68d
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.stack_gsub.zcp
@@ -0,0 +1,20 @@
+--
+-- 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) 2020 by Delphix. All rights reserved.
+--
+
+function f(s)
+ return string.gsub(s, ".", f)
+end
+
+return f("foo")
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh
new file mode 100755
index 000000000000..22ea37548173
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.ksh
@@ -0,0 +1,55 @@
+#!/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:
+# Passing the instruction limit option to channel programs should work
+# correctly. Programs that exceed these instruction limits should fail
+# gracefully.
+#
+
+verify_runnable "both"
+
+log_assert "Timeouts work correctly."
+
+log_mustnot_checkerror_program "Channel program timed out" \
+ $TESTPOOL $ZCP_ROOT/lua_core/tst.timeout.zcp
+
+function test_instr_limit
+{
+ typeset lim=$1
+
+ error=$(zfs program -t $lim $TESTPOOL $ZCP_ROOT/lua_core/tst.timeout.zcp 2>&1)
+ [[ $? -ne 0 ]] || log_fail "Channel program with limit $lim exited 0: $error"
+
+ instrs_run=$(echo $error | awk -F "chunk" '{print $2}' | awk '{print $1}')
+ if [[ $instrs_run -lt $(( $lim - 100 )) ]]; then
+ log_fail "Runtime (${instrs_run} instr) < limit (${lim} - 100 instr)"
+ elif [[ $instrs_run -gt $(( $lim + 100 )) ]]; then
+ log_fail "Runtime (${instrs_run} instr) > limit (${lim} + 100 instr)"
+ fi
+ log_note "With limit $lim the program ended after $instrs_run instructions"
+}
+
+test_instr_limit 1000
+test_instr_limit 10000
+test_instr_limit 100000
+test_instr_limit 1000000
+test_instr_limit 2000000
+
+log_pass "Timeouts work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp
new file mode 100644
index 000000000000..d74b1a73c2e5
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/lua_core/tst.timeout.zcp
@@ -0,0 +1,22 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- This program runs forever (until timed out).
+
+i = 1;
+while true do
+ i = i + 1
+end;
+return i
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am
new file mode 100644
index 000000000000..4d9aa9cebbfc
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/Makefile.am
@@ -0,0 +1,53 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/channel_program/synctask_core
+dist_pkgdata_SCRIPTS = \
+ cleanup.ksh \
+ setup.ksh \
+ tst.destroy_fs.ksh \
+ tst.destroy_snap.ksh \
+ tst.get_count_and_limit.ksh \
+ tst.get_index_props.ksh \
+ tst.get_mountpoint.ksh \
+ tst.get_neg.ksh \
+ tst.get_number_props.ksh \
+ tst.get_string_props.ksh \
+ tst.get_type.ksh \
+ tst.get_userquota.ksh \
+ tst.get_written.ksh \
+ tst.inherit.ksh \
+ tst.list_bookmarks.ksh \
+ tst.list_children.ksh \
+ tst.list_clones.ksh \
+ tst.list_holds.ksh \
+ tst.list_snapshots.ksh \
+ tst.list_system_props.ksh \
+ tst.list_user_props.ksh \
+ tst.parse_args_neg.ksh \
+ tst.promote_conflict.ksh \
+ tst.promote_multiple.ksh \
+ tst.promote_simple.ksh \
+ tst.rollback_mult.ksh \
+ tst.rollback_one.ksh \
+ tst.set_props.ksh \
+ tst.snapshot_destroy.ksh \
+ tst.snapshot_neg.ksh \
+ tst.snapshot_recursive.ksh \
+ tst.bookmark.create.ksh \
+ tst.bookmark.copy.ksh \
+ tst.snapshot_simple.ksh \
+ tst.terminate_by_signal.ksh
+
+dist_pkgdata_DATA = \
+ tst.get_index_props.out \
+ tst.get_index_props.zcp \
+ tst.get_number_props.out \
+ tst.get_number_props.zcp \
+ tst.get_string_props.out \
+ tst.get_string_props.zcp \
+ tst.promote_conflict.zcp \
+ tst.set_props.zcp \
+ tst.snapshot_destroy.zcp \
+ tst.snapshot_neg.zcp \
+ tst.snapshot_recursive.zcp \
+ tst.snapshot_simple.zcp \
+ tst.bookmark.create.zcp \
+ tst.bookmark.copy.zcp
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh
new file mode 100755
index 000000000000..3ddcb4d2759c
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/cleanup.ksh
@@ -0,0 +1,22 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup_noexit
+destroy_pool testpool2
+
+log_pass
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh
new file mode 100755
index 000000000000..5837bf1a14c7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/setup.ksh
@@ -0,0 +1,25 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+DISK=${DISKS%% *}
+
+TESTPOOLDISK=${DISKS%% *}
+TESTPOOL2DISK=${DISKS##* }
+
+default_setup ${TESTPOOLDISK}
+create_pool testpool2 ${TESTPOOL2DISK}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.ksh
new file mode 100755
index 000000000000..81f570d9e1e2
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.ksh
@@ -0,0 +1,45 @@
+#!/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) 2019, 2020 by Christian Schwarz. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Make sure bookmark copying works in channel programs
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+snapname=testsnap
+bookname=testbookmark
+bookcopyname=testbookmark_copy
+
+function cleanup
+{
+ destroy_dataset $fs "-R"
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must zfs snapshot $fs@$snapname
+log_must zfs bookmark $fs@$snapname "$fs#$bookname"
+
+log_must_program_sync $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.bookmark.copy.zcp $fs $bookname $bookcopyname
+
+log_pass "Simple bookmark copying works"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.zcp
new file mode 100644
index 000000000000..9473035f025b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.copy.zcp
@@ -0,0 +1,32 @@
+--
+-- 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) 2019, 2020 by Christian Schwarz. All rights reserved.
+--
+
+-- This program should be invoked as "zfs program <pool> <prog> <fs> <source_book> <new_book>"
+
+args = ...
+argv = args["argv"]
+fs = argv[1]
+source = fs .. "#" .. argv[2]
+new = fs .. "#" .. argv[3]
+assert(zfs.sync.bookmark(source, new) == 0)
+books = {}
+count = 0
+for s in zfs.list.bookmarks(fs) do
+ count = count + 1
+ books[s] = 1
+end
+assert(count == 2)
+assert(books[source] == 1)
+assert(books[new] == 1)
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.ksh
new file mode 100755
index 000000000000..05ec9cc67602
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.ksh
@@ -0,0 +1,43 @@
+#!/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) 2019, 2020 by Christian Schwarz. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Make sure basic bookmark functionality works in channel programs
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+snapname=testsnap
+bookname=testbookmark
+
+function cleanup
+{
+ destroy_dataset $fs "-R"
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must zfs snapshot $fs@$snapname
+
+log_must_program_sync $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.bookmark.create.zcp $fs $snapname $bookname
+
+log_pass "Simple bookmark creation works"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.zcp
new file mode 100644
index 000000000000..eb53fd16ce16
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.bookmark.create.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) 2019, 2020 by Christian Schwarz. All rights reserved.
+--
+
+-- This program should be invoked as "zfs program <pool> <prog> <fs> <snap> <book>"
+
+args = ...
+argv = args["argv"]
+assert(zfs.sync.bookmark(argv[1] .. "@" .. argv[2], argv[1] .. "#" .. argv[3]) == 0)
+books = {}
+for s in zfs.list.bookmarks(argv[1]) do
+ table.insert(books, s)
+end
+assert(#books == 1)
+assert(books[1] == (argv[1] .. "#" .. argv[3]))
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh
new file mode 100755
index 000000000000..45a3fdb66a6f
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh
@@ -0,0 +1,45 @@
+#!/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
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+
+function cleanup
+{
+ destroy_dataset $fs
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+log_must zfs unmount $fs
+
+log_must datasetexists $fs
+
+log_must_program_sync $TESTPOOL - $fs <<-EOF
+ arg = ...
+ fs = arg["argv"][1]
+ err = zfs.sync.destroy(fs)
+ msg = "destroying " .. fs .. " err=" .. err
+ return msg
+EOF
+
+log_mustnot datasetexists $fs
+
+log_pass "Destroying filesystem with channel program works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh
new file mode 100755
index 000000000000..571d7f87c1b5
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.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
+
+verify_runnable "global"
+
+snap=$TESTPOOL/$TESTFS@$TESTSNAP
+
+function cleanup
+{
+ destroy_dataset $snap
+}
+
+log_onexit cleanup
+
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+
+log_must snapexists $snap
+
+log_must_program_sync $TESTPOOL - $snap <<-EOF
+ arg = ...
+ snap = arg["argv"][1]
+ err = zfs.sync.destroy(snap)
+ msg = "destroying " .. snap .. " err=" .. err
+ return msg
+EOF
+
+log_mustnot snapexists $snap
+
+log_pass "Destroying snapshot with channel program works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh
new file mode 100755
index 000000000000..70330c91a0e7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_count_and_limit.ksh
@@ -0,0 +1,89 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+. $STF_SUITE/tests/functional/zvol/zvol_common.shlib
+
+#
+# DESCRIPTION:
+# Getting filesystem and snapshot count/limit props should work correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/testchild
+snap=$fs@$TESTSNAP
+
+function cleanup
+{
+ destroy_dataset $fs "-R"
+ log_must rm -rf $fs/foo
+ log_must rm -rf $fs/bar
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+log_must zfs create $fs/foo
+create_snapshot $fs $TESTSNAP
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$fs", "snapshot_limit")
+ assert(ans == -1)
+ assert(src == 'default')
+
+ ans, src = zfs.get_prop("$fs", "snapshot_count")
+ assert(ans == nil)
+ assert(src == nil)
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$fs", "filesystem_limit")
+ assert(ans == -1)
+ assert(src == 'default')
+
+ ans, src = zfs.get_prop("$fs", "filesystem_count")
+ assert(ans == nil)
+ assert(src == nil)
+EOF
+
+log_must zfs set snapshot_limit=10 $fs
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$fs", "snapshot_limit")
+ assert(ans == 10)
+ assert(src == '$fs')
+
+ ans, src = zfs.get_prop("$fs", "snapshot_count")
+ assert(ans == 1)
+ assert(src == nil)
+EOF
+
+log_must zfs set filesystem_limit=8 $fs
+
+log_must zfs create $fs/bar
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$fs", "filesystem_limit")
+ assert(ans == 8)
+ assert(src == '$fs')
+
+ ans, src = zfs.get_prop("$fs", "filesystem_count")
+ assert(ans == 2)
+ assert(src == nil)
+EOF
+
+log_pass "Getting filesystem and snapshot count/limit props should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh
new file mode 100755
index 000000000000..eed3e0bce5fc
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.ksh
@@ -0,0 +1,41 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Getting index props should work correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+snap=$fs@$TESTSNAP
+function cleanup
+{
+ destroy_dataset $fs "-R"
+}
+
+log_onexit cleanup
+
+log_must zfs create -o version=5 $fs
+create_snapshot $fs $TESTSNAP
+
+os=$(uname)
+log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.get_index_props.zcp $fs $snap $os
+
+log_pass "Getting index props should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out
new file mode 100644
index 000000000000..40c58b4b3aea
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.out
@@ -0,0 +1,4 @@
+Channel program fully executed with return value:
+ return:
+ 1:
+ 2:
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp
new file mode 100644
index 000000000000..10ef8e7f839a
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_index_props.zcp
@@ -0,0 +1,77 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+arg = ...
+fs = arg["argv"][1]
+snap = arg["argv"][2]
+os = arg["argv"][3]
+
+props = {}
+
+-- prop filesystem snapshot
+props['redundant_metadata'] = {{'all', 'default'}, {nil, nil}}
+props['sync'] = {{'standard', 'default'}, {nil, nil}}
+props['checksum'] = {{'on', 'default'}, {nil, nil}}
+props['dedup'] = {{'off', 'default'}, {nil, nil}}
+props['compression'] = {{'off', 'default'}, {nil, nil}}
+props['snapdir'] = {{'hidden', 'default'}, {nil, nil}}
+if os == "Linux" then
+ props['acltype'] = {{'off', 'default'}, {'off', 'default'}}
+elseif os == "FreeBSD" then
+ props['aclmode'] = {{'discard', 'default'}, {'discard', 'default'}}
+end
+props['aclinherit'] = {{'restricted','default'}, {nil, nil}}
+props['copies'] = {{'1', 'default'}, {nil, nil}}
+props['primarycache'] = {{'all', 'default'}, {'all', 'default'}}
+props['secondarycache'] = {{'all', 'default'}, {'all', 'default'}}
+props['logbias'] = {{'latency', 'default'}, {nil, nil}}
+props['atime'] = {{'on', 'default'}, {nil, nil}}
+props['devices'] = {{'on', 'default'}, {'on', 'default'}}
+props['exec'] = {{'on', 'default'}, {'on', 'default'}}
+props['setuid'] = {{'on', 'default'}, {'on', 'default'}}
+props['readonly'] = {{'off', 'default'}, {nil, nil}}
+if os == "FreeBSD" then
+ props['jailed'] = {{'off', 'default'}, {nil, nil}}
+else
+ props['zoned'] = {{'off', 'default'}, {nil, nil}}
+end
+props['vscan'] = {{'off', 'default'}, {nil, nil}}
+props['nbmand'] = {{'off', 'default'}, {'off', 'default'}}
+props['version'] = {{'5', nil}, {'5', nil}}
+props['canmount'] = {{'on', 'default'}, {nil, nil}}
+props['mounted'] = {{nil, nil}, {nil, nil}}
+props['defer_destroy'] = {{nil, nil}, {'off', nil}}
+props['normalization'] = {{'none', nil}, {'none', nil}}
+props['casesensitivity'] = {{'sensitive', nil}, {'sensitive', nil}}
+props['utf8only'] = {{'off', nil}, {'off', nil}}
+props['dnodesize'] = {{'legacy', 'default'}, {nil, nil}}
+props['relatime'] = {{'off', 'default'}, {nil, nil}}
+props['overlay'] = {{'off', 'default'}, {nil, nil}}
+
+fs_fails = {}
+snap_fails = {}
+for prop, expected in pairs(props) do
+ ans, src = zfs.get_prop(fs, prop)
+ if ((ans ~= expected[1][1]) or (src ~= expected[1][2])) then
+ fs_fails[prop] = {ans, src}
+ end
+
+ ans, src = zfs.get_prop(snap, prop)
+ if ((ans ~= expected[2][1]) or (src ~= expected[2][2])) then
+ snap_fails[prop] = {ans, src}
+ end
+end
+
+return {fs_fails, snap_fails}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh
new file mode 100755
index 000000000000..f3cf396aa1f2
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_mountpoint.ksh
@@ -0,0 +1,88 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Getting dataset mountpoint should work correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/testmount
+snap=$fs@$TESTSNAP
+clone=$TESTPOOL/$TESTCLONE
+mnt1=/$fs/mnt1
+mnt2=/$fs/mnt2
+
+function cleanup
+{
+ destroy_dataset $clone
+ destroy_dataset $fs "-R"
+ log_must rm -rf $mnt1
+ log_must rm -rf $mnt2
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+create_snapshot $fs $TESTSNAP
+create_clone $snap $clone
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$fs", "mountpoint")
+ assert(ans == '/$fs')
+ assert(src == 'default')
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$snap", "mountpoint")
+ assert(ans == nil)
+ assert(src == nil)
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$clone", "mountpoint")
+ assert(ans == '/$clone')
+ assert(src == 'default')
+EOF
+
+log_must mkdir $mnt1
+log_must mkdir $mnt2
+
+log_must zfs set mountpoint=$mnt1 $fs
+log_must zfs set mountpoint=$mnt2 $clone
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$fs", "mountpoint")
+ assert(ans == '$mnt1')
+ assert(src == '$fs')
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$snap", "mountpoint")
+ assert(ans == nil)
+ assert(src == nil)
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, src = zfs.get_prop("$clone", "mountpoint")
+ assert(ans == '$mnt2')
+ assert(src == '$clone')
+EOF
+
+log_pass "Getting dataset mountpoint should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh
new file mode 100755
index 000000000000..17f7a8fd9749
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_neg.ksh
@@ -0,0 +1,43 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Getting failures should work correctly.
+#
+
+verify_runnable "global"
+fs=$TESTPOOL/$TESTFS/testchild
+function cleanup
+{
+ destroy_dataset $fs
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("$fs", "notaprop")
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("notadataset", "type")
+EOF
+
+log_pass "Getting failures should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh
new file mode 100755
index 000000000000..942930fa6923
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.ksh
@@ -0,0 +1,52 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Getting number props should work correctly on filesystems,
+# snapshots and volumes.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+snap=$fs@$TESTSNAP
+vol=$TESTPOOL/$TESTVOL
+
+function cleanup
+{
+ destroy_dataset $fs "-R"
+ destroy_dataset $vol
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+create_snapshot $fs $TESTSNAP
+log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL
+
+#
+# Set snapshot_limit and filesystem_limit for the filesystem so that the
+# snapshot_count and filesystem_count properties return a value.
+#
+log_must zfs set snapshot_limit=10 filesystem_limit=10 $fs
+log_must zfs set snapshot_limit=10 $vol
+
+log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.get_number_props.zcp $fs $snap $vol
+
+log_pass "Getting number props should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out
new file mode 100644
index 000000000000..be68500097ad
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.out
@@ -0,0 +1,5 @@
+Channel program fully executed with return value:
+ return:
+ 1:
+ 2:
+ 3:
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp
new file mode 100644
index 000000000000..79969509be89
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_number_props.zcp
@@ -0,0 +1,101 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+arg = ...
+fs = arg["argv"][1]
+snap = arg["argv"][2]
+vol = arg["argv"][3]
+
+props = {}
+
+-- The values indicate whether or not a property should be returned,
+-- not the value of the property. A better approach might be to compare
+-- their values to the output of 'zfs get <prop>'
+
+-- prop filesystem snapshot volume
+props['used'] = {{true, nil}, {true, nil}, {true, nil}}
+props['available'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['referenced'] = {{true, nil}, {true, nil}, {true, nil}}
+props['compressratio'] = {{true, nil}, {true, nil}, {true, nil}}
+props['refcompressratio'] = {{true, nil}, {true, nil}, {true, nil}}
+props['volblocksize'] = {{nil, nil}, {nil, nil}, {true, nil}}
+props['usedbysnapshots'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['usedbydataset'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['usedbychildren'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['usedbyrefreservation'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['userrefs'] = {{nil, nil}, {true, nil}, {nil, nil}}
+props['written'] = {{true, nil}, {true, nil}, {true, nil}}
+props['logicalused'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['logicalreferenced'] = {{true, nil}, {true, nil}, {true, nil}}
+props['quota'] = {{true, 'default'}, {nil, nil}, {nil, nil}}
+props['reservation'] = {{true, 'default'}, {nil, nil}, {true, 'default'}}
+-- Note that zfsonlinux allows volsize for snapshot which differs from openzfs
+-- props['volsize'] = {{nil, nil}, {nil, nil}, {true, vol}}
+props['refquota'] = {{true, 'default'}, {nil, nil}, {nil, nil}}
+props['refreservation'] = {{true, 'default'}, {nil, nil}, {true, vol}}
+props['filesystem_limit'] = {{true, fs}, {nil, nil}, {nil, nil}}
+props['snapshot_limit'] = {{true, fs}, {nil, nil}, {true, vol}}
+props['filesystem_count'] = {{true, nil}, {nil, nil}, {nil, nil}}
+props['snapshot_count'] = {{true, nil}, {nil, nil}, {true, nil}}
+props['recordsize'] = {{true, 'default'}, {nil, nil}, {nil, nil}}
+props['creation'] = {{true, nil}, {true, nil}, {true, nil}}
+-- hidden props
+props['createtxg'] = {{true, nil}, {true, nil}, {true, nil}}
+props['numclones'] = {{nil, nil}, {true, nil}, {nil, nil}}
+props['guid'] = {{true, nil}, {true, nil}, {true, nil}}
+props['useraccounting'] = {{true, nil}, {true, nil}, {true, nil}}
+props['unique'] = {{true, nil}, {true, nil}, {true, nil}}
+props['objsetid'] = {{true, nil}, {true, nil}, {true, nil}}
+props['inconsistent'] = {{true, nil}, {true, nil}, {true, nil}}
+
+
+fs_fails = {}
+snap_fails = {}
+vol_fails = {}
+
+function match(n, ans, src, expected)
+ if ((expected[n][1] == nil) and (ans ~= nil)) then
+ return false
+ end
+
+ if ((expected[n][1] == true) and (ans == nil)) then
+ return false
+ end
+
+ if (expected[n][2] ~= src) then
+ return false
+ end
+
+ return true
+end
+
+for prop, expected in pairs(props) do
+ ans, src = zfs.get_prop(fs, prop)
+ if not (match(1, ans, src, expected)) then
+ fs_fails[prop] = {ans, src}
+ end
+
+ ans, src = zfs.get_prop(snap, prop)
+ if not (match(2, ans, src, expected)) then
+ snap_fails[prop] = {ans, src}
+ end
+
+ ans, src = zfs.get_prop(vol, prop)
+ if not (match(3, ans, src, expected)) then
+ vol_fails[prop] = {ans, src}
+ end
+end
+
+return {fs_fails, snap_fails, vol_fails}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh
new file mode 100755
index 000000000000..b7d784489ac8
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.ksh
@@ -0,0 +1,45 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Getting string props should work correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/testchild
+snap=$fs@$TESTSNAP
+clone=$TESTPOOL/$TESTCLONE
+
+
+function cleanup
+{
+ datasetexists $clone && log_must zfs destroy $clone
+ datasetexists $fs && log_must zfs destroy -R $fs
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+create_snapshot $fs $TESTSNAP
+create_clone $snap $clone
+
+log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.get_string_props.zcp $fs $snap $clone
+
+log_pass "Getting string props should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out
new file mode 100644
index 000000000000..be68500097ad
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.out
@@ -0,0 +1,5 @@
+Channel program fully executed with return value:
+ return:
+ 1:
+ 2:
+ 3:
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp
new file mode 100644
index 000000000000..899bdc0336c0
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_string_props.zcp
@@ -0,0 +1,73 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+arg = ...
+fs = arg["argv"][1]
+snap = arg["argv"][2]
+clone = arg["argv"][3]
+
+props = {}
+
+-- prop filesystem snapshot clone
+props['origin'] = {{nil, nil}, {nil, nil}, {snap, nil}}
+props['clones'] = {{nil, nil}, {{clone}, nil}, {nil, nil}}
+props['mountpoint'] = {{'/' .. fs, 'default'}, {nil, nil}, {'/' .. clone, 'default'}}
+props['sharenfs'] = {{'off', 'default'}, {nil, nil}, {'off', 'default'}}
+props['type'] = {{'filesystem', nil}, {'snapshot', nil}, {'filesystem', nil}}
+props['sharesmb'] = {{'off', 'default'}, {nil, nil}, {'off', 'default'}}
+props['mlslabel'] = {{'none', 'default'}, {'none', 'default'}, {'none', 'default'}}
+props['receive_resume_token'] = {{nil, nil}, {nil, nil}, {nil, nil}}
+-- hidden props
+props['name'] = {{fs, nil}, {snap, nil}, {clone, nil}}
+props['iscsioptions'] = {{nil, nil}, {nil, nil}, {nil, nil}}
+props['prevsnap'] = {{snap, nil}, {nil, nil}, {snap, nil}}
+
+
+fs_fails = {}
+snap_fails = {}
+clone_fails = {}
+
+
+function list_match(t1, t2)
+ if t1 == t2 then return true end
+ return (t1[1] == t2[1])
+end
+
+function match(n, prop, ans, src, expected)
+ if ((prop == 'clones') or (prop == 'redact_snaps')) then
+ return (list_match(ans, expected[n][1]) and (src == expected[n][2]))
+ else
+ return ((ans == expected[n][1]) and (src == expected[n][2]))
+ end
+end
+
+for prop, expected in pairs(props) do
+ ans, src = zfs.get_prop(fs, prop)
+ if not (match(1, prop, ans, src, expected)) then
+ fs_fails[prop] = {ans, src}
+ end
+
+ ans, src = zfs.get_prop(snap, prop)
+ if not (match(2, prop, ans, src, expected)) then
+ snap_fails[prop] = {ans, src}
+ end
+
+ ans, src = zfs.get_prop(clone, prop)
+ if not (match(3, prop, ans, src, expected)) then
+ clone_fails[prop] = {ans, src}
+ end
+end
+
+return {fs_fails, snap_fails, clone_fails}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.ksh
new file mode 100755
index 000000000000..d040e8962e06
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_type.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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+. $STF_SUITE/tests/functional/zvol/zvol_common.shlib
+
+#
+# DESCRIPTION:
+# Getting type should work correctly.
+#
+
+verify_runnable "global"
+fs=$TESTPOOL/$TESTFS/testchild
+snap=$fs@$TESTSNAP
+vol=$TESTPOOL/$TESTVOL
+function cleanup
+{
+ destroy_dataset $snap
+ destroy_dataset $fs
+ destroy_dataset $vol
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+create_snapshot $fs $TESTSNAP
+log_must zfs create -V $VOLSIZE $vol
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("$fs", "type")
+ assert(ans == "filesystem")
+
+ ans, setpoint = zfs.get_prop("$snap", "type")
+ assert(ans == "snapshot")
+
+ ans, setpoint = zfs.get_prop("$vol", "type")
+ assert(ans == "volume")
+EOF
+
+
+log_pass "Getting type should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh
new file mode 100755
index 000000000000..80cc4ad418cc
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_userquota.ksh
@@ -0,0 +1,80 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+# DESCRIPTION:
+# "Getting {user,group}{quota,used}, should work correctly."
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+fs1=$TESTPOOL/$TESTFS/nextchild
+userid='123'
+groupid='456'
+
+function cleanup
+{
+ destroy_dataset $fs
+ destroy_dataset $fs1
+}
+
+log_onexit cleanup
+
+log_must zfs create -o userquota@$userid=$UQUOTA_SIZE \
+ -o groupquota@$groupid=$GQUOTA_SIZE $fs
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("$fs", "userquota@$userid")
+ assert(ans == $UQUOTA_SIZE)
+ assert(setpoint == "$fs")
+
+ ans, setpoint = zfs.get_prop("$fs", "userused@$userid")
+ assert(ans == 0)
+ assert(setpoint == "$fs")
+
+ ans, setpoint = zfs.get_prop("$fs", "groupquota@$groupid")
+ assert(ans == $GQUOTA_SIZE)
+ assert(setpoint == "$fs")
+
+ ans, setpoint = zfs.get_prop("$fs", "groupused@$groupid")
+ assert(ans == 0)
+ assert(setpoint == "$fs")
+EOF
+
+log_must zfs create $fs1
+log_must_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("$fs1", "userquota@$userid")
+ assert(ans == nil)
+ assert(setpoint == nil)
+
+ ans, setpoint = zfs.get_prop("$fs1", "userused@$userid")
+ assert(ans == 0)
+ assert(setpoint == "$fs1")
+
+ ans, setpoint = zfs.get_prop("$fs1", "groupquota@$groupid")
+ assert(ans == nil)
+ assert(setpoint == nil)
+
+ ans, setpoint = zfs.get_prop("$fs1", "groupused@$groupid")
+ assert(ans == 0)
+ assert(setpoint == "$fs1")
+EOF
+
+log_pass "Getting {user,group}{quota,used}, should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh
new file mode 100755
index 000000000000..9755e6f82e58
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.get_written.ksh
@@ -0,0 +1,57 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+. $STF_SUITE/tests/functional/zvol/zvol_common.shlib
+
+#
+# DESCRIPTION:
+# Getting written@ props should work correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/testchild
+snap=$fs@$TESTSNAP
+dir=/$fs/dir
+
+function cleanup
+{
+ destroy_dataset $fs "-R"
+ log_must rm -rf $dir
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+create_snapshot $fs $TESTSNAP
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("$fs", "written@$TESTSNAP")
+ assert(ans == 0)
+
+EOF
+
+log_must mkdir $dir
+sync
+
+log_must_program $TESTPOOL - <<-EOF
+ ans, setpoint = zfs.get_prop("$fs", "written@$TESTSNAP")
+ assert(ans > 0)
+
+EOF
+
+log_pass "Getting written@ props should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.inherit.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.inherit.ksh
new file mode 100755
index 000000000000..e199b4c8b07d
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.inherit.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 2020 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS
+testprop="com.joyent:testprop"
+testval="testval"
+
+log_must dataset_setprop $fs $testprop $testval
+log_must_program_sync $TESTPOOL - $fs $testprop <<-EOF
+ arg = ...
+ fs = arg["argv"][1]
+ prop = arg["argv"][2]
+ err = zfs.sync.inherit(fs, prop)
+ msg = "resetting " .. prop .. " on " .. fs .. " err=" .. err
+ return msg
+EOF
+
+
+prop=$(get_prop $testprop $fs)
+[[ "$prop" == "-" ]] || log_fail "Property still set after inheriting"
+
+log_pass "Inherit/clear property with channel program works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_bookmarks.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_bookmarks.ksh
new file mode 100755
index 000000000000..7456177f725b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_bookmarks.ksh
@@ -0,0 +1,120 @@
+#!/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) 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Listing zfs bookmarks should work correctly.
+#
+
+verify_runnable "global"
+
+TESTBOOK=$TESTPOOL/$TESTFS#testbook
+TESTBOOK1=$TESTBOOK-1
+TESTBOOK2=$TESTBOOK-2
+TESTBOOK3=$TESTBOOK-3
+
+function cleanup
+{
+ bkmarkexists $TESTBOOK && log_must zfs destroy $TESTBOOK
+ bkmarkexists $TESTBOOK1 && log_must zfs destroy $TESTBOOK1
+ bkmarkexists $TESTBOOK2 && log_must zfs destroy $TESTBOOK2
+ bkmarkexists $TESTBOOK3 && log_must zfs destroy $TESTBOOK3
+ destroy_snapshot
+}
+
+log_onexit cleanup
+
+create_snapshot
+
+# 0 bookmarks handled correctly
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.bookmarks("$TESTPOOL/$TESTFS") do
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+
+# Create a bookmark
+log_must zfs bookmark $TESTPOOL/$TESTFS@$TESTSNAP $TESTBOOK
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.bookmarks("$TESTPOOL/$TESTFS") do
+ assert(s == "$TESTBOOK")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+log_must zfs bookmark $TESTPOOL/$TESTFS@$TESTSNAP $TESTBOOK1
+log_must zfs bookmark $TESTPOOL/$TESTFS@$TESTSNAP $TESTBOOK2
+log_must zfs bookmark $TESTPOOL/$TESTFS@$TESTSNAP $TESTBOOK3
+
+# All bookmarks appear exactly once
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTBOOK"] = false
+ a["$TESTBOOK1"] = false
+ a["$TESTBOOK2"] = false
+ a["$TESTBOOK3"] = false
+ n = 0
+ for s in zfs.list.bookmarks("$TESTPOOL/$TESTFS") do
+ assert(not a[s])
+ a[s] = true
+ n = n + 1
+ end
+ assert(n == 4)
+ assert(a["$TESTBOOK"] and
+ a["$TESTBOOK1"] and
+ a["$TESTBOOK2"] and
+ a["$TESTBOOK3"])
+ return 0
+EOF
+
+# Nonexistent input
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.bookmarks("$TESTPOOL/nonexistent-fs")
+ return 0
+EOF
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.bookmarks("nonexistent-pool/$TESTFS")
+ return 0
+EOF
+
+# Can't look in a different pool than the one specified on command line
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.bookmarks("testpool2")
+ return 0
+EOF
+
+# Can't have bookmarks on snapshots, only on filesystems
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.bookmarks("$TESTPOOL/$TESTFS@$TESTSNAP")
+ return 0
+EOF
+
+# Can't have bookmarks on bookmarks, only on filesystems
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.bookmarks("$TESTBOOK")
+ return 0
+EOF
+
+log_pass "Listing zfs bookmarks should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh
new file mode 100755
index 000000000000..06b82cab4f34
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_children.ksh
@@ -0,0 +1,125 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Listing zfs children should work correctly.
+#
+
+verify_runnable "global"
+
+log_assert "Listing zfs children should work correctly."
+
+TESTCHILD=$TESTPOOL/$TESTFS/testchild
+TESTCHILD1=$TESTCHILD-1
+TESTCHILD2=$TESTCHILD-2
+TESTCHILD3=$TESTCHILD-3
+
+function cleanup
+{
+ destroy_dataset $TESTCHILD
+ destroy_dataset $TESTCHILD1
+ destroy_dataset $TESTCHILD2
+ destroy_dataset $TESTCHILD3
+}
+
+log_onexit cleanup
+
+# 0 children handled correctly
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.children("$TESTPOOL/$TESTFS") do
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.children("$TESTPOOL") do
+ assert(s == "$TESTPOOL/$TESTFS")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+# Create a child fs
+log_must zfs create $TESTCHILD
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.children("$TESTPOOL/$TESTFS") do
+ assert(s == "$TESTCHILD")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+log_must zfs create $TESTCHILD1
+log_must zfs create $TESTCHILD2
+log_must zfs create $TESTCHILD3
+
+# All children appear exactly once
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTCHILD"] = false
+ a["$TESTCHILD1"] = false
+ a["$TESTCHILD2"] = false
+ a["$TESTCHILD3"] = false
+ n = 0
+ for s in zfs.list.children("$TESTPOOL/$TESTFS") do
+ assert(not a[s])
+ a[s] = true
+ n = n + 1
+ end
+ assert(n == 4)
+ assert(a["$TESTCHILD"] and
+ a["$TESTCHILD1"] and
+ a["$TESTCHILD2"] and
+ a["$TESTCHILD3"])
+ return 0
+EOF
+
+# Bad input
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.children("$TESTPOOL/not-a-fs")
+ return 0
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.children("not-a-pool/$TESTFS")
+ return 0
+EOF
+
+# Can't look in a different pool than the one specified on command line
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.children("rpool")
+ return 0
+EOF
+
+create_snapshot
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.children("$TESTPOOL/$TESTFS@$TESTSNAP")
+ return 0
+EOF
+destroy_snapshot
+
+log_pass "Listing zfs children should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh
new file mode 100755
index 000000000000..68d053283d31
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_clones.ksh
@@ -0,0 +1,116 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Listing zfs clones should work correctly.
+#
+
+verify_runnable "global"
+
+log_assert "Listing zfs clones should work correctly."
+
+function cleanup
+{
+ destroy_dataset $TESTPOOL/$TESTFS@$TESTSNAP "-R"
+}
+
+log_onexit cleanup
+
+# Take snapshot to test with ($TESTSNAP)
+create_snapshot
+
+# 0 clones handled correctly
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.clones("$TESTPOOL/$TESTFS@$TESTSNAP") do
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+
+# Create a clone ($TESTCLONE)
+create_clone
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.clones("$TESTPOOL/$TESTFS@$TESTSNAP") do
+ assert(s == "$TESTPOOL/$TESTCLONE")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+TESTCLONE1=${TESTCLONE}-1
+TESTCLONE2=${TESTCLONE}-2
+TESTCLONE3=${TESTCLONE}-3
+create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE1
+create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE2
+create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE3
+
+# All clones appear exactly once
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTPOOL/$TESTCLONE"] = false
+ a["$TESTPOOL/$TESTCLONE1"] = false
+ a["$TESTPOOL/$TESTCLONE2"] = false
+ a["$TESTPOOL/$TESTCLONE3"] = false
+ n = 0
+ for s in zfs.list.clones("$TESTPOOL/$TESTFS@$TESTSNAP") do
+ assert(not a[s])
+ a[s] = true
+ n = n + 1
+ end
+ assert(n == 4)
+ assert(a["$TESTPOOL/$TESTCLONE"] and
+ a["$TESTPOOL/$TESTCLONE1"] and
+ a["$TESTPOOL/$TESTCLONE2"] and
+ a["$TESTPOOL/$TESTCLONE3"])
+ return 0
+EOF
+
+# Bad input
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.clones("$TESTPOOL/not-a-fs@$TESTSNAP")
+ return 0
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.clones("$TESTPOOL/$TESTFS@not-a-snap")
+ return 0
+EOF
+
+# Can't look in a different pool than the one specified on command line
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.clones("rpool")
+ return 0
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.clones("not-a-pool/$TESTFS@$TESTSNAP")
+ return 0
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.clones("$TESTPOOL/$TESTFS")
+ return 0
+EOF
+
+log_pass "Listing zfs clones should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_holds.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_holds.ksh
new file mode 100755
index 000000000000..2a471bdecbfc
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_holds.ksh
@@ -0,0 +1,121 @@
+#!/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) 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Listing zfs holds should work correctly.
+#
+
+verify_runnable "global"
+
+TESTHOLD=testhold-tag
+TESTHOLD1=$TESTHOLD-1
+TESTHOLD2=$TESTHOLD-2
+TESTHOLD3=$TESTHOLD-3
+SNAP=$TESTPOOL/$TESTFS@$TESTSNAP
+
+function cleanup
+{
+ holdexists $TESTHOLD $SNAP && log_must zfs release $TESTHOLD $SNAP
+ holdexists $TESTHOLD1 $SNAP && log_must zfs release $TESTHOLD1 $SNAP
+ holdexists $TESTHOLD2 $SNAP && log_must zfs release $TESTHOLD2 $SNAP
+ holdexists $TESTHOLD3 $SNAP && log_must zfs release $TESTHOLD3 $SNAP
+ destroy_snapshot
+}
+
+log_onexit cleanup
+
+create_snapshot
+
+# 0 holds handled correctly
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.holds("$SNAP") do
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+
+# Create a hold
+log_must zfs hold $TESTHOLD $SNAP
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.holds("$SNAP") do
+ assert(s == "$TESTHOLD")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+log_must zfs hold $TESTHOLD1 $SNAP
+log_must zfs hold $TESTHOLD2 $SNAP
+log_must zfs hold $TESTHOLD3 $SNAP
+
+# All holds appear exactly once
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTHOLD"] = false
+ a["$TESTHOLD1"] = false
+ a["$TESTHOLD2"] = false
+ a["$TESTHOLD3"] = false
+ n = 0
+ for s in zfs.list.holds("$SNAP") do
+ assert(not a[s])
+ a[s] = true
+ n = n + 1
+ end
+ assert(n == 4)
+ assert(a["$TESTHOLD"] and
+ a["$TESTHOLD1"] and
+ a["$TESTHOLD2"] and
+ a["$TESTHOLD3"])
+ return 0
+EOF
+
+# Nonexistent input
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.holds("$TESTPOOL/nonexistent-fs@nonexistent-snap")
+ return 0
+EOF
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.holds("nonexistent-pool/$TESTFS")
+ return 0
+EOF
+
+# Can't look in a different pool than the one specified on command line
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.holds("testpool2")
+ return 0
+EOF
+
+# Can't have holds on filesystems
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.holds("$TESTPOOL/$TESTFS")
+ return 0
+EOF
+
+# Can't have holds on bookmarks
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.holds("$TESTPOOL/$TESTFS#bookmark")
+ return 0
+EOF
+
+log_pass "Listing zfs holds should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh
new file mode 100755
index 000000000000..7bc36606c957
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_snapshots.ksh
@@ -0,0 +1,112 @@
+#!/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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Listing zfs snapshots should work correctly.
+#
+
+verify_runnable "global"
+
+log_assert "Listing zfs snapshots should work correctly."
+
+function cleanup
+{
+ destroy_dataset $TESTPOOL/$TESTFS@$TESTSNAP
+ destroy_dataset $TESTPOOL/$TESTFS@$TESTSNAP1
+ destroy_dataset $TESTPOOL/$TESTFS@$TESTSNAP2
+ destroy_dataset $TESTPOOL/$TESTFS@$TESTSNAP3
+}
+
+log_onexit cleanup
+
+# 0 snapshots handled correctly
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.snapshots("$TESTPOOL/$TESTFS") do
+ zfs.debug("ERROR: found snapshot " .. s)
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+
+# Take a snapshot, make sure it appears
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for s in zfs.list.snapshots("$TESTPOOL/$TESTFS") do
+ assert(s == "$TESTPOOL/$TESTFS@$TESTSNAP")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+TESTSNAP1=${TESTSNAP}-1
+TESTSNAP2=${TESTSNAP}-2
+TESTSNAP3=${TESTSNAP}-3
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP1
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP2
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP3
+
+# All snapshots appear exactly once
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTPOOL/$TESTFS@$TESTSNAP"] = false
+ a["$TESTPOOL/$TESTFS@$TESTSNAP1"] = false
+ a["$TESTPOOL/$TESTFS@$TESTSNAP2"] = false
+ a["$TESTPOOL/$TESTFS@$TESTSNAP3"] = false
+ n = 0
+ for s in zfs.list.snapshots("$TESTPOOL/$TESTFS") do
+ assert(not a[s])
+ a[s] = true
+ n = n + 1
+ end
+ assert(n == 4)
+ assert(a["$TESTPOOL/$TESTFS@$TESTSNAP"] and
+ a["$TESTPOOL/$TESTFS@$TESTSNAP1"] and
+ a["$TESTPOOL/$TESTFS@$TESTSNAP2"] and
+ a["$TESTPOOL/$TESTFS@$TESTSNAP3"])
+ return 0
+EOF
+
+# Bad input
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.snapshots("$TESTPOOL/not-a-fs")
+ return 0
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.snapshots("not-a-pool/$TESTFS")
+ return 0
+EOF
+
+# Can't look in a different pool than the one specified on command line
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.snapshots("rpool")
+ return 0
+EOF
+
+log_mustnot_program $TESTPOOL - <<-EOF
+ zfs.list.snapshots("$TESTPOOL/${TESTFS}@$TESTSNAP")
+ return 0
+EOF
+
+log_pass "Listing zfs snapshots should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.ksh
new file mode 100755
index 000000000000..24ab65a195b9
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_system_props.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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+. $STF_SUITE/tests/functional/zvol/zvol_common.shlib
+
+#
+# DESCRIPTION:
+# Listing system properties should succeed
+#
+
+verify_runnable "global"
+fs=$TESTPOOL/$TESTFS/testchild
+snap=$fs@$TESTSNAP
+vol=$TESTPOOL/$TESTVOL
+function cleanup
+{
+ destroy_dataset $snap
+ destroy_dataset $fs
+ destroy_dataset $vol
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+create_snapshot $fs $TESTSNAP
+log_must zfs create -V $VOLSIZE $vol
+
+log_must_program $TESTPOOL - <<-EOF
+ zfs.list.system_properties("$fs")
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ zfs.list.system_properties("$snap")
+EOF
+
+log_must_program $TESTPOOL - <<-EOF
+ zfs.list.system_properties("$vol")
+EOF
+
+log_pass "Listing system properties should succeed."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_user_props.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_user_props.ksh
new file mode 100755
index 000000000000..a454a2753302
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.list_user_props.ksh
@@ -0,0 +1,147 @@
+#!/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) 2017 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Listing zfs user properties should work correctly.
+#
+# Note, that this file tests both zfs.list.user_properties
+# and it's alias zfs.list.properties.
+#
+
+verify_runnable "global"
+
+TESTPROP="org.openzfs:test_property"
+TESTPROP1=$TESTPROP-1
+TESTPROP2=$TESTPROP-2
+TESTPROP3=$TESTPROP-3
+TESTPROP4=$TESTPROP-4
+
+TESTVAL="true"
+TESTVAL1="busy"
+TESTVAL2="9223372036854775808"
+TESTVAL3="801f2266-3715-41f4-9080-3d5e913b0f15"
+TESTVAL4="TOZwOfACvQtmDyiq68elB3a3g9YYyxBjSnLtN3ZyQYNOAKykzIE2khKKOBncJiDx"
+
+
+# 0 properties handled correctly
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for p in zfs.list.user_properties("$TESTPOOL/$TESTFS") do
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for p in zfs.list.properties("$TESTPOOL/$TESTFS") do
+ n = n + 1
+ end
+ assert(n == 0)
+ return 0
+EOF
+
+# Add a single user property
+log_must zfs set $TESTPROP="$TESTVAL" $TESTPOOL/$TESTFS
+
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for p,v in zfs.list.user_properties("$TESTPOOL/$TESTFS") do
+ assert(p == "$TESTPROP")
+ assert(v == "$TESTVAL")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+log_must_program $TESTPOOL - <<-EOF
+ n = 0
+ for p,v in zfs.list.properties("$TESTPOOL/$TESTFS") do
+ assert(p == "$TESTPROP")
+ assert(v == "$TESTVAL")
+ n = n + 1
+ end
+ assert(n == 1)
+ return 0
+EOF
+
+log_must zfs set $TESTPROP1="$TESTVAL1" $TESTPOOL/$TESTFS
+log_must zfs set $TESTPROP2="$TESTVAL2" $TESTPOOL/$TESTFS
+log_must zfs set $TESTPROP3="$TESTVAL3" $TESTPOOL/$TESTFS
+log_must zfs set $TESTPROP4="$TESTVAL4" $TESTPOOL/$TESTFS
+
+# All user properties have correct value and appear exactly once
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTPROP"] = false
+ a["$TESTPROP1"] = false
+ a["$TESTPROP2"] = false
+ a["$TESTPROP3"] = false
+ a["$TESTPROP4"] = false
+ m = {}
+ m["$TESTPROP"] = "$TESTVAL"
+ m["$TESTPROP1"] = "$TESTVAL1"
+ m["$TESTPROP2"] = "$TESTVAL2"
+ m["$TESTPROP3"] = "$TESTVAL3"
+ m["$TESTPROP4"] = "$TESTVAL4"
+ n = 0
+ for p,v in zfs.list.user_properties("$TESTPOOL/$TESTFS") do
+ assert(not a[p])
+ a[p] = true
+ assert(v == m[p])
+ n = n + 1
+ end
+ assert(n == 5)
+ assert(a["$TESTPROP"] and
+ a["$TESTPROP1"] and
+ a["$TESTPROP2"] and
+ a["$TESTPROP3"] and
+ a["$TESTPROP4"])
+ return 0
+EOF
+log_must_program $TESTPOOL - <<-EOF
+ a = {}
+ a["$TESTPROP"] = false
+ a["$TESTPROP1"] = false
+ a["$TESTPROP2"] = false
+ a["$TESTPROP3"] = false
+ a["$TESTPROP4"] = false
+ m = {}
+ m["$TESTPROP"] = "$TESTVAL"
+ m["$TESTPROP1"] = "$TESTVAL1"
+ m["$TESTPROP2"] = "$TESTVAL2"
+ m["$TESTPROP3"] = "$TESTVAL3"
+ m["$TESTPROP4"] = "$TESTVAL4"
+ n = 0
+ for p,v in zfs.list.properties("$TESTPOOL/$TESTFS") do
+ assert(not a[p])
+ a[p] = true
+ assert(v == m[p])
+ n = n + 1
+ end
+ assert(n == 5)
+ assert(a["$TESTPROP"] and
+ a["$TESTPROP1"] and
+ a["$TESTPROP2"] and
+ a["$TESTPROP3"] and
+ a["$TESTPROP4"])
+ return 0
+EOF
+
+log_pass "Listing zfs user properties should work correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh
new file mode 100755
index 000000000000..01bdb63eb678
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.parse_args_neg.ksh
@@ -0,0 +1,50 @@
+#!/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:
+# Try calling zfs.sync.destroy with various arguments that are not
+# valid. The script should fail.
+#
+
+verify_runnable "global"
+
+set -A progs "zfs.sync.destroy(\"foo\", \"bar\")" \
+ "zfs.sync.destroy(\"foo\", 12345)" \
+ "zfs.sync.destroy(12345)" \
+ "zfs.sync.destroy()" \
+ "zfs.sync.destroy{\"foo\", bar=true}" \
+ "zfs.sync.destroy{\"foo\", defer=12345}" \
+ "zfs.sync.destroy{\"foo\", defer=true, 12345}" \
+ "zfs.sync.destroy{\"foo\", defer=true, bar=12345}" \
+ "zfs.sync.destroy{\"foo\", bar=true, defer=true}" \
+ "zfs.sync.destroy{defer=true}" \
+ "zfs.sync.destroy{12345, defer=true}"
+
+
+typeset -i i=0
+while (( i < ${#progs[*]} )); do
+ log_note "running program: ${progs[i]}"
+ # output should contain the usage message, which contains "destroy{"
+ log_mustnot_checkerror_program "destroy{" $TESTPOOL - <<-EOF
+ ${progs[i]}
+ EOF
+ ((i = i + 1))
+done
+
+log_pass "Invalid arguments to zfs.sync.destroy generate errors."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh
new file mode 100755
index 000000000000..b2840377b559
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh
@@ -0,0 +1,55 @@
+#!/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:
+# Attempting to promote a clone when it shares a snapshot name with
+# its parent filesystem should fail and return the name of the
+# conflicting snapshot.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+clone=$TESTPOOL/$TESTFS/testchild_clone
+snap=promote_conflict_snap
+
+function cleanup
+{
+ for to_destroy in $fs $clone; do
+ destroy_dataset $to_destroy "-R"
+ done
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+log_must zfs snapshot $fs@$snap
+log_must zfs clone $fs@$snap $clone
+log_must zfs snapshot $clone@$snap
+
+#
+# This channel program is expected to return successfully, but fail to execute
+# the promote command since the snapshot names collide. It returns the error
+# code and description, which should be EEXIST (17) and the name of the
+# conflicting snapshot.
+#
+log_must_program_sync $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.promote_conflict.zcp $clone
+
+log_pass "Promoting a clone with a conflicting snapshot fails."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp
new file mode 100644
index 000000000000..287328a40d31
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.zcp
@@ -0,0 +1,23 @@
+--
+-- 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 by Delphix. All rights reserved.
+--
+
+-- This program should be invoked as "zfs program <pool> <prog> <clone>"
+
+args = ...
+argv = args["argv"]
+err, desc = zfs.sync.promote(argv[1])
+assert(err == EEXIST)
+assert(#desc == 1)
+assert(desc[1] == "promote_conflict_snap")
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh
new file mode 100755
index 000000000000..7618a34bc8e1
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh
@@ -0,0 +1,71 @@
+#!/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:
+# Multiple interacting promotions in a single txg should succeed.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+clone1=$TESTPOOL/$TESTFS/testchild_clone1
+clone2=$TESTPOOL/$TESTFS/testchild_clone2
+snap1=$fs@testchild_snap1
+snap2=$clone1@testchild_snap2
+
+function cleanup
+{
+ for to_destroy in $fs $clone1 $clone2; do
+ destroy_dataset $to_destroy "-R"
+ done
+}
+
+log_onexit cleanup
+
+#
+# Create a chain of clones and snapshots:
+#
+# snap1 -----------> fs
+# \--> snap2 --> clone1
+# \---> clone2
+#
+# Then promote clone2 twice, resulting in:
+#
+# snap1 --> snap2 --> clone2
+# \ \---> clone1
+# \------------> fs
+#
+# We then attempt to destroy clone1, which should succeed since it no
+# longer has any dependents.
+#
+log_must zfs create $fs
+log_must zfs snapshot $snap1
+log_must zfs clone $snap1 $clone1
+log_must zfs snapshot $snap2
+log_must zfs clone $snap2 $clone2
+
+log_must zfs unmount -f $clone1
+
+log_must_program_sync $TESTPOOL - <<-EOF
+ assert(zfs.sync.promote("$clone2") == 0)
+ assert(zfs.sync.promote("$clone2") == 0)
+ assert(zfs.sync.destroy("$clone1") == 0)
+EOF
+
+log_pass "Multiple promotes and destroying a demoted fs in one txg works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh
new file mode 100755
index 000000000000..541c6840e0c0
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh
@@ -0,0 +1,47 @@
+#!/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:
+# Promoting a clone should work correctly.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+clone=$TESTPOOL/$TESTFS/testchild_clone
+snap=$fs@$TESTSNAP
+
+function cleanup
+{
+ for to_destroy in $fs $clone; do
+ destroy_dataset $to_destroy "-R"
+ done
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+log_must zfs snapshot $snap
+log_must zfs clone $snap $clone
+
+log_must_program_sync $TESTPOOL - <<-EOF
+ assert(zfs.sync.promote("$clone") == 0)
+EOF
+
+log_pass "Promoting a clone with a channel program works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh
new file mode 100755
index 000000000000..2fbbc73d5fe8
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh
@@ -0,0 +1,60 @@
+#!/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
+
+verify_runnable "global"
+snap1=$TESTPOOL/$TESTFS@$TESTSNAP1
+snap2=$TESTPOOL/$TESTFS@$TESTSNAP2
+fs=$TESTPOOL/$TESTFS
+file=$TESTDIR/$TESTFILE0
+
+function cleanup
+{
+ destroy_dataset $snap1 && log_must rm $file
+}
+
+log_onexit cleanup
+
+log_must mkfile 128b $file
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP1
+log_must rm $file
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP2
+
+log_must snapexists $snap1
+log_must snapexists $snap2
+log_must zfs unmount $fs
+
+log_must_program_sync $TESTPOOL - $fs $snap2 <<-EOF
+ arg = ...
+ fs = arg["argv"][1]
+ snap = arg["argv"][2]
+ err = zfs.sync.rollback(fs)
+ if err == 0 then
+ err = zfs.sync.destroy(snap)
+ end
+ if err == 0 then
+ err = zfs.sync.rollback(fs)
+ end
+ msg = "rolling back " .. fs .. " err=" .. err
+ return msg
+EOF
+
+log_must zfs mount $fs
+log_must [ -f $file ]
+log_mustnot snapexists $snap2
+
+log_pass "Rolling back snapshot with channel program works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh
new file mode 100755
index 000000000000..12834430163c
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh
@@ -0,0 +1,49 @@
+#!/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
+
+verify_runnable "global"
+snap=$TESTPOOL/$TESTFS@$TESTSNAP
+fs=$TESTPOOL/$TESTFS
+file=$TESTDIR/$TESTFILE0
+
+function cleanup
+{
+ destroy_dataset $snap && log_must rm $file
+}
+
+log_onexit cleanup
+
+log_must mkfile 128b $file
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+log_must rm $file
+
+log_must snapexists $snap
+log_must zfs unmount $fs
+
+log_must_program_sync $TESTPOOL - $fs <<-EOF
+ arg = ...
+ fs = arg["argv"][1]
+ err = zfs.sync.rollback(fs)
+ msg = "rolling back " .. fs .. " err=" .. err
+ return msg
+EOF
+
+log_must zfs mount $fs
+log_must [ -f $file ]
+
+log_pass "Rolling back snapshot with channel program works."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.ksh
new file mode 100755
index 000000000000..6ac1c2b205cd
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.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 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION:
+# Setting user props should work correctly on datasets.
+#
+
+verify_runnable "global"
+
+fs=$TESTPOOL/$TESTFS/testchild
+
+function cleanup
+{
+ destroy_dataset $fs "-R"
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must_program_sync $TESTPOOL $ZCP_ROOT/synctask_core/tst.set_props.zcp $fs
+
+log_pass "Setting props from channel program works correctly."
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.zcp
new file mode 100644
index 000000000000..756263a9d082
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.set_props.zcp
@@ -0,0 +1,109 @@
+--
+-- 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) 2017 by Delphix. All rights reserved.
+-- Copyright 2020 Joyent, Inc.
+--
+
+arg = ...
+fs = arg["argv"][1]
+
+-- values from zfs.h
+maxname = 256 -- ZAP_MAXNAMELEN
+maxvalue = 8192 -- ZAP_MAXVALUELEN
+
+pos_props = {}
+neg_props = {}
+
+-- In lua, strings are immutable, so to avoid a bunch of copies, we
+-- build the value in a table and use concat (which appears to be the
+-- recommend method for such things).
+largeprop = {}
+for i = 0,maxvalue,8
+do
+ table.insert(largeprop, "aaaaaaaa")
+end
+-- add an extra character so we spill over the limit
+table.insert(largeprop, "b")
+
+largepropv = table.concat(largeprop)
+
+largepropname = { "b:" }
+for i = 0,maxname,8
+do
+ table.insert(largepropname, "aaaaaaaa")
+end
+largepropnamev = table.concat(largepropname)
+
+pos_props["a:prop"] = {"hello"}
+
+-- For neg_props, an optional expected error value can be added after the
+-- property value as seen below.
+neg_props["notaproperty"] = {"hello", EINVAL}
+neg_props["a:very.long.property.value"] = { largepropv, E2BIG }
+neg_props[largepropnamev] = {"greetings", ENAMETOOLONG }
+
+-- non-user properties aren't currently supported
+-- Even if they were, the argument must be a string due to requirements of
+-- the ZCP api.
+neg_props["mountpoint"] = {"/foo/bar"}
+neg_props["copies"] = { "2" }
+
+-- read-only properties should never succeed
+neg_props["guid"] = { "12345" }
+
+set_fail = {}
+val_fail = {}
+
+-- Test properties that should work
+for prop, values in pairs(pos_props) do
+ for i, val in ipairs(values) do
+ old_val, src = zfs.get_prop(fs, prop)
+
+ -- Attempt to set the property to the specified value
+ err = zfs.sync.set_prop(fs, prop, val)
+
+ if (err ~= 0) then
+ set_fail[prop] = err -- tuple of prop, val that resulted in error
+ else
+ -- use get_prop to check that the set took affect
+ new_val, src = zfs.get_prop(fs, prop)
+ if (tostring(new_val) ~= tostring(val)) then
+ val_fail[prop] = new_val
+ end
+
+ -- We modified the prop, restore old value (if one existed)
+ if (old_val ~= nil) then
+ err = zfs.sync.set_prop(fs, prop, old_val)
+ if (err ~= 0) then return err end
+ else
+ -- Didn't have an old value, delete (inherit) instead
+ err = zfs.sync.inherit(fs, prop)
+ if (err ~= 0) then return err end
+ end
+ end
+ end
+end
+
+-- Test properties that should fail
+for prop, expected in pairs(neg_props) do
+ exp_val = expected[1]
+ exp_err = expected[2]
+
+ -- Attempt to set the property to the specified value
+ err = zfs.sync.set_prop(fs, prop, exp_val)
+ if (err == 0 or (exp_err ~= nil and err ~= exp_err)) then
+ set_fail[prop] = err -- tuple of prop, val that resulted in error
+ end
+end
+
+return {set_fail, val_fail}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh
new file mode 100755
index 000000000000..b0cdfb84f4ea
--- /dev/null
+++ b/sys/contrib/openzfs/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
+{
+ destroy_dataset $fs "-R"
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must_program_sync $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.snapshot_destroy.zcp $fs
+
+log_pass "Creating/destroying snapshots in one channel program works"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.zcp
new file mode 100644
index 000000000000..6fbfb06ad433
--- /dev/null
+++ b/sys/contrib/openzfs/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/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
new file mode 100755
index 000000000000..8d6cd38310e6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
@@ -0,0 +1,45 @@
+#!/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
+ destroy_dataset $fs "-R"
+ done
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs1
+log_must zfs create $fs2
+log_must zfs snapshot $fs1@snap1
+
+log_must_program_sync $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/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.zcp
new file mode 100644
index 000000000000..5cae324bc90e
--- /dev/null
+++ b/sys/contrib/openzfs/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/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh
new file mode 100755
index 000000000000..e3bf10b69168
--- /dev/null
+++ b/sys/contrib/openzfs/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
+{
+ destroy_dataset $rootfs "-R"
+}
+
+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_sync $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/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.zcp
new file mode 100644
index 000000000000..097940d71198
--- /dev/null
+++ b/sys/contrib/openzfs/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/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh
new file mode 100755
index 000000000000..4a7acaddaf8f
--- /dev/null
+++ b/sys/contrib/openzfs/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
+{
+ destroy_dataset $fs "-R"
+}
+
+log_onexit cleanup
+
+log_must zfs create $fs
+
+log_must_program_sync $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.snapshot_simple.zcp $fs $snapname
+
+log_pass "Simple snapshotting works"
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.zcp
new file mode 100644
index 000000000000..215e013df1d2
--- /dev/null
+++ b/sys/contrib/openzfs/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]))
diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.terminate_by_signal.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.terminate_by_signal.ksh
new file mode 100755
index 000000000000..0a5fb804ac39
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/channel_program/synctask_core/tst.terminate_by_signal.ksh
@@ -0,0 +1,98 @@
+#!/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) 2017 by Delphix. All rights reserved.
+#
+. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
+
+#
+# DESCRIPTION: Execute a long-running zfs channel program and attempt to
+# cancel it by sending a signal.
+#
+
+verify_runnable "global"
+
+rootfs=$TESTPOOL/$TESTFS
+snapname=snap
+limit=50000000
+
+function cleanup
+{
+ datasetexists $rootfs && log_must zfs destroy -R $rootfs
+}
+
+log_onexit cleanup
+
+#
+# Create a working set of 100 file systems
+#
+for i in {1..100}; do
+ log_must zfs create "$rootfs/child$i"
+done
+
+#
+# Attempt to create 100 snapshots with zfs.sync.snapshot() along with some
+# time consuming efforts. We use loops of zfs.check.* (dry run operations)
+# to consume instructions before the next zfs.sync.snapshot() occurs.
+#
+# Without a signal interruption this ZCP would take several minutes and
+# generate over 30 million Lua instructions.
+#
+function chan_prog
+{
+zfs program -t $limit $TESTPOOL - $rootfs $snapname <<-EOF
+ arg = ...
+ fs = arg["argv"][1]
+ snap = arg["argv"][2]
+ for child in zfs.list.children(fs) do
+ local snapname = child .. "@" .. snap
+ zfs.check.snapshot(snapname)
+ zfs.sync.snapshot(snapname)
+ for i=1,20000,1 do
+ zfs.check.snapshot(snapname)
+ zfs.check.destroy(snapname)
+ zfs.check.destroy(fs)
+ end
+ end
+ return "should not have reached here"
+EOF
+}
+
+log_note "Executing a long-running zfs program in the background"
+chan_prog &
+CHILD=$!
+
+#
+# After waiting, send a kill signal to the channel program process.
+# This should stop the ZCP near a million instructions but still have
+# created some of the snapshots. Note that since the above zfs program
+# command might get wrapped, we also issue a kill to the group.
+#
+sleep 10
+log_pos pkill -P $CHILD
+log_pos kill $CHILD
+
+#
+# Make sure the channel program did not fully complete by enforcing
+# that not all of the snapshots were created.
+#
+snap_count=$(zfs list -t snapshot | grep $TESTPOOL | wc -l)
+log_note "$snap_count snapshots created by ZCP"
+
+if [ "$snap_count" -eq 0 ]; then
+ log_fail "Channel program failed to run."
+elif [ "$snap_count" -gt 90 ]; then
+ log_fail "Too many snapshots after a cancel ($snap_count)."
+else
+ log_pass "Canceling a long-running channel program works."
+fi