diff options
Diffstat (limited to 'source3/torture/test_g_lock.c')
-rw-r--r-- | source3/torture/test_g_lock.c | 1378 |
1 files changed, 1378 insertions, 0 deletions
diff --git a/source3/torture/test_g_lock.c b/source3/torture/test_g_lock.c new file mode 100644 index 0000000..5113339 --- /dev/null +++ b/source3/torture/test_g_lock.c @@ -0,0 +1,1378 @@ +/* + * Unix SMB/CIFS implementation. + * Test g_lock API + * Copyright (C) Volker Lendecke 2017 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "torture/proto.h" +#include "system/filesys.h" +#include "g_lock.h" +#include "messages.h" +#include "lib/util/server_id.h" +#include "lib/util/sys_rw.h" +#include "lib/util/util_tdb.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/global_contexts.h" + +static bool get_g_lock_ctx(TALLOC_CTX *mem_ctx, + struct tevent_context **ev, + struct messaging_context **msg, + struct g_lock_ctx **ctx) +{ + *ev = global_event_context(); + if (*ev == NULL) { + fprintf(stderr, "tevent_context_init failed\n"); + return false; + } + *msg = global_messaging_context(); + if (*msg == NULL) { + fprintf(stderr, "messaging_init failed\n"); + TALLOC_FREE(*ev); + return false; + } + *ctx = g_lock_ctx_init(*ev, *msg); + if (*ctx == NULL) { + fprintf(stderr, "g_lock_ctx_init failed\n"); + TALLOC_FREE(*msg); + TALLOC_FREE(*ev); + return false; + } + + return true; +} + +bool run_g_lock1(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock1"; + NTSTATUS status; + bool ret = false; + bool ok; + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_EQUAL(status, NT_STATUS_WAS_LOCKED)) { + fprintf(stderr, "Double lock got %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_unlock(ctx, string_term_tdb_data(lockname)); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_unlock failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_unlock(ctx, string_term_tdb_data(lockname)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + fprintf(stderr, "g_lock_unlock returned: %s\n", + nt_errstr(status)); + goto fail; + } + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +struct lock2_parser_state { + uint8_t *rdata; + bool ok; +}; + +static void lock2_parser(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data) +{ + struct lock2_parser_state *state = private_data; + + if (datalen != sizeof(uint8_t)) { + return; + } + *state->rdata = *data; + state->ok = true; +} + +/* + * Test g_lock_write_data + */ + +bool run_g_lock2(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock2"; + uint8_t data = 42; + uint8_t rdata; + struct lock2_parser_state state = { .rdata = &rdata }; + NTSTATUS status; + bool ret = false; + bool ok; + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + status = g_lock_write_data(ctx, string_term_tdb_data(lockname), + &data, sizeof(data)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_LOCKED)) { + fprintf(stderr, "unlocked g_lock_write_data returned %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_write_data(ctx, string_term_tdb_data(lockname), + &data, sizeof(data)); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_write_data failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_unlock(ctx, string_term_tdb_data(lockname)); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_unlock failed: %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_dump(ctx, string_term_tdb_data(lockname), + lock2_parser, &state); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_dump failed: %s\n", + nt_errstr(status)); + goto fail; + } + + if (!state.ok) { + fprintf(stderr, "Could not parse data\n"); + goto fail; + } + if (rdata != data) { + fprintf(stderr, "Returned %"PRIu8", expected %"PRIu8"\n", + rdata, data); + goto fail; + } + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +struct lock3_parser_state { + struct server_id self; + enum g_lock_type lock_type; + bool ok; +}; + +static void lock3_parser(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data) +{ + struct lock3_parser_state *state = private_data; + size_t num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0); + const struct server_id *pid; + + if (datalen != 0) { + fprintf(stderr, "datalen=%zu\n", datalen); + return; + } + if (num_locks != 1) { + fprintf(stderr, "num_locks=%zu\n", num_locks); + return; + } + + if (state->lock_type == G_LOCK_WRITE) { + if (exclusive.pid == 0) { + fprintf(stderr, "Found READ, expected WRITE\n"); + return; + } + } else { + if (exclusive.pid != 0) { + fprintf(stderr, "Found WRITE, expected READ\n"); + return; + } + } + + pid = (exclusive.pid != 0) ? &exclusive : &shared[0]; + + if (!server_id_equal(pid, &state->self)) { + struct server_id_buf tmp1, tmp2; + fprintf(stderr, "found pid %s, expected %s\n", + server_id_str_buf(*pid, &tmp1), + server_id_str_buf(state->self, &tmp2)); + return; + } + + state->ok = true; +} + +/* + * Test lock upgrade/downgrade + */ + +bool run_g_lock3(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock3"; + struct lock3_parser_state state; + NTSTATUS status; + bool ret = false; + bool ok; + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + state.self = messaging_server_id(msg); + + status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_READ, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + state.lock_type = G_LOCK_READ; + state.ok = false; + + status = g_lock_dump(ctx, string_term_tdb_data(lockname), + lock3_parser, &state); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + fprintf(stderr, "g_lock_dump returned %s\n", + nt_errstr(status)); + goto fail; + } + if (!state.ok) { + goto fail; + } + + status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_UPGRADE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + state.lock_type = G_LOCK_WRITE; + state.ok = false; + + status = g_lock_dump(ctx, string_term_tdb_data(lockname), + lock3_parser, &state); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + fprintf(stderr, "g_lock_dump returned %s\n", + nt_errstr(status)); + goto fail; + } + if (!state.ok) { + goto fail; + } + + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +static bool lock4_child(const char *lockname, + enum g_lock_type lock_type, + int ready_pipe, + int exit_pipe) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + NTSTATUS status; + ssize_t n; + bool ok; + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + return false; + } + + status = g_lock_lock( + ctx, + string_term_tdb_data(lockname), + lock_type, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "child: g_lock_lock returned %s\n", + nt_errstr(status)); + return false; + } + + n = sys_write(ready_pipe, &ok, sizeof(ok)); + if (n != sizeof(ok)) { + fprintf(stderr, "child: write failed\n"); + return false; + } + + if (ok) { + n = sys_read(exit_pipe, &ok, sizeof(ok)); + if (n != 0) { + fprintf(stderr, "child: read failed\n"); + return false; + } + } + + return true; +} + +static void lock4_done(struct tevent_req *subreq) +{ + int *done = tevent_req_callback_data_void(subreq); + NTSTATUS status; + + status = g_lock_lock_recv(subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock_recv returned %s\n", + nt_errstr(status)); + *done = -1; + return; + } + *done = 1; +} + +static void lock4_waited(struct tevent_req *subreq) +{ + int *exit_pipe = tevent_req_callback_data_void(subreq); + pid_t child; + int status; + bool ok; + + printf("waited\n"); + + ok = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ok) { + fprintf(stderr, "tevent_wakeup_recv failed\n"); + } + close(*exit_pipe); + + child = wait(&status); + + printf("child %d exited with %d\n", (int)child, status); +} + +struct lock4_check_state { + struct server_id me; + bool ok; +}; + +static void lock4_check(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data) +{ + struct lock4_check_state *state = private_data; + size_t num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0); + + if (num_locks != 1) { + fprintf(stderr, "num_locks=%zu\n", num_locks); + return; + } + + if (exclusive.pid == 0) { + fprintf(stderr, "Wrong lock type, not WRITE\n"); + return; + } + + if (!server_id_equal(&state->me, &exclusive)) { + struct server_id_buf buf1, buf2; + fprintf(stderr, "me=%s, locker=%s\n", + server_id_str_buf(state->me, &buf1), + server_id_str_buf(exclusive, &buf2)); + return; + } + + state->ok = true; +} + +/* + * Test a lock conflict: Contend with a WRITE lock + */ + +bool run_g_lock4(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock4"; + TDB_DATA key = string_term_tdb_data(lockname); + pid_t child; + int ready_pipe[2]; + int exit_pipe[2]; + NTSTATUS status; + bool ret = false; + struct tevent_req *req; + bool ok; + int done; + + if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) { + perror("pipe failed"); + return false; + } + + child = fork(); + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + if (child == -1) { + perror("fork failed"); + return false; + } + + if (child == 0) { + close(ready_pipe[0]); + close(exit_pipe[1]); + ok = lock4_child( + lockname, G_LOCK_WRITE, ready_pipe[1], exit_pipe[0]); + exit(ok ? 0 : 1); + } + + close(ready_pipe[1]); + close(exit_pipe[0]); + + if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) { + perror("read failed"); + return false; + } + + if (!ok) { + fprintf(stderr, "child returned error\n"); + return false; + } + + status = g_lock_lock( + ctx, key, G_LOCK_WRITE, (struct timeval) { .tv_usec = 1 }); + if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_lock( + ctx, key, G_LOCK_READ, (struct timeval) { .tv_usec = 1 }); + if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + req = g_lock_lock_send(ev, ev, ctx, key, G_LOCK_WRITE); + if (req == NULL) { + fprintf(stderr, "g_lock_lock send failed\n"); + goto fail; + } + tevent_req_set_callback(req, lock4_done, &done); + + req = tevent_wakeup_send(ev, ev, timeval_current_ofs(1, 0)); + if (req == NULL) { + fprintf(stderr, "tevent_wakeup_send failed\n"); + goto fail; + } + tevent_req_set_callback(req, lock4_waited, &exit_pipe[1]); + + done = 0; + + while (done == 0) { + int tevent_ret = tevent_loop_once(ev); + if (tevent_ret != 0) { + perror("tevent_loop_once failed"); + goto fail; + } + } + + { + struct lock4_check_state state = { + .me = messaging_server_id(msg) + }; + + status = g_lock_dump(ctx, key, lock4_check, &state); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_dump failed: %s\n", + nt_errstr(status)); + goto fail; + } + if (!state.ok) { + fprintf(stderr, "lock4_check failed\n"); + goto fail; + } + } + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +/* + * Test a lock conflict: Contend with a READ lock + */ + +bool run_g_lock4a(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock4a"; + TDB_DATA key = string_term_tdb_data(lockname); + pid_t child; + int ready_pipe[2]; + int exit_pipe[2]; + NTSTATUS status; + bool ret = false; + struct tevent_req *req; + bool ok; + int done; + + if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) { + perror("pipe failed"); + return false; + } + + child = fork(); + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + if (child == -1) { + perror("fork failed"); + return false; + } + + if (child == 0) { + close(ready_pipe[0]); + close(exit_pipe[1]); + ok = lock4_child( + lockname, G_LOCK_READ, ready_pipe[1], exit_pipe[0]); + exit(ok ? 0 : 1); + } + + close(ready_pipe[1]); + close(exit_pipe[0]); + + if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) { + perror("read failed"); + return false; + } + + if (!ok) { + fprintf(stderr, "child returned error\n"); + return false; + } + + status = g_lock_lock( + ctx, key, G_LOCK_WRITE, (struct timeval) { .tv_usec = 1 }); + if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_lock( + ctx, key, G_LOCK_READ, (struct timeval) { .tv_usec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + status = g_lock_unlock(ctx, key); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_unlock returned %s\n", + nt_errstr(status)); + goto fail; + } + + req = g_lock_lock_send(ev, ev, ctx, key, G_LOCK_WRITE); + if (req == NULL) { + fprintf(stderr, "g_lock_lock send failed\n"); + goto fail; + } + tevent_req_set_callback(req, lock4_done, &done); + + req = tevent_wakeup_send(ev, ev, timeval_current_ofs(1, 0)); + if (req == NULL) { + fprintf(stderr, "tevent_wakeup_send failed\n"); + goto fail; + } + tevent_req_set_callback(req, lock4_waited, &exit_pipe[1]); + + done = 0; + + while (done == 0) { + int tevent_ret = tevent_loop_once(ev); + if (tevent_ret != 0) { + perror("tevent_loop_once failed"); + goto fail; + } + } + + { + struct lock4_check_state state = { + .me = messaging_server_id(msg) + }; + + status = g_lock_dump(ctx, key, lock4_check, &state); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_dump failed: %s\n", + nt_errstr(status)); + goto fail; + } + if (!state.ok) { + fprintf(stderr, "lock4_check failed\n"); + goto fail; + } + } + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +struct lock5_parser_state { + size_t num_locks; +}; + +static void lock5_parser(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data) +{ + struct lock5_parser_state *state = private_data; + state->num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0); +} + +/* + * Test heuristic cleanup + */ + +bool run_g_lock5(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock5"; + pid_t child; + int exit_pipe[2], ready_pipe[2]; + NTSTATUS status; + size_t i, nprocs; + int ret; + bool ok; + ssize_t nread; + char c; + + nprocs = 5; + + if ((pipe(exit_pipe) != 0) || (pipe(ready_pipe) != 0)) { + perror("pipe failed"); + return false; + } + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + fprintf(stderr, "get_g_lock_ctx failed"); + return false; + } + + for (i=0; i<nprocs; i++) { + + child = fork(); + + if (child == -1) { + perror("fork failed"); + return false; + } + + if (child == 0) { + TALLOC_FREE(ctx); + + status = reinit_after_fork(msg, ev, false, ""); + + close(ready_pipe[0]); + close(exit_pipe[1]); + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + fprintf(stderr, "get_g_lock_ctx failed"); + exit(1); + } + status = g_lock_lock(ctx, + string_term_tdb_data(lockname), + G_LOCK_READ, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "child g_lock_lock failed %s\n", + nt_errstr(status)); + exit(1); + } + close(ready_pipe[1]); + nread = sys_read(exit_pipe[0], &c, sizeof(c)); + if (nread != 0) { + fprintf(stderr, "sys_read returned %zu (%s)\n", + nread, strerror(errno)); + exit(1); + } + exit(0); + } + } + + close(ready_pipe[1]); + + nread = sys_read(ready_pipe[0], &c, sizeof(c)); + if (nread != 0) { + fprintf(stderr, "sys_read returned %zu (%s)\n", + nread, strerror(errno)); + return false; + } + + close(exit_pipe[1]); + + for (i=0; i<nprocs; i++) { + int child_status; + ret = waitpid(-1, &child_status, 0); + if (ret == -1) { + perror("waitpid failed"); + return false; + } + } + + for (i=0; i<nprocs; i++) { + struct lock5_parser_state state; + + status = g_lock_dump(ctx, string_term_tdb_data(lockname), + lock5_parser, &state); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_dump returned %s\n", + nt_errstr(status)); + return false; + } + + if (state.num_locks != (nprocs - i)) { + fprintf(stderr, "nlocks=%zu, expected %zu\n", + state.num_locks, (nprocs-i)); + return false; + } + + status = g_lock_lock(ctx, string_term_tdb_data(lockname), + G_LOCK_READ, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed %s\n", + nt_errstr(status)); + return false; + } + status = g_lock_unlock(ctx, string_term_tdb_data(lockname)); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_unlock failed %s\n", + nt_errstr(status)); + return false; + } + } + + + return true; +} + +struct lock6_parser_state { + size_t num_locks; +}; + +static void lock6_parser(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data) +{ + struct lock6_parser_state *state = private_data; + state->num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0); +} + +/* + * Test cleanup with contention and stale locks + */ + +bool run_g_lock6(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + TDB_DATA lockname = string_term_tdb_data("lock6"); + pid_t child; + int exit_pipe[2], ready_pipe[2]; + NTSTATUS status; + size_t i, nprocs; + int ret; + bool ok; + ssize_t nread; + char c; + + if ((pipe(exit_pipe) != 0) || (pipe(ready_pipe) != 0)) { + perror("pipe failed"); + return false; + } + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + fprintf(stderr, "get_g_lock_ctx failed"); + return false; + } + + /* + * Wipe all stale locks -- in clustered mode there's no + * CLEAR_IF_FIRST + */ + status = g_lock_lock(ctx, lockname, G_LOCK_WRITE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed: %s\n", + nt_errstr(status)); + return false; + } + status = g_lock_unlock(ctx, lockname); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_unlock failed: %s\n", + nt_errstr(status)); + return false; + } + + nprocs = 2; + for (i=0; i<nprocs; i++) { + + child = fork(); + + if (child == -1) { + perror("fork failed"); + return false; + } + + if (child == 0) { + TALLOC_FREE(ctx); + + status = reinit_after_fork(msg, ev, false, ""); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "reinit_after_fork failed: %s\n", + nt_errstr(status)); + exit(1); + } + + close(ready_pipe[0]); + close(exit_pipe[1]); + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + fprintf(stderr, "get_g_lock_ctx failed"); + exit(1); + } + status = g_lock_lock(ctx, + lockname, + G_LOCK_READ, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "child g_lock_lock failed %s\n", + nt_errstr(status)); + exit(1); + } + if (i == 0) { + exit(0); + } + close(ready_pipe[1]); + nread = sys_read(exit_pipe[0], &c, sizeof(c)); + if (nread != 0) { + fprintf(stderr, "sys_read returned %zu (%s)\n", + nread, strerror(errno)); + exit(1); + } + exit(0); + } + } + + close(ready_pipe[1]); + + nread = sys_read(ready_pipe[0], &c, sizeof(c)); + if (nread != 0) { + fprintf(stderr, "sys_read returned %zd (%s)\n", + nread, strerror(errno)); + return false; + } + + { + int child_status; + ret = waitpid(-1, &child_status, 0); + if (ret == -1) { + perror("waitpid failed"); + return false; + } + } + + { + struct lock6_parser_state state; + + status = g_lock_dump(ctx, lockname, lock6_parser, &state); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_dump returned %s\n", + nt_errstr(status)); + return false; + } + + if (state.num_locks != nprocs) { + fprintf(stderr, "nlocks=%zu, expected %zu\n", + state.num_locks, nprocs); + return false; + } + + status = g_lock_lock(ctx, + lockname, + G_LOCK_WRITE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + fprintf(stderr, "g_lock_lock should have failed with %s - %s\n", + nt_errstr(NT_STATUS_IO_TIMEOUT), + nt_errstr(status)); + return false; + } + + status = g_lock_lock(ctx, lockname, G_LOCK_READ, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed: %s\n", + nt_errstr(status)); + return false; + } + } + + close(exit_pipe[1]); + + { + int child_status; + ret = waitpid(-1, &child_status, 0); + if (ret == -1) { + perror("waitpid failed"); + return false; + } + } + + status = g_lock_lock(ctx, lockname, G_LOCK_UPGRADE, + (struct timeval) { .tv_sec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed: %s\n", + nt_errstr(status)); + return false; + } + + return true; +} + +/* + * Test upgrade deadlock + */ + +bool run_g_lock7(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + const char *lockname = "lock7"; + TDB_DATA key = string_term_tdb_data(lockname); + pid_t child; + int ready_pipe[2]; + int down_pipe[2]; + ssize_t n; + NTSTATUS status; + bool ret = false; + bool ok = true; + + if ((pipe(ready_pipe) != 0) || (pipe(down_pipe) != 0)) { + perror("pipe failed"); + return false; + } + + child = fork(); + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + if (child == -1) { + perror("fork failed"); + return false; + } + + if (child == 0) { + struct tevent_req *req = NULL; + + close(ready_pipe[0]); + ready_pipe[0] = -1; + close(down_pipe[1]); + down_pipe[1] = -1; + + status = reinit_after_fork(msg, ev, false, ""); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "reinit_after_fork failed: %s\n", + nt_errstr(status)); + exit(1); + } + + printf("%d: locking READ\n", (int)getpid()); + + status = g_lock_lock( + ctx, + key, + G_LOCK_READ, + (struct timeval) { .tv_usec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_lock(READ) failed: %s\n", + nt_errstr(status)); + exit(1); + } + + ok = true; + + n = sys_write(ready_pipe[1], &ok, sizeof(ok)); + if (n != sizeof(ok)) { + fprintf(stderr, + "sys_write failed: %s\n", + strerror(errno)); + exit(1); + } + + n = sys_read(down_pipe[0], &ok, sizeof(ok)); + if (n != sizeof(ok)) { + fprintf(stderr, + "sys_read failed: %s\n", + strerror(errno)); + exit(1); + } + + printf("%d: starting UPGRADE\n", (int)getpid()); + + req = g_lock_lock_send( + msg, + ev, + ctx, + key, + G_LOCK_UPGRADE); + if (req == NULL) { + fprintf(stderr, "g_lock_lock_send(UPGRADE) failed\n"); + exit(1); + } + + n = sys_write(ready_pipe[1], &ok, sizeof(ok)); + if (n != sizeof(ok)) { + fprintf(stderr, + "sys_write failed: %s\n", + strerror(errno)); + exit(1); + } + + exit(0); + } + + close(ready_pipe[1]); + close(down_pipe[0]); + + if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) { + perror("read failed"); + return false; + } + if (!ok) { + fprintf(stderr, "child returned error\n"); + return false; + } + + status = g_lock_lock( + ctx, + key, + G_LOCK_READ, + (struct timeval) { .tv_usec = 1 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_lock(READ) failed: %s\n", + nt_errstr(status)); + goto fail; + } + + n = sys_write(down_pipe[1], &ok, sizeof(ok)); + if (n != sizeof(ok)) { + fprintf(stderr, + "sys_write failed: %s\n", + strerror(errno)); + goto fail; + } + + if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) { + perror("read failed"); + goto fail; + } + + status = g_lock_lock( + ctx, + key, + G_LOCK_UPGRADE, + (struct timeval) { .tv_sec = 10 }); + if (!NT_STATUS_EQUAL(status, NT_STATUS_POSSIBLE_DEADLOCK)) { + fprintf(stderr, + "g_lock_lock returned %s\n", + nt_errstr(status)); + goto fail; + } + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +bool run_g_lock8(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + struct tevent_req *req = NULL; + TDB_DATA lockname = string_term_tdb_data("lock8"); + NTSTATUS status; + bool ok; + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + fprintf(stderr, "get_g_lock_ctx failed"); + return false; + } + + req = g_lock_watch_data_send( + ev, ev, ctx, lockname, (struct server_id) { .pid = 0 }); + if (req == NULL) { + fprintf(stderr, "get_g_lock_ctx failed"); + return false; + } + + status = g_lock_lock( + ctx, + lockname, + G_LOCK_WRITE, + (struct timeval) { .tv_sec = 999 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_lock failed: %s\n", + nt_errstr(status)); + return false; + } + + status = g_lock_write_data( + ctx, lockname, lockname.dptr, lockname.dsize); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_write_data failed: %s\n", + nt_errstr(status)); + return false; + } + + status = g_lock_write_data(ctx, lockname, NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_write_data failed: %s\n", + nt_errstr(status)); + return false; + } + + status = g_lock_unlock(ctx, lockname); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "g_lock_unlock failed: %s\n", + nt_errstr(status)); + return false; + } + + ok = tevent_req_poll_ntstatus(req, ev, &status); + if (!ok) { + fprintf(stderr, "tevent_req_poll_ntstatus failed\n"); + return false; + } + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "tevent_req_poll_ntstatus failed: %s\n", + nt_errstr(status)); + return false; + } + + return true; +} + +extern int torture_numops; +extern int torture_nprocs; + +static struct timeval tp1, tp2; + +static void start_timer(void) +{ + gettimeofday(&tp1,NULL); +} + +static double end_timer(void) +{ + gettimeofday(&tp2,NULL); + return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - + (tp1.tv_sec + (tp1.tv_usec*1.0e-6)); +} + +/* + * g_lock ping_pong + */ + +bool run_g_lock_ping_pong(int dummy) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *ctx = NULL; + fstring name; + NTSTATUS status; + int i = 0; + bool ret = false; + bool ok; + unsigned count = 0; + + torture_nprocs = MAX(2, torture_nprocs); + + ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx); + if (!ok) { + goto fail; + } + + start_timer(); + + snprintf(name, sizeof(name), "ping_pong_%d", i); + + status = g_lock_lock(ctx, string_term_tdb_data(name), G_LOCK_WRITE, + (struct timeval) { .tv_sec = 60 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed: %s\n", + nt_errstr(status)); + goto fail; + } + + for (i=0; i<torture_numops; i++) { + + name[10] = '0' + ((i+1) % torture_nprocs); + + status = g_lock_lock(ctx, string_term_tdb_data(name), + G_LOCK_WRITE, + (struct timeval) { .tv_sec = 60 }); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_lock failed: %s\n", + nt_errstr(status)); + goto fail; + } + + name[10] = '0' + ((i) % torture_nprocs); + + status = g_lock_unlock(ctx, string_term_tdb_data(name)); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "g_lock_unlock failed: %s\n", + nt_errstr(status)); + goto fail; + } + + count++; + + if (end_timer() > 1.0) { + printf("%8u locks/sec\r", + (unsigned)(2*count/end_timer())); + fflush(stdout); + start_timer(); + count=0; + } + } + + ret = true; +fail: + TALLOC_FREE(ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} |