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 27 nvlist_t *nvl; 28 const char *pool; 29 boolean_t unexpected_failures; 30 31 static boolean_t 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 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 86 test(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 125 static 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 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 { 266 for (int i = 0; i < 10; i++) { 267 nvlist_t *newval = fnvlist_alloc(); 268 fnvlist_add_nvlist(newval, "key", nvl); 269 fnvlist_free(nvl); 270 nvl = newval; 271 } 272 test("deeply_nested_pos", B_TRUE, B_TRUE); 273 } 274 { 275 for (int i = 0; i < 90; i++) { 276 nvlist_t *newval = fnvlist_alloc(); 277 fnvlist_add_nvlist(newval, "key", nvl); 278 fnvlist_free(nvl); 279 nvl = newval; 280 } 281 test("deeply_nested_neg", B_FALSE, B_FALSE); 282 } 283 284 free(bigstring); 285 fnvlist_free(nvl); 286 } 287 288 int 289 main(int argc, const char *argv[]) 290 { 291 (void) libzfs_core_init(); 292 293 if (argc != 2) { 294 (void) printf("usage: %s <pool>\n", 295 argv[0]); 296 exit(2); 297 } 298 pool = argv[1]; 299 300 run_tests(); 301 302 libzfs_core_fini(); 303 return (unexpected_failures); 304 } 305