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