diff options
Diffstat (limited to 'sys/contrib/openzfs/tests/zfs-tests/cmd')
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); +} |