Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/common/proc.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* (C) Copyright 2019-2022 Intel Corporation.
* (C) Copyright 2026 Hewlett Packard Enterprise Development LP
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*/
Expand Down Expand Up @@ -69,6 +70,63 @@ crt_proc_struct_daos_acl(crt_proc_t proc, crt_proc_op_t proc_op,
return rc;
}

static int
crt_proc_struct_daos_prop_byteval(crt_proc_t proc, crt_proc_op_t proc_op,
struct daos_prop_byteval **data, uint32_t type)
{
struct daos_prop_byteval *bv;
uint64_t len = 0;
int rc;

if (proc == NULL || data == NULL)
return -DER_INVAL;

if (FREEING(proc_op)) {
bv = *data;
if (bv != NULL) {
D_FREE(bv->dpb_data);
D_FREE(bv);
*data = NULL;
}
return 0;
}

if (ENCODING(proc_op) && *data != NULL)
len = (*data)->dpb_len;

rc = crt_proc_uint64_t(proc, proc_op, &len);
if (rc)
return rc;

if (len > DAOS_PROP_BYTEVAL_MAX_LEN) {
D_ERROR("byteval prop %u len " DF_U64 " exceeds max %u\n", type, len,
DAOS_PROP_BYTEVAL_MAX_LEN);
return -DER_INVAL;
}

if (DECODING(proc_op)) {
if (len == 0) {
*data = NULL;
return 0;
}
D_ALLOC_PTR(bv);
if (bv == NULL)
return -DER_NOMEM;
D_ALLOC(bv->dpb_data, len);
if (bv->dpb_data == NULL) {
D_FREE(bv);
return -DER_NOMEM;
}
bv->dpb_len = len;
*data = bv;
}

if (len > 0)
rc = crt_proc_memcpy(proc, proc_op, (*data)->dpb_data, len);

return rc;
}

static int
crt_proc_prop_entries(crt_proc_t proc, crt_proc_op_t proc_op, daos_prop_t *prop)
{
Expand Down Expand Up @@ -106,6 +164,10 @@ crt_proc_prop_entries(crt_proc_t proc, crt_proc_op_t proc_op, daos_prop_t *prop)
} else if (entry->dpe_type == DAOS_PROP_PO_SVC_LIST) {
rc = crt_proc_d_rank_list_t(proc, proc_op,
(d_rank_list_t **)&entry->dpe_val_ptr);
} else if (daos_prop_has_byteval(entry)) {
rc = crt_proc_struct_daos_prop_byteval(
proc, proc_op, (struct daos_prop_byteval **)&entry->dpe_val_ptr,
entry->dpe_type);
} else if (entry->dpe_type == DAOS_PROP_CO_ROOTS) {
struct daos_prop_co_roots *roots;

Expand Down
43 changes: 38 additions & 5 deletions src/common/prop.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,11 @@ bool
daos_prop_has_byteval(struct daos_prop_entry *entry)
{
switch (entry->dpe_type) {
/*
* e.g. DAOS_PROP_PO_POOL_CA (DAOS-18783)
*/
default:
return false;
case DAOS_PROP_PO_POOL_CA:
case DAOS_PROP_PO_CERT_WATERMARKS:
return true;
}
return false;
}

static void
Expand Down Expand Up @@ -329,6 +328,9 @@ daos_prop_valid(daos_prop_t *prop, bool pool, bool input)
/* for output parameter need not check entry value */
if (!input)
continue;
/* Byteval payload semantics are validated at the trust boundary above. */
if (daos_prop_has_byteval(&prop->dpp_entries[i]))
continue;
switch (type) {
/* pool properties */
case DAOS_PROP_PO_LABEL:
Expand Down Expand Up @@ -800,6 +802,11 @@ daos_prop_entry_set_byteval(struct daos_prop_entry *entry, const void *data, siz
D_ERROR("Entry type %d does not expect a byteval\n", entry->dpe_type);
return -DER_INVAL;
}
if (len > DAOS_PROP_BYTEVAL_MAX_LEN) {
D_ERROR("byteval prop %d len %zu exceeds max %u\n", entry->dpe_type, len,
DAOS_PROP_BYTEVAL_MAX_LEN);
return -DER_INVAL;
}

if (entry->dpe_val_ptr != NULL) {
bv = entry->dpe_val_ptr;
Expand Down Expand Up @@ -970,6 +977,10 @@ daos_prop_copy(daos_prop_t *prop_req, daos_prop_t *prop_reply)
D_GOTO(out, rc);

roots_alloc = true;
} else if (daos_prop_has_byteval(entry_reply)) {
rc = daos_prop_entry_copy(entry_reply, entry_req);
if (rc)
D_GOTO(out, rc);
} else {
entry_req->dpe_val = entry_reply->dpe_val;
}
Expand Down Expand Up @@ -1098,6 +1109,28 @@ daos_prop_entry_cmp_acl(struct daos_prop_entry *entry1,
return 0;
}

int
daos_prop_entry_cmp_byteval(struct daos_prop_entry *entry1, struct daos_prop_entry *entry2)
{
struct daos_prop_byteval *bv1 = entry1->dpe_val_ptr;
struct daos_prop_byteval *bv2 = entry2->dpe_val_ptr;
size_t len1 = (bv1 == NULL) ? 0 : bv1->dpb_len;
size_t len2 = (bv2 == NULL) ? 0 : bv2->dpb_len;

D_ASSERT(daos_prop_has_byteval(entry1));
D_ASSERT(daos_prop_has_byteval(entry2));

if (len1 != len2) {
D_ERROR("byteval prop %u len mismatch: %zu != %zu\n", entry1->dpe_type, len1, len2);
return -DER_MISMATCH;
}
if (len1 > 0 && memcmp(bv1->dpb_data, bv2->dpb_data, len1) != 0) {
D_ERROR("byteval prop %u content mismatch\n", entry1->dpe_type);
return -DER_MISMATCH;
}
return 0;
}

static int
parse_entry(char *str, struct daos_prop_entry *entry)
{
Expand Down
159 changes: 158 additions & 1 deletion src/common/tests/prop_tests.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* (C) Copyright 2020-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
*/
Expand Down Expand Up @@ -491,6 +491,157 @@ test_daos_prop_valid_cont_success_no_val_check(void **state)
daos_prop_free(prop);
}

static void
test_daos_prop_has_byteval_types(void **state)
{
struct daos_prop_entry entry = {0};

entry.dpe_type = DAOS_PROP_PO_POOL_CA;
assert_true(daos_prop_has_byteval(&entry));

entry.dpe_type = DAOS_PROP_PO_LABEL;
assert_false(daos_prop_has_byteval(&entry));
}

static void
test_daos_prop_byteval_set_round_trip(void **state)
{
const uint8_t payload[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01, 0x02, 0x03};
daos_prop_t *prop;
struct daos_prop_entry *entry;
struct daos_prop_byteval *bv;

prop = daos_prop_alloc(1);
assert_non_null(prop);
prop->dpp_entries[0].dpe_type = DAOS_PROP_PO_POOL_CA;

assert_rc_equal(daos_prop_set_byteval(prop, DAOS_PROP_PO_POOL_CA, payload, sizeof(payload)),
0);

entry = daos_prop_entry_get(prop, DAOS_PROP_PO_POOL_CA);
assert_non_null(entry);
bv = entry->dpe_val_ptr;
assert_non_null(bv);
assert_int_equal(bv->dpb_len, sizeof(payload));
assert_memory_equal(bv->dpb_data, payload, sizeof(payload));

daos_prop_free(prop);
}

static void
test_daos_prop_byteval_oversize_rejected(void **state)
{
struct daos_prop_entry entry = {0};
uint8_t *blob;
size_t oversize;
int rc;

entry.dpe_type = DAOS_PROP_PO_POOL_CA;

oversize = (size_t)DAOS_PROP_BYTEVAL_MAX_LEN + 1;
D_ALLOC(blob, 16);
assert_non_null(blob);

rc = daos_prop_entry_set_byteval(&entry, blob, oversize);
assert_rc_equal(rc, -DER_INVAL);
assert_null(entry.dpe_val_ptr);

D_FREE(blob);
}

static void
test_daos_prop_byteval_dup_preserves_value(void **state)
{
const uint8_t payload[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
daos_prop_t *src;
daos_prop_t *dst;
struct daos_prop_entry *src_entry;
struct daos_prop_entry *dst_entry;
struct daos_prop_byteval *src_bv;
struct daos_prop_byteval *dst_bv;

src = daos_prop_alloc(1);
assert_non_null(src);
src->dpp_entries[0].dpe_type = DAOS_PROP_PO_CERT_WATERMARKS;
assert_rc_equal(
daos_prop_set_byteval(src, DAOS_PROP_PO_CERT_WATERMARKS, payload, sizeof(payload)), 0);

dst = daos_prop_dup(src, true /* pool */, false /* input */);
assert_non_null(dst);

src_entry = daos_prop_entry_get(src, DAOS_PROP_PO_CERT_WATERMARKS);
dst_entry = daos_prop_entry_get(dst, DAOS_PROP_PO_CERT_WATERMARKS);
assert_non_null(src_entry);
assert_non_null(dst_entry);

src_bv = src_entry->dpe_val_ptr;
dst_bv = dst_entry->dpe_val_ptr;
assert_non_null(dst_bv);
assert_ptr_not_equal(src_bv, dst_bv);
assert_int_equal(dst_bv->dpb_len, sizeof(payload));
assert_memory_equal(dst_bv->dpb_data, payload, sizeof(payload));

daos_prop_free(src);
daos_prop_free(dst);
}

/* Empty byteval (NULL ptr / zero len) must round-trip through dup as NULL,
* not as -DER_NOMEM from a zero-size allocation.
*/
static void
test_daos_prop_byteval_empty_dup(void **state)
{
daos_prop_t *src;
daos_prop_t *dst;
struct daos_prop_entry *dst_entry;

src = daos_prop_alloc(1);
assert_non_null(src);
src->dpp_entries[0].dpe_type = DAOS_PROP_PO_POOL_CA;
src->dpp_entries[0].dpe_val_ptr = NULL;

dst = daos_prop_dup(src, true /* pool */, false /* input */);
assert_non_null(dst);

dst_entry = daos_prop_entry_get(dst, DAOS_PROP_PO_POOL_CA);
assert_non_null(dst_entry);
assert_null(dst_entry->dpe_val_ptr);

daos_prop_free(src);
daos_prop_free(dst);
}

static void
test_daos_prop_byteval_cmp(void **state)
{
const uint8_t a[] = {0x01, 0x02, 0x03, 0x04};
const uint8_t b[] = {0x01, 0x02, 0x03, 0x05}; /* differs in last byte */
struct daos_prop_entry e1 = {.dpe_type = DAOS_PROP_PO_POOL_CA};
struct daos_prop_entry e2 = {.dpe_type = DAOS_PROP_PO_POOL_CA};

/* both NULL -> match */
assert_rc_equal(daos_prop_entry_cmp_byteval(&e1, &e2), 0);

/* NULL vs populated -> mismatch */
assert_rc_equal(daos_prop_entry_set_byteval(&e1, a, sizeof(a)), 0);
assert_rc_equal(daos_prop_entry_cmp_byteval(&e1, &e2), -DER_MISMATCH);

/* equal payloads -> match */
assert_rc_equal(daos_prop_entry_set_byteval(&e2, a, sizeof(a)), 0);
assert_rc_equal(daos_prop_entry_cmp_byteval(&e1, &e2), 0);

/* different content, same length -> mismatch */
assert_rc_equal(daos_prop_entry_set_byteval(&e2, b, sizeof(b)), 0);
assert_rc_equal(daos_prop_entry_cmp_byteval(&e1, &e2), -DER_MISMATCH);

/* different length -> mismatch */
assert_rc_equal(daos_prop_entry_set_byteval(&e2, a, sizeof(a) - 1), 0);
assert_rc_equal(daos_prop_entry_cmp_byteval(&e1, &e2), -DER_MISMATCH);

daos_prop_entry_set_byteval(&e1, NULL, 0);
daos_prop_entry_set_byteval(&e2, NULL, 0);
}

static int
suite_setup(void **state)
{
Expand Down Expand Up @@ -521,6 +672,12 @@ main(void)
cmocka_unit_test(test_daos_prop_valid_duplicate_types),
cmocka_unit_test(test_daos_prop_valid_pool_success_no_val_check),
cmocka_unit_test(test_daos_prop_valid_cont_success_no_val_check),
cmocka_unit_test(test_daos_prop_has_byteval_types),
cmocka_unit_test(test_daos_prop_byteval_set_round_trip),
cmocka_unit_test(test_daos_prop_byteval_oversize_rejected),
cmocka_unit_test(test_daos_prop_byteval_dup_preserves_value),
cmocka_unit_test(test_daos_prop_byteval_empty_dup),
cmocka_unit_test(test_daos_prop_byteval_cmp),
};

return cmocka_run_group_tests_name("common_prop", tests, suite_setup, suite_teardown);
Expand Down
11 changes: 11 additions & 0 deletions src/control/cmd/daos_agent/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//
// (C) Copyright 2020-2024 Intel Corporation.
// (C) Copyright 2025 Google LLC
// (C) Copyright 2026 Hewlett Packard Enterprise Development LP
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand All @@ -11,6 +12,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"time"

Expand All @@ -28,6 +30,10 @@ const (
defaultRuntimeDir = "/var/run/daos_agent"
)

func defaultNodeCertDir(systemName string) string {
return filepath.Join("/etc/daos/certs", systemName, "node_certs")
}

type refreshMinutes time.Duration

func (rm *refreshMinutes) UnmarshalYAML(unmarshal func(interface{}) error) error {
Expand Down Expand Up @@ -137,6 +143,7 @@ type Config struct {
FabricInterfaces []*NUMAFabricConfig `yaml:"fabric_ifaces,omitempty"`
ProviderIdx uint // TODO SRS-31: Enable with multiprovider functionality
Telemetry TelemetryConfig `yaml:",inline"`
NodeCertDir string `yaml:"node_cert_dir,omitempty"`
}

// Validate performs basic validation of the configuration.
Expand All @@ -157,6 +164,10 @@ func (c *Config) Validate() error {
return err
}

if c.NodeCertDir == "" {
c.NodeCertDir = defaultNodeCertDir(c.SystemName)
}

return nil
}

Expand Down
Loading
Loading