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