summaryrefslogtreecommitdiffstats
path: root/source3/torture/test_g_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/torture/test_g_lock.c')
-rw-r--r--source3/torture/test_g_lock.c1403
1 files changed, 1403 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..fee9127
--- /dev/null
+++ b/source3/torture/test_g_lock.c
@@ -0,0 +1,1403 @@
+/*
+ * 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL,
+ NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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, NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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, NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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,
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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 },
+ NULL, NULL);
+ 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;
+}