aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/tests/zfs-tests/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/tests/zfs-tests/cmd')
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/Makefile.am34
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/Makefile.am32
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/btree_test.c554
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/chg_usr_exec.c77
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/Makefile.am10
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/devname2devid.c160
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/dir_rd_update.c136
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/file_check.c86
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_common.h70
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/file_trunc.c240
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/file_write.c258
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/get_diff.c109
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/largest_file.c145
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/Makefile.am10
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c1060
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/mkbusy.c177
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/Makefile.am8
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/mkfile.c282
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/mkfiles.c66
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/mktree.c191
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/mmap_exec.c72
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/Makefile.am10
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/mmap_libaio.c88
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/Makefile.am7
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/mmapwrite.c162
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/Makefile.am10
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/nvlist_to_lua.c305
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/randfree_file.c125
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/Makefile.am9
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/randwritecomp.c194
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/readmmap.c138
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/rename_dir.c88
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile.am7
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/rm_lnkcnt_zero_file.c159
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/Makefile.am7
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/stride_dd.c214
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/Makefile.am7
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/threadsappend.c135
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/user_ns_exec.c179
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/.gitignore1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/Makefile.am6
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/xattrtest.c718
83 files changed, 6462 insertions, 0 deletions
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/Makefile.am
new file mode 100644
index 000000000000..20e85cf6bb3f
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/Makefile.am
@@ -0,0 +1,34 @@
+EXTRA_DIST = file_common.h
+
+SUBDIRS = \
+ btree_test \
+ chg_usr_exec \
+ devname2devid \
+ dir_rd_update \
+ file_check \
+ file_trunc \
+ file_write \
+ get_diff \
+ largest_file \
+ libzfs_input_check \
+ mkbusy \
+ mkfile \
+ mkfiles \
+ mktree \
+ mmap_exec \
+ mmap_libaio \
+ mmapwrite \
+ nvlist_to_lua \
+ randwritecomp \
+ readmmap \
+ rename_dir \
+ rm_lnkcnt_zero_file \
+ stride_dd \
+ threadsappend
+
+if BUILD_LINUX
+SUBDIRS += \
+ randfree_file \
+ user_ns_exec \
+ xattrtest
+endif
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/.gitignore
new file mode 100644
index 000000000000..73777c4c1f4b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/.gitignore
@@ -0,0 +1 @@
+/btree_test
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/Makefile.am
new file mode 100644
index 000000000000..4c9a1a4cc296
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/Makefile.am
@@ -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 by Delphix. All rights reserved.
+#
+
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+DEFAULT_INCLUDES += \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/libspl/include
+
+# Unconditionally enable ASSERTs
+AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
+
+pkgexec_PROGRAMS = btree_test
+btree_test_SOURCES = btree_test.c
+
+btree_test_LDADD = \
+ $(abs_top_builddir)/lib/libzpool/libzpool.la \
+ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/btree_test.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/btree_test.c
new file mode 100644
index 000000000000..e1995c03af3e
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/btree_test/btree_test.c
@@ -0,0 +1,554 @@
+/*
+ * 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 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/avl.h>
+#include <sys/btree.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#define BUFSIZE 256
+
+int seed = 0;
+int stress_timeout = 180;
+int contents_frequency = 100;
+int tree_limit = 64 * 1024;
+boolean_t stress_only = B_FALSE;
+
+static void
+usage(int exit_value)
+{
+ (void) fprintf(stderr, "Usage:\tbtree_test -n <test_name>\n");
+ (void) fprintf(stderr, "\tbtree_test -s [-r <seed>] [-l <limit>] "
+ "[-t timeout>] [-c check_contents]\n");
+ (void) fprintf(stderr, "\tbtree_test [-r <seed>] [-l <limit>] "
+ "[-t timeout>] [-c check_contents]\n");
+ (void) fprintf(stderr, "\n With the -n option, run the named "
+ "negative test. With the -s option,\n");
+ (void) fprintf(stderr, " run the stress test according to the "
+ "other options passed. With\n");
+ (void) fprintf(stderr, " neither, run all the positive tests, "
+ "including the stress test with\n");
+ (void) fprintf(stderr, " the default options.\n");
+ (void) fprintf(stderr, "\n Options that control the stress test\n");
+ (void) fprintf(stderr, "\t-c stress iterations after which to compare "
+ "tree contents [default: 100]\n");
+ (void) fprintf(stderr, "\t-l the largest value to allow in the tree "
+ "[default: 1M]\n");
+ (void) fprintf(stderr, "\t-r random seed [default: from "
+ "gettimeofday()]\n");
+ (void) fprintf(stderr, "\t-t seconds to let the stress test run "
+ "[default: 180]\n");
+ exit(exit_value);
+}
+
+typedef struct int_node {
+ avl_node_t node;
+ uint64_t data;
+} int_node_t;
+
+/*
+ * Utility functions
+ */
+
+static int
+avl_compare(const void *v1, const void *v2)
+{
+ const int_node_t *n1 = v1;
+ const int_node_t *n2 = v2;
+ uint64_t a = n1->data;
+ uint64_t b = n2->data;
+
+ return (TREE_CMP(a, b));
+}
+
+static int
+zfs_btree_compare(const void *v1, const void *v2)
+{
+ const uint64_t *a = v1;
+ const uint64_t *b = v2;
+
+ return (TREE_CMP(*a, *b));
+}
+
+static void
+verify_contents(avl_tree_t *avl, zfs_btree_t *bt)
+{
+ static int count = 0;
+ zfs_btree_index_t bt_idx = {0};
+ int_node_t *node;
+ uint64_t *data;
+
+ boolean_t forward = count % 2 == 0 ? B_TRUE : B_FALSE;
+ count++;
+
+ ASSERT3U(avl_numnodes(avl), ==, zfs_btree_numnodes(bt));
+ if (forward == B_TRUE) {
+ node = avl_first(avl);
+ data = zfs_btree_first(bt, &bt_idx);
+ } else {
+ node = avl_last(avl);
+ data = zfs_btree_last(bt, &bt_idx);
+ }
+
+ while (node != NULL) {
+ ASSERT3U(*data, ==, node->data);
+ if (forward == B_TRUE) {
+ data = zfs_btree_next(bt, &bt_idx, &bt_idx);
+ node = AVL_NEXT(avl, node);
+ } else {
+ data = zfs_btree_prev(bt, &bt_idx, &bt_idx);
+ node = AVL_PREV(avl, node);
+ }
+ }
+}
+
+static void
+verify_node(avl_tree_t *avl, zfs_btree_t *bt, int_node_t *node)
+{
+ zfs_btree_index_t bt_idx = {0};
+ zfs_btree_index_t bt_idx2 = {0};
+ int_node_t *inp;
+ uint64_t data = node->data;
+ uint64_t *rv = NULL;
+
+ ASSERT3U(avl_numnodes(avl), ==, zfs_btree_numnodes(bt));
+ ASSERT3P((rv = (uint64_t *)zfs_btree_find(bt, &data, &bt_idx)), !=,
+ NULL);
+ ASSERT3S(*rv, ==, data);
+ ASSERT3P(zfs_btree_get(bt, &bt_idx), !=, NULL);
+ ASSERT3S(data, ==, *(uint64_t *)zfs_btree_get(bt, &bt_idx));
+
+ if ((inp = AVL_NEXT(avl, node)) != NULL) {
+ ASSERT3P((rv = zfs_btree_next(bt, &bt_idx, &bt_idx2)), !=,
+ NULL);
+ ASSERT3P(rv, ==, zfs_btree_get(bt, &bt_idx2));
+ ASSERT3S(inp->data, ==, *rv);
+ } else {
+ ASSERT3U(data, ==, *(uint64_t *)zfs_btree_last(bt, &bt_idx));
+ }
+
+ if ((inp = AVL_PREV(avl, node)) != NULL) {
+ ASSERT3P((rv = zfs_btree_prev(bt, &bt_idx, &bt_idx2)), !=,
+ NULL);
+ ASSERT3P(rv, ==, zfs_btree_get(bt, &bt_idx2));
+ ASSERT3S(inp->data, ==, *rv);
+ } else {
+ ASSERT3U(data, ==, *(uint64_t *)zfs_btree_first(bt, &bt_idx));
+ }
+}
+
+/*
+ * Tests
+ */
+
+/* Verify that zfs_btree_find works correctly with a NULL index. */
+static int
+find_without_index(zfs_btree_t *bt, char *why)
+{
+ u_longlong_t *p, i = 12345;
+
+ zfs_btree_add(bt, &i);
+ if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, NULL)) == NULL ||
+ *p != i) {
+ snprintf(why, BUFSIZE, "Unexpectedly found %llu\n",
+ p == NULL ? 0 : *p);
+ return (1);
+ }
+
+ i++;
+
+ if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, NULL)) != NULL) {
+ snprintf(why, BUFSIZE, "Found bad value: %llu\n", *p);
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Verify simple insertion and removal from the tree. */
+static int
+insert_find_remove(zfs_btree_t *bt, char *why)
+{
+ u_longlong_t *p, i = 12345;
+ zfs_btree_index_t bt_idx = {0};
+
+ /* Insert 'i' into the tree, and attempt to find it again. */
+ zfs_btree_add(bt, &i);
+ if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, &bt_idx)) == NULL) {
+ snprintf(why, BUFSIZE, "Didn't find value in tree\n");
+ return (1);
+ } else if (*p != i) {
+ snprintf(why, BUFSIZE, "Found (%llu) in tree\n", *p);
+ return (1);
+ }
+ ASSERT3S(zfs_btree_numnodes(bt), ==, 1);
+ zfs_btree_verify(bt);
+
+ /* Remove 'i' from the tree, and verify it is not found. */
+ zfs_btree_remove(bt, &i);
+ if ((p = (u_longlong_t *)zfs_btree_find(bt, &i, &bt_idx)) != NULL) {
+ snprintf(why, BUFSIZE, "Found removed value (%llu)\n", *p);
+ return (1);
+ }
+ ASSERT3S(zfs_btree_numnodes(bt), ==, 0);
+ zfs_btree_verify(bt);
+
+ return (0);
+}
+
+/*
+ * Add a number of random entries into a btree and avl tree. Then walk them
+ * backwards and forwards while emptying the tree, verifying the trees look
+ * the same.
+ */
+static int
+drain_tree(zfs_btree_t *bt, char *why)
+{
+ uint64_t *p;
+ avl_tree_t avl;
+ int i = 0;
+ int_node_t *node;
+ avl_index_t avl_idx = {0};
+ zfs_btree_index_t bt_idx = {0};
+
+ avl_create(&avl, avl_compare, sizeof (int_node_t),
+ offsetof(int_node_t, node));
+
+ /* Fill both trees with the same data */
+ for (i = 0; i < 64 * 1024; i++) {
+ void *ret;
+
+ u_longlong_t randval = random();
+ node = malloc(sizeof (int_node_t));
+ if ((p = (uint64_t *)zfs_btree_find(bt, &randval, &bt_idx)) !=
+ NULL) {
+ continue;
+ }
+ zfs_btree_add_idx(bt, &randval, &bt_idx);
+
+ node->data = randval;
+ if ((ret = avl_find(&avl, node, &avl_idx)) != NULL) {
+ snprintf(why, BUFSIZE, "Found in avl: %llu\n", randval);
+ return (1);
+ }
+ avl_insert(&avl, node, avl_idx);
+ }
+
+ /* Remove data from either side of the trees, comparing the data */
+ while (avl_numnodes(&avl) != 0) {
+ uint64_t *data;
+
+ ASSERT3U(avl_numnodes(&avl), ==, zfs_btree_numnodes(bt));
+ if (avl_numnodes(&avl) % 2 == 0) {
+ node = avl_first(&avl);
+ data = zfs_btree_first(bt, &bt_idx);
+ } else {
+ node = avl_last(&avl);
+ data = zfs_btree_last(bt, &bt_idx);
+ }
+ ASSERT3U(node->data, ==, *data);
+ zfs_btree_remove_idx(bt, &bt_idx);
+ avl_remove(&avl, node);
+
+ if (avl_numnodes(&avl) == 0) {
+ break;
+ }
+
+ node = avl_first(&avl);
+ ASSERT3U(node->data, ==,
+ *(uint64_t *)zfs_btree_first(bt, NULL));
+ node = avl_last(&avl);
+ ASSERT3U(node->data, ==, *(uint64_t *)zfs_btree_last(bt, NULL));
+ }
+ ASSERT3S(zfs_btree_numnodes(bt), ==, 0);
+
+ void *avl_cookie = NULL;
+ while ((node = avl_destroy_nodes(&avl, &avl_cookie)) != NULL)
+ free(node);
+ avl_destroy(&avl);
+
+ return (0);
+}
+
+/*
+ * This test uses an avl and btree, and continually processes new random
+ * values. Each value is either removed or inserted, depending on whether
+ * or not it is found in the tree. The test periodically checks that both
+ * trees have the same data and does consistency checks. This stress
+ * option can also be run on its own from the command line.
+ */
+static int
+stress_tree(zfs_btree_t *bt, char *why)
+{
+ avl_tree_t avl;
+ int_node_t *node;
+ struct timeval tp;
+ time_t t0;
+ int insertions = 0, removals = 0, iterations = 0;
+ u_longlong_t max = 0, min = UINT64_MAX;
+
+ (void) gettimeofday(&tp, NULL);
+ t0 = tp.tv_sec;
+
+ avl_create(&avl, avl_compare, sizeof (int_node_t),
+ offsetof(int_node_t, node));
+
+ while (1) {
+ zfs_btree_index_t bt_idx = {0};
+ avl_index_t avl_idx = {0};
+
+ uint64_t randval = random() % tree_limit;
+ node = malloc(sizeof (*node));
+ node->data = randval;
+
+ max = randval > max ? randval : max;
+ min = randval < min ? randval : min;
+
+ void *ret = avl_find(&avl, node, &avl_idx);
+ if (ret == NULL) {
+ insertions++;
+ avl_insert(&avl, node, avl_idx);
+ ASSERT3P(zfs_btree_find(bt, &randval, &bt_idx), ==,
+ NULL);
+ zfs_btree_add_idx(bt, &randval, &bt_idx);
+ verify_node(&avl, bt, node);
+ } else {
+ removals++;
+ verify_node(&avl, bt, ret);
+ zfs_btree_remove(bt, &randval);
+ avl_remove(&avl, ret);
+ free(ret);
+ free(node);
+ }
+
+ zfs_btree_verify(bt);
+
+ iterations++;
+ if (iterations % contents_frequency == 0) {
+ verify_contents(&avl, bt);
+ }
+
+ zfs_btree_verify(bt);
+
+ (void) gettimeofday(&tp, NULL);
+ if (tp.tv_sec > t0 + stress_timeout) {
+ fprintf(stderr, "insertions/removals: %u/%u\nmax/min: "
+ "%llu/%llu\n", insertions, removals, max, min);
+ break;
+ }
+ }
+
+ void *avl_cookie = NULL;
+ while ((node = avl_destroy_nodes(&avl, &avl_cookie)) != NULL)
+ free(node);
+ avl_destroy(&avl);
+
+ if (stress_only) {
+ zfs_btree_index_t *idx = NULL;
+ uint64_t *rv;
+
+ while ((rv = zfs_btree_destroy_nodes(bt, &idx)) != NULL)
+ ;
+ zfs_btree_verify(bt);
+ }
+
+ return (0);
+}
+
+/*
+ * Verify inserting a duplicate value will cause a crash.
+ * Note: negative test; return of 0 is a failure.
+ */
+static int
+insert_duplicate(zfs_btree_t *bt)
+{
+ uint64_t *p, i = 23456;
+ zfs_btree_index_t bt_idx = {0};
+
+ if ((p = (uint64_t *)zfs_btree_find(bt, &i, &bt_idx)) != NULL) {
+ fprintf(stderr, "Found value in empty tree.\n");
+ return (0);
+ }
+ zfs_btree_add_idx(bt, &i, &bt_idx);
+ if ((p = (uint64_t *)zfs_btree_find(bt, &i, &bt_idx)) == NULL) {
+ fprintf(stderr, "Did not find expected value.\n");
+ return (0);
+ }
+
+ /* Crash on inserting a duplicate */
+ zfs_btree_add_idx(bt, &i, NULL);
+
+ return (0);
+}
+
+/*
+ * Verify removing a non-existent value will cause a crash.
+ * Note: negative test; return of 0 is a failure.
+ */
+static int
+remove_missing(zfs_btree_t *bt)
+{
+ uint64_t *p, i = 23456;
+ zfs_btree_index_t bt_idx = {0};
+
+ if ((p = (uint64_t *)zfs_btree_find(bt, &i, &bt_idx)) != NULL) {
+ fprintf(stderr, "Found value in empty tree.\n");
+ return (0);
+ }
+
+ /* Crash removing a nonexistent entry */
+ zfs_btree_remove(bt, &i);
+
+ return (0);
+}
+
+static int
+do_negative_test(zfs_btree_t *bt, char *test_name)
+{
+ int rval = 0;
+ struct rlimit rlim = {0};
+ setrlimit(RLIMIT_CORE, &rlim);
+
+ if (strcmp(test_name, "insert_duplicate") == 0) {
+ rval = insert_duplicate(bt);
+ } else if (strcmp(test_name, "remove_missing") == 0) {
+ rval = remove_missing(bt);
+ }
+
+ /*
+ * Return 0, since callers will expect non-zero return values for
+ * these tests, and we should have crashed before getting here anyway.
+ */
+ (void) fprintf(stderr, "Test: %s returned %d.\n", test_name, rval);
+ return (0);
+}
+
+typedef struct btree_test {
+ const char *name;
+ int (*func)(zfs_btree_t *, char *);
+} btree_test_t;
+
+static btree_test_t test_table[] = {
+ { "insert_find_remove", insert_find_remove },
+ { "find_without_index", find_without_index },
+ { "drain_tree", drain_tree },
+ { "stress_tree", stress_tree },
+ { NULL, NULL }
+};
+
+int
+main(int argc, char *argv[])
+{
+ char *negative_test = NULL;
+ int failed_tests = 0;
+ struct timeval tp;
+ zfs_btree_t bt;
+ char c;
+
+ while ((c = getopt(argc, argv, "c:l:n:r:st:")) != -1) {
+ switch (c) {
+ case 'c':
+ contents_frequency = atoi(optarg);
+ break;
+ case 'l':
+ tree_limit = atoi(optarg);
+ break;
+ case 'n':
+ negative_test = optarg;
+ break;
+ case 'r':
+ seed = atoi(optarg);
+ break;
+ case 's':
+ stress_only = B_TRUE;
+ break;
+ case 't':
+ stress_timeout = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(1);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ optind = 1;
+
+
+ if (seed == 0) {
+ (void) gettimeofday(&tp, NULL);
+ seed = tp.tv_sec;
+ }
+ srandom(seed);
+
+ zfs_btree_init();
+ zfs_btree_create(&bt, zfs_btree_compare, sizeof (uint64_t));
+
+ /*
+ * This runs the named negative test. None of them should
+ * return, as they both cause crashes.
+ */
+ if (negative_test) {
+ return (do_negative_test(&bt, negative_test));
+ }
+
+ fprintf(stderr, "Seed: %u\n", seed);
+
+ /*
+ * This is a stress test that does operations on a btree over the
+ * requested timeout period, verifying them against identical
+ * operations in an avl tree.
+ */
+ if (stress_only != 0) {
+ return (stress_tree(&bt, NULL));
+ }
+
+ /* Do the positive tests */
+ btree_test_t *test = &test_table[0];
+ while (test->name) {
+ int retval;
+ uint64_t *rv;
+ char why[BUFSIZE] = {0};
+ zfs_btree_index_t *idx = NULL;
+
+ (void) fprintf(stdout, "%-20s", test->name);
+ retval = test->func(&bt, why);
+
+ if (retval == 0) {
+ (void) fprintf(stdout, "ok\n");
+ } else {
+ (void) fprintf(stdout, "failed with %d\n", retval);
+ if (strlen(why) != 0)
+ (void) fprintf(stdout, "\t%s\n", why);
+ why[0] = '\0';
+ failed_tests++;
+ }
+
+ /* Remove all the elements and re-verify the tree */
+ while ((rv = zfs_btree_destroy_nodes(&bt, &idx)) != NULL)
+ ;
+ zfs_btree_verify(&bt);
+
+ test++;
+ }
+
+ zfs_btree_verify(&bt);
+ zfs_btree_fini();
+
+ return (failed_tests);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/.gitignore
new file mode 100644
index 000000000000..a8b44df7c5bf
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/.gitignore
@@ -0,0 +1 @@
+/chg_usr_exec
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/Makefile.am
new file mode 100644
index 000000000000..6f2968f1fac4
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = chg_usr_exec
+chg_usr_exec_SOURCES = chg_usr_exec.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/chg_usr_exec.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/chg_usr_exec.c
new file mode 100644
index 000000000000..1fa9e88a6fde
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/chg_usr_exec/chg_usr_exec.c
@@ -0,0 +1,77 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pwd.h>
+
+#define EXECSHELL "/bin/sh"
+
+int
+main(int argc, char *argv[])
+{
+ char *plogin = NULL;
+ char cmds[BUFSIZ] = { 0 };
+ char sep[] = " ";
+ struct passwd *ppw = NULL;
+ int i, len;
+
+ if (argc < 3 || strlen(argv[1]) == 0) {
+ (void) printf("\tUsage: %s <login> <commands> ...\n", argv[0]);
+ return (1);
+ }
+
+ plogin = argv[1];
+ len = 0;
+ for (i = 2; i < argc; i++) {
+ (void) snprintf(cmds+len, sizeof (cmds)-len,
+ "%s%s", argv[i], sep);
+ len += strlen(argv[i]) + strlen(sep);
+ }
+
+ if ((ppw = getpwnam(plogin)) == NULL) {
+ perror("getpwnam");
+ return (errno);
+ }
+ if (setgid(ppw->pw_gid) != 0) {
+ perror("setgid");
+ return (errno);
+ }
+ if (setuid(ppw->pw_uid) != 0) {
+ perror("setuid");
+ return (errno);
+ }
+
+ if (execl(EXECSHELL, "sh", "-c", cmds, (char *)NULL) != 0) {
+ perror("execl: " EXECSHELL);
+ return (errno);
+ }
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/.gitignore
new file mode 100644
index 000000000000..fa9fb6c509a7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/.gitignore
@@ -0,0 +1 @@
+/devname2devid
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/Makefile.am
new file mode 100644
index 000000000000..b8b630dc2dd4
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+if WANT_DEVNAME2DEVID
+pkgexec_PROGRAMS = devname2devid
+devname2devid_SOURCES = devname2devid.c
+devname2devid_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS)
+devname2devid_LDADD = $(LIBUDEV_LIBS)
+endif
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/devname2devid.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/devname2devid.c
new file mode 100644
index 000000000000..91e59c589fd5
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/devname2devid/devname2devid.c
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <libudev.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+/*
+ * Linux persistent device strings for vdev labels
+ *
+ * based on udev_device_get_devid() at zfs/lib/libzfs/libzfs_import.c
+ */
+
+#define DEV_BYID_PATH "/dev/disk/by-id/"
+
+static int
+udev_device_get_devid(struct udev_device *dev, char *bufptr, size_t buflen)
+{
+ struct udev_list_entry *entry;
+ const char *bus;
+ char devbyid[MAXPATHLEN];
+
+ /* The bus based by-id path is preferred */
+ bus = udev_device_get_property_value(dev, "ID_BUS");
+
+ if (bus == NULL) {
+ const char *dm_uuid;
+
+ /*
+ * For multipath nodes use the persistent uuid based identifier
+ *
+ * Example: 'dm-uuid-mpath-35000c5006304de3f'
+ */
+ dm_uuid = udev_device_get_property_value(dev, "DM_UUID");
+ if (dm_uuid != NULL) {
+ (void) snprintf(bufptr, buflen, "dm-uuid-%s", dm_uuid);
+ return (0);
+ }
+ return (ENODATA);
+ }
+
+ /*
+ * locate the bus specific by-id link
+ *
+ * Example: 'scsi-MG03SCA300_350000494a8cb3d67-part1'
+ */
+ (void) snprintf(devbyid, sizeof (devbyid), "%s%s-", DEV_BYID_PATH, bus);
+ entry = udev_device_get_devlinks_list_entry(dev);
+ while (entry != NULL) {
+ const char *name;
+
+ name = udev_list_entry_get_name(entry);
+ if (strncmp(name, devbyid, strlen(devbyid)) == 0) {
+ name += strlen(DEV_BYID_PATH);
+ (void) stpncpy(bufptr, name, buflen - 1);
+ bufptr[buflen - 1] = '\0';
+ return (0);
+ }
+ entry = udev_list_entry_get_next(entry);
+ }
+
+ return (ENODATA);
+}
+
+/*
+ * Usage: devname2devid <devicepath>
+ *
+ * Examples:
+ * # ./devname2devid /dev/sda1
+ * devid scsi-350000394a8caede4-part1
+ *
+ * # ./devname2devid /dev/dm-1
+ * devid: 'dm-uuid-mpath-35000c5006304de3f'
+ *
+ * This program accepts a disk or disk slice path and prints a
+ * device id.
+ *
+ * Exit values:
+ * 0 - means success
+ * 1 - means failure
+ *
+ */
+int
+main(int argc, char *argv[])
+{
+ struct udev *udev;
+ struct udev_device *dev = NULL;
+ char devid[128], nodepath[MAXPATHLEN];
+ char *device, *sysname;
+ int ret;
+
+ if (argc == 1) {
+ (void) printf("%s <devicepath> [search path]\n", argv[0]);
+ exit(1);
+ }
+ device = argv[1];
+
+ if ((udev = udev_new()) == NULL) {
+ perror("udev_new");
+ exit(1);
+ }
+
+ /* resolve path to a runtime device node instance */
+ if (realpath(device, nodepath) == NULL) {
+ perror("realpath");
+ exit(1);
+ }
+ sysname = strrchr(nodepath, '/') + 1;
+
+ if ((dev = udev_device_new_from_subsystem_sysname(udev, "block",
+ sysname)) == NULL) {
+ perror(sysname);
+ exit(1);
+ }
+
+ if ((ret = udev_device_get_devid(dev, devid, sizeof (devid))) != 0) {
+ udev_device_unref(dev);
+ errno = ret;
+ perror(sysname);
+ exit(1);
+ }
+
+ (void) printf("devid %s\n", devid);
+
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/.gitignore
new file mode 100644
index 000000000000..ec9a15f17a1d
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/.gitignore
@@ -0,0 +1 @@
+/dir_rd_update
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/Makefile.am
new file mode 100644
index 000000000000..27cc9e97e0e9
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = dir_rd_update
+dir_rd_update_SOURCES = dir_rd_update.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/dir_rd_update.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/dir_rd_update.c
new file mode 100644
index 000000000000..ea46f047a35e
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/dir_rd_update/dir_rd_update.c
@@ -0,0 +1,136 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Assertion:
+ *
+ * A read operation and directory update operation performed
+ * concurrently on the same directory can lead to deadlock
+ * on a UFS logging file system, but not on a ZFS file system.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#define TMP_DIR /tmp
+
+static char dirpath[256];
+
+int
+main(int argc, char **argv)
+{
+ char *cp1 = "";
+ int i = 0;
+ int ret = 0;
+ int testdd = 0;
+ pid_t pid;
+ static const int op_num = 5;
+
+ if (argc == 1) {
+ (void) printf("Usage: %s <mount point>\n", argv[0]);
+ exit(-1);
+ }
+ for (i = 0; i < 256; i++) {
+ dirpath[i] = 0;
+ }
+
+ cp1 = argv[1];
+ if (strlen(cp1) >= (sizeof (dirpath) - strlen("TMP_DIR"))) {
+ (void) printf("The string length of mount point is "
+ "too large\n");
+ exit(-1);
+ }
+ (void) strcpy(&dirpath[0], (const char *)cp1);
+ (void) strcat(&dirpath[strlen(dirpath)], "TMP_DIR");
+
+ ret = mkdir(dirpath, 0777);
+ if (ret != 0) {
+ if (errno != EEXIST) {
+ (void) printf("%s: mkdir(<%s>, 0777) failed: errno "
+ "(decimal)=%d\n", argv[0], dirpath, errno);
+ exit(-1);
+ }
+ }
+ testdd = open(dirpath, O_RDONLY|O_RSYNC|O_SYNC|O_DSYNC);
+ if (testdd < 0) {
+ (void) printf("%s: open(<%s>, O_RDONLY|O_RSYNC|O_SYNC|O_DSYNC)"
+ " failed: errno (decimal)=%d\n", argv[0], dirpath, errno);
+ exit(-1);
+ } else {
+ (void) close(testdd);
+ }
+ pid = fork();
+ if (pid > 0) {
+ int fd = open(dirpath, O_RDONLY|O_RSYNC|O_SYNC|O_DSYNC);
+ char buf[16];
+ int rdret;
+ int j = 0;
+
+ if (fd < 0) {
+ (void) printf("%s: open <%s> again failed:"
+ " errno = %d\n", argv[0], dirpath, errno);
+ exit(-1);
+ }
+
+ while (j < op_num) {
+ (void) sleep(1);
+ rdret = read(fd, buf, 16);
+ if (rdret == -1) {
+ (void) printf("readdir failed");
+ }
+ j++;
+ }
+ (void) close(fd);
+ } else if (pid == 0) {
+ int fd = open(dirpath, O_RDONLY);
+ int chownret;
+ int k = 0;
+
+ if (fd < 0) {
+ (void) printf("%s: open(<%s>, O_RDONLY) again failed:"
+ " errno (decimal)=%d\n", argv[0], dirpath, errno);
+ exit(-1);
+ }
+
+ while (k < op_num) {
+ (void) sleep(1);
+ chownret = fchown(fd, 0, 0);
+ if (chownret == -1) {
+ (void) printf("chown failed");
+ }
+
+ k++;
+ }
+ (void) close(fd);
+ }
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/.gitignore
new file mode 100644
index 000000000000..24fe113221d2
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/.gitignore
@@ -0,0 +1 @@
+/file_check
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/Makefile.am
new file mode 100644
index 000000000000..13027ef5bd02
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = file_check
+file_check_SOURCES = file_check.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/file_check.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/file_check.c
new file mode 100644
index 000000000000..5df0ea735bfd
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_check/file_check.c
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include "../file_common.h"
+
+static unsigned char bigbuffer[BIGBUFFERSIZE];
+
+/*
+ * Given a filename, check that the file consists entirely
+ * of a particular pattern. If the pattern is not specified a
+ * default will be used. For default values see file_common.h
+ */
+int
+main(int argc, char **argv)
+{
+ int bigfd;
+ long i, n;
+ unsigned char fillchar = DATA;
+ int bigbuffersize = BIGBUFFERSIZE;
+ int64_t read_count = 0;
+
+ /*
+ * Validate arguments
+ */
+ if (argc < 2) {
+ (void) printf("Usage: %s filename [pattern]\n",
+ argv[0]);
+ exit(1);
+ }
+
+ if (argv[2]) {
+ fillchar = atoi(argv[2]);
+ }
+
+ /*
+ * Read the file contents and check every character
+ * against the supplied pattern. Abort if the
+ * pattern check fails.
+ */
+ if ((bigfd = open(argv[1], O_RDONLY)) == -1) {
+ (void) printf("open %s failed %d\n", argv[1], errno);
+ exit(1);
+ }
+
+ do {
+ if ((n = read(bigfd, &bigbuffer, bigbuffersize)) == -1) {
+ (void) printf("read failed (%ld), %d\n", n, errno);
+ exit(errno);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (bigbuffer[i] != fillchar) {
+ (void) printf("error %s: 0x%x != 0x%x)\n",
+ argv[1], bigbuffer[i], fillchar);
+ exit(1);
+ }
+ }
+
+ read_count += n;
+ } while (n == bigbuffersize);
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_common.h b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_common.h
new file mode 100644
index 000000000000..64b1777a9ae8
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_common.h
@@ -0,0 +1,70 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef FILE_COMMON_H
+#define FILE_COMMON_H
+
+/*
+ * header file for file_* utilities. These utilities
+ * are used by the test cases to perform various file
+ * operations (append writes, for example).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#define BLOCKSZ 8192
+#define DATA 0xa5
+#define DATA_RANGE 120
+#define BIGBUFFERSIZE 0x800000
+#define BIGFILESIZE 20
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FILE_COMMON_H */
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/.gitignore
new file mode 100644
index 000000000000..90b149ff51c3
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/.gitignore
@@ -0,0 +1 @@
+/file_trunc
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/Makefile.am
new file mode 100644
index 000000000000..0455eb4a4633
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = file_trunc
+file_trunc_SOURCES = file_trunc.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/file_trunc.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/file_trunc.c
new file mode 100644
index 000000000000..69096752efa2
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_trunc/file_trunc.c
@@ -0,0 +1,240 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <string.h>
+#include <time.h>
+#include <inttypes.h>
+
+#define FSIZE 256*1024*1024
+#define BSIZE 512
+
+/* Initialize Globals */
+static long fsize = FSIZE;
+static size_t bsize = BSIZE;
+static int count = 0;
+static int rflag = 0;
+static int seed = 0;
+static int vflag = 0;
+static int errflag = 0;
+static off_t offset = 0;
+static char *filename = NULL;
+
+static void usage(char *execname);
+static void parse_options(int argc, char *argv[]);
+static void do_write(int fd);
+static void do_trunc(int fd);
+
+static void
+usage(char *execname)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-b blocksize] [-c count] [-f filesize]"
+ " [-o offset] [-s seed] [-r] [-v] filename\n", execname);
+ (void) exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i = 0;
+ int fd = -1;
+
+ parse_options(argc, argv);
+
+ fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0) {
+ perror("open");
+ exit(3);
+ }
+
+ for (i = 0; count == 0 || i < count; i++) {
+ (void) do_write(fd);
+ (void) do_trunc(fd);
+ }
+
+ (void) close(fd);
+ return (0);
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+ int c;
+
+ extern char *optarg;
+ extern int optind, optopt;
+
+ count = fsize / bsize;
+ seed = time(NULL);
+ while ((c = getopt(argc, argv, "b:c:f:o:rs:v")) != -1) {
+ switch (c) {
+ case 'b':
+ bsize = atoi(optarg);
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ break;
+
+ case 'f':
+ fsize = atoi(optarg);
+ break;
+
+ case 'o':
+ offset = atoi(optarg);
+ break;
+
+ case 'r':
+ rflag++;
+ break;
+
+ case 's':
+ seed = atoi(optarg);
+ break;
+
+ case 'v':
+ vflag++;
+ break;
+
+ case ':':
+ (void) fprintf(stderr,
+ "Option -%c requires an operand\n", optopt);
+ errflag++;
+ break;
+
+ case '?':
+ (void) fprintf(stderr,
+ "Unrecognized option: -%c\n", optopt);
+ errflag++;
+ break;
+ }
+
+ if (errflag) {
+ (void) usage(argv[0]);
+ }
+ }
+ if (argc <= optind) {
+ (void) fprintf(stderr,
+ "No filename specified\n");
+ usage(argv[0]);
+ }
+ filename = argv[optind];
+
+ if (vflag) {
+ (void) fprintf(stderr, "Seed = %d\n", seed);
+ }
+ srandom(seed);
+}
+
+static void
+do_write(int fd)
+{
+ off_t roffset = 0;
+ char *buf = NULL;
+ char *rbuf = NULL;
+
+ buf = (char *)calloc(1, bsize);
+ rbuf = (char *)calloc(1, bsize);
+ if (buf == NULL || rbuf == NULL) {
+ perror("malloc");
+ exit(4);
+ }
+
+ roffset = random() % fsize;
+ if (lseek64(fd, (offset + roffset), SEEK_SET) < 0) {
+ perror("lseek");
+ exit(5);
+ }
+
+ (void) strcpy(buf, "ZFS Test Suite Truncation Test");
+ if (write(fd, buf, bsize) < bsize) {
+ perror("write");
+ exit(6);
+ }
+
+ if (rflag) {
+ if (lseek64(fd, (offset + roffset), SEEK_SET) < 0) {
+ perror("lseek");
+ exit(7);
+ }
+
+ if (read(fd, rbuf, bsize) < bsize) {
+ perror("read");
+ exit(8);
+ }
+
+ if (memcmp(buf, rbuf, bsize) != 0) {
+ perror("memcmp");
+ exit(9);
+ }
+ }
+ if (vflag) {
+ (void) fprintf(stderr,
+ "Wrote to offset %" PRId64 "\n", (offset + roffset));
+ if (rflag) {
+ (void) fprintf(stderr,
+ "Read back from offset %" PRId64 "\n",
+ (offset + roffset));
+ }
+ }
+
+ (void) free(buf);
+ (void) free(rbuf);
+}
+
+static void
+do_trunc(int fd)
+{
+ off_t roffset = 0;
+
+ roffset = random() % fsize;
+ if (ftruncate64(fd, (offset + roffset)) < 0) {
+ perror("truncate");
+ exit(7);
+ }
+
+ if (vflag) {
+ (void) fprintf(stderr, "Truncated at offset %" PRId64 "\n",
+ (offset + roffset));
+ }
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/.gitignore
new file mode 100644
index 000000000000..9f691d580a57
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/.gitignore
@@ -0,0 +1 @@
+/file_write
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/Makefile.am
new file mode 100644
index 000000000000..60895711e7dc
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = file_write
+file_write_SOURCES = file_write.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/file_write.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/file_write.c
new file mode 100644
index 000000000000..45d296db43c6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/file_write/file_write.c
@@ -0,0 +1,258 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include "../file_common.h"
+#include <libgen.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdint.h>
+
+static unsigned char bigbuffer[BIGBUFFERSIZE];
+
+/*
+ * Writes (or appends) a given value to a file repeatedly.
+ * See header file for defaults.
+ */
+
+static void usage(char *);
+
+/*
+ * psudo-randomize the buffer
+ */
+static void randomize_buffer(int block_size) {
+ int i;
+ char rnd = rand() & 0xff;
+ for (i = 0; i < block_size; i++)
+ bigbuffer[i] ^= rnd;
+}
+
+int
+main(int argc, char **argv)
+{
+ int bigfd;
+ int c;
+ int oflag = 0;
+ int err = 0;
+ int k;
+ long i;
+ int64_t good_writes = 0;
+ uchar_t nxtfillchar;
+ char *prog = argv[0];
+ /*
+ * Default Parameters
+ */
+ int write_count = BIGFILESIZE;
+ uchar_t fillchar = DATA;
+ int block_size = BLOCKSZ;
+ char *filename = NULL;
+ char *operation = NULL;
+ offset_t noffset, offset = 0;
+ int verbose = 0;
+ int rsync = 0;
+ int wsync = 0;
+
+ /*
+ * Process Arguments
+ */
+ while ((c = getopt(argc, argv, "b:c:d:s:f:o:vwr")) != -1) {
+ switch (c) {
+ case 'b':
+ block_size = atoi(optarg);
+ break;
+ case 'c':
+ write_count = atoi(optarg);
+ break;
+ case 'd':
+ if (optarg[0] == 'R')
+ fillchar = 'R'; /* R = random data */
+ else
+ fillchar = atoi(optarg);
+ break;
+ case 's':
+ offset = atoll(optarg);
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case 'o':
+ operation = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ wsync = 1;
+ break;
+ case 'r':
+ rsync = 1;
+ break;
+ case '?':
+ (void) printf("unknown arg %c\n", optopt);
+ usage(prog);
+ break;
+ }
+ }
+
+ /*
+ * Validate Parameters
+ */
+ if (!filename) {
+ (void) printf("Filename not specified (-f <file>)\n");
+ err++;
+ }
+
+ if (!operation) {
+ (void) printf("Operation not specified (-o <operation>).\n");
+ err++;
+ }
+
+ if (block_size > BIGBUFFERSIZE) {
+ (void) printf("block_size is too large max==%d.\n",
+ BIGBUFFERSIZE);
+ err++;
+ }
+
+ if (err) {
+ usage(prog); /* no return */
+ return (1);
+ }
+
+ /*
+ * Prepare the buffer and determine the requested operation
+ */
+ nxtfillchar = fillchar;
+ k = 0;
+
+ if (fillchar == 'R')
+ srand(time(NULL));
+
+ for (i = 0; i < block_size; i++) {
+ bigbuffer[i] = nxtfillchar;
+
+ if (fillchar == 0) {
+ if ((k % DATA_RANGE) == 0) {
+ k = 0;
+ }
+ nxtfillchar = k++;
+ } else if (fillchar == 'R') {
+ nxtfillchar = rand() & 0xff;
+ }
+ }
+
+ /*
+ * using the strncmp of operation will make the operation match the
+ * first shortest match - as the operations are unique from the first
+ * character this means that we match single character operations
+ */
+ if ((strncmp(operation, "create", strlen(operation) + 1)) == 0 ||
+ (strncmp(operation, "overwrite", strlen(operation) + 1)) == 0) {
+ oflag = (O_RDWR|O_CREAT);
+ } else if ((strncmp(operation, "append", strlen(operation) + 1)) == 0) {
+ oflag = (O_RDWR|O_APPEND);
+ } else {
+ (void) printf("valid operations are <create|append> not '%s'\n",
+ operation);
+ usage(prog);
+ }
+
+ if (rsync) {
+ oflag = oflag | O_RSYNC;
+ }
+
+ if (wsync) {
+ oflag = oflag | O_SYNC;
+ }
+
+ /*
+ * Given an operation (create/overwrite/append), open the file
+ * accordingly and perform a write of the appropriate type.
+ */
+ if ((bigfd = open(filename, oflag, 0666)) == -1) {
+ (void) printf("open %s: failed [%s]%d. Aborting!\n", filename,
+ strerror(errno), errno);
+ exit(errno);
+ }
+ noffset = lseek64(bigfd, offset, SEEK_SET);
+ if (noffset != offset) {
+ (void) printf("llseek %s (%lld/%lld) failed [%s]%d.Aborting!\n",
+ filename, offset, noffset, strerror(errno), errno);
+ exit(errno);
+ }
+
+ if (verbose) {
+ (void) printf("%s: block_size = %d, write_count = %d, "
+ "offset = %lld, ", filename, block_size,
+ write_count, offset);
+ if (fillchar == 'R') {
+ (void) printf("data = [random]\n");
+ } else {
+ (void) printf("data = %s%d\n",
+ (fillchar == 0) ? "0->" : "",
+ (fillchar == 0) ? DATA_RANGE : fillchar);
+ }
+ }
+
+ for (i = 0; i < write_count; i++) {
+ ssize_t n;
+ if (fillchar == 'R')
+ randomize_buffer(block_size);
+
+ if ((n = write(bigfd, &bigbuffer, block_size)) == -1) {
+ (void) printf("write failed (%ld), good_writes = %"
+ PRId64 ", " "error: %s[%d]\n",
+ (long)n, good_writes,
+ strerror(errno),
+ errno);
+ exit(errno);
+ }
+ good_writes++;
+ }
+
+ if (verbose) {
+ (void) printf("Success: good_writes = %" PRId64 "(%"
+ PRId64 ")\n", good_writes, (good_writes * block_size));
+ }
+
+ return (0);
+}
+
+static void
+usage(char *prog)
+{
+ (void) printf("Usage: %s [-v] -o {create,overwrite,append} -f file_name"
+ " [-b block_size]\n"
+ "\t[-s offset] [-c write_count] [-d data]\n\n"
+ "Where [data] equal to zero causes chars "
+ "0->%d to be repeated throughout, or [data]\n"
+ "equal to 'R' for psudorandom data.\n",
+ prog, DATA_RANGE);
+
+ exit(1);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/.gitignore
new file mode 100644
index 000000000000..f5fc360a6839
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/.gitignore
@@ -0,0 +1 @@
+/get_diff
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/Makefile.am
new file mode 100644
index 000000000000..06c39ddd81ce
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = get_diff
+get_diff_SOURCES = get_diff.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/get_diff.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/get_diff.c
new file mode 100644
index 000000000000..2799f46b0747
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/get_diff/get_diff.c
@@ -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) 2018 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+static void
+usage(char *msg, int exit_value)
+{
+ (void) fprintf(stderr, "get_diff file redacted_file\n");
+ (void) fprintf(stderr, "%s\n", msg);
+ exit(exit_value);
+}
+
+/*
+ * This utility compares two files, an original and its redacted counterpart
+ * (in that order). It compares the files 512 bytes at a time, printing out
+ * any ranges (as offset and length) where the redacted file does not match
+ * the original. This output is used to verify that the expected ranges of
+ * a redacted file do not contain the original data.
+ */
+int
+main(int argc, char *argv[])
+{
+ off_t diff_off = 0, diff_len = 0, off = 0;
+ int fd1, fd2;
+ char *fname1, *fname2;
+ char buf1[DEV_BSIZE], buf2[DEV_BSIZE];
+ ssize_t bytes;
+
+ if (argc != 3)
+ usage("Incorrect number of arguments.", 1);
+
+ if ((fname1 = argv[1]) == NULL)
+ usage("Filename missing.", 1);
+ if ((fd1 = open(fname1, O_LARGEFILE | O_RDONLY)) < 0) {
+ perror("open1 failed");
+ exit(1);
+ }
+
+ if ((fname2 = argv[2]) == NULL)
+ usage("Redacted filename missing.", 1);
+ if ((fd2 = open(fname2, O_LARGEFILE | O_RDONLY)) < 0) {
+ perror("open2 failed");
+ exit(1);
+ }
+
+ while ((bytes = pread(fd1, buf1, DEV_BSIZE, off)) > 0) {
+ if (pread(fd2, buf2, DEV_BSIZE, off) < 0) {
+ if (errno == EIO) {
+ /*
+ * A read in a redacted section of a file will
+ * fail with EIO. If we get EIO, continue on
+ * but ensure that a comparison of buf1 and
+ * buf2 will fail, indicating a redacted block.
+ */
+ buf2[0] = ~buf1[0];
+ } else {
+ perror("pread failed");
+ exit(1);
+ }
+ }
+ if (memcmp(buf1, buf2, bytes) == 0) {
+ if (diff_len != 0) {
+ (void) fprintf(stdout, "%lld,%lld\n",
+ (long long)diff_off, (long long)diff_len);
+ assert(off == diff_off + diff_len);
+ diff_len = 0;
+ }
+ diff_off = 0;
+ } else {
+ if (diff_len == 0)
+ diff_off = off;
+ assert(off == diff_off + diff_len);
+ diff_len += bytes;
+ }
+ off += bytes;
+ }
+
+ if (diff_len != 0 && diff_len != 0) {
+ (void) fprintf(stdout, "%lld,%lld\n", (long long)diff_off,
+ (long long)diff_len);
+ }
+
+ (void) close(fd1);
+ (void) close(fd2);
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/.gitignore
new file mode 100644
index 000000000000..f8f480d06542
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/.gitignore
@@ -0,0 +1 @@
+/largest_file
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/Makefile.am
new file mode 100644
index 000000000000..a3e4e9337cf0
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = largest_file
+largest_file_SOURCES = largest_file.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/largest_file.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/largest_file.c
new file mode 100644
index 000000000000..00e1019cc8e4
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/largest_file/largest_file.c
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+#include "../file_common.h"
+#include <sys/param.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stdtypes.h>
+#include <unistd.h>
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Assertion:
+ * The last byte of the largest file size can be
+ * accessed without any errors. Also, the writing
+ * beyond the last byte of the largest file size
+ * will produce an errno of EFBIG.
+ *
+ * --------------------------------------------------------------
+ * If the write() system call below returns a "1",
+ * then the last byte can be accessed.
+ * --------------------------------------------------------------
+ */
+static void sigxfsz(int);
+static void usage(char *);
+
+int
+main(int argc, char **argv)
+{
+ int fd = 0;
+ offset_t offset = (MAXOFFSET_T - 1);
+ offset_t llseek_ret = 0;
+ int write_ret = 0;
+ int err = 0;
+ char mybuf[5] = "aaaa\0";
+ char *testfile;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ struct sigaction sa;
+
+ if (argc != 2) {
+ usage(argv[0]);
+ }
+
+ if (sigemptyset(&sa.sa_mask) == -1)
+ return (errno);
+ sa.sa_flags = 0;
+ sa.sa_handler = sigxfsz;
+ if (sigaction(SIGXFSZ, &sa, NULL) == -1)
+ return (errno);
+
+ testfile = strdup(argv[1]);
+
+ fd = open(testfile, O_CREAT | O_RDWR, mode);
+ if (fd < 0) {
+ err = errno;
+ perror("Failed to create testfile");
+ free(testfile);
+ return (err);
+ }
+
+ llseek_ret = lseek64(fd, offset, SEEK_SET);
+ if (llseek_ret < 0) {
+ err = errno;
+ perror("Failed to seek to end of testfile");
+ goto out;
+ }
+
+ write_ret = write(fd, mybuf, 1);
+ if (write_ret < 0) {
+ err = errno;
+ perror("Failed to write to end of file");
+ goto out;
+ }
+
+ offset = 0;
+ llseek_ret = lseek64(fd, offset, SEEK_CUR);
+ if (llseek_ret < 0) {
+ err = errno;
+ perror("Failed to seek to end of file");
+ goto out;
+ }
+
+ write_ret = write(fd, mybuf, 1);
+ if (write_ret < 0) {
+ if (errno == EFBIG || errno == EINVAL) {
+ (void) printf("write errno=EFBIG|EINVAL: success\n");
+ err = 0;
+ } else {
+ err = errno;
+ perror("Did not receive EFBIG");
+ }
+ } else {
+ (void) printf("write completed successfully, test failed\n");
+ err = 1;
+ }
+
+out:
+ (void) unlink(testfile);
+ free(testfile);
+ close(fd);
+ return (err);
+}
+
+static void
+usage(char *name)
+{
+ (void) printf("%s <testfile>\n", name);
+ exit(1);
+}
+
+/* ARGSUSED */
+static void
+sigxfsz(int signo)
+{
+ (void) printf("\nlargest_file: sigxfsz() caught SIGXFSZ\n");
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/.gitignore
new file mode 100644
index 000000000000..c8796008483f
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/.gitignore
@@ -0,0 +1 @@
+/libzfs_input_check
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/Makefile.am
new file mode 100644
index 000000000000..ba02f93fe2ab
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = libzfs_input_check
+
+libzfs_input_check_SOURCES = libzfs_input_check.c
+libzfs_input_check_LDADD = \
+ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
+ $(abs_top_builddir)/lib/libnvpair/libnvpair.la
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
new file mode 100644
index 000000000000..9fee37357fc3
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
@@ -0,0 +1,1060 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2018 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <libzfs_core.h>
+#include <libzutil.h>
+
+#include <sys/nvpair.h>
+#include <sys/zfs_ioctl.h>
+
+/*
+ * Test the nvpair inputs for the non-legacy zfs ioctl commands.
+ */
+
+boolean_t unexpected_failures;
+int zfs_fd;
+const char *active_test;
+
+/*
+ * Tracks which zfs_ioc_t commands were tested
+ */
+boolean_t ioc_tested[ZFS_IOC_LAST - ZFS_IOC_FIRST];
+
+/*
+ * Legacy ioctls that are skipped (for now)
+ */
+static unsigned ioc_skip[] = {
+ ZFS_IOC_POOL_CREATE,
+ ZFS_IOC_POOL_DESTROY,
+ ZFS_IOC_POOL_IMPORT,
+ ZFS_IOC_POOL_EXPORT,
+ ZFS_IOC_POOL_CONFIGS,
+ ZFS_IOC_POOL_STATS,
+ ZFS_IOC_POOL_TRYIMPORT,
+ ZFS_IOC_POOL_SCAN,
+ ZFS_IOC_POOL_FREEZE,
+ ZFS_IOC_POOL_UPGRADE,
+ ZFS_IOC_POOL_GET_HISTORY,
+
+ ZFS_IOC_VDEV_ADD,
+ ZFS_IOC_VDEV_REMOVE,
+ ZFS_IOC_VDEV_SET_STATE,
+ ZFS_IOC_VDEV_ATTACH,
+ ZFS_IOC_VDEV_DETACH,
+ ZFS_IOC_VDEV_SETPATH,
+ ZFS_IOC_VDEV_SETFRU,
+
+ ZFS_IOC_OBJSET_STATS,
+ ZFS_IOC_OBJSET_ZPLPROPS,
+ ZFS_IOC_DATASET_LIST_NEXT,
+ ZFS_IOC_SNAPSHOT_LIST_NEXT,
+ ZFS_IOC_SET_PROP,
+ ZFS_IOC_DESTROY,
+ ZFS_IOC_RENAME,
+ ZFS_IOC_RECV,
+ ZFS_IOC_SEND,
+ ZFS_IOC_INJECT_FAULT,
+ ZFS_IOC_CLEAR_FAULT,
+ ZFS_IOC_INJECT_LIST_NEXT,
+ ZFS_IOC_ERROR_LOG,
+ ZFS_IOC_CLEAR,
+ ZFS_IOC_PROMOTE,
+ ZFS_IOC_DSOBJ_TO_DSNAME,
+ ZFS_IOC_OBJ_TO_PATH,
+ ZFS_IOC_POOL_SET_PROPS,
+ ZFS_IOC_POOL_GET_PROPS,
+ ZFS_IOC_SET_FSACL,
+ ZFS_IOC_GET_FSACL,
+ ZFS_IOC_SHARE,
+ ZFS_IOC_INHERIT_PROP,
+ ZFS_IOC_SMB_ACL,
+ ZFS_IOC_USERSPACE_ONE,
+ ZFS_IOC_USERSPACE_MANY,
+ ZFS_IOC_USERSPACE_UPGRADE,
+ ZFS_IOC_OBJSET_RECVD_PROPS,
+ ZFS_IOC_VDEV_SPLIT,
+ ZFS_IOC_NEXT_OBJ,
+ ZFS_IOC_DIFF,
+ ZFS_IOC_TMP_SNAPSHOT,
+ ZFS_IOC_OBJ_TO_STATS,
+ ZFS_IOC_SPACE_WRITTEN,
+ ZFS_IOC_POOL_REGUID,
+ ZFS_IOC_SEND_PROGRESS,
+ ZFS_IOC_EVENTS_NEXT,
+ ZFS_IOC_EVENTS_CLEAR,
+ ZFS_IOC_EVENTS_SEEK,
+ ZFS_IOC_NEXTBOOT,
+ ZFS_IOC_JAIL,
+ ZFS_IOC_UNJAIL,
+};
+
+
+#define IOC_INPUT_TEST(ioc, name, req, opt, err) \
+ IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, B_FALSE)
+
+#define IOC_INPUT_TEST_WILD(ioc, name, req, opt, err) \
+ IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, B_TRUE)
+
+#define IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, wild) \
+ do { \
+ active_test = __func__ + 5; \
+ ioc_tested[ioc - ZFS_IOC_FIRST] = B_TRUE; \
+ lzc_ioctl_test(ioc, name, req, opt, err, wild); \
+ } while (0)
+
+/*
+ * run a zfs ioctl command, verify expected results and log failures
+ */
+static void
+lzc_ioctl_run(zfs_ioc_t ioc, const char *name, nvlist_t *innvl, int expected)
+{
+ zfs_cmd_t zc = {"\0"};
+ char *packed = NULL;
+ const char *variant;
+ size_t size = 0;
+ int error = 0;
+
+ switch (expected) {
+ case ZFS_ERR_IOC_ARG_UNAVAIL:
+ variant = "unsupported input";
+ break;
+ case ZFS_ERR_IOC_ARG_REQUIRED:
+ variant = "missing input";
+ break;
+ case ZFS_ERR_IOC_ARG_BADTYPE:
+ variant = "invalid input type";
+ break;
+ default:
+ variant = "valid input";
+ break;
+ }
+
+ packed = fnvlist_pack(innvl, &size);
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
+ zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_nvlist_src_size = size;
+ zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
+ zc.zc_nvlist_dst = (uint64_t)(uintptr_t)malloc(zc.zc_nvlist_dst_size);
+
+ if (zfs_ioctl_fd(zfs_fd, ioc, &zc) != 0)
+ error = errno;
+
+ if (error != expected) {
+ unexpected_failures = B_TRUE;
+ (void) fprintf(stderr, "%s: Unexpected result with %s, "
+ "error %d (expecting %d)\n",
+ active_test, variant, error, expected);
+ }
+
+ fnvlist_pack_free(packed, size);
+ free((void *)(uintptr_t)zc.zc_nvlist_dst);
+}
+
+/*
+ * Test each ioc for the following ioctl input errors:
+ * ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
+ * ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
+ * ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
+ */
+static int
+lzc_ioctl_test(zfs_ioc_t ioc, const char *name, nvlist_t *required,
+ nvlist_t *optional, int expected_error, boolean_t wildcard)
+{
+ nvlist_t *input = fnvlist_alloc();
+ nvlist_t *future = fnvlist_alloc();
+ int error = 0;
+
+ if (required != NULL) {
+ for (nvpair_t *pair = nvlist_next_nvpair(required, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(required, pair)) {
+ fnvlist_add_nvpair(input, pair);
+ }
+ }
+ if (optional != NULL) {
+ for (nvpair_t *pair = nvlist_next_nvpair(optional, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(optional, pair)) {
+ fnvlist_add_nvpair(input, pair);
+ }
+ }
+
+ /*
+ * Generic input run with 'optional' nvlist pair
+ */
+ if (!wildcard)
+ fnvlist_add_nvlist(input, "optional", future);
+ lzc_ioctl_run(ioc, name, input, expected_error);
+ if (!wildcard)
+ fnvlist_remove(input, "optional");
+
+ /*
+ * Bogus input value
+ */
+ if (!wildcard) {
+ fnvlist_add_string(input, "bogus_input", "bogus");
+ lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_UNAVAIL);
+ fnvlist_remove(input, "bogus_input");
+ }
+
+ /*
+ * Missing required inputs
+ */
+ if (required != NULL) {
+ nvlist_t *empty = fnvlist_alloc();
+ lzc_ioctl_run(ioc, name, empty, ZFS_ERR_IOC_ARG_REQUIRED);
+ nvlist_free(empty);
+ }
+
+ /*
+ * Wrong nvpair type
+ */
+ if (required != NULL || optional != NULL) {
+ /*
+ * switch the type of one of the input pairs
+ */
+ for (nvpair_t *pair = nvlist_next_nvpair(input, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(input, pair)) {
+ char pname[MAXNAMELEN];
+ data_type_t ptype;
+
+ strlcpy(pname, nvpair_name(pair), sizeof (pname));
+ pname[sizeof (pname) - 1] = '\0';
+ ptype = nvpair_type(pair);
+ fnvlist_remove_nvpair(input, pair);
+
+ switch (ptype) {
+ case DATA_TYPE_STRING:
+ fnvlist_add_uint64(input, pname, 42);
+ break;
+ default:
+ fnvlist_add_string(input, pname, "bogus");
+ break;
+ }
+ }
+ lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_BADTYPE);
+ }
+
+ nvlist_free(future);
+ nvlist_free(input);
+
+ return (error);
+}
+
+static void
+test_pool_sync(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+
+ fnvlist_add_boolean_value(required, "force", B_TRUE);
+
+ IOC_INPUT_TEST(ZFS_IOC_POOL_SYNC, pool, required, NULL, 0);
+
+ nvlist_free(required);
+}
+
+static void
+test_pool_reopen(const char *pool)
+{
+ nvlist_t *optional = fnvlist_alloc();
+
+ fnvlist_add_boolean_value(optional, "scrub_restart", B_FALSE);
+
+ IOC_INPUT_TEST(ZFS_IOC_POOL_REOPEN, pool, NULL, optional, 0);
+
+ nvlist_free(optional);
+}
+
+static void
+test_pool_checkpoint(const char *pool)
+{
+ IOC_INPUT_TEST(ZFS_IOC_POOL_CHECKPOINT, pool, NULL, NULL, 0);
+}
+
+static void
+test_pool_discard_checkpoint(const char *pool)
+{
+ int err = lzc_pool_checkpoint(pool);
+ if (err == 0 || err == ZFS_ERR_CHECKPOINT_EXISTS)
+ IOC_INPUT_TEST(ZFS_IOC_POOL_DISCARD_CHECKPOINT, pool, NULL,
+ NULL, 0);
+}
+
+static void
+test_log_history(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+
+ fnvlist_add_string(required, "message", "input check");
+
+ IOC_INPUT_TEST(ZFS_IOC_LOG_HISTORY, pool, required, NULL, 0);
+
+ nvlist_free(required);
+}
+
+static void
+test_create(const char *pool)
+{
+ char dataset[MAXNAMELEN + 32];
+
+ (void) snprintf(dataset, sizeof (dataset), "%s/create-fs", pool);
+
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *props = fnvlist_alloc();
+
+ fnvlist_add_int32(required, "type", DMU_OST_ZFS);
+ fnvlist_add_uint64(props, "recordsize", 8192);
+ fnvlist_add_nvlist(optional, "props", props);
+
+ IOC_INPUT_TEST(ZFS_IOC_CREATE, dataset, required, optional, 0);
+
+ nvlist_free(required);
+ nvlist_free(optional);
+}
+
+static void
+test_snapshot(const char *pool, const char *snapshot)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *snaps = fnvlist_alloc();
+ nvlist_t *props = fnvlist_alloc();
+
+ fnvlist_add_boolean(snaps, snapshot);
+ fnvlist_add_nvlist(required, "snaps", snaps);
+
+ fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013");
+ fnvlist_add_nvlist(optional, "props", props);
+
+ IOC_INPUT_TEST(ZFS_IOC_SNAPSHOT, pool, required, optional, 0);
+
+ nvlist_free(props);
+ nvlist_free(snaps);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+static void
+test_space_snaps(const char *snapshot)
+{
+ nvlist_t *required = fnvlist_alloc();
+ fnvlist_add_string(required, "firstsnap", snapshot);
+
+ IOC_INPUT_TEST(ZFS_IOC_SPACE_SNAPS, snapshot, required, NULL, 0);
+
+ nvlist_free(required);
+}
+
+static void
+test_destroy_snaps(const char *pool, const char *snapshot)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *snaps = fnvlist_alloc();
+
+ fnvlist_add_boolean(snaps, snapshot);
+ fnvlist_add_nvlist(required, "snaps", snaps);
+
+ IOC_INPUT_TEST(ZFS_IOC_DESTROY_SNAPS, pool, required, NULL, 0);
+
+ nvlist_free(snaps);
+ nvlist_free(required);
+}
+
+
+static void
+test_bookmark(const char *pool, const char *snapshot, const char *bookmark)
+{
+ nvlist_t *required = fnvlist_alloc();
+
+ fnvlist_add_string(required, bookmark, snapshot);
+
+ IOC_INPUT_TEST_WILD(ZFS_IOC_BOOKMARK, pool, required, NULL, 0);
+
+ nvlist_free(required);
+}
+
+static void
+test_get_bookmarks(const char *dataset)
+{
+ nvlist_t *optional = fnvlist_alloc();
+
+ fnvlist_add_boolean(optional, "guid");
+ fnvlist_add_boolean(optional, "createtxg");
+ fnvlist_add_boolean(optional, "creation");
+
+ IOC_INPUT_TEST_WILD(ZFS_IOC_GET_BOOKMARKS, dataset, NULL, optional, 0);
+
+ nvlist_free(optional);
+}
+
+static void
+test_destroy_bookmarks(const char *pool, const char *bookmark)
+{
+ nvlist_t *required = fnvlist_alloc();
+
+ fnvlist_add_boolean(required, bookmark);
+
+ IOC_INPUT_TEST_WILD(ZFS_IOC_DESTROY_BOOKMARKS, pool, required, NULL, 0);
+
+ nvlist_free(required);
+}
+
+static void
+test_clone(const char *snapshot, const char *clone)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *props = fnvlist_alloc();
+
+ fnvlist_add_string(required, "origin", snapshot);
+
+ IOC_INPUT_TEST(ZFS_IOC_CLONE, clone, required, NULL, 0);
+
+ nvlist_free(props);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+static void
+test_rollback(const char *dataset, const char *snapshot)
+{
+ nvlist_t *optional = fnvlist_alloc();
+
+ fnvlist_add_string(optional, "target", snapshot);
+
+ IOC_INPUT_TEST(ZFS_IOC_ROLLBACK, dataset, NULL, optional, B_FALSE);
+
+ nvlist_free(optional);
+}
+
+static void
+test_hold(const char *pool, const char *snapshot)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *holds = fnvlist_alloc();
+
+ fnvlist_add_string(holds, snapshot, "libzfs_check_hold");
+ fnvlist_add_nvlist(required, "holds", holds);
+ fnvlist_add_int32(optional, "cleanup_fd", zfs_fd);
+
+ IOC_INPUT_TEST(ZFS_IOC_HOLD, pool, required, optional, 0);
+
+ nvlist_free(holds);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+static void
+test_get_holds(const char *snapshot)
+{
+ IOC_INPUT_TEST(ZFS_IOC_GET_HOLDS, snapshot, NULL, NULL, 0);
+}
+
+static void
+test_release(const char *pool, const char *snapshot)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *release = fnvlist_alloc();
+
+ fnvlist_add_boolean(release, "libzfs_check_hold");
+ fnvlist_add_nvlist(required, snapshot, release);
+
+ IOC_INPUT_TEST_WILD(ZFS_IOC_RELEASE, pool, required, NULL, 0);
+
+ nvlist_free(release);
+ nvlist_free(required);
+}
+
+
+static void
+test_send_new(const char *snapshot, int fd)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+
+ fnvlist_add_int32(required, "fd", fd);
+
+ fnvlist_add_boolean(optional, "largeblockok");
+ fnvlist_add_boolean(optional, "embedok");
+ fnvlist_add_boolean(optional, "compressok");
+ fnvlist_add_boolean(optional, "rawok");
+
+ /*
+ * TODO - Resumable send is harder to set up. So we currently
+ * ignore testing for that variant.
+ */
+#if 0
+ fnvlist_add_string(optional, "fromsnap", from);
+ fnvlist_add_uint64(optional, "resume_object", resumeobj);
+ fnvlist_add_uint64(optional, "resume_offset", offset);
+ fnvlist_add_boolean(optional, "savedok");
+#endif
+ IOC_INPUT_TEST(ZFS_IOC_SEND_NEW, snapshot, required, optional, 0);
+
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+static void
+test_recv_new(const char *dataset, int fd)
+{
+ dmu_replay_record_t drr = { 0 };
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *props = fnvlist_alloc();
+ char snapshot[MAXNAMELEN + 32];
+ ssize_t count;
+
+ int cleanup_fd = open(ZFS_DEV, O_RDWR);
+
+ (void) snprintf(snapshot, sizeof (snapshot), "%s@replicant", dataset);
+
+ count = pread(fd, &drr, sizeof (drr), 0);
+ if (count != sizeof (drr)) {
+ (void) fprintf(stderr, "could not read stream: %s\n",
+ strerror(errno));
+ }
+
+ fnvlist_add_string(required, "snapname", snapshot);
+ fnvlist_add_byte_array(required, "begin_record", (uchar_t *)&drr,
+ sizeof (drr));
+ fnvlist_add_int32(required, "input_fd", fd);
+
+ fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013");
+ fnvlist_add_nvlist(optional, "localprops", props);
+ fnvlist_add_boolean(optional, "force");
+ fnvlist_add_int32(optional, "cleanup_fd", cleanup_fd);
+
+ /*
+ * TODO - Resumable receive is harder to set up. So we currently
+ * ignore testing for one.
+ */
+#if 0
+ fnvlist_add_nvlist(optional, "props", recvdprops);
+ fnvlist_add_string(optional, "origin", origin);
+ fnvlist_add_boolean(optional, "resumable");
+ fnvlist_add_uint64(optional, "action_handle", *action_handle);
+#endif
+ IOC_INPUT_TEST(ZFS_IOC_RECV_NEW, dataset, required, optional,
+ ZFS_ERR_STREAM_TRUNCATED);
+
+ nvlist_free(props);
+ nvlist_free(optional);
+ nvlist_free(required);
+
+ (void) close(cleanup_fd);
+}
+
+static void
+test_send_space(const char *snapshot1, const char *snapshot2)
+{
+ nvlist_t *optional = fnvlist_alloc();
+
+ fnvlist_add_string(optional, "from", snapshot1);
+ fnvlist_add_boolean(optional, "largeblockok");
+ fnvlist_add_boolean(optional, "embedok");
+ fnvlist_add_boolean(optional, "compressok");
+ fnvlist_add_boolean(optional, "rawok");
+
+ IOC_INPUT_TEST(ZFS_IOC_SEND_SPACE, snapshot2, NULL, optional, 0);
+
+ nvlist_free(optional);
+}
+
+static void
+test_remap(const char *dataset)
+{
+ IOC_INPUT_TEST(ZFS_IOC_REMAP, dataset, NULL, NULL, 0);
+}
+
+static void
+test_channel_program(const char *pool)
+{
+ const char *program =
+ "arg = ...\n"
+ "argv = arg[\"argv\"]\n"
+ "return argv[1]";
+ char *const argv[1] = { "Hello World!" };
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *args = fnvlist_alloc();
+
+ fnvlist_add_string(required, "program", program);
+ fnvlist_add_string_array(args, "argv", argv, 1);
+ fnvlist_add_nvlist(required, "arg", args);
+
+ fnvlist_add_boolean_value(optional, "sync", B_TRUE);
+ fnvlist_add_uint64(optional, "instrlimit", 1000 * 1000);
+ fnvlist_add_uint64(optional, "memlimit", 8192 * 1024);
+
+ IOC_INPUT_TEST(ZFS_IOC_CHANNEL_PROGRAM, pool, required, optional, 0);
+
+ nvlist_free(args);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+#define WRAPPING_KEY_LEN 32
+
+static void
+test_load_key(const char *dataset)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *hidden = fnvlist_alloc();
+ uint8_t keydata[WRAPPING_KEY_LEN] = {0};
+
+ fnvlist_add_uint8_array(hidden, "wkeydata", keydata, sizeof (keydata));
+ fnvlist_add_nvlist(required, "hidden_args", hidden);
+ fnvlist_add_boolean(optional, "noop");
+
+ IOC_INPUT_TEST(ZFS_IOC_LOAD_KEY, dataset, required, optional, EINVAL);
+ nvlist_free(hidden);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+static void
+test_change_key(const char *dataset)
+{
+ IOC_INPUT_TEST(ZFS_IOC_CHANGE_KEY, dataset, NULL, NULL, EINVAL);
+}
+
+static void
+test_unload_key(const char *dataset)
+{
+ IOC_INPUT_TEST(ZFS_IOC_UNLOAD_KEY, dataset, NULL, NULL, EACCES);
+}
+
+static void
+test_vdev_initialize(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *vdev_guids = fnvlist_alloc();
+
+ fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
+ fnvlist_add_uint64(required, ZPOOL_INITIALIZE_COMMAND,
+ POOL_INITIALIZE_START);
+ fnvlist_add_nvlist(required, ZPOOL_INITIALIZE_VDEVS, vdev_guids);
+
+ IOC_INPUT_TEST(ZFS_IOC_POOL_INITIALIZE, pool, required, NULL, EINVAL);
+ nvlist_free(vdev_guids);
+ nvlist_free(required);
+}
+
+static void
+test_vdev_trim(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *vdev_guids = fnvlist_alloc();
+
+ fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
+ fnvlist_add_uint64(required, ZPOOL_TRIM_COMMAND, POOL_TRIM_START);
+ fnvlist_add_nvlist(required, ZPOOL_TRIM_VDEVS, vdev_guids);
+ fnvlist_add_uint64(optional, ZPOOL_TRIM_RATE, 1ULL << 30);
+ fnvlist_add_boolean_value(optional, ZPOOL_TRIM_SECURE, B_TRUE);
+
+ IOC_INPUT_TEST(ZFS_IOC_POOL_TRIM, pool, required, optional, EINVAL);
+ nvlist_free(vdev_guids);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
+static int
+zfs_destroy(const char *dataset)
+{
+ zfs_cmd_t zc = {"\0"};
+ int err;
+
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
+ err = zfs_ioctl_fd(zfs_fd, ZFS_IOC_DESTROY, &zc);
+
+ return (err == 0 ? 0 : errno);
+}
+
+static void
+test_redact(const char *snapshot1, const char *snapshot2)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *snapnv = fnvlist_alloc();
+ char bookmark[MAXNAMELEN + 32];
+
+ fnvlist_add_string(required, "bookname", "testbookmark");
+ fnvlist_add_boolean(snapnv, snapshot2);
+ fnvlist_add_nvlist(required, "snapnv", snapnv);
+
+ IOC_INPUT_TEST(ZFS_IOC_REDACT, snapshot1, required, NULL, 0);
+
+ nvlist_free(snapnv);
+ nvlist_free(required);
+
+ strlcpy(bookmark, snapshot1, sizeof (bookmark));
+ *strchr(bookmark, '@') = '\0';
+ strlcat(bookmark, "#testbookmark", sizeof (bookmark) -
+ strlen(bookmark));
+ zfs_destroy(bookmark);
+}
+
+static void
+test_get_bookmark_props(const char *bookmark)
+{
+ IOC_INPUT_TEST(ZFS_IOC_GET_BOOKMARK_PROPS, bookmark, NULL, NULL, 0);
+}
+
+static void
+test_wait(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+
+ fnvlist_add_int32(required, "wait_activity", 2);
+ fnvlist_add_uint64(optional, "wait_tag", 0xdeadbeefdeadbeef);
+
+ IOC_INPUT_TEST(ZFS_IOC_WAIT, pool, required, optional, EINVAL);
+
+ nvlist_free(required);
+ nvlist_free(optional);
+}
+
+static void
+test_wait_fs(const char *dataset)
+{
+ nvlist_t *required = fnvlist_alloc();
+
+ fnvlist_add_int32(required, "wait_activity", 2);
+
+ IOC_INPUT_TEST(ZFS_IOC_WAIT_FS, dataset, required, NULL, EINVAL);
+
+ nvlist_free(required);
+}
+
+static void
+test_get_bootenv(const char *pool)
+{
+ IOC_INPUT_TEST(ZFS_IOC_GET_BOOTENV, pool, NULL, NULL, 0);
+}
+
+static void
+test_set_bootenv(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+
+ fnvlist_add_string(required, "envmap", "test");
+
+ IOC_INPUT_TEST(ZFS_IOC_SET_BOOTENV, pool, required, NULL, 0);
+
+ nvlist_free(required);
+}
+
+static void
+zfs_ioc_input_tests(const char *pool)
+{
+ char filepath[] = "/tmp/ioc_test_file_XXXXXX";
+ char dataset[ZFS_MAX_DATASET_NAME_LEN];
+ char snapbase[ZFS_MAX_DATASET_NAME_LEN + 32];
+ char snapshot[ZFS_MAX_DATASET_NAME_LEN + 32];
+ char bookmark[ZFS_MAX_DATASET_NAME_LEN + 32];
+ char backup[ZFS_MAX_DATASET_NAME_LEN];
+ char clone[ZFS_MAX_DATASET_NAME_LEN];
+ char clonesnap[ZFS_MAX_DATASET_NAME_LEN + 32];
+ int tmpfd, err;
+
+ /*
+ * Setup names and create a working dataset
+ */
+ (void) snprintf(dataset, sizeof (dataset), "%s/test-fs", pool);
+ (void) snprintf(snapbase, sizeof (snapbase), "%s@snapbase", dataset);
+ (void) snprintf(snapshot, sizeof (snapshot), "%s@snapshot", dataset);
+ (void) snprintf(bookmark, sizeof (bookmark), "%s#bookmark", dataset);
+ (void) snprintf(clone, sizeof (clone), "%s/test-fs-clone", pool);
+ (void) snprintf(clonesnap, sizeof (clonesnap), "%s@snap", clone);
+ (void) snprintf(backup, sizeof (backup), "%s/backup", pool);
+
+ err = lzc_create(dataset, DMU_OST_ZFS, NULL, NULL, 0);
+ if (err) {
+ (void) fprintf(stderr, "could not create '%s': %s\n",
+ dataset, strerror(errno));
+ exit(2);
+ }
+
+ tmpfd = mkstemp(filepath);
+ if (tmpfd < 0) {
+ (void) fprintf(stderr, "could not create '%s': %s\n",
+ filepath, strerror(errno));
+ exit(2);
+ }
+
+ /*
+ * run a test for each ioctl
+ * Note that some test build on previous test operations
+ */
+ test_pool_sync(pool);
+ test_pool_reopen(pool);
+ test_pool_checkpoint(pool);
+ test_pool_discard_checkpoint(pool);
+ test_log_history(pool);
+
+ test_create(dataset);
+ test_snapshot(pool, snapbase);
+ test_snapshot(pool, snapshot);
+
+ test_space_snaps(snapshot);
+ test_send_space(snapbase, snapshot);
+ test_send_new(snapshot, tmpfd);
+ test_recv_new(backup, tmpfd);
+
+ test_bookmark(pool, snapshot, bookmark);
+ test_get_bookmarks(dataset);
+ test_get_bookmark_props(bookmark);
+ test_destroy_bookmarks(pool, bookmark);
+
+ test_hold(pool, snapshot);
+ test_get_holds(snapshot);
+ test_release(pool, snapshot);
+
+ test_clone(snapshot, clone);
+ test_snapshot(pool, clonesnap);
+ test_redact(snapshot, clonesnap);
+ zfs_destroy(clonesnap);
+ zfs_destroy(clone);
+
+ test_rollback(dataset, snapshot);
+ test_destroy_snaps(pool, snapshot);
+ test_destroy_snaps(pool, snapbase);
+
+ test_remap(dataset);
+ test_channel_program(pool);
+
+ test_load_key(dataset);
+ test_change_key(dataset);
+ test_unload_key(dataset);
+
+ test_vdev_initialize(pool);
+ test_vdev_trim(pool);
+
+ test_wait(pool);
+ test_wait_fs(dataset);
+
+ test_set_bootenv(pool);
+ test_get_bootenv(pool);
+
+ /*
+ * cleanup
+ */
+ zfs_cmd_t zc = {"\0"};
+
+ nvlist_t *snaps = fnvlist_alloc();
+ fnvlist_add_boolean(snaps, snapshot);
+ (void) lzc_destroy_snaps(snaps, B_FALSE, NULL);
+ nvlist_free(snaps);
+
+ (void) zfs_destroy(dataset);
+ (void) zfs_destroy(backup);
+
+ (void) close(tmpfd);
+ (void) unlink(filepath);
+
+ /*
+ * All the unused slots should yield ZFS_ERR_IOC_CMD_UNAVAIL
+ */
+ for (int i = 0; i < ARRAY_SIZE(ioc_skip); i++) {
+ if (ioc_tested[ioc_skip[i] - ZFS_IOC_FIRST])
+ (void) fprintf(stderr, "cmd %d tested, not skipped!\n",
+ (int)(ioc_skip[i] - ZFS_IOC_FIRST));
+
+ ioc_tested[ioc_skip[i] - ZFS_IOC_FIRST] = B_TRUE;
+ }
+
+ (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
+ zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
+
+ for (unsigned ioc = ZFS_IOC_FIRST; ioc < ZFS_IOC_LAST; ioc++) {
+ unsigned cmd = ioc - ZFS_IOC_FIRST;
+
+ if (ioc_tested[cmd])
+ continue;
+
+ if (zfs_ioctl_fd(zfs_fd, ioc, &zc) != 0 &&
+ errno != ZFS_ERR_IOC_CMD_UNAVAIL) {
+ (void) fprintf(stderr, "cmd %d is missing a test case "
+ "(%d)\n", cmd, errno);
+ }
+ }
+}
+
+enum zfs_ioc_ref {
+#ifdef __FreeBSD__
+ ZFS_IOC_BASE = 0,
+#else
+ ZFS_IOC_BASE = ('Z' << 8),
+#endif
+ ZFS_IOC_PLATFORM_BASE = ZFS_IOC_BASE + 0x80,
+};
+
+/*
+ * Canonical reference check of /dev/zfs ioctl numbers.
+ * These cannot change and new ioctl numbers must be appended.
+ */
+static boolean_t
+validate_ioc_values(void)
+{
+ boolean_t result = B_TRUE;
+
+#define CHECK(expr) do { \
+ if (!(expr)) { \
+ result = B_FALSE; \
+ fprintf(stderr, "(%s) === FALSE\n", #expr); \
+ } \
+} while (0)
+
+ CHECK(ZFS_IOC_BASE + 0 == ZFS_IOC_POOL_CREATE);
+ CHECK(ZFS_IOC_BASE + 1 == ZFS_IOC_POOL_DESTROY);
+ CHECK(ZFS_IOC_BASE + 2 == ZFS_IOC_POOL_IMPORT);
+ CHECK(ZFS_IOC_BASE + 3 == ZFS_IOC_POOL_EXPORT);
+ CHECK(ZFS_IOC_BASE + 4 == ZFS_IOC_POOL_CONFIGS);
+ CHECK(ZFS_IOC_BASE + 5 == ZFS_IOC_POOL_STATS);
+ CHECK(ZFS_IOC_BASE + 6 == ZFS_IOC_POOL_TRYIMPORT);
+ CHECK(ZFS_IOC_BASE + 7 == ZFS_IOC_POOL_SCAN);
+ CHECK(ZFS_IOC_BASE + 8 == ZFS_IOC_POOL_FREEZE);
+ CHECK(ZFS_IOC_BASE + 9 == ZFS_IOC_POOL_UPGRADE);
+ CHECK(ZFS_IOC_BASE + 10 == ZFS_IOC_POOL_GET_HISTORY);
+ CHECK(ZFS_IOC_BASE + 11 == ZFS_IOC_VDEV_ADD);
+ CHECK(ZFS_IOC_BASE + 12 == ZFS_IOC_VDEV_REMOVE);
+ CHECK(ZFS_IOC_BASE + 13 == ZFS_IOC_VDEV_SET_STATE);
+ CHECK(ZFS_IOC_BASE + 14 == ZFS_IOC_VDEV_ATTACH);
+ CHECK(ZFS_IOC_BASE + 15 == ZFS_IOC_VDEV_DETACH);
+ CHECK(ZFS_IOC_BASE + 16 == ZFS_IOC_VDEV_SETPATH);
+ CHECK(ZFS_IOC_BASE + 17 == ZFS_IOC_VDEV_SETFRU);
+ CHECK(ZFS_IOC_BASE + 18 == ZFS_IOC_OBJSET_STATS);
+ CHECK(ZFS_IOC_BASE + 19 == ZFS_IOC_OBJSET_ZPLPROPS);
+ CHECK(ZFS_IOC_BASE + 20 == ZFS_IOC_DATASET_LIST_NEXT);
+ CHECK(ZFS_IOC_BASE + 21 == ZFS_IOC_SNAPSHOT_LIST_NEXT);
+ CHECK(ZFS_IOC_BASE + 22 == ZFS_IOC_SET_PROP);
+ CHECK(ZFS_IOC_BASE + 23 == ZFS_IOC_CREATE);
+ CHECK(ZFS_IOC_BASE + 24 == ZFS_IOC_DESTROY);
+ CHECK(ZFS_IOC_BASE + 25 == ZFS_IOC_ROLLBACK);
+ CHECK(ZFS_IOC_BASE + 26 == ZFS_IOC_RENAME);
+ CHECK(ZFS_IOC_BASE + 27 == ZFS_IOC_RECV);
+ CHECK(ZFS_IOC_BASE + 28 == ZFS_IOC_SEND);
+ CHECK(ZFS_IOC_BASE + 29 == ZFS_IOC_INJECT_FAULT);
+ CHECK(ZFS_IOC_BASE + 30 == ZFS_IOC_CLEAR_FAULT);
+ CHECK(ZFS_IOC_BASE + 31 == ZFS_IOC_INJECT_LIST_NEXT);
+ CHECK(ZFS_IOC_BASE + 32 == ZFS_IOC_ERROR_LOG);
+ CHECK(ZFS_IOC_BASE + 33 == ZFS_IOC_CLEAR);
+ CHECK(ZFS_IOC_BASE + 34 == ZFS_IOC_PROMOTE);
+ CHECK(ZFS_IOC_BASE + 35 == ZFS_IOC_SNAPSHOT);
+ CHECK(ZFS_IOC_BASE + 36 == ZFS_IOC_DSOBJ_TO_DSNAME);
+ CHECK(ZFS_IOC_BASE + 37 == ZFS_IOC_OBJ_TO_PATH);
+ CHECK(ZFS_IOC_BASE + 38 == ZFS_IOC_POOL_SET_PROPS);
+ CHECK(ZFS_IOC_BASE + 39 == ZFS_IOC_POOL_GET_PROPS);
+ CHECK(ZFS_IOC_BASE + 40 == ZFS_IOC_SET_FSACL);
+ CHECK(ZFS_IOC_BASE + 41 == ZFS_IOC_GET_FSACL);
+ CHECK(ZFS_IOC_BASE + 42 == ZFS_IOC_SHARE);
+ CHECK(ZFS_IOC_BASE + 43 == ZFS_IOC_INHERIT_PROP);
+ CHECK(ZFS_IOC_BASE + 44 == ZFS_IOC_SMB_ACL);
+ CHECK(ZFS_IOC_BASE + 45 == ZFS_IOC_USERSPACE_ONE);
+ CHECK(ZFS_IOC_BASE + 46 == ZFS_IOC_USERSPACE_MANY);
+ CHECK(ZFS_IOC_BASE + 47 == ZFS_IOC_USERSPACE_UPGRADE);
+ CHECK(ZFS_IOC_BASE + 48 == ZFS_IOC_HOLD);
+ CHECK(ZFS_IOC_BASE + 49 == ZFS_IOC_RELEASE);
+ CHECK(ZFS_IOC_BASE + 50 == ZFS_IOC_GET_HOLDS);
+ CHECK(ZFS_IOC_BASE + 51 == ZFS_IOC_OBJSET_RECVD_PROPS);
+ CHECK(ZFS_IOC_BASE + 52 == ZFS_IOC_VDEV_SPLIT);
+ CHECK(ZFS_IOC_BASE + 53 == ZFS_IOC_NEXT_OBJ);
+ CHECK(ZFS_IOC_BASE + 54 == ZFS_IOC_DIFF);
+ CHECK(ZFS_IOC_BASE + 55 == ZFS_IOC_TMP_SNAPSHOT);
+ CHECK(ZFS_IOC_BASE + 56 == ZFS_IOC_OBJ_TO_STATS);
+ CHECK(ZFS_IOC_BASE + 57 == ZFS_IOC_SPACE_WRITTEN);
+ CHECK(ZFS_IOC_BASE + 58 == ZFS_IOC_SPACE_SNAPS);
+ CHECK(ZFS_IOC_BASE + 59 == ZFS_IOC_DESTROY_SNAPS);
+ CHECK(ZFS_IOC_BASE + 60 == ZFS_IOC_POOL_REGUID);
+ CHECK(ZFS_IOC_BASE + 61 == ZFS_IOC_POOL_REOPEN);
+ CHECK(ZFS_IOC_BASE + 62 == ZFS_IOC_SEND_PROGRESS);
+ CHECK(ZFS_IOC_BASE + 63 == ZFS_IOC_LOG_HISTORY);
+ CHECK(ZFS_IOC_BASE + 64 == ZFS_IOC_SEND_NEW);
+ CHECK(ZFS_IOC_BASE + 65 == ZFS_IOC_SEND_SPACE);
+ CHECK(ZFS_IOC_BASE + 66 == ZFS_IOC_CLONE);
+ CHECK(ZFS_IOC_BASE + 67 == ZFS_IOC_BOOKMARK);
+ CHECK(ZFS_IOC_BASE + 68 == ZFS_IOC_GET_BOOKMARKS);
+ CHECK(ZFS_IOC_BASE + 69 == ZFS_IOC_DESTROY_BOOKMARKS);
+ CHECK(ZFS_IOC_BASE + 70 == ZFS_IOC_RECV_NEW);
+ CHECK(ZFS_IOC_BASE + 71 == ZFS_IOC_POOL_SYNC);
+ CHECK(ZFS_IOC_BASE + 72 == ZFS_IOC_CHANNEL_PROGRAM);
+ CHECK(ZFS_IOC_BASE + 73 == ZFS_IOC_LOAD_KEY);
+ CHECK(ZFS_IOC_BASE + 74 == ZFS_IOC_UNLOAD_KEY);
+ CHECK(ZFS_IOC_BASE + 75 == ZFS_IOC_CHANGE_KEY);
+ CHECK(ZFS_IOC_BASE + 76 == ZFS_IOC_REMAP);
+ CHECK(ZFS_IOC_BASE + 77 == ZFS_IOC_POOL_CHECKPOINT);
+ CHECK(ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_DISCARD_CHECKPOINT);
+ CHECK(ZFS_IOC_BASE + 79 == ZFS_IOC_POOL_INITIALIZE);
+ CHECK(ZFS_IOC_BASE + 80 == ZFS_IOC_POOL_TRIM);
+ CHECK(ZFS_IOC_BASE + 81 == ZFS_IOC_REDACT);
+ CHECK(ZFS_IOC_BASE + 82 == ZFS_IOC_GET_BOOKMARK_PROPS);
+ CHECK(ZFS_IOC_BASE + 83 == ZFS_IOC_WAIT);
+ CHECK(ZFS_IOC_BASE + 84 == ZFS_IOC_WAIT_FS);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 1 == ZFS_IOC_EVENTS_NEXT);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 2 == ZFS_IOC_EVENTS_CLEAR);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 3 == ZFS_IOC_EVENTS_SEEK);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 4 == ZFS_IOC_NEXTBOOT);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 5 == ZFS_IOC_JAIL);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 6 == ZFS_IOC_UNJAIL);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 7 == ZFS_IOC_SET_BOOTENV);
+ CHECK(ZFS_IOC_PLATFORM_BASE + 8 == ZFS_IOC_GET_BOOTENV);
+
+#undef CHECK
+
+ return (result);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ if (argc != 2) {
+ (void) fprintf(stderr, "usage: %s <pool>\n", argv[0]);
+ exit(2);
+ }
+
+ if (!validate_ioc_values()) {
+ (void) fprintf(stderr, "WARNING: zfs_ioc_t has binary "
+ "incompatible command values\n");
+ exit(3);
+ }
+
+ (void) libzfs_core_init();
+ zfs_fd = open(ZFS_DEV, O_RDWR);
+ if (zfs_fd < 0) {
+ (void) fprintf(stderr, "open: %s\n", strerror(errno));
+ libzfs_core_fini();
+ exit(2);
+ }
+
+ zfs_ioc_input_tests(argv[1]);
+
+ (void) close(zfs_fd);
+ libzfs_core_fini();
+
+ return (unexpected_failures);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/.gitignore
new file mode 100644
index 000000000000..18d099c08eec
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/.gitignore
@@ -0,0 +1 @@
+/mkbusy
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/Makefile.am
new file mode 100644
index 000000000000..abae69dea8c7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = mkbusy
+mkbusy_SOURCES = mkbusy.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/mkbusy.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/mkbusy.c
new file mode 100644
index 000000000000..a03076ffc003
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkbusy/mkbusy.c
@@ -0,0 +1,177 @@
+/*
+ * 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) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * Make a directory busy. If the argument is an existing file or directory,
+ * simply open it directly and pause. If not, verify that the parent directory
+ * exists, and create a new file in that directory.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+static void
+usage(char *progname)
+{
+ (void) fprintf(stderr, "Usage: %s <dirname|filename>\n", progname);
+ exit(1);
+}
+
+static void
+fail(char *err, int rval)
+{
+ perror(err);
+ exit(rval);
+}
+
+static void
+daemonize(void)
+{
+ pid_t pid;
+
+ if ((pid = fork()) < 0) {
+ fail("fork", 1);
+ } else if (pid != 0) {
+ (void) fprintf(stdout, "%ld\n", (long)pid);
+ exit(0);
+ }
+
+ (void) setsid();
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret, c;
+ boolean_t isdir = B_FALSE;
+ boolean_t fflag = B_FALSE;
+ boolean_t rflag = B_FALSE;
+ struct stat sbuf;
+ char *fpath = NULL;
+ char *prog = argv[0];
+
+ while ((c = getopt(argc, argv, "fr")) != -1) {
+ switch (c) {
+ /* Open the file or directory read only */
+ case 'r':
+ rflag = B_TRUE;
+ break;
+ /* Run in the foreground */
+ case 'f':
+ fflag = B_TRUE;
+ break;
+ default:
+ usage(prog);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage(prog);
+
+ if ((ret = stat(argv[0], &sbuf)) != 0) {
+ char *arg, *dname, *fname;
+ int arglen;
+ char *slash;
+ int rc;
+
+ /*
+ * The argument supplied doesn't exist. Copy the path, and
+ * remove the trailing slash if present.
+ */
+ if ((arg = strdup(argv[0])) == NULL)
+ fail("strdup", 1);
+ arglen = strlen(arg);
+ if (arg[arglen - 1] == '/')
+ arg[arglen - 1] = '\0';
+
+ /*
+ * Get the directory and file names, using the current directory
+ * if the provided path doesn't specify a directory at all.
+ */
+ if ((slash = strrchr(arg, '/')) == NULL) {
+ dname = strdup(".");
+ fname = strdup(arg);
+ } else {
+ *slash = '\0';
+ dname = strdup(arg);
+ fname = strdup(slash + 1);
+ }
+ free(arg);
+ if (dname == NULL || fname == NULL)
+ fail("strdup", 1);
+
+ /* The directory portion of the path must exist */
+ if ((ret = stat(dname, &sbuf)) != 0 || !(sbuf.st_mode &
+ S_IFDIR))
+ usage(prog);
+
+ rc = asprintf(&fpath, "%s/%s", dname, fname);
+ free(dname);
+ free(fname);
+ if (rc == -1 || fpath == NULL)
+ fail("asprintf", 1);
+
+ } else if ((sbuf.st_mode & S_IFMT) == S_IFREG ||
+ (sbuf.st_mode & S_IFMT) == S_IFLNK ||
+ (sbuf.st_mode & S_IFMT) == S_IFCHR ||
+ (sbuf.st_mode & S_IFMT) == S_IFBLK) {
+ fpath = strdup(argv[0]);
+ } else if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
+ fpath = strdup(argv[0]);
+ isdir = B_TRUE;
+ } else {
+ usage(prog);
+ }
+
+ if (fpath == NULL)
+ fail("strdup", 1);
+
+ if (isdir == B_FALSE) {
+ int fd, flags;
+ mode_t mode = S_IRUSR | S_IWUSR;
+
+ flags = rflag == B_FALSE ? O_CREAT | O_RDWR : O_RDONLY;
+
+ if ((fd = open(fpath, flags, mode)) < 0)
+ fail("open", 1);
+ } else {
+ DIR *dp;
+
+ if ((dp = opendir(fpath)) == NULL)
+ fail("opendir", 1);
+ }
+ free(fpath);
+
+ if (fflag == B_FALSE)
+ daemonize();
+ (void) pause();
+
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/.gitignore
new file mode 100644
index 000000000000..93e9a8a6ded4
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/.gitignore
@@ -0,0 +1 @@
+/mkfile
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/Makefile.am
new file mode 100644
index 000000000000..5f0e2e03efd9
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/Makefile.am
@@ -0,0 +1,8 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = mkfile
+mkfile_SOURCES = mkfile.c
+
+mkfile_LDADD = $(LTLIBINTL)
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/mkfile.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/mkfile.c
new file mode 100644
index 000000000000..4cf3755faa6a
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfile/mkfile.c
@@ -0,0 +1,282 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <errno.h>
+#include <sys/stdtypes.h>
+#include <sys/sysmacros.h>
+
+#define BLOCKSIZE 512 /* bytes */
+#define KILOBYTE 1024
+#define MEGABYTE (KILOBYTE * KILOBYTE)
+#define GIGABYTE (KILOBYTE * MEGABYTE)
+
+#define FILE_MODE (S_ISVTX + S_IRUSR + S_IWUSR)
+
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ char *opts;
+ off_t size;
+ size_t len;
+ size_t mult = 1;
+ char *buf = NULL;
+ size_t bufsz = 0;
+ int errors = 0;
+ int i;
+ int verbose = 0; /* option variable */
+ int nobytes = 0; /* option variable */
+ int saverr;
+
+ if (argc == 1)
+ usage();
+
+ while (argv[1] && argv[1][0] == '-') {
+ opts = &argv[1][0];
+ while (*(++opts)) {
+ switch (*opts) {
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ nobytes++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc--;
+ argv++;
+ }
+ if (argc < 3)
+ usage();
+
+ len = strlen(argv[1]);
+ if (len && isalpha(argv[1][len-1])) {
+ switch (argv[1][len-1]) {
+ case 'k':
+ case 'K':
+ mult = KILOBYTE;
+ break;
+ case 'b':
+ case 'B':
+ mult = BLOCKSIZE;
+ break;
+ case 'm':
+ case 'M':
+ mult = MEGABYTE;
+ break;
+ case 'g':
+ case 'G':
+ mult = GIGABYTE;
+ break;
+ default:
+ (void) fprintf(stderr,
+ gettext("unknown size %s\n"), argv[1]);
+ usage();
+ }
+
+ for (i = 0; i <= (len-2); i++) {
+ if (!isdigit(argv[1][i])) {
+ (void) fprintf(stderr,
+ gettext("unknown size %s\n"), argv[1]);
+ usage();
+ }
+ }
+ argv[1][len-1] = '\0';
+ }
+ size = ((off_t)atoll(argv[1]) * (off_t)mult);
+
+ argv++;
+ argc--;
+
+ while (argc > 1) {
+ int fd;
+
+ if (verbose)
+ (void) fprintf(stdout, gettext("%s %lld bytes\n"),
+ argv[1], (offset_t)size);
+ fd = open(argv[1], O_CREAT|O_TRUNC|O_RDWR, FILE_MODE);
+ if (fd < 0) {
+ saverr = errno;
+ (void) fprintf(stderr,
+ gettext("Could not open %s: %s\n"),
+ argv[1], strerror(saverr));
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ } else if (fchown(fd, getuid(), getgid()) < 0) {
+ saverr = errno;
+ (void) fprintf(stderr, gettext(
+ "Could not set owner/group of %s: %s\n"),
+ argv[1], strerror(saverr));
+ (void) close(fd);
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ } else if (lseek(fd, (off_t)size-1, SEEK_SET) < 0) {
+ saverr = errno;
+ (void) fprintf(stderr, gettext(
+ "Could not seek to offset %ld in %s: %s\n"),
+ (unsigned long)size-1, argv[1], strerror(saverr));
+ (void) close(fd);
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ } else if (write(fd, "", 1) != 1) {
+ saverr = errno;
+ (void) fprintf(stderr, gettext(
+ "Could not set length of %s: %s\n"),
+ argv[1], strerror(saverr));
+ (void) close(fd);
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ }
+
+ if (!nobytes) {
+ off_t written = 0;
+ struct stat64 st;
+
+ if (lseek(fd, (off_t)0, SEEK_SET) < 0) {
+ saverr = errno;
+ (void) fprintf(stderr, gettext(
+ "Could not seek to beginning of %s: %s\n"),
+ argv[1], strerror(saverr));
+ (void) close(fd);
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ }
+ if (fstat64(fd, &st) < 0) {
+ saverr = errno;
+ (void) fprintf(stderr, gettext(
+ "Could not fstat64 %s: %s\n"),
+ argv[1], strerror(saverr));
+ (void) close(fd);
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ }
+ if (bufsz != st.st_blksize) {
+ if (buf)
+ free(buf);
+ bufsz = (size_t)st.st_blksize;
+ buf = calloc(1, bufsz);
+ if (buf == NULL) {
+ (void) fprintf(stderr, gettext(
+ "Could not allocate buffer of"
+ " size %d\n"), (int)bufsz);
+ (void) close(fd);
+ bufsz = 0;
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ }
+ }
+ while (written < size) {
+ ssize_t result;
+ size_t bytes = (size_t)MIN(bufsz, size-written);
+
+ if ((result = write(fd, buf, bytes)) !=
+ (ssize_t)bytes) {
+ saverr = errno;
+ if (result < 0)
+ result = 0;
+ written += result;
+ (void) fprintf(stderr, gettext(
+ "%s: initialized %lu of %lu bytes: %s\n"),
+ argv[1], (unsigned long)written,
+ (unsigned long)size,
+ strerror(saverr));
+ errors++;
+ break;
+ }
+ written += bytes;
+ }
+
+ /*
+ * A write(2) call in the above loop failed so
+ * close out this file and go on (error was
+ * already incremented when the write(2) failed).
+ */
+ if (written < size) {
+ (void) close(fd);
+ argv++;
+ argc--;
+ continue;
+ }
+ }
+ if (close(fd) < 0) {
+ saverr = errno;
+ (void) fprintf(stderr, gettext(
+ "Error encountered when closing %s: %s\n"),
+ argv[1], strerror(saverr));
+ errors++;
+ argv++;
+ argc--;
+ continue;
+ }
+
+ /*
+ * Only set the modes (including the sticky bit) if we
+ * had no problems. It is not an error for the chmod(2)
+ * to fail, but do issue a warning.
+ */
+ if (chmod(argv[1], FILE_MODE) < 0)
+ (void) fprintf(stderr, gettext(
+ "warning: couldn't set mode to %#o\n"), FILE_MODE);
+
+ argv++;
+ argc--;
+ }
+ return (errors);
+}
+
+static void usage()
+{
+ (void) fprintf(stderr, gettext(
+ "Usage: mkfile [-nv] <size>[g|k|b|m] <name1> [<name2>] ...\n"));
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/.gitignore
new file mode 100644
index 000000000000..cee4858b701b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/.gitignore
@@ -0,0 +1 @@
+/mkfiles
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/Makefile.am
new file mode 100644
index 000000000000..54c21597f3eb
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = mkfiles
+mkfiles_SOURCES = mkfiles.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/mkfiles.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/mkfiles.c
new file mode 100644
index 000000000000..32abfd0c3d67
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mkfiles/mkfiles.c
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+#define MAX_INT_LENGTH 10
+
+static void
+usage(char *msg, int exit_value)
+{
+ (void) fprintf(stderr, "mkfiles basename max_file [min_file]\n");
+ (void) fprintf(stderr, "%s\n", msg);
+ exit(exit_value);
+}
+
+int
+main(int argc, char **argv)
+{
+ unsigned int numfiles = 0;
+ unsigned int first_file = 0;
+ unsigned int i;
+ char buf[MAXPATHLEN];
+
+ if (argc < 3 || argc > 4)
+ usage("Invalid number of arguments", -1);
+
+ if (sscanf(argv[2], "%u", &numfiles) != 1)
+ usage("Invalid maximum file", -2);
+
+ if (argc == 4 && sscanf(argv[3], "%u", &first_file) != 1)
+ usage("Invalid first file", -3);
+
+ for (i = first_file; i < first_file + numfiles; i++) {
+ int fd;
+ (void) snprintf(buf, MAXPATHLEN, "%s%u", argv[1], i);
+ if ((fd = open(buf, O_CREAT | O_EXCL, O_RDWR)) == -1) {
+ (void) fprintf(stderr, "Failed to create %s %s\n", buf,
+ strerror(errno));
+ return (-4);
+ } else if (fchown(fd, getuid(), getgid()) < 0) {
+ (void) fprintf(stderr, "Failed to chown %s %s\n", buf,
+ strerror(errno));
+ return (-5);
+ }
+ (void) close(fd);
+ }
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/.gitignore
new file mode 100644
index 000000000000..588bc6d1cce6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/.gitignore
@@ -0,0 +1 @@
+/mktree
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/Makefile.am
new file mode 100644
index 000000000000..88c74ae0a346
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = mktree
+mktree_SOURCES = mktree.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/mktree.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/mktree.c
new file mode 100644
index 000000000000..25b26c9e151b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mktree/mktree.c
@@ -0,0 +1,191 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/xattr.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#define TYPE_D 'D'
+#define TYPE_F 'F'
+
+static char fdname[MAXPATHLEN] = {0};
+static char *pbasedir = NULL;
+static int nlevel = 2;
+static int ndir = 2;
+static int nfile = 2;
+
+static void usage(char *this);
+static void crtfile(char *pname);
+static char *getfdname(char *pdir, char type, int level, int dir, int file);
+static int mktree(char *pbasedir, int level);
+
+int
+main(int argc, char *argv[])
+{
+ int c, ret;
+
+ while ((c = getopt(argc, argv, "b:l:d:f:")) != -1) {
+ switch (c) {
+ case 'b':
+ pbasedir = optarg;
+ break;
+ case 'l':
+ nlevel = atoi(optarg);
+ break;
+ case 'd':
+ ndir = atoi(optarg);
+ break;
+ case 'f':
+ nfile = atoi(optarg);
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ }
+ if (nlevel < 0 || ndir < 0 || nfile < 0 || pbasedir == NULL) {
+ usage(argv[0]);
+ }
+
+ ret = mktree(pbasedir, 1);
+
+ return (ret);
+}
+
+static void
+usage(char *this)
+{
+ (void) fprintf(stderr,
+ "\tUsage: %s -b <base_dir> -l [nlevel] -d [ndir] -f [nfile]\n",
+ this);
+ exit(1);
+}
+
+static int
+mktree(char *pdir, int level)
+{
+ int d, f;
+ char dname[MAXPATHLEN] = {0};
+ char fname[MAXPATHLEN] = {0};
+
+ if (level > nlevel) {
+ return (1);
+ }
+
+ for (d = 0; d < ndir; d++) {
+ (void) memset(dname, '\0', sizeof (dname));
+ (void) strcpy(dname, getfdname(pdir, TYPE_D, level, d, 0));
+
+ if (mkdir(dname, 0777) != 0) {
+ (void) fprintf(stderr, "mkdir(%s) failed."
+ "\n[%d]: %s.\n",
+ dname, errno, strerror(errno));
+ exit(errno);
+ }
+
+ /*
+ * No sub-directory need be created, only create files in it.
+ */
+ if (mktree(dname, level+1) != 0) {
+ for (f = 0; f < nfile; f++) {
+ (void) memset(fname, '\0', sizeof (fname));
+ (void) strcpy(fname,
+ getfdname(dname, TYPE_F, level+1, d, f));
+ crtfile(fname);
+ }
+ }
+ }
+
+ for (f = 0; f < nfile; f++) {
+ (void) memset(fname, '\0', sizeof (fname));
+ (void) strcpy(fname, getfdname(pdir, TYPE_F, level, d, f));
+ crtfile(fname);
+ }
+
+ return (0);
+}
+
+static char *
+getfdname(char *pdir, char type, int level, int dir, int file)
+{
+ size_t size = sizeof (fdname);
+ if (snprintf(fdname, size, "%s/%c-l%dd%df%d", pdir, type, level, dir,
+ file) >= size) {
+ (void) fprintf(stderr, "fdname truncated\n");
+ exit(EINVAL);
+ }
+ return (fdname);
+}
+
+static void
+crtfile(char *pname)
+{
+ int fd = -1;
+ int i, size;
+ char *context = "0123456789ABCDF";
+ char *pbuf;
+
+ if (pname == NULL) {
+ exit(1);
+ }
+
+ size = sizeof (char) * 1024;
+ pbuf = (char *)valloc(size);
+ for (i = 0; i < size / strlen(context); i++) {
+ int offset = i * strlen(context);
+ (void) snprintf(pbuf+offset, size-offset, "%s", context);
+ }
+
+ if ((fd = open(pname, O_CREAT|O_RDWR, 0777)) < 0) {
+ (void) fprintf(stderr, "open(%s, O_CREAT|O_RDWR, 0777) failed."
+ "\n[%d]: %s.\n", pname, errno, strerror(errno));
+ exit(errno);
+ }
+ if (write(fd, pbuf, 1024) < 1024) {
+ (void) fprintf(stderr, "write(fd, pbuf, 1024) failed."
+ "\n[%d]: %s.\n", errno, strerror(errno));
+ exit(errno);
+ }
+
+#ifdef __linux__
+ if (fsetxattr(fd, "user.xattr", pbuf, 1024, 0) < 0) {
+ (void) fprintf(stderr, "fsetxattr(fd, \"xattr\", pbuf, "
+ "1024, 0) failed.\n[%d]: %s.\n", errno, strerror(errno));
+ exit(errno);
+ }
+#endif
+
+ (void) close(fd);
+ free(pbuf);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/.gitignore
new file mode 100644
index 000000000000..63a68bbc681e
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/.gitignore
@@ -0,0 +1 @@
+/mmap_exec
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/Makefile.am
new file mode 100644
index 000000000000..ab9f81be9463
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = mmap_exec
+mmap_exec_SOURCES = mmap_exec.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/mmap_exec.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/mmap_exec.c
new file mode 100644
index 000000000000..db90adbdca10
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_exec/mmap_exec.c
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+int
+main(int argc, char *argv[])
+{
+ int error, fd;
+ struct stat statbuf;
+
+ if (argc != 2) {
+ (void) printf("Error: missing binary name.\n");
+ (void) printf("Usage:\n\t%s <binary name>\n",
+ argv[0]);
+ return (1);
+ }
+
+ errno = 0;
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ error = errno;
+ perror("open");
+ return (error);
+ }
+ if (fstat(fd, &statbuf) < 0) {
+ error = errno;
+ perror("fstat");
+ return (error);
+ }
+
+ if (mmap(0, statbuf.st_size,
+ PROT_EXEC, MAP_SHARED, fd, 0) == MAP_FAILED) {
+ error = errno;
+ perror("mmap");
+ return (error);
+ }
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/.gitignore
new file mode 100644
index 000000000000..792c8d3400b0
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/.gitignore
@@ -0,0 +1 @@
+/mmap_libaio
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/Makefile.am
new file mode 100644
index 000000000000..25f9dda2b623
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+if WANT_MMAP_LIBAIO
+pkgexec_PROGRAMS = mmap_libaio
+mmap_libaio_SOURCES = mmap_libaio.c
+mmap_libaio_CFLAGS = $(AM_CFLAGS) $(LIBAIO_CFLAGS)
+mmap_libaio_LDADD = $(LIBAIO_LIBS)
+endif
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/mmap_libaio.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/mmap_libaio.c
new file mode 100644
index 000000000000..21119ebca9d6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_libaio/mmap_libaio.c
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2018 Canonical. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libaio.h>
+#include <err.h>
+
+io_context_t io_ctx;
+
+static void
+do_sync_io(struct iocb *iocb)
+{
+ struct io_event event;
+ struct iocb *iocbs[] = { iocb };
+ struct timespec ts = { 30, 0 };
+
+ if (io_submit(io_ctx, 1, iocbs) != 1)
+ err(1, "io_submit failed");
+
+ if (io_getevents(io_ctx, 0, 1, &event, &ts) != 1)
+ err(1, "io_getevents failed");
+}
+
+int
+main(int argc, char **argv)
+{
+ char *buf;
+ int page_size = getpagesize();
+ int buf_size = strtol(argv[2], NULL, 0);
+ int rwfd;
+ struct iocb iocb;
+
+ if (io_queue_init(1024, &io_ctx))
+ err(1, "io_queue_init failed");
+
+ rwfd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (rwfd < 0)
+ err(1, "open failed");
+
+ if (ftruncate(rwfd, buf_size) < 0)
+ err(1, "ftruncate failed");
+
+ buf = mmap(0, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rwfd, 0);
+ if (buf == MAP_FAILED)
+ err(1, "mmap failed");
+
+ (void) io_prep_pwrite(&iocb, rwfd, buf, buf_size, 0);
+ do_sync_io(&iocb);
+
+ (void) io_prep_pread(&iocb, rwfd, buf, buf_size, 0);
+ do_sync_io(&iocb);
+
+ if (close(rwfd))
+ err(1, "close failed");
+
+ if (io_queue_release(io_ctx) != 0)
+ err(1, "io_queue_release failed");
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/.gitignore
new file mode 100644
index 000000000000..4e7043bbfd58
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/.gitignore
@@ -0,0 +1 @@
+/mmapwrite
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/Makefile.am
new file mode 100644
index 000000000000..b21b9e779bf2
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = mmapwrite
+mmapwrite_SOURCES = mmapwrite.c
+mmapwrite_LDADD = -lpthread
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/mmapwrite.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/mmapwrite.c
new file mode 100644
index 000000000000..458d6d8e402b
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/mmapwrite/mmapwrite.c
@@ -0,0 +1,162 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <errno.h>
+#include <err.h>
+
+/*
+ * --------------------------------------------------------------------
+ * Bug Issue Id: #7512
+ * The bug time sequence:
+ * 1. context #1, zfs_write assign a txg "n".
+ * 2. In the same process, context #2, mmap page fault (which means the mm_sem
+ * is hold) occurred, zfs_dirty_inode open a txg failed, and wait previous
+ * txg "n" completed.
+ * 3. context #1 call uiomove to write, however page fault is occurred in
+ * uiomove, which means it needs mm_sem, but mm_sem is hold by
+ * context #2, so it stuck and can't complete, then txg "n" will not
+ * complete.
+ *
+ * So context #1 and context #2 trap into the "dead lock".
+ * --------------------------------------------------------------------
+ */
+
+#define NORMAL_WRITE_TH_NUM 2
+
+static void *
+normal_writer(void *filename)
+{
+ char *file_path = filename;
+ int fd = -1;
+ ssize_t write_num = 0;
+ int page_size = getpagesize();
+
+ fd = open(file_path, O_RDWR | O_CREAT, 0777);
+ if (fd == -1) {
+ err(1, "failed to open %s", file_path);
+ }
+
+ char *buf = malloc(1);
+ while (1) {
+ write_num = write(fd, buf, 1);
+ if (write_num == 0) {
+ err(1, "write failed!");
+ break;
+ }
+ lseek(fd, page_size, SEEK_CUR);
+ }
+
+ if (buf) {
+ free(buf);
+ }
+}
+
+static void *
+map_writer(void *filename)
+{
+ int fd = -1;
+ int ret = 0;
+ char *buf = NULL;
+ int page_size = getpagesize();
+ int op_errno = 0;
+ char *file_path = filename;
+
+ while (1) {
+ ret = access(file_path, F_OK);
+ if (ret) {
+ op_errno = errno;
+ if (op_errno == ENOENT) {
+ fd = open(file_path, O_RDWR | O_CREAT, 0777);
+ if (fd == -1) {
+ err(1, "open file failed");
+ }
+
+ ret = ftruncate(fd, page_size);
+ if (ret == -1) {
+ err(1, "truncate file failed");
+ }
+ } else {
+ err(1, "access file failed!");
+ }
+ } else {
+ fd = open(file_path, O_RDWR, 0777);
+ if (fd == -1) {
+ err(1, "open file failed");
+ }
+ }
+
+ if ((buf = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ err(1, "map file failed");
+ }
+
+ if (fd != -1)
+ close(fd);
+
+ char s[10] = {0, };
+ memcpy(buf, s, 10);
+ ret = munmap(buf, page_size);
+ if (ret != 0) {
+ err(1, "unmap file failed");
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ pthread_t map_write_tid;
+ pthread_t normal_write_tid[NORMAL_WRITE_TH_NUM];
+ int i = 0;
+
+ if (argc != 3) {
+ (void) printf("usage: %s <normal write file name>"
+ "<map write file name>\n", argv[0]);
+ exit(1);
+ }
+
+ for (i = 0; i < NORMAL_WRITE_TH_NUM; i++) {
+ if (pthread_create(&normal_write_tid[i], NULL, normal_writer,
+ argv[1])) {
+ err(1, "pthread_create normal_writer failed.");
+ }
+ }
+
+ if (pthread_create(&map_write_tid, NULL, map_writer, argv[2])) {
+ err(1, "pthread_create map_writer failed.");
+ }
+
+ /* NOTREACHED */
+ pthread_join(map_write_tid, NULL);
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/.gitignore
new file mode 100644
index 000000000000..b31db6454dce
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/.gitignore
@@ -0,0 +1 @@
+/nvlist_to_lua
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/Makefile.am
new file mode 100644
index 000000000000..511b6c6913bb
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = nvlist_to_lua
+
+nvlist_to_lua_SOURCES = nvlist_to_lua.c
+nvlist_to_lua_LDADD = \
+ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
+ $(abs_top_builddir)/lib/libnvpair/libnvpair.la
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/nvlist_to_lua.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/nvlist_to_lua.c
new file mode 100644
index 000000000000..b130d667f231
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua/nvlist_to_lua.c
@@ -0,0 +1,305 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2016 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <libzfs_core.h>
+#include <sys/nvpair.h>
+
+nvlist_t *nvl;
+const char *pool;
+boolean_t unexpected_failures;
+
+static boolean_t
+nvlist_equal(nvlist_t *nvla, nvlist_t *nvlb)
+{
+ if (fnvlist_num_pairs(nvla) != fnvlist_num_pairs(nvlb))
+ return (B_FALSE);
+ /*
+ * The nvlists have the same number of pairs and keys are unique, so
+ * if every key in A is also in B and assigned to the same value, the
+ * lists are identical.
+ */
+ for (nvpair_t *pair = nvlist_next_nvpair(nvla, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(nvla, pair)) {
+ char *key = nvpair_name(pair);
+
+ if (!nvlist_exists(nvlb, key))
+ return (B_FALSE);
+
+ if (nvpair_type(pair) !=
+ nvpair_type(fnvlist_lookup_nvpair(nvlb, key)))
+ return (B_FALSE);
+
+ switch (nvpair_type(pair)) {
+ case DATA_TYPE_BOOLEAN_VALUE:
+ if (fnvpair_value_boolean_value(pair) !=
+ fnvlist_lookup_boolean_value(nvlb, key)) {
+ return (B_FALSE);
+ }
+ break;
+ case DATA_TYPE_STRING:
+ if (strcmp(fnvpair_value_string(pair),
+ fnvlist_lookup_string(nvlb, key))) {
+ return (B_FALSE);
+ }
+ break;
+ case DATA_TYPE_INT64:
+ if (fnvpair_value_int64(pair) !=
+ fnvlist_lookup_int64(nvlb, key)) {
+ return (B_FALSE);
+ }
+ break;
+ case DATA_TYPE_NVLIST:
+ if (!nvlist_equal(fnvpair_value_nvlist(pair),
+ fnvlist_lookup_nvlist(nvlb, key))) {
+ return (B_FALSE);
+ }
+ break;
+ default:
+ (void) printf("Unexpected type for nvlist_equal\n");
+ return (B_FALSE);
+ }
+ }
+ return (B_TRUE);
+}
+
+static void
+test(const char *testname, boolean_t expect_success, boolean_t expect_match)
+{
+ char *progstr = "input = ...; return {output=input}";
+
+ nvlist_t *outnvl;
+
+ (void) printf("\nrunning test '%s'; input:\n", testname);
+ dump_nvlist(nvl, 4);
+
+ int err = lzc_channel_program(pool, progstr,
+ 10 * 1000 * 1000, 10 * 1024 * 1024, nvl, &outnvl);
+
+ (void) printf("lzc_channel_program returned %u\n", err);
+ dump_nvlist(outnvl, 5);
+
+ if (err == 0 && expect_match) {
+ /*
+ * Verify that outnvl is the same as input nvl, if we expect
+ * them to be. The input and output will never match if the
+ * input contains an array (since arrays are converted to lua
+ * tables), so this is only asserted for some test cases.
+ */
+ nvlist_t *real_outnvl = fnvlist_lookup_nvlist(outnvl, "return");
+ real_outnvl = fnvlist_lookup_nvlist(real_outnvl, "output");
+ if (!nvlist_equal(nvl, real_outnvl)) {
+ unexpected_failures = B_TRUE;
+ (void) printf("unexpected input/output mismatch for "
+ "case: %s\n", testname);
+ }
+ }
+ if (err != 0 && expect_success) {
+ unexpected_failures = B_TRUE;
+ (void) printf("unexpected FAIL of case: %s\n", testname);
+ }
+
+ fnvlist_free(nvl);
+ nvl = fnvlist_alloc();
+}
+
+static void
+run_tests(void)
+{
+ const char *key = "key";
+
+ /* Note: maximum nvlist key length is 32KB */
+ int len = 1024 * 31;
+ char *bigstring = malloc(len);
+ for (int i = 0; i < len; i++)
+ bigstring[i] = 'a' + i % 26;
+ bigstring[len - 1] = '\0';
+
+ nvl = fnvlist_alloc();
+
+ fnvlist_add_boolean(nvl, key);
+ test("boolean", B_TRUE, B_FALSE);
+
+ fnvlist_add_boolean_value(nvl, key, B_TRUE);
+ test("boolean_value", B_FALSE, B_FALSE);
+
+ fnvlist_add_byte(nvl, key, 1);
+ test("byte", B_FALSE, B_FALSE);
+
+ fnvlist_add_int8(nvl, key, 1);
+ test("int8", B_FALSE, B_FALSE);
+
+ fnvlist_add_uint8(nvl, key, 1);
+ test("uint8", B_FALSE, B_FALSE);
+
+ fnvlist_add_int16(nvl, key, 1);
+ test("int16", B_FALSE, B_FALSE);
+
+ fnvlist_add_uint16(nvl, key, 1);
+ test("uint16", B_FALSE, B_FALSE);
+
+ fnvlist_add_int32(nvl, key, 1);
+ test("int32", B_FALSE, B_FALSE);
+
+ fnvlist_add_uint32(nvl, key, 1);
+ test("uint32", B_FALSE, B_FALSE);
+
+ fnvlist_add_int64(nvl, key, 1);
+ test("int64", B_TRUE, B_TRUE);
+
+ fnvlist_add_uint64(nvl, key, 1);
+ test("uint64", B_FALSE, B_FALSE);
+
+ fnvlist_add_string(nvl, key, "1");
+ test("string", B_TRUE, B_TRUE);
+
+
+ {
+ nvlist_t *val = fnvlist_alloc();
+ fnvlist_add_string(val, "subkey", "subvalue");
+ fnvlist_add_nvlist(nvl, key, val);
+ fnvlist_free(val);
+ test("nvlist", B_TRUE, B_TRUE);
+ }
+ {
+ boolean_t val[2] = { B_FALSE, B_TRUE };
+ fnvlist_add_boolean_array(nvl, key, val, 2);
+ test("boolean_array", B_FALSE, B_FALSE);
+ }
+ {
+ uchar_t val[2] = { 0, 1 };
+ fnvlist_add_byte_array(nvl, key, val, 2);
+ test("byte_array", B_FALSE, B_FALSE);
+ }
+ {
+ int8_t val[2] = { 0, 1 };
+ fnvlist_add_int8_array(nvl, key, val, 2);
+ test("int8_array", B_FALSE, B_FALSE);
+ }
+ {
+ uint8_t val[2] = { 0, 1 };
+ fnvlist_add_uint8_array(nvl, key, val, 2);
+ test("uint8_array", B_FALSE, B_FALSE);
+ }
+ {
+ int16_t val[2] = { 0, 1 };
+ fnvlist_add_int16_array(nvl, key, val, 2);
+ test("int16_array", B_FALSE, B_FALSE);
+ }
+ {
+ uint16_t val[2] = { 0, 1 };
+ fnvlist_add_uint16_array(nvl, key, val, 2);
+ test("uint16_array", B_FALSE, B_FALSE);
+ }
+ {
+ int32_t val[2] = { 0, 1 };
+ fnvlist_add_int32_array(nvl, key, val, 2);
+ test("int32_array", B_FALSE, B_FALSE);
+ }
+ {
+ uint32_t val[2] = { 0, 1 };
+ fnvlist_add_uint32_array(nvl, key, val, 2);
+ test("uint32_array", B_FALSE, B_FALSE);
+ }
+ {
+ int64_t val[2] = { 0, 1 };
+ fnvlist_add_int64_array(nvl, key, val, 2);
+ test("int64_array", B_TRUE, B_FALSE);
+ }
+ {
+ uint64_t val[2] = { 0, 1 };
+ fnvlist_add_uint64_array(nvl, key, val, 2);
+ test("uint64_array", B_FALSE, B_FALSE);
+ }
+ {
+ char *const val[2] = { "0", "1" };
+ fnvlist_add_string_array(nvl, key, val, 2);
+ test("string_array", B_TRUE, B_FALSE);
+ }
+ {
+ nvlist_t *val[2];
+ val[0] = fnvlist_alloc();
+ fnvlist_add_string(val[0], "subkey", "subvalue");
+ val[1] = fnvlist_alloc();
+ fnvlist_add_string(val[1], "subkey2", "subvalue2");
+ fnvlist_add_nvlist_array(nvl, key, val, 2);
+ fnvlist_free(val[0]);
+ fnvlist_free(val[1]);
+ test("nvlist_array", B_FALSE, B_FALSE);
+ }
+ {
+ fnvlist_add_string(nvl, bigstring, "1");
+ test("large_key", B_TRUE, B_TRUE);
+ }
+ {
+ fnvlist_add_string(nvl, key, bigstring);
+ test("large_value", B_TRUE, B_TRUE);
+ }
+ {
+ for (int i = 0; i < 1024; i++) {
+ char buf[32];
+ (void) snprintf(buf, sizeof (buf), "key-%u", i);
+ fnvlist_add_int64(nvl, buf, i);
+ }
+ test("many_keys", B_TRUE, B_TRUE);
+ }
+#ifndef __sparc__
+ {
+ for (int i = 0; i < 10; i++) {
+ nvlist_t *newval = fnvlist_alloc();
+ fnvlist_add_nvlist(newval, "key", nvl);
+ fnvlist_free(nvl);
+ nvl = newval;
+ }
+ test("deeply_nested_pos", B_TRUE, B_TRUE);
+ }
+ {
+ for (int i = 0; i < 90; i++) {
+ nvlist_t *newval = fnvlist_alloc();
+ fnvlist_add_nvlist(newval, "key", nvl);
+ fnvlist_free(nvl);
+ nvl = newval;
+ }
+ test("deeply_nested_neg", B_FALSE, B_FALSE);
+ }
+#endif
+ free(bigstring);
+ fnvlist_free(nvl);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ (void) libzfs_core_init();
+
+ if (argc != 2) {
+ (void) printf("usage: %s <pool>\n",
+ argv[0]);
+ exit(2);
+ }
+ pool = argv[1];
+
+ run_tests();
+
+ libzfs_core_fini();
+ return (unexpected_failures);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/.gitignore
new file mode 100644
index 000000000000..0f5b394c5fbd
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/.gitignore
@@ -0,0 +1 @@
+/randfree_file
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/Makefile.am
new file mode 100644
index 000000000000..6306e0e75740
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = randfree_file
+randfree_file_SOURCES = randfree_file.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/randfree_file.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/randfree_file.c
new file mode 100644
index 000000000000..c708d647e8b9
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/randfree_file/randfree_file.c
@@ -0,0 +1,125 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+#include "../file_common.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/falloc.h>
+
+/*
+ * Create a file with assigned size and then free the specified
+ * section of the file
+ */
+
+static void usage(char *progname);
+
+static void
+usage(char *progname)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-l filesize] [-s start-offset]"
+ "[-n section-len] filename\n", progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *filename = NULL;
+ char *buf = NULL;
+ size_t filesize = 0;
+ off_t start_off = 0;
+ off_t off_len = 0;
+ int fd, ch;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+
+ while ((ch = getopt(argc, argv, "l:s:n:")) != EOF) {
+ switch (ch) {
+ case 'l':
+ filesize = atoll(optarg);
+ break;
+ case 's':
+ start_off = atoll(optarg);
+ break;
+ case 'n':
+ off_len = atoll(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (optind == argc - 1)
+ filename = argv[optind];
+ else
+ usage(argv[0]);
+
+ if ((fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, mode)) < 0) {
+ perror("open");
+ return (1);
+ }
+
+ buf = (char *)calloc(1, filesize);
+ if (buf == NULL) {
+ perror("write");
+ close(fd);
+ return (1);
+ }
+ memset(buf, 'c', filesize);
+
+ if (write(fd, buf, filesize) < filesize) {
+ free(buf);
+ perror("write");
+ close(fd);
+ return (1);
+ }
+
+ free(buf);
+
+#if defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
+ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ start_off, off_len) < 0) {
+ perror("fallocate");
+ close(fd);
+ return (1);
+ }
+#else /* !(defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)) */
+ {
+ perror("FALLOC_FL_PUNCH_HOLE unsupported");
+ close(fd);
+ return (1);
+ }
+#endif /* defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE) */
+ close(fd);
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/.gitignore
new file mode 100644
index 000000000000..fb231c678cb2
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/.gitignore
@@ -0,0 +1 @@
+/randwritecomp
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/Makefile.am
new file mode 100644
index 000000000000..0002291fa7bf
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/Makefile.am
@@ -0,0 +1,9 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+DEFAULT_INCLUDES += \
+ -I$(top_srcdir)/include
+
+pkgexec_PROGRAMS = randwritecomp
+randwritecomp_SOURCES = randwritecomp.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/randwritecomp.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/randwritecomp.c
new file mode 100644
index 000000000000..708d5ee90511
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/randwritecomp/randwritecomp.c
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+/*
+ * The following is defined so the source can use
+ * lrand48() and srand48().
+ */
+#define __EXTENSIONS__
+
+#include <stdint.h>
+#include <string.h>
+#include "../file_common.h"
+
+/*
+ * The following sample was derived from real-world data
+ * of a production Oracle database.
+ */
+static uint64_t size_distribution[] = {
+ 0,
+ 1499018,
+ 352084,
+ 1503485,
+ 4206227,
+ 5626657,
+ 5387001,
+ 3733756,
+ 2233094,
+ 874652,
+ 238635,
+ 81434,
+ 33357,
+ 13106,
+ 2009,
+ 1,
+ 23660,
+};
+
+
+static uint64_t distribution_n;
+
+static uint8_t randbuf[BLOCKSZ];
+
+static void
+rwc_pwrite(int fd, const void *buf, size_t nbytes, off_t offset)
+{
+ size_t nleft = nbytes;
+ ssize_t nwrite = 0;
+
+ nwrite = pwrite(fd, buf, nbytes, offset);
+ if (nwrite < 0) {
+ perror("pwrite");
+ exit(EXIT_FAILURE);
+ }
+
+ nleft -= nwrite;
+ if (nleft != 0) {
+ (void) fprintf(stderr, "warning: pwrite: "
+ "wrote %zu out of %zu bytes\n",
+ (nbytes - nleft), nbytes);
+ }
+}
+
+static void
+fillbuf(char *buf)
+{
+ uint64_t rv = lrand48() % distribution_n;
+ uint64_t sum = 0;
+
+ uint64_t i;
+ for (i = 0;
+ i < sizeof (size_distribution) / sizeof (size_distribution[0]);
+ i++) {
+ sum += size_distribution[i];
+ if (rv < sum)
+ break;
+ }
+
+ bcopy(randbuf, buf, BLOCKSZ);
+ if (i == 0)
+ bzero(buf, BLOCKSZ - 10);
+ else if (i < 16)
+ bzero(buf, BLOCKSZ - i * 512 + 256);
+ /*LINTED: E_BAD_PTR_CAST_ALIGN*/
+ ((uint32_t *)buf)[0] = lrand48();
+}
+
+static void
+exit_usage(void)
+{
+ (void) printf("usage: ");
+ (void) printf("randwritecomp <file> [-s] [nwrites]\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+sequential_writes(int fd, char *buf, uint64_t nblocks, int64_t n)
+{
+ for (int64_t i = 0; n == -1 || i < n; i++) {
+ fillbuf(buf);
+
+ static uint64_t j = 0;
+ if (j == 0)
+ j = lrand48() % nblocks;
+ rwc_pwrite(fd, buf, BLOCKSZ, j * BLOCKSZ);
+ j++;
+ if (j >= nblocks)
+ j = 0;
+ }
+}
+
+static void
+random_writes(int fd, char *buf, uint64_t nblocks, int64_t n)
+{
+ for (int64_t i = 0; n == -1 || i < n; i++) {
+ fillbuf(buf);
+ rwc_pwrite(fd, buf, BLOCKSZ, (lrand48() % nblocks) * BLOCKSZ);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd, err;
+ char *filename = NULL;
+ char buf[BLOCKSZ];
+ struct stat ss;
+ uint64_t nblocks;
+ int64_t n = -1;
+ int sequential = 0;
+
+ if (argc < 2)
+ exit_usage();
+
+ argv++;
+ if (strcmp("-s", argv[0]) == 0) {
+ sequential = 1;
+ argv++;
+ }
+
+ if (argv[0] == NULL)
+ exit_usage();
+ else
+ filename = argv[0];
+
+ argv++;
+ if (argv[0] != NULL)
+ n = strtoull(argv[0], NULL, 0);
+
+ fd = open(filename, O_RDWR|O_CREAT, 0666);
+ err = fstat(fd, &ss);
+ if (err != 0) {
+ (void) fprintf(stderr,
+ "error: fstat returned error code %d\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ nblocks = ss.st_size / BLOCKSZ;
+ if (nblocks == 0) {
+ (void) fprintf(stderr, "error: "
+ "file is too small (min allowed size is %d bytes)\n",
+ BLOCKSZ);
+ exit(EXIT_FAILURE);
+ }
+
+ srand48(getpid());
+ for (int i = 0; i < BLOCKSZ; i++)
+ randbuf[i] = lrand48();
+
+ distribution_n = 0;
+ for (uint64_t i = 0;
+ i < sizeof (size_distribution) / sizeof (size_distribution[0]);
+ i++) {
+ distribution_n += size_distribution[i];
+ }
+
+ if (sequential)
+ sequential_writes(fd, buf, nblocks, n);
+ else
+ random_writes(fd, buf, nblocks, n);
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/.gitignore
new file mode 100644
index 000000000000..3799193a92be
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/.gitignore
@@ -0,0 +1 @@
+/readmmap
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/Makefile.am
new file mode 100644
index 000000000000..9b735c287e69
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = readmmap
+readmmap_SOURCES = readmmap.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/readmmap.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/readmmap.c
new file mode 100644
index 000000000000..e21c2c867d9a
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/readmmap/readmmap.c
@@ -0,0 +1,138 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * --------------------------------------------------------------
+ * BugId 5047993 : Getting bad read data.
+ *
+ * Usage: readmmap <filename>
+ *
+ * where:
+ * filename is an absolute path to the file name.
+ *
+ * Return values:
+ * 1 : error
+ * 0 : no errors
+ * --------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <time.h>
+
+int
+main(int argc, char **argv)
+{
+ char *filename = "badfile";
+ size_t size = 4395;
+ size_t idx = 0;
+ char *buf = NULL;
+ char *map = NULL;
+ int fd = -1, bytes, retval = 0;
+ unsigned seed;
+
+ if (argc < 2 || optind == argc) {
+ (void) fprintf(stderr,
+ "usage: %s <file name>\n", argv[0]);
+ exit(1);
+ }
+
+ if ((buf = calloc(1, size)) == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+
+ filename = argv[optind];
+
+ (void) remove(filename);
+
+ fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fd == -1) {
+ perror("open to create");
+ retval = 1;
+ goto end;
+ }
+
+ bytes = write(fd, buf, size);
+ if (bytes != size) {
+ (void) printf("short write: %d != %zd\n", bytes, size);
+ retval = 1;
+ goto end;
+ }
+
+ map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap");
+ retval = 1;
+ goto end;
+ }
+ seed = time(NULL);
+ srandom(seed);
+
+ idx = random() % size;
+ map[idx] = 1;
+
+ if (msync(map, size, MS_SYNC) != 0) {
+ perror("msync");
+ retval = 1;
+ goto end;
+ }
+
+ if (munmap(map, size) != 0) {
+ perror("munmap");
+ retval = 1;
+ goto end;
+ }
+
+ bytes = pread(fd, buf, size, 0);
+ if (bytes != size) {
+ (void) printf("short read: %d != %zd\n", bytes, size);
+ retval = 1;
+ goto end;
+ }
+
+ if (buf[idx] != 1) {
+ (void) printf(
+ "bad data from read! got buf[%zd]=%d, expected 1\n",
+ idx, buf[idx]);
+ retval = 1;
+ goto end;
+ }
+
+ (void) printf("good data from read: buf[%zd]=1\n", idx);
+end:
+ if (fd != -1) {
+ (void) close(fd);
+ }
+ if (buf != NULL) {
+ free(buf);
+ }
+
+ return (retval);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/.gitignore
new file mode 100644
index 000000000000..39a0cb222ad1
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/.gitignore
@@ -0,0 +1 @@
+/rename_dir
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/Makefile.am
new file mode 100644
index 000000000000..21971cd888fb
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = rename_dir
+rename_dir_SOURCES = rename_dir.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/rename_dir.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/rename_dir.c
new file mode 100644
index 000000000000..5f80f7229462
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/rename_dir/rename_dir.c
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * Assertion:
+ * Create two directory trees in zfs filesystem, and rename
+ * directory across the directory structure. ZFS can handle
+ * the race situation.
+ */
+
+/*
+ * Need to create the following directory structures before
+ * running this program:
+ *
+ * mkdir -p 1/2/3/4/5 a/b/c/d/e
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <strings.h>
+
+int
+main(int argc, char *argvp[])
+{
+ int i = 1;
+
+ switch (fork()) {
+ case -1:
+ perror("fork");
+ exit(1);
+ break;
+ case 0:
+ while (i > 0) {
+ int c_count = 0;
+ if (rename("a/b/c", "1/2/3/c") == 0)
+ c_count++;
+ if (rename("1/2/3/c", "a/b/c") == 0)
+ c_count++;
+ if (c_count) {
+ (void) fprintf(stderr, "c_count: %d", c_count);
+ }
+ }
+ break;
+ default:
+ while (i > 0) {
+ int p_count = 0;
+ if (rename("1", "a/b/c/d/e/1") == 0)
+ p_count++;
+ if (rename("a/b/c/d/e/1", "1") == 0)
+ p_count++;
+ if (p_count) {
+ (void) fprintf(stderr, "p_count: %d", p_count);
+ }
+ }
+ break;
+ }
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/.gitignore
new file mode 100644
index 000000000000..fc6323fb3ff3
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/.gitignore
@@ -0,0 +1 @@
+/rm_lnkcnt_zero_file
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile.am
new file mode 100644
index 000000000000..90fc8d0541b6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = rm_lnkcnt_zero_file
+rm_lnkcnt_zero_file_SOURCES = rm_lnkcnt_zero_file.c
+rm_lnkcnt_zero_file_LDADD = -lpthread
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/rm_lnkcnt_zero_file.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/rm_lnkcnt_zero_file.c
new file mode 100644
index 000000000000..e262ecefea92
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/rm_lnkcnt_zero_file/rm_lnkcnt_zero_file.c
@@ -0,0 +1,159 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * --------------------------------------------------------------------
+ * The purpose of this test is to see if the bug reported (#4723351) for
+ * UFS exists when using a ZFS file system.
+ * --------------------------------------------------------------------
+ *
+ */
+#define _REENTRANT 1
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+
+static char *filebase;
+
+static int
+pickidx(void)
+{
+ return (random() % 1000);
+}
+
+/* ARGSUSED */
+static void *
+mover(void *a)
+{
+ char buf[256];
+ int idx, len, ret;
+
+ len = strlen(filebase) + 5;
+
+ for (;;) {
+ idx = pickidx();
+ (void) snprintf(buf, len, "%s.%03d", filebase, idx);
+ ret = rename(filebase, buf);
+ if (ret < 0 && errno != ENOENT)
+ (void) perror("renaming file");
+ }
+
+ return (NULL);
+}
+
+/* ARGSUSED */
+static void *
+cleaner(void *a)
+{
+ char buf[256];
+ int idx, len, ret;
+
+ len = strlen(filebase) + 5;
+
+ for (;;) {
+ idx = pickidx();
+ (void) snprintf(buf, len, "%s.%03d", filebase, idx);
+ ret = remove(buf);
+ if (ret < 0 && errno != ENOENT)
+ (void) perror("removing file");
+ }
+
+ return (NULL);
+}
+
+static void *
+writer(void *a)
+{
+ int *fd = (int *)a;
+ int ret;
+
+ for (;;) {
+ if (*fd != -1)
+ (void) close (*fd);
+
+ *fd = open(filebase, O_APPEND | O_RDWR | O_CREAT, 0644);
+ if (*fd == -1) {
+ perror("fail to open test file, refreshing it");
+ continue;
+ }
+
+ ret = write(*fd, "test\n", 5);
+ if (ret != 5)
+ perror("writing file");
+ }
+
+ return (NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+ pthread_t tid;
+
+ if (argc == 1) {
+ (void) printf("Usage: %s <filebase>\n", argv[0]);
+ exit(-1);
+ }
+
+ filebase = argv[1];
+ fd = open(filebase, O_APPEND | O_RDWR | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("creating test file");
+ exit(-1);
+ }
+
+ (void) pthread_setconcurrency(4); /* 3 threads + main */
+ (void) pthread_create(&tid, NULL, mover, NULL);
+ (void) pthread_create(&tid, NULL, cleaner, NULL);
+ (void) pthread_create(&tid, NULL, writer, (void *) &fd);
+
+ for (;;) {
+ int ret;
+ struct stat st;
+
+ ret = stat(filebase, &st);
+ if (ret == 0 && (st.st_nlink > 2 || st.st_nlink < 1)) {
+ (void) printf("st.st_nlink = %d, exiting\n", \
+ (int)st.st_nlink);
+ exit(0);
+ }
+ (void) sleep(1);
+ }
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/.gitignore
new file mode 100644
index 000000000000..7c072ee0dec6
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/.gitignore
@@ -0,0 +1 @@
+/stride_dd
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/Makefile.am
new file mode 100644
index 000000000000..d6f1adbac2b7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = stride_dd
+stride_dd_SOURCES = stride_dd.c
+stride_dd_LDADD = -lrt
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/stride_dd.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/stride_dd.c
new file mode 100644
index 000000000000..88bd532923c7
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd/stride_dd.c
@@ -0,0 +1,214 @@
+/*
+ * 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) 2018 by Delphix. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int bsize = 0;
+static int count = 0;
+static char *ifile = NULL;
+static char *ofile = NULL;
+static int stride = 0;
+static int seek = 0;
+static char *execname = "stride_dd";
+
+static void usage(void);
+static void parse_options(int argc, char *argv[]);
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: %s -i inputfile -o outputfile -b blocksize -c count \n"
+ " -s stride [ -k seekblocks]\n"
+ "\n"
+ "Simplified version of dd that supports the stride option.\n"
+ "A stride of n means that for each block written, n - 1 blocks\n"
+ "are skipped in both the input and output file. A stride of 1\n"
+ "means that blocks are read and written consecutively.\n"
+ "All numeric parameters must be integers.\n"
+ "\n"
+ " inputfile: File to read from\n"
+ " outputfile: File to write to\n"
+ " blocksize: Size of each block to read/write\n"
+ " count: Number of blocks to read/write\n"
+ " stride: Read/write a block then skip (stride - 1) blocks\n"
+ " seekblocks: Number of blocks to skip at start of output\n",
+ execname);
+ (void) exit(1);
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+ int c;
+ int errflag = 0;
+
+ execname = argv[0];
+
+ extern char *optarg;
+ extern int optind, optopt;
+
+ while ((c = getopt(argc, argv, ":b:c:i:o:s:k:")) != -1) {
+ switch (c) {
+ case 'b':
+ bsize = atoi(optarg);
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ break;
+
+ case 'i':
+ ifile = optarg;
+ break;
+
+ case 'o':
+ ofile = optarg;
+ break;
+
+ case 's':
+ stride = atoi(optarg);
+ break;
+
+ case 'k':
+ seek = atoi(optarg);
+ break;
+
+ case ':':
+ (void) fprintf(stderr,
+ "Option -%c requires an operand\n", optopt);
+ errflag++;
+ break;
+
+ case '?':
+ default:
+ (void) fprintf(stderr,
+ "Unrecognized option: -%c\n", optopt);
+ errflag++;
+ break;
+ }
+
+ if (errflag) {
+ (void) usage();
+ }
+ }
+
+ if (bsize <= 0 || count <= 0 || stride <= 0 || ifile == NULL ||
+ ofile == NULL || seek < 0) {
+ (void) fprintf(stderr,
+ "Required parameter(s) missing or invalid.\n");
+ (void) usage();
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int ifd;
+ int ofd;
+ void *buf;
+ int c;
+
+ parse_options(argc, argv);
+
+ ifd = open(ifile, O_RDONLY);
+ if (ifd == -1) {
+ (void) fprintf(stderr, "%s: %s: ", execname, ifile);
+ perror("open");
+ exit(2);
+ }
+
+ ofd = open(ofile, O_WRONLY | O_CREAT, 0666);
+ if (ofd == -1) {
+ (void) fprintf(stderr, "%s: %s: ", execname, ofile);
+ perror("open");
+ exit(2);
+ }
+
+ /*
+ * We use valloc because some character block devices expect a
+ * page-aligned buffer.
+ */
+ int err = posix_memalign(&buf, 4096, bsize);
+ if (err != 0) {
+ (void) fprintf(stderr,
+ "%s: %s\n", execname, strerror(err));
+ exit(2);
+ }
+
+ if (seek > 0) {
+ if (lseek(ofd, seek * bsize, SEEK_CUR) == -1) {
+ perror("output lseek");
+ exit(2);
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ c = read(ifd, buf, bsize);
+ if (c != bsize) {
+
+ perror("read");
+ exit(2);
+ }
+ if (c != bsize) {
+ if (c < 0) {
+ perror("read");
+ } else {
+ (void) fprintf(stderr,
+ "%s: unexpected short read, read %d "
+ "bytes, expected %d\n", execname,
+ c, bsize);
+ }
+ exit(2);
+ }
+
+ c = write(ofd, buf, bsize);
+ if (c != bsize) {
+ if (c < 0) {
+ perror("write");
+ } else {
+ (void) fprintf(stderr,
+ "%s: unexpected short write, wrote %d "
+ "bytes, expected %d\n", execname,
+ c, bsize);
+ }
+ exit(2);
+ }
+
+ if (stride > 1) {
+ if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) {
+ perror("input lseek");
+ exit(2);
+ }
+ if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) {
+ perror("output lseek");
+ exit(2);
+ }
+ }
+ }
+ free(buf);
+
+ (void) close(ofd);
+ (void) close(ifd);
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/.gitignore
new file mode 100644
index 000000000000..4c8c8cdf34c1
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/.gitignore
@@ -0,0 +1 @@
+/threadsappend
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/Makefile.am
new file mode 100644
index 000000000000..f030b42d50fe
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = threadsappend
+threadsappend_SOURCES = threadsappend.c
+threadsappend_LDADD = -lpthread
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/threadsappend.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/threadsappend.c
new file mode 100644
index 000000000000..25710a3c12ef
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/threadsappend/threadsappend.c
@@ -0,0 +1,135 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/*
+ * The size of the output file, "go.out", should be 80*8192*2 = 1310720
+ *
+ * $ cd /tmp; go; ls -l go.out
+ * done.
+ * -rwxr-xr-x 1 jdm staff 1310720 Apr 13 19:45 go.out
+ * $ cd /zfs; go; ls -l go.out
+ * done.
+ * -rwxr-xr-x 1 jdm staff 663552 Apr 13 19:45 go.out
+ *
+ * The file on zfs is short as it does not appear that zfs is making the
+ * implicit seek to EOF and the actual write atomic. From the SUSv3
+ * interface spec, behavior is undefined if concurrent writes are performed
+ * from multi-processes to a single file. So I don't know if this is a
+ * standards violation, but I cannot find any such disclaimers in our
+ * man pages. This issue came up at a customer site in another context, and
+ * the suggestion was to open the file with O_APPEND, but that wouldn't
+ * help with zfs(see 4977529). Also see bug# 5031301.
+ */
+
+static int outfd = 0;
+
+static void *
+go(void *data)
+{
+ int ret, i = 0, n = *(int *)data;
+ char buf[8192] = {0};
+ (void) memset(buf, n, sizeof (buf));
+
+ for (i = 0; i < 80; i++) {
+ ret = write(outfd, buf, sizeof (buf));
+ if (ret != sizeof (buf))
+ perror("write");
+ }
+ return (NULL);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: zfs_threadsappend <file name>\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ pthread_t tid;
+ int ret = 0;
+ long ncpus = 0;
+ int i;
+
+ if (argc != 2) {
+ usage();
+ }
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (ncpus < 0) {
+ (void) fprintf(stderr,
+ "Invalid return from sysconf(_SC_NPROCESSORS_ONLN)"
+ " : errno (decimal)=%d\n", errno);
+ exit(1);
+ }
+ if (ncpus < 2) {
+ (void) fprintf(stderr,
+ "Must execute this binary on a multi-processor system\n");
+ exit(1);
+ }
+
+ outfd = open(argv[optind++], O_RDWR|O_CREAT|O_APPEND|O_TRUNC, 0777);
+ if (outfd == -1) {
+ (void) fprintf(stderr,
+ "zfs_threadsappend: "
+ "open(%s, O_RDWR|O_CREAT|O_APPEND|O_TRUNC, 0777)"
+ " failed\n", argv[optind]);
+ perror("open");
+ exit(1);
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = pthread_create(&tid, NULL, go, (void *)&i);
+ if (ret != 0) {
+ (void) fprintf(stderr,
+ "zfs_threadsappend: thr_create(#%d) "
+ "failed error=%d\n", i+1, ret);
+ exit(1);
+ }
+ }
+
+ while (pthread_join(tid, NULL) == 0)
+ continue;
+
+ return (0);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/.gitignore
new file mode 100644
index 000000000000..655867a640a3
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/.gitignore
@@ -0,0 +1 @@
+/user_ns_exec
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/Makefile.am
new file mode 100644
index 000000000000..5b4bc9aaa683
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = user_ns_exec
+user_ns_exec_SOURCES = user_ns_exec.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/user_ns_exec.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/user_ns_exec.c
new file mode 100644
index 000000000000..cd46738bd0bd
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/user_ns_exec/user_ns_exec.c
@@ -0,0 +1,179 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sched.h>
+
+#define EXECSHELL "/bin/sh"
+#define UIDMAP "0 100000 65536"
+
+static int
+child_main(int argc, char *argv[], int sync_pipe)
+{
+ char sync_buf;
+ char cmds[BUFSIZ] = { 0 };
+ char sep[] = " ";
+ int i, len;
+
+ if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) {
+ perror("unshare");
+ return (1);
+ }
+
+ /* tell parent we entered the new namespace */
+ if (write(sync_pipe, "1", 1) != 1) {
+ perror("write");
+ return (1);
+ }
+
+ /* wait for parent to setup the uid mapping */
+ if (read(sync_pipe, &sync_buf, 1) != 1) {
+ (void) fprintf(stderr, "user namespace setup failed\n");
+ return (1);
+ }
+
+ close(sync_pipe);
+
+ if (setuid(0) != 0) {
+ perror("setuid");
+ return (1);
+ }
+ if (setgid(0) != 0) {
+ perror("setgid");
+ return (1);
+ }
+
+ len = 0;
+ for (i = 1; i < argc; i++) {
+ (void) snprintf(cmds+len, sizeof (cmds)-len,
+ "%s%s", argv[i], sep);
+ len += strlen(argv[i]) + strlen(sep);
+ }
+
+ if (execl(EXECSHELL, "sh", "-c", cmds, (char *)NULL) != 0) {
+ perror("execl: " EXECSHELL);
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+set_idmap(pid_t pid, const char *file)
+{
+ int result = 0;
+ int mapfd;
+ char path[PATH_MAX];
+
+ (void) snprintf(path, sizeof (path), "/proc/%d/%s", (int)pid, file);
+
+ mapfd = open(path, O_WRONLY);
+ if (mapfd < 0) {
+ result = errno;
+ perror("open");
+ return (errno);
+ }
+
+ if (write(mapfd, UIDMAP, sizeof (UIDMAP)-1) != sizeof (UIDMAP)-1) {
+ perror("write");
+ result = (errno);
+ }
+
+ close(mapfd);
+
+ return (result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char sync_buf;
+ int result, wstatus;
+ int syncfd[2];
+ pid_t child;
+
+ if (argc < 2 || strlen(argv[1]) == 0) {
+ (void) printf("\tUsage: %s <commands> ...\n", argv[0]);
+ return (1);
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, syncfd) != 0) {
+ perror("socketpair");
+ return (1);
+ }
+
+ child = fork();
+ if (child == (pid_t)-1) {
+ perror("fork");
+ return (1);
+ }
+
+ if (child == 0) {
+ close(syncfd[0]);
+ return (child_main(argc, argv, syncfd[1]));
+ }
+
+ close(syncfd[1]);
+
+ result = 0;
+ /* wait for the child to have unshared its namespaces */
+ if (read(syncfd[0], &sync_buf, 1) != 1) {
+ perror("read");
+ kill(child, SIGKILL);
+ result = 1;
+ goto reap;
+ }
+
+ /* write uid mapping */
+ if (set_idmap(child, "uid_map") != 0 ||
+ set_idmap(child, "gid_map") != 0) {
+ result = 1;
+ kill(child, SIGKILL);
+ goto reap;
+ }
+
+ /* tell the child to proceed */
+ if (write(syncfd[0], "1", 1) != 1) {
+ perror("write");
+ kill(child, SIGKILL);
+ result = 1;
+ goto reap;
+ }
+ close(syncfd[0]);
+
+reap:
+ while (waitpid(child, &wstatus, 0) != child)
+ kill(child, SIGKILL);
+ if (result == 0)
+ result = WEXITSTATUS(wstatus);
+
+ return (result);
+}
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/.gitignore
new file mode 100644
index 000000000000..7d2128383639
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/.gitignore
@@ -0,0 +1 @@
+/xattrtest
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/Makefile.am
new file mode 100644
index 000000000000..7398ae634629
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
+
+pkgexec_PROGRAMS = xattrtest
+xattrtest_SOURCES = xattrtest.c
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/xattrtest.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/xattrtest.c
new file mode 100644
index 000000000000..42c510ed082d
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest/xattrtest.c
@@ -0,0 +1,718 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2016 Lawrence Livermore National Security, LLC.
+ */
+
+/*
+ * An extended attribute (xattr) correctness test. This program creates
+ * N files and sets M attrs on them of size S. Optionally is will verify
+ * a pattern stored in the xattr.
+ */
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/xattr.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <linux/limits.h>
+
+extern char *program_invocation_short_name;
+
+#define ERROR(fmt, ...) \
+ fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
+ program_invocation_short_name, __FILE__, __LINE__, \
+ __func__, ## __VA_ARGS__);
+
+static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:";
+static const struct option longopts[] = {
+ { "help", no_argument, 0, 'h' },
+ { "verbose", no_argument, 0, 'v' },
+ { "verify", no_argument, 0, 'y' },
+ { "nth", required_argument, 0, 'n' },
+ { "files", required_argument, 0, 'f' },
+ { "xattrs", required_argument, 0, 'x' },
+ { "size", required_argument, 0, 's' },
+ { "path", required_argument, 0, 'p' },
+ { "synccaches", no_argument, 0, 'c' },
+ { "dropcaches", no_argument, 0, 'd' },
+ { "script", required_argument, 0, 't' },
+ { "seed", required_argument, 0, 'e' },
+ { "random", no_argument, 0, 'r' },
+ { "randomvalue", no_argument, 0, 'R' },
+ { "keep", no_argument, 0, 'k' },
+ { "only", required_argument, 0, 'o' },
+ { 0, 0, 0, 0 }
+};
+
+enum phases {
+ PHASE_ALL = 0,
+ PHASE_CREATE,
+ PHASE_SETXATTR,
+ PHASE_GETXATTR,
+ PHASE_UNLINK,
+ PHASE_INVAL
+};
+
+static int verbose = 0;
+static int verify = 0;
+static int synccaches = 0;
+static int dropcaches = 0;
+static int nth = 0;
+static int files = 1000;
+static int xattrs = 1;
+static int size = 6;
+static int size_is_random = 0;
+static int value_is_random = 0;
+static int keep_files = 0;
+static int phase = PHASE_ALL;
+static char path[PATH_MAX] = "/tmp/xattrtest";
+static char script[PATH_MAX] = "/bin/true";
+static char xattrbytes[XATTR_SIZE_MAX];
+
+static int
+usage(int argc, char **argv)
+{
+ fprintf(stderr,
+ "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
+ " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
+ argv[0]);
+
+ fprintf(stderr,
+ " --help -h This help\n"
+ " --verbose -v Increase verbosity\n"
+ " --verify -y Verify xattr contents\n"
+ " --nth -n <nth> Print every nth file\n"
+ " --files -f <files> Set xattrs on N files\n"
+ " --xattrs -x <xattrs> Set N xattrs on each file\n"
+ " --size -s <bytes> Set N bytes per xattr\n"
+ " --path -p <path> Path to files\n"
+ " --synccaches -c Sync caches between phases\n"
+ " --dropcaches -d Drop caches between phases\n"
+ " --script -t <script> Exec script between phases\n"
+ " --seed -e <seed> Random seed value\n"
+ " --random -r Randomly sized xattrs [16-size]\n"
+ " --randomvalue -R Random xattr values\n"
+ " --keep -k Don't unlink files\n"
+ " --only -o <num> Only run phase N\n"
+ " 0=all, 1=create, 2=setxattr,\n"
+ " 3=getxattr, 4=unlink\n\n");
+
+ return (1);
+}
+
+static int
+parse_args(int argc, char **argv)
+{
+ long seed = time(NULL);
+ int c;
+ int rc = 0;
+
+ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ return (usage(argc, argv));
+ case 'v':
+ verbose++;
+ break;
+ case 'y':
+ verify = 1;
+ break;
+ case 'n':
+ nth = strtol(optarg, NULL, 0);
+ break;
+ case 'f':
+ files = strtol(optarg, NULL, 0);
+ break;
+ case 'x':
+ xattrs = strtol(optarg, NULL, 0);
+ break;
+ case 's':
+ size = strtol(optarg, NULL, 0);
+ if (size > XATTR_SIZE_MAX) {
+ fprintf(stderr, "Error: the -s value may not "
+ "be greater than %d\n", XATTR_SIZE_MAX);
+ rc = 1;
+ }
+ break;
+ case 'p':
+ strncpy(path, optarg, PATH_MAX);
+ path[PATH_MAX - 1] = '\0';
+ break;
+ case 'c':
+ synccaches = 1;
+ break;
+ case 'd':
+ dropcaches = 1;
+ break;
+ case 't':
+ strncpy(script, optarg, PATH_MAX);
+ script[PATH_MAX - 1] = '\0';
+ break;
+ case 'e':
+ seed = strtol(optarg, NULL, 0);
+ break;
+ case 'r':
+ size_is_random = 1;
+ break;
+ case 'R':
+ value_is_random = 1;
+ break;
+ case 'k':
+ keep_files = 1;
+ break;
+ case 'o':
+ phase = strtol(optarg, NULL, 0);
+ if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {
+ fprintf(stderr, "Error: the -o value must be "
+ "greater than %d and less than %d\n",
+ PHASE_ALL, PHASE_INVAL);
+ rc = 1;
+ }
+ break;
+ default:
+ rc = 1;
+ break;
+ }
+ }
+
+ if (rc != 0)
+ return (rc);
+
+ srandom(seed);
+
+ if (verbose) {
+ fprintf(stdout, "verbose: %d\n", verbose);
+ fprintf(stdout, "verify: %d\n", verify);
+ fprintf(stdout, "nth: %d\n", nth);
+ fprintf(stdout, "files: %d\n", files);
+ fprintf(stdout, "xattrs: %d\n", xattrs);
+ fprintf(stdout, "size: %d\n", size);
+ fprintf(stdout, "path: %s\n", path);
+ fprintf(stdout, "synccaches: %d\n", synccaches);
+ fprintf(stdout, "dropcaches: %d\n", dropcaches);
+ fprintf(stdout, "script: %s\n", script);
+ fprintf(stdout, "seed: %ld\n", seed);
+ fprintf(stdout, "random size: %d\n", size_is_random);
+ fprintf(stdout, "random value: %d\n", value_is_random);
+ fprintf(stdout, "keep: %d\n", keep_files);
+ fprintf(stdout, "only: %d\n", phase);
+ fprintf(stdout, "%s", "\n");
+ }
+
+ return (rc);
+}
+
+static int
+drop_caches(void)
+{
+ char file[] = "/proc/sys/vm/drop_caches";
+ int fd, rc;
+
+ fd = open(file, O_WRONLY);
+ if (fd == -1) {
+ ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);
+ return (errno);
+ }
+
+ rc = write(fd, "3", 1);
+ if ((rc == -1) || (rc != 1)) {
+ ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);
+ (void) close(fd);
+ return (errno);
+ }
+
+ rc = close(fd);
+ if (rc == -1) {
+ ERROR("Error %d: close(%d)\n", errno, fd);
+ return (errno);
+ }
+
+ return (0);
+}
+
+static int
+run_process(const char *path, char *argv[])
+{
+ pid_t pid;
+ int rc, devnull_fd;
+
+ pid = vfork();
+ if (pid == 0) {
+ devnull_fd = open("/dev/null", O_WRONLY);
+
+ if (devnull_fd < 0)
+ _exit(-1);
+
+ (void) dup2(devnull_fd, STDOUT_FILENO);
+ (void) dup2(devnull_fd, STDERR_FILENO);
+ close(devnull_fd);
+
+ (void) execvp(path, argv);
+ _exit(-1);
+ } else if (pid > 0) {
+ int status;
+
+ while ((rc = waitpid(pid, &status, 0)) == -1 &&
+ errno == EINTR) { }
+
+ if (rc < 0 || !WIFEXITED(status))
+ return (-1);
+
+ return (WEXITSTATUS(status));
+ }
+
+ return (-1);
+}
+
+static int
+post_hook(char *phase)
+{
+ char *argv[3] = { script, phase, (char *)0 };
+ int rc;
+
+ if (synccaches)
+ sync();
+
+ if (dropcaches) {
+ rc = drop_caches();
+ if (rc)
+ return (rc);
+ }
+
+ rc = run_process(script, argv);
+ if (rc)
+ return (rc);
+
+ return (0);
+}
+
+#define USEC_PER_SEC 1000000
+
+static void
+timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)
+{
+ while (usec >= USEC_PER_SEC) {
+ usec -= USEC_PER_SEC;
+ sec++;
+ }
+
+ while (usec < 0) {
+ usec += USEC_PER_SEC;
+ sec--;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+static void
+timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
+{
+ timeval_normalize(delta,
+ tv1->tv_sec - tv2->tv_sec,
+ tv1->tv_usec - tv2->tv_usec);
+}
+
+static double
+timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)
+{
+ struct timeval delta;
+
+ timeval_sub(&delta, tv1, tv2);
+ return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);
+}
+
+static int
+create_files(void)
+{
+ int i, rc;
+ char *file = NULL;
+ struct timeval start, stop;
+ double seconds;
+ size_t fsize;
+
+ fsize = PATH_MAX;
+ file = malloc(fsize);
+ if (file == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
+ PATH_MAX);
+ goto out;
+ }
+
+ (void) gettimeofday(&start, NULL);
+
+ for (i = 1; i <= files; i++) {
+ if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
+ rc = EINVAL;
+ ERROR("Error %d: path too long\n", rc);
+ goto out;
+ }
+
+ if (nth && ((i % nth) == 0))
+ fprintf(stdout, "create: %s\n", file);
+
+ rc = unlink(file);
+ if ((rc == -1) && (errno != ENOENT)) {
+ ERROR("Error %d: unlink(%s)\n", errno, file);
+ rc = errno;
+ goto out;
+ }
+
+ rc = open(file, O_CREAT, 0644);
+ if (rc == -1) {
+ ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
+ errno, file);
+ rc = errno;
+ goto out;
+ }
+
+ rc = close(rc);
+ if (rc == -1) {
+ ERROR("Error %d: close(%d)\n", errno, rc);
+ rc = errno;
+ goto out;
+ }
+ }
+
+ (void) gettimeofday(&stop, NULL);
+ seconds = timeval_sub_seconds(&stop, &start);
+ fprintf(stdout, "create: %f seconds %f creates/second\n",
+ seconds, files / seconds);
+
+ rc = post_hook("post");
+out:
+ if (file)
+ free(file);
+
+ return (rc);
+}
+
+static int
+get_random_bytes(char *buf, size_t bytes)
+{
+ int rand;
+ ssize_t bytes_read = 0;
+
+ rand = open("/dev/urandom", O_RDONLY);
+
+ if (rand < 0)
+ return (rand);
+
+ while (bytes_read < bytes) {
+ ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);
+ if (rc < 0)
+ break;
+ bytes_read += rc;
+ }
+
+ (void) close(rand);
+
+ return (bytes_read);
+}
+
+static int
+setxattrs(void)
+{
+ int i, j, rnd_size = size, shift, rc = 0;
+ char name[XATTR_NAME_MAX];
+ char *value = NULL;
+ char *file = NULL;
+ struct timeval start, stop;
+ double seconds;
+ size_t fsize;
+
+ value = malloc(XATTR_SIZE_MAX);
+ if (value == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
+ XATTR_SIZE_MAX);
+ goto out;
+ }
+
+ fsize = PATH_MAX;
+ file = malloc(fsize);
+ if (file == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
+ PATH_MAX);
+ goto out;
+ }
+
+ (void) gettimeofday(&start, NULL);
+
+ for (i = 1; i <= files; i++) {
+ if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
+ rc = EINVAL;
+ ERROR("Error %d: path too long\n", rc);
+ goto out;
+ }
+
+ if (nth && ((i % nth) == 0))
+ fprintf(stdout, "setxattr: %s\n", file);
+
+ for (j = 1; j <= xattrs; j++) {
+ if (size_is_random)
+ rnd_size = (random() % (size - 16)) + 16;
+
+ (void) sprintf(name, "user.%d", j);
+ shift = sprintf(value, "size=%d ", rnd_size);
+ memcpy(value + shift, xattrbytes,
+ sizeof (xattrbytes) - shift);
+
+ rc = lsetxattr(file, name, value, rnd_size, 0);
+ if (rc == -1) {
+ ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
+ errno, file, name, rnd_size);
+ goto out;
+ }
+ }
+ }
+
+ (void) gettimeofday(&stop, NULL);
+ seconds = timeval_sub_seconds(&stop, &start);
+ fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",
+ seconds, (files * xattrs) / seconds);
+
+ rc = post_hook("post");
+out:
+ if (file)
+ free(file);
+
+ if (value)
+ free(value);
+
+ return (rc);
+}
+
+static int
+getxattrs(void)
+{
+ int i, j, rnd_size, shift, rc = 0;
+ char name[XATTR_NAME_MAX];
+ char *verify_value = NULL;
+ char *verify_string;
+ char *value = NULL;
+ char *value_string;
+ char *file = NULL;
+ struct timeval start, stop;
+ double seconds;
+ size_t fsize;
+
+ verify_value = malloc(XATTR_SIZE_MAX);
+ if (verify_value == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc,
+ XATTR_SIZE_MAX);
+ goto out;
+ }
+
+ value = malloc(XATTR_SIZE_MAX);
+ if (value == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
+ XATTR_SIZE_MAX);
+ goto out;
+ }
+
+ verify_string = value_is_random ? "<random>" : verify_value;
+ value_string = value_is_random ? "<random>" : value;
+
+ fsize = PATH_MAX;
+ file = malloc(fsize);
+
+ if (file == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
+ PATH_MAX);
+ goto out;
+ }
+
+ (void) gettimeofday(&start, NULL);
+
+ for (i = 1; i <= files; i++) {
+ if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
+ rc = EINVAL;
+ ERROR("Error %d: path too long\n", rc);
+ goto out;
+ }
+
+ if (nth && ((i % nth) == 0))
+ fprintf(stdout, "getxattr: %s\n", file);
+
+ for (j = 1; j <= xattrs; j++) {
+ (void) sprintf(name, "user.%d", j);
+
+ rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);
+ if (rc == -1) {
+ ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
+ errno, file, name, XATTR_SIZE_MAX);
+ goto out;
+ }
+
+ if (!verify)
+ continue;
+
+ sscanf(value, "size=%d [a-z]", &rnd_size);
+ shift = sprintf(verify_value, "size=%d ",
+ rnd_size);
+ memcpy(verify_value + shift, xattrbytes,
+ sizeof (xattrbytes) - shift);
+
+ if (rnd_size != rc ||
+ memcmp(verify_value, value, rnd_size)) {
+ ERROR("Error %d: verify failed\n "
+ "verify: %s\n value: %s\n", EINVAL,
+ verify_string, value_string);
+ rc = 1;
+ goto out;
+ }
+ }
+ }
+
+ (void) gettimeofday(&stop, NULL);
+ seconds = timeval_sub_seconds(&stop, &start);
+ fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",
+ seconds, (files * xattrs) / seconds);
+
+ rc = post_hook("post");
+out:
+ if (file)
+ free(file);
+
+ if (value)
+ free(value);
+
+ if (verify_value)
+ free(verify_value);
+
+ return (rc);
+}
+
+static int
+unlink_files(void)
+{
+ int i, rc;
+ char *file = NULL;
+ struct timeval start, stop;
+ double seconds;
+ size_t fsize;
+
+ fsize = PATH_MAX;
+ file = malloc(fsize);
+ if (file == NULL) {
+ rc = ENOMEM;
+ ERROR("Error %d: malloc(%d) bytes for file name\n",
+ rc, PATH_MAX);
+ goto out;
+ }
+
+ (void) gettimeofday(&start, NULL);
+
+ for (i = 1; i <= files; i++) {
+ if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
+ rc = EINVAL;
+ ERROR("Error %d: path too long\n", rc);
+ goto out;
+ }
+
+ if (nth && ((i % nth) == 0))
+ fprintf(stdout, "unlink: %s\n", file);
+
+ rc = unlink(file);
+ if ((rc == -1) && (errno != ENOENT)) {
+ ERROR("Error %d: unlink(%s)\n", errno, file);
+ free(file);
+ return (errno);
+ }
+ }
+
+ (void) gettimeofday(&stop, NULL);
+ seconds = timeval_sub_seconds(&stop, &start);
+ fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",
+ seconds, files / seconds);
+
+ rc = post_hook("post");
+out:
+ if (file)
+ free(file);
+
+ return (rc);
+}
+
+int
+main(int argc, char **argv)
+{
+ int rc;
+
+ rc = parse_args(argc, argv);
+ if (rc)
+ return (rc);
+
+ if (value_is_random) {
+ size_t rndsz = sizeof (xattrbytes);
+
+ rc = get_random_bytes(xattrbytes, rndsz);
+ if (rc < rndsz) {
+ ERROR("Error %d: get_random_bytes() wanted %zd "
+ "got %d\n", errno, rndsz, rc);
+ return (rc);
+ }
+ } else {
+ memset(xattrbytes, 'x', sizeof (xattrbytes));
+ }
+
+ if (phase == PHASE_ALL || phase == PHASE_CREATE) {
+ rc = create_files();
+ if (rc)
+ return (rc);
+ }
+
+ if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {
+ rc = setxattrs();
+ if (rc)
+ return (rc);
+ }
+
+ if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {
+ rc = getxattrs();
+ if (rc)
+ return (rc);
+ }
+
+ if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {
+ rc = unlink_files();
+ if (rc)
+ return (rc);
+ }
+
+ return (0);
+}