xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/nvlist_to_lua.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * This file and its contents are supplied under the terms of the
6  * Common Development and Distribution License ("CDDL"), version 1.0.
7  * You may only use this file in accordance with the terms of version
8  * 1.0 of the CDDL.
9  *
10  * A full copy of the text of the CDDL should have accompanied this
11  * source.  A copy of the CDDL is also available via the Internet at
12  * http://www.illumos.org/license/CDDL.
13  *
14  * CDDL HEADER END
15  */
16 
17 /*
18  * Copyright (c) 2016 by Delphix. All rights reserved.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <libzfs_core.h>
25 #include <sys/nvpair.h>
26 
27 static nvlist_t *nvl;
28 static const char *pool;
29 static boolean_t unexpected_failures;
30 
31 static boolean_t
nvlist_equal(nvlist_t * nvla,nvlist_t * nvlb)32 nvlist_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 		const 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 
85 static void
test(const char * testname,boolean_t expect_success,boolean_t expect_match)86 test(const char *testname, boolean_t expect_success, boolean_t expect_match)
87 {
88 	const 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 
125 static void
run_tests(void)126 run_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 	if (bigstring == NULL) {
134 		perror("malloc");
135 		exit(EXIT_FAILURE);
136 	}
137 
138 	for (int i = 0; i < len; i++)
139 		bigstring[i] = 'a' + i % 26;
140 	bigstring[len - 1] = '\0';
141 
142 	nvl = fnvlist_alloc();
143 
144 	fnvlist_add_boolean(nvl, key);
145 	test("boolean", B_TRUE, B_FALSE);
146 
147 	fnvlist_add_boolean_value(nvl, key, B_TRUE);
148 	test("boolean_value", B_FALSE, B_FALSE);
149 
150 	fnvlist_add_byte(nvl, key, 1);
151 	test("byte", B_FALSE, B_FALSE);
152 
153 	fnvlist_add_int8(nvl, key, 1);
154 	test("int8", B_FALSE, B_FALSE);
155 
156 	fnvlist_add_uint8(nvl, key, 1);
157 	test("uint8", B_FALSE, B_FALSE);
158 
159 	fnvlist_add_int16(nvl, key, 1);
160 	test("int16", B_FALSE, B_FALSE);
161 
162 	fnvlist_add_uint16(nvl, key, 1);
163 	test("uint16", B_FALSE, B_FALSE);
164 
165 	fnvlist_add_int32(nvl, key, 1);
166 	test("int32", B_FALSE, B_FALSE);
167 
168 	fnvlist_add_uint32(nvl, key, 1);
169 	test("uint32", B_FALSE, B_FALSE);
170 
171 	fnvlist_add_int64(nvl, key, 1);
172 	test("int64", B_TRUE, B_TRUE);
173 
174 	fnvlist_add_uint64(nvl, key, 1);
175 	test("uint64", B_FALSE, B_FALSE);
176 
177 	fnvlist_add_string(nvl, key, "1");
178 	test("string", B_TRUE, B_TRUE);
179 
180 
181 	{
182 		nvlist_t *val = fnvlist_alloc();
183 		fnvlist_add_string(val, "subkey", "subvalue");
184 		fnvlist_add_nvlist(nvl, key, val);
185 		fnvlist_free(val);
186 		test("nvlist", B_TRUE, B_TRUE);
187 	}
188 	{
189 		boolean_t val[2] = { B_FALSE, B_TRUE };
190 		fnvlist_add_boolean_array(nvl, key, val, 2);
191 		test("boolean_array", B_FALSE, B_FALSE);
192 	}
193 	{
194 		uchar_t val[2] = { 0, 1 };
195 		fnvlist_add_byte_array(nvl, key, val, 2);
196 		test("byte_array", B_FALSE, B_FALSE);
197 	}
198 	{
199 		int8_t val[2] = { 0, 1 };
200 		fnvlist_add_int8_array(nvl, key, val, 2);
201 		test("int8_array", B_FALSE, B_FALSE);
202 	}
203 	{
204 		uint8_t val[2] = { 0, 1 };
205 		fnvlist_add_uint8_array(nvl, key, val, 2);
206 		test("uint8_array", B_FALSE, B_FALSE);
207 	}
208 	{
209 		int16_t val[2] = { 0, 1 };
210 		fnvlist_add_int16_array(nvl, key, val, 2);
211 		test("int16_array", B_FALSE, B_FALSE);
212 	}
213 	{
214 		uint16_t val[2] = { 0, 1 };
215 		fnvlist_add_uint16_array(nvl, key, val, 2);
216 		test("uint16_array", B_FALSE, B_FALSE);
217 	}
218 	{
219 		int32_t val[2] = { 0, 1 };
220 		fnvlist_add_int32_array(nvl, key, val, 2);
221 		test("int32_array", B_FALSE, B_FALSE);
222 	}
223 	{
224 		uint32_t val[2] = { 0, 1 };
225 		fnvlist_add_uint32_array(nvl, key, val, 2);
226 		test("uint32_array", B_FALSE, B_FALSE);
227 	}
228 	{
229 		int64_t val[2] = { 0, 1 };
230 		fnvlist_add_int64_array(nvl, key, val, 2);
231 		test("int64_array", B_TRUE, B_FALSE);
232 	}
233 	{
234 		uint64_t val[2] = { 0, 1 };
235 		fnvlist_add_uint64_array(nvl, key, val, 2);
236 		test("uint64_array", B_FALSE, B_FALSE);
237 	}
238 	{
239 		const char *val[2] = { "0", "1" };
240 		fnvlist_add_string_array(nvl, key, val, 2);
241 		test("string_array", B_TRUE, B_FALSE);
242 	}
243 	{
244 		nvlist_t *val[2];
245 		val[0] = fnvlist_alloc();
246 		fnvlist_add_string(val[0], "subkey", "subvalue");
247 		val[1] = fnvlist_alloc();
248 		fnvlist_add_string(val[1], "subkey2", "subvalue2");
249 		fnvlist_add_nvlist_array(nvl, key, (const nvlist_t **)val, 2);
250 		fnvlist_free(val[0]);
251 		fnvlist_free(val[1]);
252 		test("nvlist_array", B_FALSE, B_FALSE);
253 	}
254 	{
255 		fnvlist_add_string(nvl, bigstring, "1");
256 		test("large_key", B_TRUE, B_TRUE);
257 	}
258 	{
259 		fnvlist_add_string(nvl, key, bigstring);
260 		test("large_value", B_TRUE, B_TRUE);
261 	}
262 	{
263 		for (int i = 0; i < 1024; i++) {
264 			char buf[32];
265 			(void) snprintf(buf, sizeof (buf), "key-%u", i);
266 			fnvlist_add_int64(nvl, buf, i);
267 		}
268 		test("many_keys", B_TRUE, B_TRUE);
269 	}
270 #ifndef __sparc__
271 	{
272 		for (int i = 0; i < 10; i++) {
273 			nvlist_t *newval = fnvlist_alloc();
274 			fnvlist_add_nvlist(newval, "key", nvl);
275 			fnvlist_free(nvl);
276 			nvl = newval;
277 		}
278 		test("deeply_nested_pos", B_TRUE, B_TRUE);
279 	}
280 	{
281 		for (int i = 0; i < 90; i++) {
282 			nvlist_t *newval = fnvlist_alloc();
283 			fnvlist_add_nvlist(newval, "key", nvl);
284 			fnvlist_free(nvl);
285 			nvl = newval;
286 		}
287 		test("deeply_nested_neg", B_FALSE, B_FALSE);
288 	}
289 #endif
290 	free(bigstring);
291 	fnvlist_free(nvl);
292 }
293 
294 int
main(int argc,const char * argv[])295 main(int argc, const char *argv[])
296 {
297 	(void) libzfs_core_init();
298 
299 	if (argc != 2) {
300 		(void) printf("usage: %s <pool>\n",
301 		    argv[0]);
302 		exit(2);
303 	}
304 	pool = argv[1];
305 
306 	run_tests();
307 
308 	libzfs_core_fini();
309 	return (unexpected_failures);
310 }
311