mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
ldb:tests: Add test to show that locks are released on TALLOC_FREE(req)
Signed-off-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
4b7c274cc0
commit
86e8425418
@ -2076,6 +2076,229 @@ static void test_ldb_modify_during_whole_search(void **state)
|
||||
assert_int_equal(res2->count, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test is also complex.
|
||||
*
|
||||
* The purpose is to test if a modify can occur during an ldb_search()
|
||||
* before the request is destroyed with TALLOC_FREE()
|
||||
*
|
||||
* This would be a failure if in process
|
||||
* (1) and (2):
|
||||
* - (1) ldb_search() starts and waits
|
||||
* - (2) an entry in the DB is allowed to change before the ldb_wait() is called
|
||||
* - (1) the original process can see the modification before the TALLOC_FREE()
|
||||
* also we check that
|
||||
* - (1) the original process can see the modification after the TALLOC_FREE()
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This purpose of this callback is to trigger a write in
|
||||
* the child process before the ldb_wait() is called
|
||||
*
|
||||
* In ldb 1.1.31 ldb_search() omitted to take a all-record
|
||||
* lock for the full duration of the search and callbacks
|
||||
*
|
||||
* We assume that if the write will proceed, it will proceed in a 3
|
||||
* second window after the function is called.
|
||||
*/
|
||||
|
||||
static int test_ldb_modify_before_ldb_wait_callback1(struct ldb_request *req,
|
||||
struct ldb_reply *ares)
|
||||
{
|
||||
switch (ares->type) {
|
||||
case LDB_REPLY_ENTRY:
|
||||
case LDB_REPLY_REFERRAL:
|
||||
return LDB_SUCCESS;
|
||||
|
||||
case LDB_REPLY_DONE:
|
||||
break;
|
||||
}
|
||||
|
||||
return ldb_request_done(req, LDB_SUCCESS);
|
||||
}
|
||||
|
||||
static void test_ldb_modify_before_ldb_wait(void **state)
|
||||
{
|
||||
struct search_test_ctx *search_test_ctx = talloc_get_type_abort(*state,
|
||||
struct search_test_ctx);
|
||||
int ret;
|
||||
struct ldb_request *req;
|
||||
pid_t pid;
|
||||
int wstatus;
|
||||
struct ldb_dn *search_dn;
|
||||
struct ldb_dn *basedn;
|
||||
struct ldb_result *res2;
|
||||
int pipes[2];
|
||||
char buf[2];
|
||||
pid_t child_pid;
|
||||
unsigned res_count;
|
||||
|
||||
search_dn = ldb_dn_new_fmt(search_test_ctx,
|
||||
search_test_ctx->ldb_test_ctx->ldb,
|
||||
"cn=test_search_cn,"
|
||||
"dc=search_test_entry");
|
||||
assert_non_null(search_dn);
|
||||
|
||||
basedn = ldb_dn_new_fmt(search_test_ctx,
|
||||
search_test_ctx->ldb_test_ctx->ldb,
|
||||
"%s",
|
||||
search_test_ctx->base_dn);
|
||||
assert_non_null(basedn);
|
||||
|
||||
/*
|
||||
* The search just needs to call DONE, we don't care about the
|
||||
* contents of the search for this test
|
||||
*/
|
||||
ret = ldb_build_search_req(&req,
|
||||
search_test_ctx->ldb_test_ctx->ldb,
|
||||
search_test_ctx,
|
||||
basedn,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
"(&(!(filterAttr=*))"
|
||||
"(cn=test_search_cn))",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
test_ldb_modify_before_ldb_wait_callback1,
|
||||
NULL);
|
||||
assert_int_equal(ret, 0);
|
||||
ret = ldb_request(search_test_ctx->ldb_test_ctx->ldb, req);
|
||||
|
||||
ret = pipe(pipes);
|
||||
assert_int_equal(ret, 0);
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
TALLOC_CTX *tmp_ctx = NULL;
|
||||
struct ldb_message *msg;
|
||||
struct ldb_message_element *el;
|
||||
TALLOC_FREE(search_test_ctx->ldb_test_ctx->ldb);
|
||||
TALLOC_FREE(search_test_ctx->ldb_test_ctx->ev);
|
||||
search_test_ctx->ldb_test_ctx->ev = tevent_context_init(search_test_ctx->ldb_test_ctx);
|
||||
if (search_test_ctx->ldb_test_ctx->ev == NULL) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
|
||||
search_test_ctx->ldb_test_ctx->ldb = ldb_init(search_test_ctx->ldb_test_ctx,
|
||||
search_test_ctx->ldb_test_ctx->ev);
|
||||
if (search_test_ctx->ldb_test_ctx->ldb == NULL) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
|
||||
ret = ldb_connect(search_test_ctx->ldb_test_ctx->ldb,
|
||||
search_test_ctx->ldb_test_ctx->dbpath, 0, NULL);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
tmp_ctx = talloc_new(search_test_ctx->ldb_test_ctx);
|
||||
if (tmp_ctx == NULL) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
|
||||
msg = ldb_msg_new(tmp_ctx);
|
||||
if (msg == NULL) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* We must re-create this DN from a string to ensure
|
||||
* it does not reference the now-gone LDB context of
|
||||
* the parent
|
||||
*/
|
||||
msg->dn = ldb_dn_new_fmt(search_test_ctx,
|
||||
search_test_ctx->ldb_test_ctx->ldb,
|
||||
"cn=test_search_cn,"
|
||||
"dc=search_test_entry");
|
||||
|
||||
if (msg->dn == NULL) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
|
||||
ret = ldb_msg_add_string(msg, "filterAttr", "TRUE");
|
||||
if (ret != 0) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
el = ldb_msg_find_element(msg, "filterAttr");
|
||||
if (el == NULL) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
el->flags = LDB_FLAG_MOD_REPLACE;
|
||||
|
||||
ret = ldb_transaction_start(search_test_ctx->ldb_test_ctx->ldb);
|
||||
if (ret != 0) {
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
if (write(pipes[1], "GO", 2) != 2) {
|
||||
exit(LDB_ERR_OPERATIONS_ERROR);
|
||||
}
|
||||
|
||||
ret = ldb_modify(search_test_ctx->ldb_test_ctx->ldb, msg);
|
||||
if (ret != 0) {
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
ret = ldb_transaction_commit(search_test_ctx->ldb_test_ctx->ldb);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
ret = read(pipes[0], buf, 2);
|
||||
assert_int_equal(ret, 2);
|
||||
|
||||
sleep(3);
|
||||
|
||||
/*
|
||||
* If writes are not blocked until after the (never called) ldb_wait(), we
|
||||
* will be able to successfully search for this modification
|
||||
* here
|
||||
*/
|
||||
|
||||
ret = ldb_search(search_test_ctx->ldb_test_ctx->ldb, search_test_ctx,
|
||||
&res2, search_dn, LDB_SCOPE_BASE, NULL,
|
||||
"filterAttr=TRUE");
|
||||
|
||||
/*
|
||||
* We avoid making assertions before TALLOC_FREE()ing the request,
|
||||
* lest the assert fail and mess with the clean-up because we still
|
||||
* have locks.
|
||||
*/
|
||||
res_count = res2->count;
|
||||
TALLOC_FREE(req);
|
||||
|
||||
/* We should not have got the result */
|
||||
assert_int_equal(res_count, 0);
|
||||
assert_int_equal(ret, 0);
|
||||
|
||||
pid = waitpid(child_pid, &wstatus, 0);
|
||||
assert_int_equal(pid, child_pid);
|
||||
|
||||
assert_true(WIFEXITED(wstatus));
|
||||
|
||||
assert_int_equal(WEXITSTATUS(wstatus), 0);
|
||||
|
||||
/*
|
||||
* If writes are blocked until after the search request was freed, we
|
||||
* will be able to successfully search for this modification
|
||||
* now
|
||||
*/
|
||||
|
||||
search_dn = ldb_dn_new_fmt(search_test_ctx,
|
||||
search_test_ctx->ldb_test_ctx->ldb,
|
||||
"cn=test_search_cn,"
|
||||
"dc=search_test_entry");
|
||||
|
||||
ret = ldb_search(search_test_ctx->ldb_test_ctx->ldb,
|
||||
search_test_ctx,
|
||||
&res2, search_dn, LDB_SCOPE_BASE, NULL,
|
||||
"filterAttr=TRUE");
|
||||
assert_int_equal(ret, 0);
|
||||
|
||||
/* We got the result */
|
||||
assert_int_equal(res2->count, 1);
|
||||
}
|
||||
|
||||
static int ldb_case_test_setup(void **state)
|
||||
{
|
||||
int ret;
|
||||
@ -2640,6 +2863,9 @@ int main(int argc, const char **argv)
|
||||
cmocka_unit_test_setup_teardown(test_ldb_modify_during_whole_search,
|
||||
ldb_search_test_setup,
|
||||
ldb_search_test_teardown),
|
||||
cmocka_unit_test_setup_teardown(test_ldb_modify_before_ldb_wait,
|
||||
ldb_search_test_setup,
|
||||
ldb_search_test_teardown),
|
||||
cmocka_unit_test_setup_teardown(test_ldb_attrs_case_insensitive,
|
||||
ldb_case_test_setup,
|
||||
ldb_case_test_teardown),
|
||||
|
Loading…
Reference in New Issue
Block a user