1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source.  A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15
16/*
17 * Copyright (c) 2016 by Delphix. All rights reserved.
18 */
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <strings.h>
24#include <libzfs_core.h>
25#include <sys/nvpair.h>
26
27nvlist_t *nvl;
28const char *pool;
29boolean_t unexpected_failures;
30
31static boolean_t
32nvlist_equal(nvlist_t *nvla, nvlist_t *nvlb)
33{
34	if (fnvlist_num_pairs(nvla) != fnvlist_num_pairs(nvlb))
35		return (B_FALSE);
36	/*
37	 * The nvlists have the same number of pairs and keys are unique, so
38	 * if every key in A is also in B and assigned to the same value, the
39	 * lists are identical.
40	 */
41	for (nvpair_t *pair = nvlist_next_nvpair(nvla, NULL);
42	    pair != NULL; pair = nvlist_next_nvpair(nvla, pair)) {
43		char *key = nvpair_name(pair);
44
45		if (!nvlist_exists(nvlb, key))
46			return (B_FALSE);
47
48		if (nvpair_type(pair) !=
49		    nvpair_type(fnvlist_lookup_nvpair(nvlb, key)))
50			return (B_FALSE);
51
52		switch (nvpair_type(pair)) {
53		case DATA_TYPE_BOOLEAN_VALUE:
54			if (fnvpair_value_boolean_value(pair) !=
55			    fnvlist_lookup_boolean_value(nvlb, key)) {
56				return (B_FALSE);
57			}
58			break;
59		case DATA_TYPE_STRING:
60			if (strcmp(fnvpair_value_string(pair),
61			    fnvlist_lookup_string(nvlb, key))) {
62				return (B_FALSE);
63			}
64			break;
65		case DATA_TYPE_INT64:
66			if (fnvpair_value_int64(pair) !=
67			    fnvlist_lookup_int64(nvlb, key)) {
68				return (B_FALSE);
69			}
70			break;
71		case DATA_TYPE_NVLIST:
72			if (!nvlist_equal(fnvpair_value_nvlist(pair),
73			    fnvlist_lookup_nvlist(nvlb, key))) {
74				return (B_FALSE);
75			}
76			break;
77		default:
78			(void) printf("Unexpected type for nvlist_equal\n");
79			return (B_FALSE);
80		}
81	}
82	return (B_TRUE);
83}
84
85static void
86test(const char *testname, boolean_t expect_success, boolean_t expect_match)
87{
88	char *progstr = "input = ...; return {output=input}";
89
90	nvlist_t *outnvl;
91
92	(void) printf("\nrunning test '%s'; input:\n", testname);
93	dump_nvlist(nvl, 4);
94
95	int err = lzc_channel_program(pool, progstr,
96	    10 * 1000 * 1000, 10 * 1024 * 1024, nvl, &outnvl);
97
98	(void) printf("lzc_channel_program returned %u\n", err);
99	dump_nvlist(outnvl, 5);
100
101	if (err == 0 && expect_match) {
102		/*
103		 * Verify that outnvl is the same as input nvl, if we expect
104		 * them to be. The input and output will never match if the
105		 * input contains an array (since arrays are converted to lua
106		 * tables), so this is only asserted for some test cases.
107		 */
108		nvlist_t *real_outnvl = fnvlist_lookup_nvlist(outnvl, "return");
109		real_outnvl = fnvlist_lookup_nvlist(real_outnvl, "output");
110		if (!nvlist_equal(nvl, real_outnvl)) {
111			unexpected_failures = B_TRUE;
112			(void) printf("unexpected input/output mismatch for "
113			    "case: %s\n", testname);
114		}
115	}
116	if (err != 0 && expect_success) {
117		unexpected_failures = B_TRUE;
118		(void) printf("unexpected FAIL of case: %s\n", testname);
119	}
120
121	fnvlist_free(nvl);
122	nvl = fnvlist_alloc();
123}
124
125static void
126run_tests(void)
127{
128	const char *key = "key";
129
130	/* Note: maximum nvlist key length is 32KB */
131	int len = 1024 * 31;
132	char *bigstring = malloc(len);
133	for (int i = 0; i < len; i++)
134		bigstring[i] = 'a' + i % 26;
135	bigstring[len - 1] = '\0';
136
137	nvl = fnvlist_alloc();
138
139	fnvlist_add_boolean(nvl, key);
140	test("boolean", B_TRUE, B_FALSE);
141
142	fnvlist_add_boolean_value(nvl, key, B_TRUE);
143	test("boolean_value", B_FALSE, B_FALSE);
144
145	fnvlist_add_byte(nvl, key, 1);
146	test("byte", B_FALSE, B_FALSE);
147
148	fnvlist_add_int8(nvl, key, 1);
149	test("int8", B_FALSE, B_FALSE);
150
151	fnvlist_add_uint8(nvl, key, 1);
152	test("uint8", B_FALSE, B_FALSE);
153
154	fnvlist_add_int16(nvl, key, 1);
155	test("int16", B_FALSE, B_FALSE);
156
157	fnvlist_add_uint16(nvl, key, 1);
158	test("uint16", B_FALSE, B_FALSE);
159
160	fnvlist_add_int32(nvl, key, 1);
161	test("int32", B_FALSE, B_FALSE);
162
163	fnvlist_add_uint32(nvl, key, 1);
164	test("uint32", B_FALSE, B_FALSE);
165
166	fnvlist_add_int64(nvl, key, 1);
167	test("int64", B_TRUE, B_TRUE);
168
169	fnvlist_add_uint64(nvl, key, 1);
170	test("uint64", B_FALSE, B_FALSE);
171
172	fnvlist_add_string(nvl, key, "1");
173	test("string", B_TRUE, B_TRUE);
174
175
176	{
177		nvlist_t *val = fnvlist_alloc();
178		fnvlist_add_string(val, "subkey", "subvalue");
179		fnvlist_add_nvlist(nvl, key, val);
180		fnvlist_free(val);
181		test("nvlist", B_TRUE, B_TRUE);
182	}
183	{
184		boolean_t val[2] = { B_FALSE, B_TRUE };
185		fnvlist_add_boolean_array(nvl, key, val, 2);
186		test("boolean_array", B_FALSE, B_FALSE);
187	}
188	{
189		uchar_t val[2] = { 0, 1 };
190		fnvlist_add_byte_array(nvl, key, val, 2);
191		test("byte_array", B_FALSE, B_FALSE);
192	}
193	{
194		int8_t val[2] = { 0, 1 };
195		fnvlist_add_int8_array(nvl, key, val, 2);
196		test("int8_array", B_FALSE, B_FALSE);
197	}
198	{
199		uint8_t val[2] = { 0, 1 };
200		fnvlist_add_uint8_array(nvl, key, val, 2);
201		test("uint8_array", B_FALSE, B_FALSE);
202	}
203	{
204		int16_t val[2] = { 0, 1 };
205		fnvlist_add_int16_array(nvl, key, val, 2);
206		test("int16_array", B_FALSE, B_FALSE);
207	}
208	{
209		uint16_t val[2] = { 0, 1 };
210		fnvlist_add_uint16_array(nvl, key, val, 2);
211		test("uint16_array", B_FALSE, B_FALSE);
212	}
213	{
214		int32_t val[2] = { 0, 1 };
215		fnvlist_add_int32_array(nvl, key, val, 2);
216		test("int32_array", B_FALSE, B_FALSE);
217	}
218	{
219		uint32_t val[2] = { 0, 1 };
220		fnvlist_add_uint32_array(nvl, key, val, 2);
221		test("uint32_array", B_FALSE, B_FALSE);
222	}
223	{
224		int64_t val[2] = { 0, 1 };
225		fnvlist_add_int64_array(nvl, key, val, 2);
226		test("int64_array", B_TRUE, B_FALSE);
227	}
228	{
229		uint64_t val[2] = { 0, 1 };
230		fnvlist_add_uint64_array(nvl, key, val, 2);
231		test("uint64_array", B_FALSE, B_FALSE);
232	}
233	{
234		char *const val[2] = { "0", "1" };
235		fnvlist_add_string_array(nvl, key, val, 2);
236		test("string_array", B_TRUE, B_FALSE);
237	}
238	{
239		nvlist_t *val[2];
240		val[0] = fnvlist_alloc();
241		fnvlist_add_string(val[0], "subkey", "subvalue");
242		val[1] = fnvlist_alloc();
243		fnvlist_add_string(val[1], "subkey2", "subvalue2");
244		fnvlist_add_nvlist_array(nvl, key, val, 2);
245		fnvlist_free(val[0]);
246		fnvlist_free(val[1]);
247		test("nvlist_array", B_FALSE, B_FALSE);
248	}
249	{
250		fnvlist_add_string(nvl, bigstring, "1");
251		test("large_key", B_TRUE, B_TRUE);
252	}
253	{
254		fnvlist_add_string(nvl, key, bigstring);
255		test("large_value", B_TRUE, B_TRUE);
256	}
257	{
258		for (int i = 0; i < 1024; i++) {
259			char buf[32];
260			(void) snprintf(buf, sizeof (buf), "key-%u", i);
261			fnvlist_add_int64(nvl, buf, i);
262		}
263		test("many_keys", B_TRUE, B_TRUE);
264	}
265#ifndef __sparc__
266	{
267		for (int i = 0; i < 10; i++) {
268			nvlist_t *newval = fnvlist_alloc();
269			fnvlist_add_nvlist(newval, "key", nvl);
270			fnvlist_free(nvl);
271			nvl = newval;
272		}
273		test("deeply_nested_pos", B_TRUE, B_TRUE);
274	}
275	{
276		for (int i = 0; i < 90; i++) {
277			nvlist_t *newval = fnvlist_alloc();
278			fnvlist_add_nvlist(newval, "key", nvl);
279			fnvlist_free(nvl);
280			nvl = newval;
281		}
282		test("deeply_nested_neg", B_FALSE, B_FALSE);
283	}
284#endif
285	free(bigstring);
286	fnvlist_free(nvl);
287}
288
289int
290main(int argc, const char *argv[])
291{
292	(void) libzfs_core_init();
293
294	if (argc != 2) {
295		(void) printf("usage: %s <pool>\n",
296		    argv[0]);
297		exit(2);
298	}
299	pool = argv[1];
300
301	run_tests();
302
303	libzfs_core_fini();
304	return (unexpected_failures);
305}
306