diff --git a/src/control/cmd/ddb/main.go b/src/control/cmd/ddb/main.go index b7ed3672f81..e1985bddc79 100644 --- a/src/control/cmd/ddb/main.go +++ b/src/control/cmd/ddb/main.go @@ -1,6 +1,6 @@ // // (C) Copyright 2022-2024 Intel Corporation. -// (C) Copyright 2025 Hewlett Packard Enterprise Development LP +// (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP // // SPDX-License-Identifier: BSD-2-Clause-Patent // @@ -179,10 +179,36 @@ Example Paths: return nil } + if os.Getenv("DD_MASK") == "" { + os.Setenv("DD_MASK", "mgmt,epc,csum,md,df,io,trace") + } if opts.Debug { - log.WithLogLevel(logging.LogLevelDebug) - log.Debug("debug output enabled") + log.WithLogLevel(logging.LogLevelTrace) + if os.Getenv("D_LOG_MASK") == "" { + os.Setenv("D_LOG_MASK", "INFO,DDB=DEBUG") + } + // Show debug output and above on stderr to not pollute stdout + // NOTE: DD_STDERR can only be used with INFO and above. + if os.Getenv("DD_STDERR") == "" { + os.Setenv("DD_STDERR", "EMIT") + } + if os.Getenv("D_LOG_FILE") == "" { + os.Setenv("D_LOG_FILE", "/dev/stderr") + } + } else { + log.WithLogLevel(logging.LogLevelError) + if os.Getenv("D_LOG_MASK") == "" { + os.Setenv("D_LOG_MASK", "ERR,DDB=WARN") + } + // Only show warnings and above on stderr to not pollute stdout + if os.Getenv("DD_STDERR") == "" { + os.Setenv("DD_STDERR", "WARN") + } + if os.Getenv("D_LOG_FILE") == "" { + os.Setenv("D_LOG_FILE", "/dev/null") + } } + log.Debug("debug output enabled") ctx, cleanup, err := InitDdb(log) if err != nil { diff --git a/src/include/daos/debug.h b/src/include/daos/debug.h index a9feeb022cd..f9e8c685084 100644 --- a/src/include/daos/debug.h +++ b/src/include/daos/debug.h @@ -1,6 +1,6 @@ /** * (C) Copyright 2015-2024 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -55,7 +55,8 @@ ACTION(il, il, arg) \ ACTION(csum, csum, arg) \ ACTION(pipeline, pipeline, arg) \ - ACTION(stack, stack, arg) + ACTION(stack, stack, arg) \ + ACTION(ddb, ddb, arg) #define DAOS_FOREACH_DB(ACTION, arg) \ /** metadata operation */ \ diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index 9ada3ab78db..9345450b1b8 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -5,9 +5,12 @@ * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) + #include #include #include + #include #include diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index fe1cd3b52e8..946cecd6fda 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -5,24 +5,24 @@ * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) -#include -#include #include #include -#include "daos_errno.h" -#include "daos_srv/vos_types.h" -#include "daos_types.h" +#include +#include +#include +#include +#include +#include + #include "ddb_common.h" #include "ddb_parse.h" #include "ddb.h" #include "ddb_vos.h" #include "ddb_printer.h" -#include "daos.h" #include "ddb_tree_path.h" -#include "gurt/common.h" -#include "gurt/debug.h" #define ilog_path_required_error_message "Path to object, dkey, or akey required\n" #define error_msg_write_mode_only "Can only modify the VOS tree in 'write mode'\n" diff --git a/src/utils/ddb/ddb_main.c b/src/utils/ddb/ddb_main.c index 514df0e73ca..9849425903e 100644 --- a/src/utils/ddb/ddb_main.c +++ b/src/utils/ddb/ddb_main.c @@ -5,15 +5,18 @@ * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) + +#include +#include #include #include + +#include "ddb.h" #include "ddb_common.h" #include "ddb_parse.h" #include "ddb_vos.h" -#include "ddb.h" -#include -#include int ddb_init() diff --git a/src/utils/ddb/ddb_mgmt.c b/src/utils/ddb/ddb_mgmt.c index 12387df6444..72ba09edceb 100644 --- a/src/utils/ddb/ddb_mgmt.c +++ b/src/utils/ddb/ddb_mgmt.c @@ -1,9 +1,10 @@ /** * (C) Copyright 2025 Vdura Inc. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) #include #include @@ -13,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/src/utils/ddb/ddb_parse.c b/src/utils/ddb/ddb_parse.c index cb8633e8dba..b3488c65934 100644 --- a/src/utils/ddb/ddb_parse.c +++ b/src/utils/ddb/ddb_parse.c @@ -4,11 +4,16 @@ * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) #include #include -#include -#include "daos_errno.h" +#include +#include +#include + +#include + #include "ddb_common.h" #include "ddb_parse.h" @@ -20,55 +25,123 @@ safe_strcat(char *dst, const char *src, size_t dst_size) strncat(dst, src, remaining_space); } +static void +print_regx_error(int rc, regex_t *preg, const char *regex_buf) +{ + char *buf; + size_t buf_size; + + buf_size = regerror(rc, preg, NULL, 0); + D_ALLOC_ARRAY(buf, buf_size); + D_ASSERT(buf != NULL); + regerror(rc, preg, buf, buf_size); + D_CRIT("Invalid regex '%s': %s", regex_buf, buf); + D_FREE(buf); +} + int vos_path_parse(const char *path, struct vos_file_parts *vos_file_parts) { - uint32_t path_len = strlen(path) + 1; - char *path_copy; - char *tok; - int rc = -DER_INVAL; + enum { + DB_PATH_IDX = 1, + POOL_UUID_IDX = 3, + VOS_FILE_NAME_IDX = 5, + TARGET_IDX_IDX = 6, + MATCH_SIZE = 7, + POOL_UUID_LEN = 36 + }; + const char *regex_buf = + "^(/?([^/]+/)*)([0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})/" + "(vos-([0-9]|([1-9][0-9]+)))$"; + char *endptr; + uint64_t target_idx; + regex_t preg; + char pool_uuid[POOL_UUID_LEN + 1]; + regmatch_t match[MATCH_SIZE + 1]; + size_t vos_file_name_len; + int rc; D_ASSERT(path != NULL && vos_file_parts != NULL); - D_ALLOC(path_copy, path_len); - if (path_copy == NULL) - return -DER_NOMEM; - strcpy(path_copy, path); - - tok = strtok(path_copy, "/"); - while (tok != NULL && rc != 0) { - rc = uuid_parse(tok, vos_file_parts->vf_pool_uuid); - if (!SUCCESS(rc)) { - safe_strcat(vos_file_parts->vf_db_path, "/", DB_PATH_LEN); - safe_strcat(vos_file_parts->vf_db_path, tok, DB_PATH_LEN); + rc = regcomp(&preg, regex_buf, REG_EXTENDED); + if (rc != 0) { + print_regx_error(rc, &preg, regex_buf); + rc = -DER_INVAL; + goto out; + } + + rc = regexec(&preg, path, MATCH_SIZE, match, 0); + if (rc == REG_NOMATCH) { + D_ERROR("Innvalid VOS path: '%s'\n", path); + rc = -DER_INVAL; + goto out_preg; + } + D_ASSERT(SUCCESS(rc)); + + vos_file_parts->vf_db_path[0] = '\0'; + if ((match[DB_PATH_IDX].rm_eo - match[DB_PATH_IDX].rm_so) != 0) { + D_ASSERT(match[DB_PATH_IDX].rm_so == 0); + if (match[DB_PATH_IDX].rm_eo > DB_PATH_SIZE) { + D_ERROR("DB path '%.*s' too long in VOS path '%s': get=%i, max=%i\n", + match[DB_PATH_IDX].rm_eo - 1, &path[match[DB_PATH_IDX].rm_so], path, + match[DB_PATH_IDX].rm_eo - 1, DB_PATH_SIZE - 1); + rc = -DER_INVAL; + goto out_preg; } - tok = strtok(NULL, "/"); + memcpy(vos_file_parts->vf_db_path, path, match[DB_PATH_IDX].rm_eo - 1); + vos_file_parts->vf_db_path[match[DB_PATH_IDX].rm_eo - 1] = '\0'; } - if (rc != 0 || tok == NULL) { - D_ERROR("Incomplete path: %s\n", path); - D_GOTO(done, rc = -DER_INVAL); + D_ASSERT(match[POOL_UUID_IDX].rm_so != (regoff_t)-1); + D_ASSERT(match[POOL_UUID_IDX].rm_eo - match[POOL_UUID_IDX].rm_so == POOL_UUID_LEN); + memcpy(pool_uuid, &path[match[POOL_UUID_IDX].rm_so], POOL_UUID_LEN); + pool_uuid[POOL_UUID_LEN] = '\0'; + rc = uuid_parse(pool_uuid, vos_file_parts->vf_pool_uuid); + if (!SUCCESS(rc)) { + D_CRIT("Invalid Pool UUID '%s' in VOS path '%s'\n", pool_uuid, path); + rc = -DER_INVAL; + goto out_preg; } - strncpy(vos_file_parts->vf_vos_file, tok, ARRAY_SIZE(vos_file_parts->vf_vos_file) - 1); - - /* - * file name should be vos-N ... split on "-" - * If not, might be test, just assume target of 0 - */ - strtok(tok, "-"); - tok = strtok(NULL, "-"); - if (tok != NULL) { - D_WARN("vos file name not in correct format: %s\n", vos_file_parts->vf_vos_file); - vos_file_parts->vf_target_idx = atoi(tok); + D_ASSERT(match[VOS_FILE_NAME_IDX].rm_so != (regoff_t)-1); + vos_file_name_len = match[VOS_FILE_NAME_IDX].rm_eo - match[VOS_FILE_NAME_IDX].rm_so; + if (vos_file_name_len + 1 > VOS_FILE_NAME_SIZE) { + D_ERROR("VOS file name '%.*s' too long in VOS path '%s': get=%zu, max=%i\n", + (int)vos_file_name_len, &path[match[VOS_FILE_NAME_IDX].rm_so], path, + vos_file_name_len, VOS_FILE_NAME_SIZE - 1); + rc = -DER_INVAL; + goto out_preg; + } + memcpy(vos_file_parts->vf_vos_file_name, &path[match[VOS_FILE_NAME_IDX].rm_so], + vos_file_name_len); + vos_file_parts->vf_vos_file_name[vos_file_name_len] = '\0'; + + D_ASSERT(match[TARGET_IDX_IDX].rm_so != (regoff_t)-1); + errno = 0; + target_idx = strtoull(&path[match[TARGET_IDX_IDX].rm_so], &endptr, 10); + if (errno != 0 || endptr == &path[match[TARGET_IDX_IDX].rm_so] || *endptr != '\0') { + D_CRIT("Invalid target index '%s' in VOS path '%s': %s\n", + &path[match[TARGET_IDX_IDX].rm_so], path, strerror(errno)); + rc = -DER_INVAL; + goto out_preg; + } + if (target_idx > UINT32_MAX) { + D_ERROR("Target index " DF_U64 + "' out of range in VOS path '%s': min=0 , max=%" PRIu32 "\n", + target_idx, path, UINT32_MAX); + rc = -DER_INVAL; + goto out_preg; } + vos_file_parts->vf_target_idx = target_idx; -done: - if (!SUCCESS(rc)) { - /* Reset to if not valid */ + rc = -DER_SUCCESS; + +out_preg: + regfree(&preg); +out: + /* Reset to zero if not valid */ + if (!SUCCESS(rc)) memset(vos_file_parts, 0, sizeof(*vos_file_parts)); - } - D_FREE(path_copy); return rc; } diff --git a/src/utils/ddb/ddb_parse.h b/src/utils/ddb/ddb_parse.h index a5ac83fcefb..9a1235a6813 100644 --- a/src/utils/ddb/ddb_parse.h +++ b/src/utils/ddb/ddb_parse.h @@ -15,12 +15,12 @@ #include #include "ddb_common.h" -#define DB_PATH_LEN 64 +enum { DB_PATH_SIZE = 64, VOS_FILE_NAME_SIZE = 16 }; struct vos_file_parts { - char vf_db_path[DB_PATH_LEN]; - uuid_t vf_pool_uuid; - char vf_vos_file[16]; - uint32_t vf_target_idx; + char vf_db_path[DB_PATH_SIZE]; + uuid_t vf_pool_uuid; + char vf_vos_file_name[VOS_FILE_NAME_SIZE]; + uint32_t vf_target_idx; }; /* Parse a path to a VOS file to get needed parts for initializing vos */ diff --git a/src/utils/ddb/ddb_printer.c b/src/utils/ddb/ddb_printer.c index 99302ece91c..dd78efbb481 100644 --- a/src/utils/ddb/ddb_printer.c +++ b/src/utils/ddb/ddb_printer.c @@ -1,9 +1,10 @@ /** * (C) Copyright 2022-2024 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) #include "ddb_printer.h" diff --git a/src/utils/ddb/ddb_spdk.c b/src/utils/ddb/ddb_spdk.c index fe1dff9a822..aa55ce8e782 100644 --- a/src/utils/ddb/ddb_spdk.c +++ b/src/utils/ddb/ddb_spdk.c @@ -1,12 +1,12 @@ /** * (C) Copyright 2022 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) -#include -#include +#include #include #include #include @@ -14,7 +14,9 @@ #include #include #include -#include + +#include +#include #include "ddb_common.h" #include "ddb_spdk.h" diff --git a/src/utils/ddb/ddb_tree_path.c b/src/utils/ddb/ddb_tree_path.c index e57bff9b245..3bfc104424e 100644 --- a/src/utils/ddb/ddb_tree_path.c +++ b/src/utils/ddb/ddb_tree_path.c @@ -1,8 +1,10 @@ /** * (C) Copyright 2023-2024 Intel Corporation. + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) #include "ddb_tree_path.h" #include "ddb_printer.h" diff --git a/src/utils/ddb/ddb_vos.c b/src/utils/ddb/ddb_vos.c index 22160ae0e8f..a5011183189 100644 --- a/src/utils/ddb/ddb_vos.c +++ b/src/utils/ddb/ddb_vos.c @@ -1,24 +1,27 @@ /** * (C) Copyright 2022-2025 Intel Corporation. * (C) Copyright 2025 Vdura Inc. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#define D_LOGFAC DD_FAC(ddb) #include #include #include + #include -#include -#include #include +#include #include + #include "ddb_common.h" #include "ddb_parse.h" #include "ddb_mgmt.h" #include "ddb_vos.h" #include "ddb_spdk.h" + #define ddb_vos_iterate(param, iter_type, recursive, anchors, cb, args) \ vos_iterate(param, iter_type, recursive, \ anchors, cb, NULL, args, NULL) @@ -77,7 +80,7 @@ dv_pool_destroy(const char *path) return rc; } - if (strncmp(path_parts.vf_vos_file, "rdb", 3) == 0) + if (strncmp(path_parts.vf_vos_file_name, "rdb", 3) == 0) flags |= VOS_POF_RDB; rc = vos_pool_destroy_ex(path, path_parts.vf_pool_uuid, flags); diff --git a/src/utils/ddb/tests/SConscript b/src/utils/ddb/tests/SConscript index 34ad70ed94c..63f054a4063 100644 --- a/src/utils/ddb/tests/SConscript +++ b/src/utils/ddb/tests/SConscript @@ -50,10 +50,11 @@ def scons(): prereqs.require(tenv, 'argobots', 'spdk', 'pmdk') # Required for dtx_act_discard_invalid tests. # This function is validated by its respective unit tests. - tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=vos_dtx_discard_invalid']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=vos_dtx_discard_invalid', '-Wl,--wrap=regcomp', + '-Wl,--wrap=uuid_parse', '-Wl,--wrap=strtoull']) libs = ['uuid', 'daos_common_pmem', 'gurt', 'vea', 'abt', 'bio', 'cmocka', 'pthread', 'pmemobj'] - src = ['ddb_ut.c', 'ddb_vos_ut.c'] + src = ['ddb_ut.c', 'ddb_vos_ut.c', 'ddb_parse_ut.c'] # vos and mock object files to wrap vos symbols vos_obj = tenv.Object(Glob('../../../vos/*.c')) mock_obj = tenv.Object(Glob('../../../dtx/tests/*_mock.c')) diff --git a/src/utils/ddb/tests/ddb_parse_tests.c b/src/utils/ddb/tests/ddb_parse_tests.c index 6c42ae5a415..05d9ebd7e11 100644 --- a/src/utils/ddb/tests/ddb_parse_tests.c +++ b/src/utils/ddb/tests/ddb_parse_tests.c @@ -13,6 +13,14 @@ #include "ddb_cmocka.h" #include "ddb_test_driver.h" +/* + * ----------------------------------------------- + * Mock implementations + * ----------------------------------------------- + */ + +#define MOCKED_POOL_UUID_STR "12345678-1234-1234-1234-123456789012" + static int fake_print(const char *fmt, ...) { @@ -25,27 +33,83 @@ fake_print(const char *fmt, ...) * ----------------------------------------------- */ -#define assert_invalid_f_path(path, parts) assert_invalid(vos_path_parse(path, &parts)) -#define assert_f_path(path, parts) assert_success(vos_path_parse(path, &parts)) - static void -vos_file_parts_tests(void **state) +vos_file_parse_test_errors(void **state) { + uuid_t pool_uuid; struct vos_file_parts parts = {0}; - uuid_t expected_uuid; + int rc; - uuid_parse("12345678-1234-1234-1234-123456789012", expected_uuid); + rc = uuid_parse(MOCKED_POOL_UUID_STR, pool_uuid); + assert_rc_equal(rc, 0); - assert_invalid_f_path("", parts); - assert_invalid_f_path("/mnt/daos", parts); - assert_invalid_f_path("/mnt/daos/12345678-1234-1234-1234-123456789012", parts); + /* Test invalid vos paths not respecting regex */ + rc = vos_path_parse("", &parts); + assert_rc_equal(rc, -DER_INVAL); - assert_f_path("/mnt/daos/12345678-1234-1234-1234-123456789012/vos-1", parts); + rc = vos_path_parse("/mnt/daos", &parts); + assert_rc_equal(rc, -DER_INVAL); + + rc = vos_path_parse("/mnt/daos/" MOCKED_POOL_UUID_STR, &parts); + assert_rc_equal(rc, -DER_INVAL); + + rc = vos_path_parse("//mnt/daos/" MOCKED_POOL_UUID_STR "/vos-1", &parts); + assert_rc_equal(rc, -DER_INVAL); + + rc = vos_path_parse("/mnt/daos/g2345678-1234-1234-1234-123456789012/vos-1", &parts); + assert_rc_equal(rc, -DER_INVAL); + /* Test invalid vos paths with too long db path */ + rc = vos_path_parse( + "/long/path/!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!/" MOCKED_POOL_UUID_STR + "/vos-1", + &parts); + assert_rc_equal(rc, -DER_INVAL); + + /* Test invalid vos paths with too long vos file name */ + rc = vos_path_parse("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-999999999999", &parts); + assert_rc_equal(rc, -DER_INVAL); + + /* Test invalid vos paths with invalid target idx */ + rc = vos_path_parse("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-99999999999", &parts); + assert_rc_equal(rc, -DER_INVAL); +} + +static void +vos_file_parse_test_success(void **state) +{ + uuid_t expected_uuid; + struct vos_file_parts parts = {0}; + int rc; + + rc = uuid_parse(MOCKED_POOL_UUID_STR, expected_uuid); + assert_rc_equal(rc, 0); + + /* Test with absolute path */ + rc = vos_path_parse("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", &parts); + assert_rc_equal(rc, 0); assert_string_equal("/mnt/daos", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); - assert_string_equal("vos-1", parts.vf_vos_file); - assert_int_equal(1, parts.vf_target_idx); + assert_string_equal("vos-0", parts.vf_vos_file_name); + assert_int_equal(0, parts.vf_target_idx); + + /* Test with relative path */ + memset(&parts, 0, sizeof(parts)); + rc = vos_path_parse("mnt/daos/" MOCKED_POOL_UUID_STR "/vos-42", &parts); + assert_rc_equal(rc, 0); + assert_string_equal("mnt/daos", parts.vf_db_path); + assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); + assert_string_equal("vos-42", parts.vf_vos_file_name); + assert_int_equal(42, parts.vf_target_idx); + + /* Test with null db path */ + memset(&parts, 1, sizeof(parts)); + rc = vos_path_parse(MOCKED_POOL_UUID_STR "/vos-666", &parts); + assert_rc_equal(rc, 0); + assert_string_equal("", parts.vf_db_path); + assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); + assert_string_equal("vos-666", parts.vf_vos_file_name); + assert_int_equal(666, parts.vf_target_idx); } #define assert_vtp_eq(a, b) \ @@ -293,8 +357,9 @@ int ddb_parse_tests_run() { static const struct CMUnitTest tests[] = { - TEST(vos_file_parts_tests), TEST(parse_dtx_id_tests), TEST(keys_are_parsed_correctly), - TEST(pool_flags_tests), TEST(date2cmt_time_tests), + TEST(vos_file_parse_test_errors), TEST(vos_file_parse_test_success), + TEST(parse_dtx_id_tests), TEST(keys_are_parsed_correctly), + TEST(pool_flags_tests), TEST(date2cmt_time_tests), }; return cmocka_run_group_tests_name("DDB helper parsing function tests", tests, NULL, NULL); diff --git a/src/utils/ddb/tests/ddb_parse_ut.c b/src/utils/ddb/tests/ddb_parse_ut.c new file mode 100644 index 00000000000..752c1111d4c --- /dev/null +++ b/src/utils/ddb/tests/ddb_parse_ut.c @@ -0,0 +1,146 @@ +/** + * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ + +#include +#include +#include +#include + +#include + +#include "ddb_common.h" +#include "ddb_parse.h" +#include "ddb_cmocka.h" + +/* + * ----------------------------------------------- + * Mock implementations + * ----------------------------------------------- + */ + +#define MOCKED_POOL_UUID_STR "12345678-1234-1234-1234-123456789012" + +static bool mock_regcomp = false; +static bool mock_uuid_parse = false; +static bool mock_strtoull = false; + +extern int +__real_regcomp(regex_t *restrict preg, const char *restrict regex, int cflags); + +int +__wrap_regcomp(regex_t *restrict preg, const char *restrict regex, int cflags) +{ + if (mock_regcomp) + return mock(); + return __real_regcomp(preg, regex, cflags); +} + +extern int +__real_uuid_parse(const char *uuid_str, uuid_t uuid); + +int +__wrap_uuid_parse(const char *uuid_str, uuid_t uuid) +{ + if (mock_uuid_parse) + return mock(); + return __real_uuid_parse(uuid_str, uuid); +} + +extern int +__real_strtoull(const char *nptr, char **endptr, int base); + +unsigned long long +__wrap_strtoull(const char *nptr, char **endptr, int base) +{ + if (mock_strtoull) { + errno = mock(); + return mock(); + } + return __real_strtoull(nptr, endptr, base); +} + +/* + * ----------------------------------------------- + * Test implementations + * ----------------------------------------------- + */ + +#define MOCK_SETUP(x) \ + static int vos_file_parse_test_crit_##x##_setup(void **unused) \ + { \ + mock_##x = true; \ + return 0; \ + } + +#define MOCK_TEARDOWN(x) \ + static int vos_file_parse_test_crit_##x##_teardown(void **unused) \ + { \ + mock_##x = false; \ + return 0; \ + } + +MOCK_SETUP(regcomp) +MOCK_TEARDOWN(regcomp) +static void +vos_file_parse_test_crit_regcomp(void **state) +{ + struct vos_file_parts parts = {0}; + int rc; + + /* Testing regcomp failure */ + will_return(__wrap_regcomp, REG_ESPACE); + rc = vos_path_parse(MOCKED_POOL_UUID_STR "/vos-0", &parts); + assert_rc_equal(rc, -DER_INVAL); +} + +MOCK_SETUP(uuid_parse) +MOCK_TEARDOWN(uuid_parse) +static void +vos_file_parse_test_crit_uuid_parse(void **state) +{ + struct vos_file_parts parts = {0}; + int rc; + + /* Testing uuid_parse failure */ + will_return(__wrap_uuid_parse, -1); + rc = vos_path_parse(MOCKED_POOL_UUID_STR "/vos-0", &parts); + assert_rc_equal(rc, -DER_INVAL); +} + +MOCK_SETUP(strtoull) +MOCK_TEARDOWN(strtoull) +static void +vos_file_parse_test_crit_strtoull(void **state) +{ + struct vos_file_parts parts = {0}; + int rc; + + /* Testing strtoull failure */ + will_return(__wrap_strtoull, ERANGE); + will_return(__wrap_strtoull, ULLONG_MAX); + rc = vos_path_parse(MOCKED_POOL_UUID_STR "/vos-0", &parts); + assert_rc_equal(rc, -DER_INVAL); +} + +/* + * ----------------------------------------------- + * Execute + * ----------------------------------------------- + */ +#define TEST(x) \ + { \ + #x, x, x##_setup, x##_teardown \ + } + +int +ddb_parse_ut_run() +{ + static const struct CMUnitTest tests[] = {TEST(vos_file_parse_test_crit_regcomp), + TEST(vos_file_parse_test_crit_uuid_parse), + TEST(vos_file_parse_test_crit_strtoull)}; + return cmocka_run_group_tests_name("DDB helper parsing function tests", tests, NULL, NULL); +} diff --git a/src/utils/ddb/tests/ddb_test_driver.c b/src/utils/ddb/tests/ddb_test_driver.c index 5eb1fa214c8..8bb74d5f2f8 100644 --- a/src/utils/ddb/tests/ddb_test_driver.c +++ b/src/utils/ddb/tests/ddb_test_driver.c @@ -213,8 +213,7 @@ ddb_test_pool_setup(struct dt_vos_pool_ctx *tctx) return rc; } - snprintf(tctx->dvt_pmem_file, ARRAY_SIZE(tctx->dvt_pmem_file), - "%s/ddb_vos_test", dir); + snprintf(tctx->dvt_pmem_file, ARRAY_SIZE(tctx->dvt_pmem_file), "%s/vos-0", dir); } if (uuid_is_null(tctx->dvt_pool_uuid)) uuid_parse(pool_uuid, tctx->dvt_pool_uuid); @@ -648,7 +647,7 @@ int main(int argc, char *argv[]) /* filtering suites and tests */ char test_suites[] = ""; #if CMOCKA_FILTER_SUPPORTED == 1 /** requires cmocka 1.1.5 */ - cmocka_set_test_filter("**"); + cmocka_set_test_filter((argc == 1) ? "**" : argv[1]); #endif RUN_TEST_SUIT('a', ddb_parse_tests_run); RUN_TEST_SUIT('b', ddb_vos_tests_run); diff --git a/src/utils/ddb/tests/ddb_ut.c b/src/utils/ddb/tests/ddb_ut.c index 3ea69695d55..0932162c8c5 100644 --- a/src/utils/ddb/tests/ddb_ut.c +++ b/src/utils/ddb/tests/ddb_ut.c @@ -1,5 +1,5 @@ /** - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -15,8 +15,10 @@ #include #include "ddb.h" -int -ddb_vos_tests_run(void); +extern int +ddb_vos_ut_run(void); +extern int +ddb_parse_ut_run(void); struct ddb_test_driver_arguments { bool dtda_create_vos_file; @@ -91,9 +93,10 @@ main(int argc, char *argv[]) /* filtering suites and tests */ char test_suites[] = ""; #if CMOCKA_FILTER_SUPPORTED == 1 /** requires cmocka 1.1.5 */ - cmocka_set_test_filter("*dtx_act_discard_invalid*"); + cmocka_set_test_filter((argc == 1) ? "**" : argv[1]); #endif - RUN_TEST_SUIT('c', ddb_vos_tests_run); + RUN_TEST_SUIT('a', ddb_parse_ut_run); + RUN_TEST_SUIT('b', ddb_vos_ut_run); ddb_fini(); if (rc > 0) diff --git a/src/utils/ddb/tests/ddb_vos_ut.c b/src/utils/ddb/tests/ddb_vos_ut.c index a4f943bd6ad..2a5be46c21b 100644 --- a/src/utils/ddb/tests/ddb_vos_ut.c +++ b/src/utils/ddb/tests/ddb_vos_ut.c @@ -1,5 +1,5 @@ /** - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -50,7 +50,7 @@ const struct CMUnitTest dv_test_cases[] = { }; int -ddb_vos_tests_run() +ddb_vos_ut_run() { return cmocka_run_group_tests_name("DDB VOS Interface Unit Tests", dv_test_cases, NULL, NULL);