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 
26 static nvlist_t *nvl;
27 static const char *pool;
28 static boolean_t unexpected_failures;
29 
30 static boolean_t
nvlist_equal(nvlist_t * nvla,nvlist_t * nvlb)31 nvlist_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 
84 static void
test(const char * testname,boolean_t expect_success,boolean_t expect_match)85 test(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 
124 static void
run_tests(void)125 run_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 
293 int
main(int argc,const char * argv[])294 main(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