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 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 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 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 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 for (int i = 0; i < len; i++) 133 bigstring[i] = 'a' + i % 26; 134 bigstring[len - 1] = '\0'; 135 136 nvl = fnvlist_alloc(); 137 138 fnvlist_add_boolean(nvl, key); 139 test("boolean", B_TRUE, B_FALSE); 140 141 fnvlist_add_boolean_value(nvl, key, B_TRUE); 142 test("boolean_value", B_FALSE, B_FALSE); 143 144 fnvlist_add_byte(nvl, key, 1); 145 test("byte", B_FALSE, B_FALSE); 146 147 fnvlist_add_int8(nvl, key, 1); 148 test("int8", B_FALSE, B_FALSE); 149 150 fnvlist_add_uint8(nvl, key, 1); 151 test("uint8", B_FALSE, B_FALSE); 152 153 fnvlist_add_int16(nvl, key, 1); 154 test("int16", B_FALSE, B_FALSE); 155 156 fnvlist_add_uint16(nvl, key, 1); 157 test("uint16", B_FALSE, B_FALSE); 158 159 fnvlist_add_int32(nvl, key, 1); 160 test("int32", B_FALSE, B_FALSE); 161 162 fnvlist_add_uint32(nvl, key, 1); 163 test("uint32", B_FALSE, B_FALSE); 164 165 fnvlist_add_int64(nvl, key, 1); 166 test("int64", B_TRUE, B_TRUE); 167 168 fnvlist_add_uint64(nvl, key, 1); 169 test("uint64", B_FALSE, B_FALSE); 170 171 fnvlist_add_string(nvl, key, "1"); 172 test("string", B_TRUE, B_TRUE); 173 174 175 { 176 nvlist_t *val = fnvlist_alloc(); 177 fnvlist_add_string(val, "subkey", "subvalue"); 178 fnvlist_add_nvlist(nvl, key, val); 179 fnvlist_free(val); 180 test("nvlist", B_TRUE, B_TRUE); 181 } 182 { 183 boolean_t val[2] = { B_FALSE, B_TRUE }; 184 fnvlist_add_boolean_array(nvl, key, val, 2); 185 test("boolean_array", B_FALSE, B_FALSE); 186 } 187 { 188 uchar_t val[2] = { 0, 1 }; 189 fnvlist_add_byte_array(nvl, key, val, 2); 190 test("byte_array", B_FALSE, B_FALSE); 191 } 192 { 193 int8_t val[2] = { 0, 1 }; 194 fnvlist_add_int8_array(nvl, key, val, 2); 195 test("int8_array", B_FALSE, B_FALSE); 196 } 197 { 198 uint8_t val[2] = { 0, 1 }; 199 fnvlist_add_uint8_array(nvl, key, val, 2); 200 test("uint8_array", B_FALSE, B_FALSE); 201 } 202 { 203 int16_t val[2] = { 0, 1 }; 204 fnvlist_add_int16_array(nvl, key, val, 2); 205 test("int16_array", B_FALSE, B_FALSE); 206 } 207 { 208 uint16_t val[2] = { 0, 1 }; 209 fnvlist_add_uint16_array(nvl, key, val, 2); 210 test("uint16_array", B_FALSE, B_FALSE); 211 } 212 { 213 int32_t val[2] = { 0, 1 }; 214 fnvlist_add_int32_array(nvl, key, val, 2); 215 test("int32_array", B_FALSE, B_FALSE); 216 } 217 { 218 uint32_t val[2] = { 0, 1 }; 219 fnvlist_add_uint32_array(nvl, key, val, 2); 220 test("uint32_array", B_FALSE, B_FALSE); 221 } 222 { 223 int64_t val[2] = { 0, 1 }; 224 fnvlist_add_int64_array(nvl, key, val, 2); 225 test("int64_array", B_TRUE, B_FALSE); 226 } 227 { 228 uint64_t val[2] = { 0, 1 }; 229 fnvlist_add_uint64_array(nvl, key, val, 2); 230 test("uint64_array", B_FALSE, B_FALSE); 231 } 232 { 233 const char *val[2] = { "0", "1" }; 234 fnvlist_add_string_array(nvl, key, val, 2); 235 test("string_array", B_TRUE, B_FALSE); 236 } 237 { 238 nvlist_t *val[2]; 239 val[0] = fnvlist_alloc(); 240 fnvlist_add_string(val[0], "subkey", "subvalue"); 241 val[1] = fnvlist_alloc(); 242 fnvlist_add_string(val[1], "subkey2", "subvalue2"); 243 fnvlist_add_nvlist_array(nvl, key, (const nvlist_t **)val, 2); 244 fnvlist_free(val[0]); 245 fnvlist_free(val[1]); 246 test("nvlist_array", B_FALSE, B_FALSE); 247 } 248 { 249 fnvlist_add_string(nvl, bigstring, "1"); 250 test("large_key", B_TRUE, B_TRUE); 251 } 252 { 253 fnvlist_add_string(nvl, key, bigstring); 254 test("large_value", B_TRUE, B_TRUE); 255 } 256 { 257 for (int i = 0; i < 1024; i++) { 258 char buf[32]; 259 (void) snprintf(buf, sizeof (buf), "key-%u", i); 260 fnvlist_add_int64(nvl, buf, i); 261 } 262 test("many_keys", B_TRUE, B_TRUE); 263 } 264 #ifndef __sparc__ 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 #endif 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