2018-02-08 16:16:23 +00:00
|
|
|
/*
|
|
|
|
* CDDL HEADER START
|
|
|
|
*
|
|
|
|
* This file and its contents are supplied under the terms of the
|
|
|
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
|
|
|
* You may only use this file in accordance with the terms of version
|
|
|
|
* 1.0 of the CDDL.
|
|
|
|
*
|
|
|
|
* A full copy of the text of the CDDL should have accompanied this
|
|
|
|
* source. A copy of the CDDL is also available via the Internet at
|
|
|
|
* http://www.illumos.org/license/CDDL.
|
|
|
|
*
|
|
|
|
* CDDL HEADER END
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 by Delphix. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <libzfs_core.h>
|
|
|
|
#include <sys/nvpair.h>
|
|
|
|
|
2022-01-22 00:56:46 +00:00
|
|
|
static nvlist_t *nvl;
|
|
|
|
static const char *pool;
|
|
|
|
static boolean_t unexpected_failures;
|
2018-02-08 16:16:23 +00:00
|
|
|
|
|
|
|
static boolean_t
|
|
|
|
nvlist_equal(nvlist_t *nvla, nvlist_t *nvlb)
|
|
|
|
{
|
|
|
|
if (fnvlist_num_pairs(nvla) != fnvlist_num_pairs(nvlb))
|
|
|
|
return (B_FALSE);
|
|
|
|
/*
|
|
|
|
* The nvlists have the same number of pairs and keys are unique, so
|
|
|
|
* if every key in A is also in B and assigned to the same value, the
|
|
|
|
* lists are identical.
|
|
|
|
*/
|
|
|
|
for (nvpair_t *pair = nvlist_next_nvpair(nvla, NULL);
|
|
|
|
pair != NULL; pair = nvlist_next_nvpair(nvla, pair)) {
|
|
|
|
char *key = nvpair_name(pair);
|
|
|
|
|
|
|
|
if (!nvlist_exists(nvlb, key))
|
|
|
|
return (B_FALSE);
|
|
|
|
|
|
|
|
if (nvpair_type(pair) !=
|
|
|
|
nvpair_type(fnvlist_lookup_nvpair(nvlb, key)))
|
|
|
|
return (B_FALSE);
|
|
|
|
|
|
|
|
switch (nvpair_type(pair)) {
|
|
|
|
case DATA_TYPE_BOOLEAN_VALUE:
|
|
|
|
if (fnvpair_value_boolean_value(pair) !=
|
|
|
|
fnvlist_lookup_boolean_value(nvlb, key)) {
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_STRING:
|
|
|
|
if (strcmp(fnvpair_value_string(pair),
|
|
|
|
fnvlist_lookup_string(nvlb, key))) {
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT64:
|
|
|
|
if (fnvpair_value_int64(pair) !=
|
|
|
|
fnvlist_lookup_int64(nvlb, key)) {
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_NVLIST:
|
|
|
|
if (!nvlist_equal(fnvpair_value_nvlist(pair),
|
|
|
|
fnvlist_lookup_nvlist(nvlb, key))) {
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) printf("Unexpected type for nvlist_equal\n");
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (B_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test(const char *testname, boolean_t expect_success, boolean_t expect_match)
|
|
|
|
{
|
2022-04-19 18:38:30 +00:00
|
|
|
const char *progstr = "input = ...; return {output=input}";
|
2018-02-08 16:16:23 +00:00
|
|
|
|
|
|
|
nvlist_t *outnvl;
|
|
|
|
|
|
|
|
(void) printf("\nrunning test '%s'; input:\n", testname);
|
|
|
|
dump_nvlist(nvl, 4);
|
|
|
|
|
|
|
|
int err = lzc_channel_program(pool, progstr,
|
|
|
|
10 * 1000 * 1000, 10 * 1024 * 1024, nvl, &outnvl);
|
|
|
|
|
|
|
|
(void) printf("lzc_channel_program returned %u\n", err);
|
|
|
|
dump_nvlist(outnvl, 5);
|
|
|
|
|
|
|
|
if (err == 0 && expect_match) {
|
|
|
|
/*
|
|
|
|
* Verify that outnvl is the same as input nvl, if we expect
|
|
|
|
* them to be. The input and output will never match if the
|
|
|
|
* input contains an array (since arrays are converted to lua
|
|
|
|
* tables), so this is only asserted for some test cases.
|
|
|
|
*/
|
|
|
|
nvlist_t *real_outnvl = fnvlist_lookup_nvlist(outnvl, "return");
|
|
|
|
real_outnvl = fnvlist_lookup_nvlist(real_outnvl, "output");
|
|
|
|
if (!nvlist_equal(nvl, real_outnvl)) {
|
|
|
|
unexpected_failures = B_TRUE;
|
|
|
|
(void) printf("unexpected input/output mismatch for "
|
|
|
|
"case: %s\n", testname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (err != 0 && expect_success) {
|
|
|
|
unexpected_failures = B_TRUE;
|
|
|
|
(void) printf("unexpected FAIL of case: %s\n", testname);
|
|
|
|
}
|
|
|
|
|
|
|
|
fnvlist_free(nvl);
|
|
|
|
nvl = fnvlist_alloc();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_tests(void)
|
|
|
|
{
|
|
|
|
const char *key = "key";
|
|
|
|
|
|
|
|
/* Note: maximum nvlist key length is 32KB */
|
|
|
|
int len = 1024 * 31;
|
|
|
|
char *bigstring = malloc(len);
|
Handle possible null pointers from malloc/strdup/strndup()
GCC 12.1.1_p20220625's static analyzer caught these.
Of the two in the btree test, one had previously been caught by Coverity
and Smatch, but GCC flagged it as a false positive. Upon examining how
other test cases handle this, the solution was changed from
`ASSERT3P(node, !=, NULL);` to using `perror()` to be consistent with
the fixes to the other fixes done to the ZTS code.
That approach was also used in ZED since I did not see a better way of
handling this there. Also, upon inspection, additional unchecked
pointers from malloc()/calloc()/strdup() were found in ZED, so those
were handled too.
In other parts of the code, the existing methods to avoid issues from
memory allocators returning NULL were used, such as using
`umem_alloc(size, UMEM_NOFAIL)` or returning `ENOMEM`.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Signed-off-by: Richard Yao <richard.yao@alumni.stonybrook.edu>
Closes #13979
2022-10-07 00:18:40 +00:00
|
|
|
if (bigstring == NULL) {
|
|
|
|
perror("malloc");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2018-02-08 16:16:23 +00:00
|
|
|
for (int i = 0; i < len; i++)
|
|
|
|
bigstring[i] = 'a' + i % 26;
|
|
|
|
bigstring[len - 1] = '\0';
|
|
|
|
|
|
|
|
nvl = fnvlist_alloc();
|
|
|
|
|
|
|
|
fnvlist_add_boolean(nvl, key);
|
|
|
|
test("boolean", B_TRUE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_boolean_value(nvl, key, B_TRUE);
|
|
|
|
test("boolean_value", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_byte(nvl, key, 1);
|
|
|
|
test("byte", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_int8(nvl, key, 1);
|
|
|
|
test("int8", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_uint8(nvl, key, 1);
|
|
|
|
test("uint8", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_int16(nvl, key, 1);
|
|
|
|
test("int16", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_uint16(nvl, key, 1);
|
|
|
|
test("uint16", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_int32(nvl, key, 1);
|
|
|
|
test("int32", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_uint32(nvl, key, 1);
|
|
|
|
test("uint32", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_int64(nvl, key, 1);
|
|
|
|
test("int64", B_TRUE, B_TRUE);
|
|
|
|
|
|
|
|
fnvlist_add_uint64(nvl, key, 1);
|
|
|
|
test("uint64", B_FALSE, B_FALSE);
|
|
|
|
|
|
|
|
fnvlist_add_string(nvl, key, "1");
|
|
|
|
test("string", B_TRUE, B_TRUE);
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
nvlist_t *val = fnvlist_alloc();
|
|
|
|
fnvlist_add_string(val, "subkey", "subvalue");
|
|
|
|
fnvlist_add_nvlist(nvl, key, val);
|
|
|
|
fnvlist_free(val);
|
|
|
|
test("nvlist", B_TRUE, B_TRUE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
boolean_t val[2] = { B_FALSE, B_TRUE };
|
|
|
|
fnvlist_add_boolean_array(nvl, key, val, 2);
|
|
|
|
test("boolean_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uchar_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_byte_array(nvl, key, val, 2);
|
|
|
|
test("byte_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
int8_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_int8_array(nvl, key, val, 2);
|
|
|
|
test("int8_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uint8_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_uint8_array(nvl, key, val, 2);
|
|
|
|
test("uint8_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
int16_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_int16_array(nvl, key, val, 2);
|
|
|
|
test("int16_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uint16_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_uint16_array(nvl, key, val, 2);
|
|
|
|
test("uint16_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
int32_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_int32_array(nvl, key, val, 2);
|
|
|
|
test("int32_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uint32_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_uint32_array(nvl, key, val, 2);
|
|
|
|
test("uint32_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
int64_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_int64_array(nvl, key, val, 2);
|
|
|
|
test("int64_array", B_TRUE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uint64_t val[2] = { 0, 1 };
|
|
|
|
fnvlist_add_uint64_array(nvl, key, val, 2);
|
|
|
|
test("uint64_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
2022-04-19 18:38:30 +00:00
|
|
|
const char *val[2] = { "0", "1" };
|
|
|
|
fnvlist_add_string_array(nvl, key, val, 2);
|
2018-02-08 16:16:23 +00:00
|
|
|
test("string_array", B_TRUE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
nvlist_t *val[2];
|
|
|
|
val[0] = fnvlist_alloc();
|
|
|
|
fnvlist_add_string(val[0], "subkey", "subvalue");
|
|
|
|
val[1] = fnvlist_alloc();
|
|
|
|
fnvlist_add_string(val[1], "subkey2", "subvalue2");
|
2021-12-07 01:19:13 +00:00
|
|
|
fnvlist_add_nvlist_array(nvl, key, (const nvlist_t **)val, 2);
|
2018-02-08 16:16:23 +00:00
|
|
|
fnvlist_free(val[0]);
|
|
|
|
fnvlist_free(val[1]);
|
|
|
|
test("nvlist_array", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fnvlist_add_string(nvl, bigstring, "1");
|
|
|
|
test("large_key", B_TRUE, B_TRUE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fnvlist_add_string(nvl, key, bigstring);
|
|
|
|
test("large_value", B_TRUE, B_TRUE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 1024; i++) {
|
|
|
|
char buf[32];
|
|
|
|
(void) snprintf(buf, sizeof (buf), "key-%u", i);
|
|
|
|
fnvlist_add_int64(nvl, buf, i);
|
|
|
|
}
|
|
|
|
test("many_keys", B_TRUE, B_TRUE);
|
|
|
|
}
|
|
|
|
#ifndef __sparc__
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
nvlist_t *newval = fnvlist_alloc();
|
|
|
|
fnvlist_add_nvlist(newval, "key", nvl);
|
|
|
|
fnvlist_free(nvl);
|
|
|
|
nvl = newval;
|
|
|
|
}
|
|
|
|
test("deeply_nested_pos", B_TRUE, B_TRUE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 90; i++) {
|
|
|
|
nvlist_t *newval = fnvlist_alloc();
|
|
|
|
fnvlist_add_nvlist(newval, "key", nvl);
|
|
|
|
fnvlist_free(nvl);
|
|
|
|
nvl = newval;
|
|
|
|
}
|
|
|
|
test("deeply_nested_neg", B_FALSE, B_FALSE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
free(bigstring);
|
|
|
|
fnvlist_free(nvl);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, const char *argv[])
|
|
|
|
{
|
|
|
|
(void) libzfs_core_init();
|
|
|
|
|
|
|
|
if (argc != 2) {
|
|
|
|
(void) printf("usage: %s <pool>\n",
|
|
|
|
argv[0]);
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
pool = argv[1];
|
|
|
|
|
|
|
|
run_tests();
|
|
|
|
|
|
|
|
libzfs_core_fini();
|
|
|
|
return (unexpected_failures);
|
|
|
|
}
|