From: Alan T. DeKok Date: Wed, 13 Sep 2023 16:49:16 +0000 (-0400) Subject: add fr_pair_delete_by_da_nested() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ad4fede434b50016dc2315dfd70ea98997644c2b;p=thirdparty%2Ffreeradius-server.git add fr_pair_delete_by_da_nested() and use it in server macros. This change allows the uses of pair_delete_request() etc. to work properly with nested attributes --- diff --git a/src/lib/server/pair.h b/src/lib/server/pair.h index 0a9a608ae8b..ce7a792dde8 100644 --- a/src/lib/server/pair.h +++ b/src/lib/server/pair.h @@ -157,8 +157,8 @@ RCSIDH(server_pair_h, "$Id$") */ #define pair_delete(_list, _pair_or_da) \ _Generic((_pair_or_da), \ - fr_dict_attr_t const * : fr_pair_delete_by_da(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \ - fr_dict_attr_t * : fr_pair_delete_by_da(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \ + fr_dict_attr_t const * : fr_pair_delete_by_da_nested(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \ + fr_dict_attr_t * : fr_pair_delete_by_da_nested(_list, UNCONST(fr_dict_attr_t *, _pair_or_da)), \ fr_pair_t * : fr_pair_delete(_list, UNCONST(fr_pair_t *, _pair_or_da)) \ ) diff --git a/src/lib/util/pair.c b/src/lib/util/pair.c index c84a077dea8..694c4ef62aa 100644 --- a/src/lib/util/pair.c +++ b/src/lib/util/pair.c @@ -1590,6 +1590,93 @@ int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da) return cnt; } +/** Delete matching pairs from the specified list + * + * @param[in,out] list to search for attributes in or delete attributes from. + * @param[in] da to match. + * @return + * - >0 the number of pairs deleted. + * - 0 if no pairs were deleted. + */ +int fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da) +{ + int cnt = 0; + fr_pair_t *vp; + fr_dict_attr_t const **find; /* DA currently being looked for */ + fr_pair_list_t *cur_list; /* Current list being searched */ + fr_da_stack_t da_stack; + + if (da->depth <= 1) return fr_pair_delete_by_da(list, da); + + if (fr_pair_list_empty(list)) return 0; + + /* + * Similar to fr_pair_find_by_da_nested() + */ + fr_proto_da_stack_build(&da_stack, da); + cur_list = list; + find = &da_stack.da[0]; + vp = NULL; + + /* + * Loop over the list at each level until we find a matching da. + */ + while (true) { + fr_pair_t *next; + + fr_assert((*find)->depth <= da->depth); + + /* + * Find a vp which matches a given da. If found, + * recurse into the child list to find the child + * attribute. + * + */ + next = fr_pair_find_by_da(cur_list, vp, *find); + if (next) { + /* + * We've found a match for the requested + * da - delete it + */ + if ((*find) == da) { + do { + fr_pair_delete(cur_list, next); + cnt++; + } while ((next = fr_pair_find_by_da(cur_list, vp, *find)) != NULL); + + return cnt; + } + + /* + * Prepare to search the next level. + */ + cur_list = &next->vp_group; + find++; + vp = NULL; + continue; + } + + /* + * We hit the end of the top-level list. Therefore we found nothing. + */ + if (cur_list == list) break; + + /* + * We hit the end of *A* list. Go to the parent + * VP, and then find its list. + * + * We still then have to go to the next attribute + * in the parent list, as we've checked all of the + * children of this VP. + */ + find--; + vp = fr_pair_list_parent(cur_list); + cur_list = fr_pair_parent_list(vp); + } + + return fr_pair_delete_by_da(list, da); +} + /** Delete matching pairs from the specified list * * @param[in] list to delete attributes from. diff --git a/src/lib/util/pair.h b/src/lib/util/pair.h index 6658354325f..16b644fb5e0 100644 --- a/src/lib/util/pair.h +++ b/src/lib/util/pair.h @@ -522,6 +522,8 @@ static inline CC_HINT(nonnull(3,4)) int fr_pair_find_or_append_by_da(TALLOC_CTX int fr_pair_delete_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da) CC_HINT(nonnull); +int fr_pair_delete_by_da_nested(fr_pair_list_t *list, fr_dict_attr_t const *da) CC_HINT(nonnull); + fr_pair_t *fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp) CC_HINT(nonnull); /* functions for FR_TYPE_STRUCTURAL */ diff --git a/src/lib/util/pair_tests.c b/src/lib/util/pair_tests.c index 20e652b96ae..396e056f7e6 100644 --- a/src/lib/util/pair_tests.c +++ b/src/lib/util/pair_tests.c @@ -156,6 +156,33 @@ static void test_fr_pair_afrom_da_nested(void) talloc_free(parent); /* not vp! */ } +static void test_fr_pair_delete_by_da_nested(void) +{ + int count; + fr_pair_t *vp, *parent = NULL; + fr_pair_list_t local_pairs; + + fr_pair_list_init(&local_pairs); + + TEST_CASE("Allocation using fr_pair_afrom_da_nested"); + TEST_CHECK((vp = fr_pair_afrom_da_nested(autofree, &local_pairs, fr_dict_attr_test_tlv_string)) != NULL); + + TEST_CHECK(vp && vp->da == fr_dict_attr_test_tlv_string); + TEST_MSG("Expected attr(%s) == vp->da->attr(%s)", fr_dict_attr_test_tlv_string->name, vp->da->name); + + TEST_CASE("Deleted nested pair"); + TEST_CHECK((count = fr_pair_delete_by_da_nested(&local_pairs, fr_dict_attr_test_tlv_string)) == 1); + + TEST_CASE("Top list still has the tlv attribute"); + parent = fr_pair_find_by_da(&local_pairs, NULL, fr_dict_attr_test_tlv); + TEST_ASSERT(parent != NULL); + + TEST_CASE("Parent list does not have the tlv child attribute"); + TEST_CHECK(fr_pair_find_by_da(&parent->vp_group, NULL, fr_dict_attr_test_tlv_string) == NULL); + + fr_pair_list_free(&local_pairs); +} + static void test_fr_pair_copy(void) { fr_pair_t *vp, *copy; @@ -1405,6 +1432,7 @@ TEST_LIST = { { "fr_pair_update_by_da_parent", test_fr_pair_update_by_da_parent }, { "fr_pair_delete", test_fr_pair_delete }, { "fr_pair_delete_by_da", test_fr_pair_delete_by_da }, + { "fr_pair_delete_by_da_nested", test_fr_pair_delete_by_da_nested }, /* Compare */ { "fr_pair_cmp", test_fr_pair_cmp },