smbd: Make reopen_from_fsp() public
[samba4-gss.git] / source3 / lib / g_lock.c
blobd0ca32199ea94c76c2c6f4cad9bf5c16ca02c263
1 /*
2 Unix SMB/CIFS implementation.
3 global locks based on dbwrap and messaging
4 Copyright (C) 2009 by Volker Lendecke
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "lib/util/server_id.h"
23 #include "lib/util/debug.h"
24 #include "lib/util/talloc_stack.h"
25 #include "lib/util/samba_util.h"
26 #include "lib/util_path.h"
27 #include "dbwrap/dbwrap.h"
28 #include "dbwrap/dbwrap_open.h"
29 #include "dbwrap/dbwrap_watch.h"
30 #include "g_lock.h"
31 #include "util_tdb.h"
32 #include "../lib/util/tevent_ntstatus.h"
33 #include "messages.h"
34 #include "serverid.h"
36 struct g_lock_ctx {
37 struct db_context *db;
38 struct messaging_context *msg;
39 enum dbwrap_lock_order lock_order;
40 bool busy;
43 struct g_lock {
44 struct server_id exclusive;
45 size_t num_shared;
46 uint8_t *shared;
47 uint64_t unique_lock_epoch;
48 uint64_t unique_data_epoch;
49 size_t datalen;
50 uint8_t *data;
53 static bool g_lock_parse(uint8_t *buf, size_t buflen, struct g_lock *lck)
55 struct server_id exclusive;
56 size_t num_shared, shared_len, data_len;
57 uint64_t unique_lock_epoch;
58 uint64_t unique_data_epoch;
60 if (buflen < (SERVER_ID_BUF_LENGTH + /* exclusive */
61 sizeof(uint64_t) + /* unique_lock_epoch */
62 sizeof(uint64_t) + /* unique_data_epoch */
63 sizeof(uint32_t))) { /* num_shared */
64 struct g_lock ret = {
65 .exclusive.pid = 0,
66 .unique_lock_epoch = generate_unique_u64(0),
67 .unique_data_epoch = generate_unique_u64(0),
69 *lck = ret;
70 return true;
73 server_id_get(&exclusive, buf);
74 buf += SERVER_ID_BUF_LENGTH;
75 buflen -= SERVER_ID_BUF_LENGTH;
77 unique_lock_epoch = BVAL(buf, 0);
78 buf += sizeof(uint64_t);
79 buflen -= sizeof(uint64_t);
81 unique_data_epoch = BVAL(buf, 0);
82 buf += sizeof(uint64_t);
83 buflen -= sizeof(uint64_t);
85 num_shared = IVAL(buf, 0);
86 buf += sizeof(uint32_t);
87 buflen -= sizeof(uint32_t);
89 if (num_shared > buflen/SERVER_ID_BUF_LENGTH) {
90 DBG_DEBUG("num_shared=%zu, buflen=%zu\n",
91 num_shared,
92 buflen);
93 return false;
96 shared_len = num_shared * SERVER_ID_BUF_LENGTH;
97 data_len = buflen - shared_len;
99 *lck = (struct g_lock) {
100 .exclusive = exclusive,
101 .num_shared = num_shared,
102 .shared = num_shared == 0 ? NULL : buf,
103 .unique_lock_epoch = unique_lock_epoch,
104 .unique_data_epoch = unique_data_epoch,
105 .datalen = data_len,
106 .data = data_len == 0 ? NULL : buf + shared_len,
109 return true;
112 static void g_lock_get_shared(const struct g_lock *lck,
113 size_t i,
114 struct server_id *shared)
116 if (i >= lck->num_shared) {
117 abort();
119 server_id_get(shared, lck->shared + i*SERVER_ID_BUF_LENGTH);
122 static void g_lock_del_shared(struct g_lock *lck, size_t i)
124 if (i >= lck->num_shared) {
125 abort();
127 lck->num_shared -= 1;
128 if (i < lck->num_shared) {
129 memcpy(lck->shared + i*SERVER_ID_BUF_LENGTH,
130 lck->shared + lck->num_shared*SERVER_ID_BUF_LENGTH,
131 SERVER_ID_BUF_LENGTH);
135 static NTSTATUS g_lock_store(
136 struct db_record *rec,
137 struct g_lock *lck,
138 struct server_id *new_shared,
139 const TDB_DATA *new_dbufs,
140 size_t num_new_dbufs)
142 uint8_t exclusive[SERVER_ID_BUF_LENGTH];
143 uint8_t seqnum_buf[sizeof(uint64_t)*2];
144 uint8_t sizebuf[sizeof(uint32_t)];
145 uint8_t new_shared_buf[SERVER_ID_BUF_LENGTH];
147 struct TDB_DATA dbufs[6 + num_new_dbufs];
149 dbufs[0] = (TDB_DATA) {
150 .dptr = exclusive, .dsize = sizeof(exclusive),
152 dbufs[1] = (TDB_DATA) {
153 .dptr = seqnum_buf, .dsize = sizeof(seqnum_buf),
155 dbufs[2] = (TDB_DATA) {
156 .dptr = sizebuf, .dsize = sizeof(sizebuf),
158 dbufs[3] = (TDB_DATA) {
159 .dptr = lck->shared,
160 .dsize = lck->num_shared * SERVER_ID_BUF_LENGTH,
162 dbufs[4] = (TDB_DATA) { 0 };
163 dbufs[5] = (TDB_DATA) {
164 .dptr = lck->data, .dsize = lck->datalen,
167 if (num_new_dbufs != 0) {
168 memcpy(&dbufs[6],
169 new_dbufs,
170 num_new_dbufs * sizeof(TDB_DATA));
173 server_id_put(exclusive, lck->exclusive);
174 SBVAL(seqnum_buf, 0, lck->unique_lock_epoch);
175 SBVAL(seqnum_buf, 8, lck->unique_data_epoch);
177 if (new_shared != NULL) {
178 if (lck->num_shared >= UINT32_MAX) {
179 return NT_STATUS_BUFFER_OVERFLOW;
182 server_id_put(new_shared_buf, *new_shared);
184 dbufs[4] = (TDB_DATA) {
185 .dptr = new_shared_buf,
186 .dsize = sizeof(new_shared_buf),
189 lck->num_shared += 1;
192 SIVAL(sizebuf, 0, lck->num_shared);
194 return dbwrap_record_storev(rec, dbufs, ARRAY_SIZE(dbufs), 0);
197 struct g_lock_ctx *g_lock_ctx_init_backend(
198 TALLOC_CTX *mem_ctx,
199 struct messaging_context *msg,
200 struct db_context **backend)
202 struct g_lock_ctx *result;
204 result = talloc_zero(mem_ctx, struct g_lock_ctx);
205 if (result == NULL) {
206 return NULL;
208 result->msg = msg;
209 result->lock_order = DBWRAP_LOCK_ORDER_NONE;
211 result->db = db_open_watched(result, backend, msg);
212 if (result->db == NULL) {
213 DBG_WARNING("db_open_watched failed\n");
214 TALLOC_FREE(result);
215 return NULL;
217 return result;
220 void g_lock_set_lock_order(struct g_lock_ctx *ctx,
221 enum dbwrap_lock_order lock_order)
223 ctx->lock_order = lock_order;
226 struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx,
227 struct messaging_context *msg)
229 char *db_path = NULL;
230 struct db_context *backend = NULL;
231 struct g_lock_ctx *ctx = NULL;
233 db_path = lock_path(mem_ctx, "g_lock.tdb");
234 if (db_path == NULL) {
235 return NULL;
238 backend = db_open(
239 mem_ctx,
240 db_path,
242 TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH|TDB_VOLATILE,
243 O_RDWR|O_CREAT,
244 0600,
245 DBWRAP_LOCK_ORDER_3,
246 DBWRAP_FLAG_NONE);
247 TALLOC_FREE(db_path);
248 if (backend == NULL) {
249 DBG_WARNING("Could not open g_lock.tdb\n");
250 return NULL;
253 ctx = g_lock_ctx_init_backend(mem_ctx, msg, &backend);
254 return ctx;
257 static void g_lock_cleanup_dead(
258 struct g_lock *lck,
259 struct server_id *dead_blocker)
261 bool exclusive_died;
262 struct server_id_buf tmp;
264 if (dead_blocker == NULL) {
265 return;
268 exclusive_died = server_id_equal(dead_blocker, &lck->exclusive);
270 if (exclusive_died) {
271 DBG_DEBUG("Exclusive holder %s died\n",
272 server_id_str_buf(lck->exclusive, &tmp));
273 lck->exclusive.pid = 0;
276 if (lck->num_shared != 0) {
277 bool shared_died;
278 struct server_id shared;
280 g_lock_get_shared(lck, 0, &shared);
281 shared_died = server_id_equal(dead_blocker, &shared);
283 if (shared_died) {
284 DBG_DEBUG("Shared holder %s died\n",
285 server_id_str_buf(shared, &tmp));
286 g_lock_del_shared(lck, 0);
291 static ssize_t g_lock_find_shared(
292 struct g_lock *lck,
293 const struct server_id *self)
295 size_t i;
297 for (i=0; i<lck->num_shared; i++) {
298 struct server_id shared;
299 bool same;
301 g_lock_get_shared(lck, i, &shared);
303 same = server_id_equal(self, &shared);
304 if (same) {
305 return i;
309 return -1;
312 static void g_lock_cleanup_shared(struct g_lock *lck)
314 size_t i;
315 struct server_id check;
316 bool exists;
318 if (lck->num_shared == 0) {
319 return;
323 * Read locks can stay around forever if the process dies. Do
324 * a heuristic check for process existence: Check one random
325 * process for existence. Hopefully this will keep runaway
326 * read locks under control.
328 i = generate_random() % lck->num_shared;
329 g_lock_get_shared(lck, i, &check);
331 exists = serverid_exists(&check);
332 if (!exists) {
333 struct server_id_buf tmp;
334 DBG_DEBUG("Shared locker %s died -- removing\n",
335 server_id_str_buf(check, &tmp));
336 g_lock_del_shared(lck, i);
340 struct g_lock_lock_cb_state {
341 struct g_lock_ctx *ctx;
342 struct db_record *rec;
343 struct g_lock *lck;
344 struct server_id *new_shared;
345 g_lock_lock_cb_fn_t cb_fn;
346 void *cb_private;
347 TALLOC_CTX *update_mem_ctx;
348 TDB_DATA updated_data;
349 bool existed;
350 bool modified;
351 bool unlock;
354 NTSTATUS g_lock_lock_cb_dump(struct g_lock_lock_cb_state *cb_state,
355 void (*fn)(struct server_id exclusive,
356 size_t num_shared,
357 const struct server_id *shared,
358 const uint8_t *data,
359 size_t datalen,
360 void *private_data),
361 void *private_data)
363 struct g_lock *lck = cb_state->lck;
365 /* We allow a cb_fn only for G_LOCK_WRITE for now... */
366 SMB_ASSERT(lck->num_shared == 0);
368 fn(lck->exclusive,
369 0, /* num_shared */
370 NULL, /* shared */
371 lck->data,
372 lck->datalen,
373 private_data);
375 return NT_STATUS_OK;
378 NTSTATUS g_lock_lock_cb_writev(struct g_lock_lock_cb_state *cb_state,
379 const TDB_DATA *dbufs,
380 size_t num_dbufs)
382 NTSTATUS status;
384 status = dbwrap_merge_dbufs(&cb_state->updated_data,
385 cb_state->update_mem_ctx,
386 dbufs, num_dbufs);
387 if (!NT_STATUS_IS_OK(status)) {
388 return status;
391 cb_state->modified = true;
392 cb_state->lck->data = cb_state->updated_data.dptr;
393 cb_state->lck->datalen = cb_state->updated_data.dsize;
395 return NT_STATUS_OK;
398 void g_lock_lock_cb_unlock(struct g_lock_lock_cb_state *cb_state)
400 cb_state->unlock = true;
403 struct g_lock_lock_cb_watch_data_state {
404 struct tevent_context *ev;
405 struct g_lock_ctx *ctx;
406 TDB_DATA key;
407 struct server_id blocker;
408 bool blockerdead;
409 uint64_t unique_lock_epoch;
410 uint64_t unique_data_epoch;
411 uint64_t watch_instance;
412 NTSTATUS status;
415 static void g_lock_lock_cb_watch_data_done(struct tevent_req *subreq);
417 struct tevent_req *g_lock_lock_cb_watch_data_send(
418 TALLOC_CTX *mem_ctx,
419 struct tevent_context *ev,
420 struct g_lock_lock_cb_state *cb_state,
421 struct server_id blocker)
423 struct tevent_req *req = NULL;
424 struct g_lock_lock_cb_watch_data_state *state = NULL;
425 struct tevent_req *subreq = NULL;
426 TDB_DATA key = dbwrap_record_get_key(cb_state->rec);
428 req = tevent_req_create(
429 mem_ctx, &state, struct g_lock_lock_cb_watch_data_state);
430 if (req == NULL) {
431 return NULL;
433 state->ev = ev;
434 state->ctx = cb_state->ctx;
435 state->blocker = blocker;
437 state->key = tdb_data_talloc_copy(state, key);
438 if (tevent_req_nomem(state->key.dptr, req)) {
439 return tevent_req_post(req, ev);
442 state->unique_lock_epoch = cb_state->lck->unique_lock_epoch;
443 state->unique_data_epoch = cb_state->lck->unique_data_epoch;
445 DBG_DEBUG("state->unique_data_epoch=%"PRIu64"\n", state->unique_data_epoch);
447 subreq = dbwrap_watched_watch_send(
448 state, state->ev, cb_state->rec, 0, state->blocker);
449 if (tevent_req_nomem(subreq, req)) {
450 return tevent_req_post(req, ev);
452 tevent_req_set_callback(subreq, g_lock_lock_cb_watch_data_done, req);
454 return req;
457 static void g_lock_lock_cb_watch_data_done_fn(
458 struct db_record *rec,
459 TDB_DATA value,
460 void *private_data)
462 struct tevent_req *req = talloc_get_type_abort(
463 private_data, struct tevent_req);
464 struct g_lock_lock_cb_watch_data_state *state = tevent_req_data(
465 req, struct g_lock_lock_cb_watch_data_state);
466 struct tevent_req *subreq = NULL;
467 struct g_lock lck;
468 bool ok;
470 ok = g_lock_parse(value.dptr, value.dsize, &lck);
471 if (!ok) {
472 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
473 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
474 return;
477 if (lck.unique_data_epoch != state->unique_data_epoch) {
478 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
479 DBG_DEBUG("lck.unique_data_epoch=%"PRIu64", "
480 "state->unique_data_epoch=%"PRIu64"\n",
481 lck.unique_data_epoch,
482 state->unique_data_epoch);
483 state->status = NT_STATUS_OK;
484 return;
488 * The lock epoch changed, so we better
489 * remove ourself from the waiter list
490 * (most likely the first position)
491 * and re-add us at the end of the list.
493 * This gives other lock waiters a change
494 * to make progress.
496 * Otherwise we'll keep our waiter instance alive,
497 * keep waiting (most likely at first position).
499 if (lck.unique_lock_epoch != state->unique_lock_epoch) {
500 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
501 state->watch_instance = dbwrap_watched_watch_add_instance(rec);
502 state->unique_lock_epoch = lck.unique_lock_epoch;
505 subreq = dbwrap_watched_watch_send(
506 state, state->ev, rec, state->watch_instance, state->blocker);
507 if (subreq == NULL) {
508 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
509 state->status = NT_STATUS_NO_MEMORY;
510 return;
512 tevent_req_set_callback(subreq, g_lock_lock_cb_watch_data_done, req);
514 state->status = NT_STATUS_EVENT_PENDING;
517 static void g_lock_lock_cb_watch_data_done(struct tevent_req *subreq)
519 struct tevent_req *req = tevent_req_callback_data(
520 subreq, struct tevent_req);
521 struct g_lock_lock_cb_watch_data_state *state = tevent_req_data(
522 req, struct g_lock_lock_cb_watch_data_state);
523 NTSTATUS status;
524 uint64_t instance = 0;
526 status = dbwrap_watched_watch_recv(
527 subreq, &instance, &state->blockerdead, &state->blocker);
528 TALLOC_FREE(subreq);
529 if (tevent_req_nterror(req, status)) {
530 DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
531 nt_errstr(status));
532 return;
535 state->watch_instance = instance;
537 status = dbwrap_do_locked(
538 state->ctx->db, state->key, g_lock_lock_cb_watch_data_done_fn, req);
539 if (tevent_req_nterror(req, status)) {
540 DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status));
541 return;
543 if (NT_STATUS_EQUAL(state->status, NT_STATUS_EVENT_PENDING)) {
544 return;
546 if (tevent_req_nterror(req, state->status)) {
547 return;
549 tevent_req_done(req);
552 NTSTATUS g_lock_lock_cb_watch_data_recv(
553 struct tevent_req *req,
554 bool *blockerdead,
555 struct server_id *blocker)
557 struct g_lock_lock_cb_watch_data_state *state = tevent_req_data(
558 req, struct g_lock_lock_cb_watch_data_state);
559 NTSTATUS status;
561 if (tevent_req_is_nterror(req, &status)) {
562 return status;
564 if (blockerdead != NULL) {
565 *blockerdead = state->blockerdead;
567 if (blocker != NULL) {
568 *blocker = state->blocker;
571 return NT_STATUS_OK;
574 void g_lock_lock_cb_wake_watchers(struct g_lock_lock_cb_state *cb_state)
576 struct g_lock *lck = cb_state->lck;
578 lck->unique_data_epoch = generate_unique_u64(lck->unique_data_epoch);
579 cb_state->modified = true;
582 static NTSTATUS g_lock_lock_cb_run_and_store(struct g_lock_lock_cb_state *cb_state)
584 struct g_lock *lck = cb_state->lck;
585 NTSTATUS success_status = NT_STATUS_OK;
586 NTSTATUS status;
588 if (cb_state->cb_fn != NULL) {
590 SMB_ASSERT(lck->num_shared == 0);
591 SMB_ASSERT(cb_state->new_shared == NULL);
593 if (cb_state->ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) {
594 const char *name = dbwrap_name(cb_state->ctx->db);
595 dbwrap_lock_order_lock(name, cb_state->ctx->lock_order);
598 cb_state->ctx->busy = true;
599 cb_state->cb_fn(cb_state, cb_state->cb_private);
600 cb_state->ctx->busy = false;
602 if (cb_state->ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) {
603 const char *name = dbwrap_name(cb_state->ctx->db);
604 dbwrap_lock_order_unlock(name, cb_state->ctx->lock_order);
608 if (cb_state->unlock) {
610 * Unlocked should wake up watchers.
612 * We no longer need the lock, so
613 * force a wakeup of the next watchers,
614 * even if we don't do any update.
616 dbwrap_watched_watch_reset_alerting(cb_state->rec);
617 dbwrap_watched_watch_force_alerting(cb_state->rec);
618 if (!cb_state->modified) {
620 * The record was not changed at
621 * all, so we can also avoid
622 * storing the lck.unique_lock_epoch
623 * change
625 return NT_STATUS_WAS_UNLOCKED;
627 lck->exclusive = (struct server_id) { .pid = 0 };
628 cb_state->new_shared = NULL;
630 if (lck->datalen == 0) {
631 if (!cb_state->existed) {
632 return NT_STATUS_WAS_UNLOCKED;
635 status = dbwrap_record_delete(cb_state->rec);
636 if (!NT_STATUS_IS_OK(status)) {
637 DBG_WARNING("dbwrap_record_delete() failed: %s\n",
638 nt_errstr(status));
639 return status;
641 return NT_STATUS_WAS_UNLOCKED;
644 success_status = NT_STATUS_WAS_UNLOCKED;
647 status = g_lock_store(cb_state->rec,
648 cb_state->lck,
649 cb_state->new_shared,
650 NULL, 0);
651 if (!NT_STATUS_IS_OK(status)) {
652 DBG_WARNING("g_lock_store() failed: %s\n",
653 nt_errstr(status));
654 return status;
657 return success_status;
660 struct g_lock_lock_state {
661 struct tevent_context *ev;
662 struct g_lock_ctx *ctx;
663 TDB_DATA key;
664 enum g_lock_type type;
665 bool retry;
666 g_lock_lock_cb_fn_t cb_fn;
667 void *cb_private;
670 struct g_lock_lock_fn_state {
671 struct g_lock_lock_state *req_state;
672 struct server_id *dead_blocker;
674 struct tevent_req *watch_req;
675 uint64_t watch_instance;
676 NTSTATUS status;
679 static int g_lock_lock_state_destructor(struct g_lock_lock_state *s);
681 static NTSTATUS g_lock_trylock(
682 struct db_record *rec,
683 struct g_lock_lock_fn_state *state,
684 TDB_DATA data,
685 struct server_id *blocker)
687 struct g_lock_lock_state *req_state = state->req_state;
688 struct server_id self = messaging_server_id(req_state->ctx->msg);
689 enum g_lock_type type = req_state->type;
690 bool retry = req_state->retry;
691 struct g_lock lck = { .exclusive.pid = 0 };
692 struct g_lock_lock_cb_state cb_state = {
693 .ctx = req_state->ctx,
694 .rec = rec,
695 .lck = &lck,
696 .cb_fn = req_state->cb_fn,
697 .cb_private = req_state->cb_private,
698 .existed = data.dsize != 0,
699 .update_mem_ctx = talloc_tos(),
701 struct server_id_buf tmp;
702 NTSTATUS status;
703 bool ok;
705 ok = g_lock_parse(data.dptr, data.dsize, &lck);
706 if (!ok) {
707 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
708 DBG_DEBUG("g_lock_parse failed\n");
709 return NT_STATUS_INTERNAL_DB_CORRUPTION;
712 g_lock_cleanup_dead(&lck, state->dead_blocker);
714 lck.unique_lock_epoch = generate_unique_u64(lck.unique_lock_epoch);
716 if (lck.exclusive.pid != 0) {
717 bool self_exclusive = server_id_equal(&self, &lck.exclusive);
719 if (!self_exclusive) {
720 bool exists = serverid_exists(&lck.exclusive);
721 if (!exists) {
722 lck.exclusive = (struct server_id) { .pid=0 };
723 goto noexclusive;
726 DBG_DEBUG("%s has an exclusive lock\n",
727 server_id_str_buf(lck.exclusive, &tmp));
729 if (type == G_LOCK_DOWNGRADE) {
730 struct server_id_buf tmp2;
732 dbwrap_watched_watch_remove_instance(rec,
733 state->watch_instance);
735 DBG_DEBUG("%s: Trying to downgrade %s\n",
736 server_id_str_buf(self, &tmp),
737 server_id_str_buf(
738 lck.exclusive, &tmp2));
739 return NT_STATUS_NOT_LOCKED;
742 if (type == G_LOCK_UPGRADE) {
743 ssize_t shared_idx;
745 dbwrap_watched_watch_remove_instance(rec,
746 state->watch_instance);
748 shared_idx = g_lock_find_shared(&lck, &self);
750 if (shared_idx == -1) {
751 DBG_DEBUG("Trying to upgrade %s "
752 "without "
753 "existing shared lock\n",
754 server_id_str_buf(
755 self, &tmp));
756 return NT_STATUS_NOT_LOCKED;
760 * We're trying to upgrade, and the
761 * exclusive lock is taken by someone
762 * else. This means that someone else
763 * is waiting for us to give up our
764 * shared lock. If we now also wait
765 * for someone to give their shared
766 * lock, we will deadlock.
769 DBG_DEBUG("Trying to upgrade %s while "
770 "someone else is also "
771 "trying to upgrade\n",
772 server_id_str_buf(self, &tmp));
773 return NT_STATUS_POSSIBLE_DEADLOCK;
776 DBG_DEBUG("Waiting for lck.exclusive=%s\n",
777 server_id_str_buf(lck.exclusive, &tmp));
780 * We will return NT_STATUS_LOCK_NOT_GRANTED
781 * and need to monitor the record.
783 * If we don't have a watcher instance yet,
784 * we should add one.
786 if (state->watch_instance == 0) {
787 state->watch_instance =
788 dbwrap_watched_watch_add_instance(rec);
791 *blocker = lck.exclusive;
792 return NT_STATUS_LOCK_NOT_GRANTED;
795 if (type == G_LOCK_DOWNGRADE) {
796 DBG_DEBUG("Downgrading %s from WRITE to READ\n",
797 server_id_str_buf(self, &tmp));
799 lck.exclusive = (struct server_id) { .pid = 0 };
800 goto do_shared;
803 if (!retry) {
804 dbwrap_watched_watch_remove_instance(rec,
805 state->watch_instance);
807 DBG_DEBUG("%s already locked by self\n",
808 server_id_str_buf(self, &tmp));
809 return NT_STATUS_WAS_LOCKED;
812 g_lock_cleanup_shared(&lck);
814 if (lck.num_shared != 0) {
815 g_lock_get_shared(&lck, 0, blocker);
817 DBG_DEBUG("Continue waiting for shared lock %s\n",
818 server_id_str_buf(*blocker, &tmp));
821 * We will return NT_STATUS_LOCK_NOT_GRANTED
822 * and need to monitor the record.
824 * If we don't have a watcher instance yet,
825 * we should add one.
827 if (state->watch_instance == 0) {
828 state->watch_instance =
829 dbwrap_watched_watch_add_instance(rec);
832 return NT_STATUS_LOCK_NOT_GRANTED;
836 * Retry after a conflicting lock was released..
837 * All pending readers are gone so we got the lock...
839 goto got_lock;
842 noexclusive:
844 if (type == G_LOCK_UPGRADE) {
845 ssize_t shared_idx = g_lock_find_shared(&lck, &self);
847 if (shared_idx == -1) {
848 dbwrap_watched_watch_remove_instance(rec,
849 state->watch_instance);
851 DBG_DEBUG("Trying to upgrade %s without "
852 "existing shared lock\n",
853 server_id_str_buf(self, &tmp));
854 return NT_STATUS_NOT_LOCKED;
857 g_lock_del_shared(&lck, shared_idx);
858 type = G_LOCK_WRITE;
861 if (type == G_LOCK_WRITE) {
862 ssize_t shared_idx = g_lock_find_shared(&lck, &self);
864 if (shared_idx != -1) {
865 dbwrap_watched_watch_remove_instance(rec,
866 state->watch_instance);
867 DBG_DEBUG("Trying to writelock existing shared %s\n",
868 server_id_str_buf(self, &tmp));
869 return NT_STATUS_WAS_LOCKED;
872 lck.exclusive = self;
874 g_lock_cleanup_shared(&lck);
876 if (lck.num_shared == 0) {
878 * If we store ourself as exclusive writer,
879 * without any pending readers ...
881 goto got_lock;
884 if (state->watch_instance == 0) {
886 * Here we have lck.num_shared != 0.
888 * We will return NT_STATUS_LOCK_NOT_GRANTED
889 * below.
891 * And don't have a watcher instance yet!
893 * We add it here before g_lock_store()
894 * in order to trigger just one
895 * low level dbwrap_do_locked() call.
897 state->watch_instance =
898 dbwrap_watched_watch_add_instance(rec);
901 status = g_lock_store(rec, &lck, NULL, NULL, 0);
902 if (!NT_STATUS_IS_OK(status)) {
903 DBG_DEBUG("g_lock_store() failed: %s\n",
904 nt_errstr(status));
905 return status;
908 talloc_set_destructor(
909 req_state, g_lock_lock_state_destructor);
911 g_lock_get_shared(&lck, 0, blocker);
913 DBG_DEBUG("Waiting for %zu shared locks, "
914 "picking blocker %s\n",
915 lck.num_shared,
916 server_id_str_buf(*blocker, &tmp));
918 return NT_STATUS_LOCK_NOT_GRANTED;
921 do_shared:
923 g_lock_cleanup_shared(&lck);
924 cb_state.new_shared = &self;
925 goto got_lock;
927 got_lock:
929 * We got the lock we asked for, so we no
930 * longer need to monitor the record.
932 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
934 status = g_lock_lock_cb_run_and_store(&cb_state);
935 if (!NT_STATUS_IS_OK(status) &&
936 !NT_STATUS_EQUAL(status, NT_STATUS_WAS_UNLOCKED))
938 DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n",
939 nt_errstr(status));
940 return status;
943 talloc_set_destructor(req_state, NULL);
944 return status;
947 static void g_lock_lock_fn(
948 struct db_record *rec,
949 TDB_DATA value,
950 void *private_data)
952 struct g_lock_lock_fn_state *state = private_data;
953 struct server_id blocker = {0};
956 * We're trying to get a lock and if we are
957 * successful in doing that, we should not
958 * wakeup any other waiters, all they would
959 * find is that we're holding a lock they
960 * are conflicting with.
962 dbwrap_watched_watch_skip_alerting(rec);
964 state->status = g_lock_trylock(rec, state, value, &blocker);
965 if (!NT_STATUS_IS_OK(state->status)) {
966 DBG_DEBUG("g_lock_trylock returned %s\n",
967 nt_errstr(state->status));
969 if (!NT_STATUS_EQUAL(state->status, NT_STATUS_LOCK_NOT_GRANTED)) {
970 return;
973 state->watch_req = dbwrap_watched_watch_send(
974 state->req_state, state->req_state->ev, rec, state->watch_instance, blocker);
975 if (state->watch_req == NULL) {
976 state->status = NT_STATUS_NO_MEMORY;
980 static int g_lock_lock_state_destructor(struct g_lock_lock_state *s)
982 NTSTATUS status = g_lock_unlock(s->ctx, s->key);
983 if (!NT_STATUS_IS_OK(status)) {
984 DBG_DEBUG("g_lock_unlock failed: %s\n", nt_errstr(status));
986 return 0;
989 static void g_lock_lock_retry(struct tevent_req *subreq);
991 struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
992 struct tevent_context *ev,
993 struct g_lock_ctx *ctx,
994 TDB_DATA key,
995 enum g_lock_type type,
996 g_lock_lock_cb_fn_t cb_fn,
997 void *cb_private)
999 struct tevent_req *req;
1000 struct g_lock_lock_state *state;
1001 struct g_lock_lock_fn_state fn_state;
1002 NTSTATUS status;
1003 bool ok;
1005 SMB_ASSERT(!ctx->busy);
1007 req = tevent_req_create(mem_ctx, &state, struct g_lock_lock_state);
1008 if (req == NULL) {
1009 return NULL;
1011 state->ev = ev;
1012 state->ctx = ctx;
1013 state->key = key;
1014 state->type = type;
1015 state->cb_fn = cb_fn;
1016 state->cb_private = cb_private;
1018 fn_state = (struct g_lock_lock_fn_state) {
1019 .req_state = state,
1023 * We allow a cb_fn only for G_LOCK_WRITE for now.
1025 * It's all we currently need and it makes a few things
1026 * easier to implement.
1028 if (unlikely(cb_fn != NULL && type != G_LOCK_WRITE)) {
1029 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_6);
1030 return tevent_req_post(req, ev);
1033 status = dbwrap_do_locked(ctx->db, key, g_lock_lock_fn, &fn_state);
1034 if (tevent_req_nterror(req, status)) {
1035 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
1036 nt_errstr(status));
1037 return tevent_req_post(req, ev);
1040 if (NT_STATUS_IS_OK(fn_state.status)) {
1041 tevent_req_done(req);
1042 return tevent_req_post(req, ev);
1044 if (!NT_STATUS_EQUAL(fn_state.status, NT_STATUS_LOCK_NOT_GRANTED)) {
1045 tevent_req_nterror(req, fn_state.status);
1046 return tevent_req_post(req, ev);
1049 if (tevent_req_nomem(fn_state.watch_req, req)) {
1050 return tevent_req_post(req, ev);
1053 ok = tevent_req_set_endtime(
1054 fn_state.watch_req,
1055 state->ev,
1056 timeval_current_ofs(5 + generate_random() % 5, 0));
1057 if (!ok) {
1058 tevent_req_oom(req);
1059 return tevent_req_post(req, ev);
1061 tevent_req_set_callback(fn_state.watch_req, g_lock_lock_retry, req);
1063 return req;
1066 static void g_lock_lock_retry(struct tevent_req *subreq)
1068 struct tevent_req *req = tevent_req_callback_data(
1069 subreq, struct tevent_req);
1070 struct g_lock_lock_state *state = tevent_req_data(
1071 req, struct g_lock_lock_state);
1072 struct g_lock_lock_fn_state fn_state;
1073 struct server_id blocker = { .pid = 0 };
1074 bool blockerdead = false;
1075 NTSTATUS status;
1076 uint64_t instance = 0;
1078 status = dbwrap_watched_watch_recv(subreq, &instance, &blockerdead, &blocker);
1079 DBG_DEBUG("watch_recv returned %s\n", nt_errstr(status));
1080 TALLOC_FREE(subreq);
1082 if (!NT_STATUS_IS_OK(status) &&
1083 !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
1084 tevent_req_nterror(req, status);
1085 return;
1088 state->retry = true;
1090 fn_state = (struct g_lock_lock_fn_state) {
1091 .req_state = state,
1092 .dead_blocker = blockerdead ? &blocker : NULL,
1093 .watch_instance = instance,
1096 status = dbwrap_do_locked(state->ctx->db, state->key,
1097 g_lock_lock_fn, &fn_state);
1098 if (tevent_req_nterror(req, status)) {
1099 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
1100 nt_errstr(status));
1101 return;
1104 if (NT_STATUS_IS_OK(fn_state.status)) {
1105 tevent_req_done(req);
1106 return;
1108 if (!NT_STATUS_EQUAL(fn_state.status, NT_STATUS_LOCK_NOT_GRANTED)) {
1109 tevent_req_nterror(req, fn_state.status);
1110 return;
1113 if (tevent_req_nomem(fn_state.watch_req, req)) {
1114 return;
1117 if (!tevent_req_set_endtime(
1118 fn_state.watch_req, state->ev,
1119 timeval_current_ofs(5 + generate_random() % 5, 0))) {
1120 return;
1122 tevent_req_set_callback(fn_state.watch_req, g_lock_lock_retry, req);
1125 NTSTATUS g_lock_lock_recv(struct tevent_req *req)
1127 struct g_lock_lock_state *state = tevent_req_data(
1128 req, struct g_lock_lock_state);
1129 struct g_lock_ctx *ctx = state->ctx;
1130 NTSTATUS status;
1132 if (tevent_req_is_nterror(req, &status)) {
1133 if (NT_STATUS_EQUAL(status, NT_STATUS_WAS_UNLOCKED)) {
1134 return NT_STATUS_OK;
1136 return status;
1139 if ((ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) &&
1140 ((state->type == G_LOCK_READ) ||
1141 (state->type == G_LOCK_WRITE))) {
1142 const char *name = dbwrap_name(ctx->db);
1143 dbwrap_lock_order_lock(name, ctx->lock_order);
1146 return NT_STATUS_OK;
1149 struct g_lock_lock_simple_state {
1150 struct g_lock_ctx *ctx;
1151 struct server_id me;
1152 enum g_lock_type type;
1153 NTSTATUS status;
1154 g_lock_lock_cb_fn_t cb_fn;
1155 void *cb_private;
1158 static void g_lock_lock_simple_fn(
1159 struct db_record *rec,
1160 TDB_DATA value,
1161 void *private_data)
1163 struct g_lock_lock_simple_state *state = private_data;
1164 struct server_id_buf buf;
1165 struct g_lock lck = { .exclusive.pid = 0 };
1166 struct g_lock_lock_cb_state cb_state = {
1167 .ctx = state->ctx,
1168 .rec = rec,
1169 .lck = &lck,
1170 .cb_fn = state->cb_fn,
1171 .cb_private = state->cb_private,
1172 .existed = value.dsize != 0,
1173 .update_mem_ctx = talloc_tos(),
1175 bool ok;
1177 ok = g_lock_parse(value.dptr, value.dsize, &lck);
1178 if (!ok) {
1179 DBG_DEBUG("g_lock_parse failed\n");
1180 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1181 return;
1184 if (lck.exclusive.pid != 0) {
1185 DBG_DEBUG("locked by %s\n",
1186 server_id_str_buf(lck.exclusive, &buf));
1187 goto not_granted;
1190 if (state->type == G_LOCK_WRITE) {
1191 if (lck.num_shared != 0) {
1192 DBG_DEBUG("num_shared=%zu\n", lck.num_shared);
1193 goto not_granted;
1195 lck.exclusive = state->me;
1196 } else if (state->type == G_LOCK_READ) {
1197 g_lock_cleanup_shared(&lck);
1198 cb_state.new_shared = &state->me;
1199 } else {
1200 smb_panic(__location__);
1203 lck.unique_lock_epoch = generate_unique_u64(lck.unique_lock_epoch);
1206 * We are going to store us as owner,
1207 * so we got what we were waiting for.
1209 * So we no longer need to monitor the
1210 * record.
1212 dbwrap_watched_watch_skip_alerting(rec);
1214 state->status = g_lock_lock_cb_run_and_store(&cb_state);
1215 if (!NT_STATUS_IS_OK(state->status) &&
1216 !NT_STATUS_EQUAL(state->status, NT_STATUS_WAS_UNLOCKED))
1218 DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n",
1219 nt_errstr(state->status));
1220 return;
1223 return;
1225 not_granted:
1226 state->status = NT_STATUS_LOCK_NOT_GRANTED;
1229 NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, TDB_DATA key,
1230 enum g_lock_type type, struct timeval timeout,
1231 g_lock_lock_cb_fn_t cb_fn,
1232 void *cb_private)
1234 TALLOC_CTX *frame;
1235 struct tevent_context *ev;
1236 struct tevent_req *req;
1237 struct timeval end;
1238 NTSTATUS status;
1240 SMB_ASSERT(!ctx->busy);
1243 * We allow a cb_fn only for G_LOCK_WRITE for now.
1245 * It's all we currently need and it makes a few things
1246 * easier to implement.
1248 if (unlikely(cb_fn != NULL && type != G_LOCK_WRITE)) {
1249 return NT_STATUS_INVALID_PARAMETER_5;
1252 if ((type == G_LOCK_READ) || (type == G_LOCK_WRITE)) {
1254 * This is an abstraction violation: Normally we do
1255 * the sync wrappers around async functions with full
1256 * nested event contexts. However, this is used in
1257 * very hot code paths, so avoid the event context
1258 * creation for the good path where there's no lock
1259 * contention. My benchmark gave a factor of 2
1260 * improvement for lock/unlock.
1262 struct g_lock_lock_simple_state state = {
1263 .ctx = ctx,
1264 .me = messaging_server_id(ctx->msg),
1265 .type = type,
1266 .cb_fn = cb_fn,
1267 .cb_private = cb_private,
1269 status = dbwrap_do_locked(
1270 ctx->db, key, g_lock_lock_simple_fn, &state);
1271 if (!NT_STATUS_IS_OK(status)) {
1272 DBG_DEBUG("dbwrap_do_locked() failed: %s\n",
1273 nt_errstr(status));
1274 return status;
1277 DBG_DEBUG("status=%s, state.status=%s\n",
1278 nt_errstr(status),
1279 nt_errstr(state.status));
1281 if (NT_STATUS_IS_OK(state.status)) {
1282 if (ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) {
1283 const char *name = dbwrap_name(ctx->db);
1284 dbwrap_lock_order_lock(name, ctx->lock_order);
1286 return NT_STATUS_OK;
1288 if (NT_STATUS_EQUAL(state.status, NT_STATUS_WAS_UNLOCKED)) {
1289 /* without dbwrap_lock_order_lock() */
1290 return NT_STATUS_OK;
1292 if (!NT_STATUS_EQUAL(
1293 state.status, NT_STATUS_LOCK_NOT_GRANTED)) {
1294 return state.status;
1297 if (timeval_is_zero(&timeout)) {
1298 return NT_STATUS_LOCK_NOT_GRANTED;
1302 * Fall back to the full g_lock_trylock logic,
1303 * g_lock_lock_simple_fn() called above only covers
1304 * the uncontended path.
1308 frame = talloc_stackframe();
1309 status = NT_STATUS_NO_MEMORY;
1311 ev = samba_tevent_context_init(frame);
1312 if (ev == NULL) {
1313 goto fail;
1315 req = g_lock_lock_send(frame, ev, ctx, key, type, cb_fn, cb_private);
1316 if (req == NULL) {
1317 goto fail;
1319 end = timeval_current_ofs(timeout.tv_sec, timeout.tv_usec);
1320 if (!tevent_req_set_endtime(req, ev, end)) {
1321 goto fail;
1323 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1324 goto fail;
1326 status = g_lock_lock_recv(req);
1327 fail:
1328 TALLOC_FREE(frame);
1329 return status;
1332 struct g_lock_unlock_state {
1333 struct server_id self;
1334 NTSTATUS status;
1337 static void g_lock_unlock_fn(
1338 struct db_record *rec,
1339 TDB_DATA value,
1340 void *private_data)
1342 struct g_lock_unlock_state *state = private_data;
1343 struct server_id_buf tmp1, tmp2;
1344 struct g_lock lck;
1345 size_t i;
1346 bool ok, exclusive;
1348 ok = g_lock_parse(value.dptr, value.dsize, &lck);
1349 if (!ok) {
1350 DBG_DEBUG("g_lock_parse() failed\n");
1351 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1352 return;
1355 exclusive = server_id_equal(&state->self, &lck.exclusive);
1357 for (i=0; i<lck.num_shared; i++) {
1358 struct server_id shared;
1359 g_lock_get_shared(&lck, i, &shared);
1360 if (server_id_equal(&state->self, &shared)) {
1361 break;
1365 if (i < lck.num_shared) {
1366 if (exclusive) {
1367 DBG_DEBUG("%s both exclusive and shared (%zu)\n",
1368 server_id_str_buf(state->self, &tmp1),
1370 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1371 return;
1373 g_lock_del_shared(&lck, i);
1374 } else {
1375 if (!exclusive) {
1376 DBG_DEBUG("Lock not found, self=%s, lck.exclusive=%s, "
1377 "num_shared=%zu\n",
1378 server_id_str_buf(state->self, &tmp1),
1379 server_id_str_buf(lck.exclusive, &tmp2),
1380 lck.num_shared);
1381 state->status = NT_STATUS_NOT_FOUND;
1382 return;
1384 lck.exclusive = (struct server_id) { .pid = 0 };
1387 if ((lck.exclusive.pid == 0) &&
1388 (lck.num_shared == 0) &&
1389 (lck.datalen == 0)) {
1390 state->status = dbwrap_record_delete(rec);
1391 return;
1394 if (!exclusive && lck.exclusive.pid != 0) {
1396 * We only had a read lock and there's
1397 * someone waiting for an exclusive lock.
1399 * Don't alert the exclusive lock waiter
1400 * if there are still other read lock holders.
1402 g_lock_cleanup_shared(&lck);
1403 if (lck.num_shared != 0) {
1404 dbwrap_watched_watch_skip_alerting(rec);
1408 lck.unique_lock_epoch = generate_unique_u64(lck.unique_lock_epoch);
1410 state->status = g_lock_store(rec, &lck, NULL, NULL, 0);
1413 NTSTATUS g_lock_unlock(struct g_lock_ctx *ctx, TDB_DATA key)
1415 struct g_lock_unlock_state state = {
1416 .self = messaging_server_id(ctx->msg),
1418 NTSTATUS status;
1420 SMB_ASSERT(!ctx->busy);
1422 status = dbwrap_do_locked(ctx->db, key, g_lock_unlock_fn, &state);
1423 if (!NT_STATUS_IS_OK(status)) {
1424 DBG_WARNING("dbwrap_do_locked failed: %s\n",
1425 nt_errstr(status));
1426 return status;
1428 if (!NT_STATUS_IS_OK(state.status)) {
1429 DBG_WARNING("g_lock_unlock_fn failed: %s\n",
1430 nt_errstr(state.status));
1431 return state.status;
1434 if (ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) {
1435 const char *name = dbwrap_name(ctx->db);
1436 dbwrap_lock_order_unlock(name, ctx->lock_order);
1439 return NT_STATUS_OK;
1442 struct g_lock_writev_data_state {
1443 TDB_DATA key;
1444 struct server_id self;
1445 const TDB_DATA *dbufs;
1446 size_t num_dbufs;
1447 NTSTATUS status;
1450 static void g_lock_writev_data_fn(
1451 struct db_record *rec,
1452 TDB_DATA value,
1453 void *private_data)
1455 struct g_lock_writev_data_state *state = private_data;
1456 struct g_lock lck;
1457 bool exclusive;
1458 bool ok;
1461 * We're holding an exclusive write lock.
1463 * Now we're updating the content of the record.
1465 * We should not wakeup any other waiters, all they
1466 * would find is that we're still holding a lock they
1467 * are conflicting with.
1469 dbwrap_watched_watch_skip_alerting(rec);
1471 ok = g_lock_parse(value.dptr, value.dsize, &lck);
1472 if (!ok) {
1473 DBG_DEBUG("g_lock_parse for %s failed\n",
1474 tdb_data_dbg(state->key));
1475 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1476 return;
1479 exclusive = server_id_equal(&state->self, &lck.exclusive);
1482 * Make sure we're really exclusive. We are marked as
1483 * exclusive when we are waiting for an exclusive lock
1485 exclusive &= (lck.num_shared == 0);
1487 if (!exclusive) {
1488 struct server_id_buf buf1, buf2;
1489 DBG_DEBUG("Not locked by us: self=%s, lck.exclusive=%s, "
1490 "lck.num_shared=%zu\n",
1491 server_id_str_buf(state->self, &buf1),
1492 server_id_str_buf(lck.exclusive, &buf2),
1493 lck.num_shared);
1494 state->status = NT_STATUS_NOT_LOCKED;
1495 return;
1498 lck.unique_data_epoch = generate_unique_u64(lck.unique_data_epoch);
1499 lck.data = NULL;
1500 lck.datalen = 0;
1501 state->status = g_lock_store(
1502 rec, &lck, NULL, state->dbufs, state->num_dbufs);
1505 NTSTATUS g_lock_writev_data(
1506 struct g_lock_ctx *ctx,
1507 TDB_DATA key,
1508 const TDB_DATA *dbufs,
1509 size_t num_dbufs)
1511 struct g_lock_writev_data_state state = {
1512 .key = key,
1513 .self = messaging_server_id(ctx->msg),
1514 .dbufs = dbufs,
1515 .num_dbufs = num_dbufs,
1517 NTSTATUS status;
1519 SMB_ASSERT(!ctx->busy);
1521 status = dbwrap_do_locked(
1522 ctx->db, key, g_lock_writev_data_fn, &state);
1523 if (!NT_STATUS_IS_OK(status)) {
1524 DBG_WARNING("dbwrap_do_locked failed: %s\n",
1525 nt_errstr(status));
1526 return status;
1528 if (!NT_STATUS_IS_OK(state.status)) {
1529 DBG_WARNING("g_lock_writev_data_fn failed: %s\n",
1530 nt_errstr(state.status));
1531 return state.status;
1534 return NT_STATUS_OK;
1537 NTSTATUS g_lock_write_data(struct g_lock_ctx *ctx, TDB_DATA key,
1538 const uint8_t *buf, size_t buflen)
1540 TDB_DATA dbuf = {
1541 .dptr = discard_const_p(uint8_t, buf),
1542 .dsize = buflen,
1544 return g_lock_writev_data(ctx, key, &dbuf, 1);
1547 struct g_lock_locks_state {
1548 int (*fn)(TDB_DATA key, void *private_data);
1549 void *private_data;
1552 static int g_lock_locks_fn(struct db_record *rec, void *priv)
1554 TDB_DATA key;
1555 struct g_lock_locks_state *state = (struct g_lock_locks_state *)priv;
1557 key = dbwrap_record_get_key(rec);
1558 return state->fn(key, state->private_data);
1561 int g_lock_locks_read(struct g_lock_ctx *ctx,
1562 int (*fn)(TDB_DATA key, void *private_data),
1563 void *private_data)
1565 struct g_lock_locks_state state;
1566 NTSTATUS status;
1567 int count;
1569 SMB_ASSERT(!ctx->busy);
1571 state.fn = fn;
1572 state.private_data = private_data;
1574 status = dbwrap_traverse_read(ctx->db,
1575 g_lock_locks_fn,
1576 &state,
1577 &count);
1578 if (!NT_STATUS_IS_OK(status)) {
1579 return -1;
1581 return count;
1584 int g_lock_locks(struct g_lock_ctx *ctx,
1585 int (*fn)(TDB_DATA key, void *private_data),
1586 void *private_data)
1588 struct g_lock_locks_state state;
1589 NTSTATUS status;
1590 int count;
1592 SMB_ASSERT(!ctx->busy);
1594 state.fn = fn;
1595 state.private_data = private_data;
1597 status = dbwrap_traverse(ctx->db, g_lock_locks_fn, &state, &count);
1598 if (!NT_STATUS_IS_OK(status)) {
1599 return -1;
1601 return count;
1604 struct g_lock_dump_state {
1605 TALLOC_CTX *mem_ctx;
1606 TDB_DATA key;
1607 void (*fn)(struct server_id exclusive,
1608 size_t num_shared,
1609 const struct server_id *shared,
1610 const uint8_t *data,
1611 size_t datalen,
1612 void *private_data);
1613 void *private_data;
1614 NTSTATUS status;
1615 enum dbwrap_req_state req_state;
1618 static void g_lock_dump_fn(TDB_DATA key, TDB_DATA data,
1619 void *private_data)
1621 struct g_lock_dump_state *state = private_data;
1622 struct g_lock lck = (struct g_lock) { .exclusive.pid = 0 };
1623 struct server_id *shared = NULL;
1624 size_t i;
1625 bool ok;
1627 ok = g_lock_parse(data.dptr, data.dsize, &lck);
1628 if (!ok) {
1629 DBG_DEBUG("g_lock_parse failed for %s\n",
1630 tdb_data_dbg(state->key));
1631 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1632 return;
1635 if (lck.num_shared > 0) {
1636 shared = talloc_array(
1637 state->mem_ctx, struct server_id, lck.num_shared);
1638 if (shared == NULL) {
1639 DBG_DEBUG("talloc failed\n");
1640 state->status = NT_STATUS_NO_MEMORY;
1641 return;
1645 for (i=0; i<lck.num_shared; i++) {
1646 g_lock_get_shared(&lck, i, &shared[i]);
1649 state->fn(lck.exclusive,
1650 lck.num_shared,
1651 shared,
1652 lck.data,
1653 lck.datalen,
1654 state->private_data);
1656 TALLOC_FREE(shared);
1658 state->status = NT_STATUS_OK;
1661 NTSTATUS g_lock_dump(struct g_lock_ctx *ctx, TDB_DATA key,
1662 void (*fn)(struct server_id exclusive,
1663 size_t num_shared,
1664 const struct server_id *shared,
1665 const uint8_t *data,
1666 size_t datalen,
1667 void *private_data),
1668 void *private_data)
1670 struct g_lock_dump_state state = {
1671 .mem_ctx = ctx, .key = key,
1672 .fn = fn, .private_data = private_data
1674 NTSTATUS status;
1676 SMB_ASSERT(!ctx->busy);
1678 status = dbwrap_parse_record(ctx->db, key, g_lock_dump_fn, &state);
1679 if (!NT_STATUS_IS_OK(status)) {
1680 DBG_DEBUG("dbwrap_parse_record returned %s\n",
1681 nt_errstr(status));
1682 return status;
1684 if (!NT_STATUS_IS_OK(state.status)) {
1685 DBG_DEBUG("g_lock_dump_fn returned %s\n",
1686 nt_errstr(state.status));
1687 return state.status;
1689 return NT_STATUS_OK;
1692 static void g_lock_dump_done(struct tevent_req *subreq);
1694 struct tevent_req *g_lock_dump_send(
1695 TALLOC_CTX *mem_ctx,
1696 struct tevent_context *ev,
1697 struct g_lock_ctx *ctx,
1698 TDB_DATA key,
1699 void (*fn)(struct server_id exclusive,
1700 size_t num_shared,
1701 const struct server_id *shared,
1702 const uint8_t *data,
1703 size_t datalen,
1704 void *private_data),
1705 void *private_data)
1707 struct tevent_req *req = NULL, *subreq = NULL;
1708 struct g_lock_dump_state *state = NULL;
1710 SMB_ASSERT(!ctx->busy);
1712 req = tevent_req_create(mem_ctx, &state, struct g_lock_dump_state);
1713 if (req == NULL) {
1714 return NULL;
1716 state->mem_ctx = state;
1717 state->key = key;
1718 state->fn = fn;
1719 state->private_data = private_data;
1721 SMB_ASSERT(!ctx->busy);
1723 subreq = dbwrap_parse_record_send(
1724 state,
1726 ctx->db,
1727 key,
1728 g_lock_dump_fn,
1729 state,
1730 &state->req_state);
1731 if (tevent_req_nomem(subreq, req)) {
1732 return tevent_req_post(req, ev);
1734 tevent_req_set_callback(subreq, g_lock_dump_done, req);
1735 return req;
1738 static void g_lock_dump_done(struct tevent_req *subreq)
1740 struct tevent_req *req = tevent_req_callback_data(
1741 subreq, struct tevent_req);
1742 struct g_lock_dump_state *state = tevent_req_data(
1743 req, struct g_lock_dump_state);
1744 NTSTATUS status;
1746 status = dbwrap_parse_record_recv(subreq);
1747 TALLOC_FREE(subreq);
1748 if (tevent_req_nterror(req, status) ||
1749 tevent_req_nterror(req, state->status)) {
1750 return;
1752 tevent_req_done(req);
1755 NTSTATUS g_lock_dump_recv(struct tevent_req *req)
1757 return tevent_req_simple_recv_ntstatus(req);
1760 int g_lock_seqnum(struct g_lock_ctx *ctx)
1762 return dbwrap_get_seqnum(ctx->db);
1765 struct g_lock_watch_data_state {
1766 struct tevent_context *ev;
1767 struct g_lock_ctx *ctx;
1768 TDB_DATA key;
1769 struct server_id blocker;
1770 bool blockerdead;
1771 uint64_t unique_lock_epoch;
1772 uint64_t unique_data_epoch;
1773 uint64_t watch_instance;
1774 NTSTATUS status;
1777 static void g_lock_watch_data_done(struct tevent_req *subreq);
1779 static void g_lock_watch_data_send_fn(
1780 struct db_record *rec,
1781 TDB_DATA value,
1782 void *private_data)
1784 struct tevent_req *req = talloc_get_type_abort(
1785 private_data, struct tevent_req);
1786 struct g_lock_watch_data_state *state = tevent_req_data(
1787 req, struct g_lock_watch_data_state);
1788 struct tevent_req *subreq = NULL;
1789 struct g_lock lck;
1790 bool ok;
1792 ok = g_lock_parse(value.dptr, value.dsize, &lck);
1793 if (!ok) {
1794 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1795 return;
1797 state->unique_lock_epoch = lck.unique_lock_epoch;
1798 state->unique_data_epoch = lck.unique_data_epoch;
1800 DBG_DEBUG("state->unique_data_epoch=%"PRIu64"\n", state->unique_data_epoch);
1802 subreq = dbwrap_watched_watch_send(
1803 state, state->ev, rec, 0, state->blocker);
1804 if (subreq == NULL) {
1805 state->status = NT_STATUS_NO_MEMORY;
1806 return;
1808 tevent_req_set_callback(subreq, g_lock_watch_data_done, req);
1810 state->status = NT_STATUS_EVENT_PENDING;
1813 struct tevent_req *g_lock_watch_data_send(
1814 TALLOC_CTX *mem_ctx,
1815 struct tevent_context *ev,
1816 struct g_lock_ctx *ctx,
1817 TDB_DATA key,
1818 struct server_id blocker)
1820 struct tevent_req *req = NULL;
1821 struct g_lock_watch_data_state *state = NULL;
1822 NTSTATUS status;
1824 SMB_ASSERT(!ctx->busy);
1826 req = tevent_req_create(
1827 mem_ctx, &state, struct g_lock_watch_data_state);
1828 if (req == NULL) {
1829 return NULL;
1831 state->ev = ev;
1832 state->ctx = ctx;
1833 state->blocker = blocker;
1835 state->key = tdb_data_talloc_copy(state, key);
1836 if (tevent_req_nomem(state->key.dptr, req)) {
1837 return tevent_req_post(req, ev);
1840 status = dbwrap_do_locked(
1841 ctx->db, key, g_lock_watch_data_send_fn, req);
1842 if (tevent_req_nterror(req, status)) {
1843 DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status));
1844 return tevent_req_post(req, ev);
1847 if (NT_STATUS_EQUAL(state->status, NT_STATUS_EVENT_PENDING)) {
1848 return req;
1850 if (tevent_req_nterror(req, state->status)) {
1851 return tevent_req_post(req, ev);
1853 tevent_req_done(req);
1854 return tevent_req_post(req, ev);
1857 static void g_lock_watch_data_done_fn(
1858 struct db_record *rec,
1859 TDB_DATA value,
1860 void *private_data)
1862 struct tevent_req *req = talloc_get_type_abort(
1863 private_data, struct tevent_req);
1864 struct g_lock_watch_data_state *state = tevent_req_data(
1865 req, struct g_lock_watch_data_state);
1866 struct tevent_req *subreq = NULL;
1867 struct g_lock lck;
1868 bool ok;
1870 ok = g_lock_parse(value.dptr, value.dsize, &lck);
1871 if (!ok) {
1872 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
1873 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
1874 return;
1877 if (lck.unique_data_epoch != state->unique_data_epoch) {
1878 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
1879 DBG_DEBUG("lck.unique_data_epoch=%"PRIu64", "
1880 "state->unique_data_epoch=%"PRIu64"\n",
1881 lck.unique_data_epoch,
1882 state->unique_data_epoch);
1883 state->status = NT_STATUS_OK;
1884 return;
1888 * The lock epoch changed, so we better
1889 * remove ourself from the waiter list
1890 * (most likely the first position)
1891 * and re-add us at the end of the list.
1893 * This gives other lock waiters a change
1894 * to make progress.
1896 * Otherwise we'll keep our waiter instance alive,
1897 * keep waiting (most likely at first position).
1899 if (lck.unique_lock_epoch != state->unique_lock_epoch) {
1900 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
1901 state->watch_instance = dbwrap_watched_watch_add_instance(rec);
1902 state->unique_lock_epoch = lck.unique_lock_epoch;
1905 subreq = dbwrap_watched_watch_send(
1906 state, state->ev, rec, state->watch_instance, state->blocker);
1907 if (subreq == NULL) {
1908 dbwrap_watched_watch_remove_instance(rec, state->watch_instance);
1909 state->status = NT_STATUS_NO_MEMORY;
1910 return;
1912 tevent_req_set_callback(subreq, g_lock_watch_data_done, req);
1914 state->status = NT_STATUS_EVENT_PENDING;
1917 static void g_lock_watch_data_done(struct tevent_req *subreq)
1919 struct tevent_req *req = tevent_req_callback_data(
1920 subreq, struct tevent_req);
1921 struct g_lock_watch_data_state *state = tevent_req_data(
1922 req, struct g_lock_watch_data_state);
1923 NTSTATUS status;
1924 uint64_t instance = 0;
1926 status = dbwrap_watched_watch_recv(
1927 subreq, &instance, &state->blockerdead, &state->blocker);
1928 TALLOC_FREE(subreq);
1929 if (tevent_req_nterror(req, status)) {
1930 DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
1931 nt_errstr(status));
1932 return;
1935 state->watch_instance = instance;
1937 status = dbwrap_do_locked(
1938 state->ctx->db, state->key, g_lock_watch_data_done_fn, req);
1939 if (tevent_req_nterror(req, status)) {
1940 DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status));
1941 return;
1943 if (NT_STATUS_EQUAL(state->status, NT_STATUS_EVENT_PENDING)) {
1944 return;
1946 if (tevent_req_nterror(req, state->status)) {
1947 return;
1949 tevent_req_done(req);
1952 NTSTATUS g_lock_watch_data_recv(
1953 struct tevent_req *req,
1954 bool *blockerdead,
1955 struct server_id *blocker)
1957 struct g_lock_watch_data_state *state = tevent_req_data(
1958 req, struct g_lock_watch_data_state);
1959 NTSTATUS status;
1961 if (tevent_req_is_nterror(req, &status)) {
1962 return status;
1964 if (blockerdead != NULL) {
1965 *blockerdead = state->blockerdead;
1967 if (blocker != NULL) {
1968 *blocker = state->blocker;
1971 return NT_STATUS_OK;
1974 static void g_lock_wake_watchers_fn(
1975 struct db_record *rec,
1976 TDB_DATA value,
1977 void *private_data)
1979 struct g_lock lck = { .exclusive.pid = 0 };
1980 NTSTATUS status;
1981 bool ok;
1983 ok = g_lock_parse(value.dptr, value.dsize, &lck);
1984 if (!ok) {
1985 DBG_WARNING("g_lock_parse failed\n");
1986 return;
1989 lck.unique_data_epoch = generate_unique_u64(lck.unique_data_epoch);
1991 status = g_lock_store(rec, &lck, NULL, NULL, 0);
1992 if (!NT_STATUS_IS_OK(status)) {
1993 DBG_WARNING("g_lock_store failed: %s\n", nt_errstr(status));
1994 return;
1998 void g_lock_wake_watchers(struct g_lock_ctx *ctx, TDB_DATA key)
2000 NTSTATUS status;
2002 SMB_ASSERT(!ctx->busy);
2004 status = dbwrap_do_locked(ctx->db, key, g_lock_wake_watchers_fn, NULL);
2005 if (!NT_STATUS_IS_OK(status)) {
2006 DBG_DEBUG("dbwrap_do_locked returned %s\n",
2007 nt_errstr(status));