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/>.
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"
32 #include "../lib/util/tevent_ntstatus.h"
37 struct db_context
*db
;
38 struct messaging_context
*msg
;
39 enum dbwrap_lock_order lock_order
;
44 struct server_id exclusive
;
47 uint64_t unique_lock_epoch
;
48 uint64_t unique_data_epoch
;
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
;
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 */
66 .unique_lock_epoch
= generate_unique_u64(0),
67 .unique_data_epoch
= generate_unique_u64(0),
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",
96 shared_len
= num_shared
* SERVER_ID_BUF_LENGTH
;
98 *lck
= (struct g_lock
) {
99 .exclusive
= exclusive
,
100 .num_shared
= num_shared
,
102 .unique_lock_epoch
= unique_lock_epoch
,
103 .unique_data_epoch
= unique_data_epoch
,
104 .datalen
= buflen
-shared_len
,
105 .data
= buf
+shared_len
,
111 static void g_lock_get_shared(const struct g_lock
*lck
,
113 struct server_id
*shared
)
115 if (i
>= lck
->num_shared
) {
118 server_id_get(shared
, lck
->shared
+ i
*SERVER_ID_BUF_LENGTH
);
121 static void g_lock_del_shared(struct g_lock
*lck
, size_t i
)
123 if (i
>= lck
->num_shared
) {
126 lck
->num_shared
-= 1;
127 if (i
< lck
->num_shared
) {
128 memcpy(lck
->shared
+ i
*SERVER_ID_BUF_LENGTH
,
129 lck
->shared
+ lck
->num_shared
*SERVER_ID_BUF_LENGTH
,
130 SERVER_ID_BUF_LENGTH
);
134 static NTSTATUS
g_lock_store(
135 struct db_record
*rec
,
137 struct server_id
*new_shared
,
138 const TDB_DATA
*new_dbufs
,
139 size_t num_new_dbufs
)
141 uint8_t exclusive
[SERVER_ID_BUF_LENGTH
];
142 uint8_t seqnum_buf
[sizeof(uint64_t)*2];
143 uint8_t sizebuf
[sizeof(uint32_t)];
144 uint8_t new_shared_buf
[SERVER_ID_BUF_LENGTH
];
146 struct TDB_DATA dbufs
[6 + num_new_dbufs
];
148 dbufs
[0] = (TDB_DATA
) {
149 .dptr
= exclusive
, .dsize
= sizeof(exclusive
),
151 dbufs
[1] = (TDB_DATA
) {
152 .dptr
= seqnum_buf
, .dsize
= sizeof(seqnum_buf
),
154 dbufs
[2] = (TDB_DATA
) {
155 .dptr
= sizebuf
, .dsize
= sizeof(sizebuf
),
157 dbufs
[3] = (TDB_DATA
) {
159 .dsize
= lck
->num_shared
* SERVER_ID_BUF_LENGTH
,
161 dbufs
[4] = (TDB_DATA
) { 0 };
162 dbufs
[5] = (TDB_DATA
) {
163 .dptr
= lck
->data
, .dsize
= lck
->datalen
,
166 if (num_new_dbufs
!= 0) {
169 num_new_dbufs
* sizeof(TDB_DATA
));
172 server_id_put(exclusive
, lck
->exclusive
);
173 SBVAL(seqnum_buf
, 0, lck
->unique_lock_epoch
);
174 SBVAL(seqnum_buf
, 8, lck
->unique_data_epoch
);
176 if (new_shared
!= NULL
) {
177 if (lck
->num_shared
>= UINT32_MAX
) {
178 return NT_STATUS_BUFFER_OVERFLOW
;
181 server_id_put(new_shared_buf
, *new_shared
);
183 dbufs
[4] = (TDB_DATA
) {
184 .dptr
= new_shared_buf
,
185 .dsize
= sizeof(new_shared_buf
),
188 lck
->num_shared
+= 1;
191 SIVAL(sizebuf
, 0, lck
->num_shared
);
193 return dbwrap_record_storev(rec
, dbufs
, ARRAY_SIZE(dbufs
), 0);
196 struct g_lock_ctx
*g_lock_ctx_init_backend(
198 struct messaging_context
*msg
,
199 struct db_context
**backend
)
201 struct g_lock_ctx
*result
;
203 result
= talloc_zero(mem_ctx
, struct g_lock_ctx
);
204 if (result
== NULL
) {
208 result
->lock_order
= DBWRAP_LOCK_ORDER_NONE
;
210 result
->db
= db_open_watched(result
, backend
, msg
);
211 if (result
->db
== NULL
) {
212 DBG_WARNING("db_open_watched failed\n");
219 void g_lock_set_lock_order(struct g_lock_ctx
*ctx
,
220 enum dbwrap_lock_order lock_order
)
222 ctx
->lock_order
= lock_order
;
225 struct g_lock_ctx
*g_lock_ctx_init(TALLOC_CTX
*mem_ctx
,
226 struct messaging_context
*msg
)
228 char *db_path
= NULL
;
229 struct db_context
*backend
= NULL
;
230 struct g_lock_ctx
*ctx
= NULL
;
232 db_path
= lock_path(mem_ctx
, "g_lock.tdb");
233 if (db_path
== NULL
) {
241 TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
|TDB_VOLATILE
,
246 TALLOC_FREE(db_path
);
247 if (backend
== NULL
) {
248 DBG_WARNING("Could not open g_lock.tdb\n");
252 ctx
= g_lock_ctx_init_backend(mem_ctx
, msg
, &backend
);
256 static void g_lock_cleanup_dead(
258 struct server_id
*dead_blocker
)
261 struct server_id_buf tmp
;
263 if (dead_blocker
== NULL
) {
267 exclusive_died
= server_id_equal(dead_blocker
, &lck
->exclusive
);
269 if (exclusive_died
) {
270 DBG_DEBUG("Exclusive holder %s died\n",
271 server_id_str_buf(lck
->exclusive
, &tmp
));
272 lck
->exclusive
.pid
= 0;
275 if (lck
->num_shared
!= 0) {
277 struct server_id shared
;
279 g_lock_get_shared(lck
, 0, &shared
);
280 shared_died
= server_id_equal(dead_blocker
, &shared
);
283 DBG_DEBUG("Shared holder %s died\n",
284 server_id_str_buf(shared
, &tmp
));
285 g_lock_del_shared(lck
, 0);
290 static ssize_t
g_lock_find_shared(
292 const struct server_id
*self
)
296 for (i
=0; i
<lck
->num_shared
; i
++) {
297 struct server_id shared
;
300 g_lock_get_shared(lck
, i
, &shared
);
302 same
= server_id_equal(self
, &shared
);
311 static void g_lock_cleanup_shared(struct g_lock
*lck
)
314 struct server_id check
;
317 if (lck
->num_shared
== 0) {
322 * Read locks can stay around forever if the process dies. Do
323 * a heuristic check for process existence: Check one random
324 * process for existence. Hopefully this will keep runaway
325 * read locks under control.
327 i
= generate_random() % lck
->num_shared
;
328 g_lock_get_shared(lck
, i
, &check
);
330 exists
= serverid_exists(&check
);
332 struct server_id_buf tmp
;
333 DBG_DEBUG("Shared locker %s died -- removing\n",
334 server_id_str_buf(check
, &tmp
));
335 g_lock_del_shared(lck
, i
);
339 struct g_lock_lock_cb_state
{
340 struct g_lock_ctx
*ctx
;
341 struct db_record
*rec
;
343 struct server_id
*new_shared
;
344 g_lock_lock_cb_fn_t cb_fn
;
346 TALLOC_CTX
*update_mem_ctx
;
347 TDB_DATA updated_data
;
353 NTSTATUS
g_lock_lock_cb_dump(struct g_lock_lock_cb_state
*cb_state
,
354 void (*fn
)(struct server_id exclusive
,
356 const struct server_id
*shared
,
362 struct g_lock
*lck
= cb_state
->lck
;
364 /* We allow a cb_fn only for G_LOCK_WRITE for now... */
365 SMB_ASSERT(lck
->num_shared
== 0);
377 NTSTATUS
g_lock_lock_cb_writev(struct g_lock_lock_cb_state
*cb_state
,
378 const TDB_DATA
*dbufs
,
383 status
= dbwrap_merge_dbufs(&cb_state
->updated_data
,
384 cb_state
->update_mem_ctx
,
386 if (!NT_STATUS_IS_OK(status
)) {
390 cb_state
->modified
= true;
391 cb_state
->lck
->data
= cb_state
->updated_data
.dptr
;
392 cb_state
->lck
->datalen
= cb_state
->updated_data
.dsize
;
397 void g_lock_lock_cb_unlock(struct g_lock_lock_cb_state
*cb_state
)
399 cb_state
->unlock
= true;
402 struct g_lock_lock_cb_watch_data_state
{
403 struct tevent_context
*ev
;
404 struct g_lock_ctx
*ctx
;
406 struct server_id blocker
;
408 uint64_t unique_lock_epoch
;
409 uint64_t unique_data_epoch
;
410 uint64_t watch_instance
;
414 static void g_lock_lock_cb_watch_data_done(struct tevent_req
*subreq
);
416 struct tevent_req
*g_lock_lock_cb_watch_data_send(
418 struct tevent_context
*ev
,
419 struct g_lock_lock_cb_state
*cb_state
,
420 struct server_id blocker
)
422 struct tevent_req
*req
= NULL
;
423 struct g_lock_lock_cb_watch_data_state
*state
= NULL
;
424 struct tevent_req
*subreq
= NULL
;
425 TDB_DATA key
= dbwrap_record_get_key(cb_state
->rec
);
427 req
= tevent_req_create(
428 mem_ctx
, &state
, struct g_lock_lock_cb_watch_data_state
);
433 state
->ctx
= cb_state
->ctx
;
434 state
->blocker
= blocker
;
436 state
->key
= tdb_data_talloc_copy(state
, key
);
437 if (tevent_req_nomem(state
->key
.dptr
, req
)) {
438 return tevent_req_post(req
, ev
);
441 state
->unique_lock_epoch
= cb_state
->lck
->unique_lock_epoch
;
442 state
->unique_data_epoch
= cb_state
->lck
->unique_data_epoch
;
444 DBG_DEBUG("state->unique_data_epoch=%"PRIu64
"\n", state
->unique_data_epoch
);
446 subreq
= dbwrap_watched_watch_send(
447 state
, state
->ev
, cb_state
->rec
, 0, state
->blocker
);
448 if (tevent_req_nomem(subreq
, req
)) {
449 return tevent_req_post(req
, ev
);
451 tevent_req_set_callback(subreq
, g_lock_lock_cb_watch_data_done
, req
);
456 static void g_lock_lock_cb_watch_data_done_fn(
457 struct db_record
*rec
,
461 struct tevent_req
*req
= talloc_get_type_abort(
462 private_data
, struct tevent_req
);
463 struct g_lock_lock_cb_watch_data_state
*state
= tevent_req_data(
464 req
, struct g_lock_lock_cb_watch_data_state
);
465 struct tevent_req
*subreq
= NULL
;
469 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
471 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
472 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
476 if (lck
.unique_data_epoch
!= state
->unique_data_epoch
) {
477 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
478 DBG_DEBUG("lck.unique_data_epoch=%"PRIu64
", "
479 "state->unique_data_epoch=%"PRIu64
"\n",
480 lck
.unique_data_epoch
,
481 state
->unique_data_epoch
);
482 state
->status
= NT_STATUS_OK
;
487 * The lock epoch changed, so we better
488 * remove ourself from the waiter list
489 * (most likely the first position)
490 * and re-add us at the end of the list.
492 * This gives other lock waiters a change
495 * Otherwise we'll keep our waiter instance alive,
496 * keep waiting (most likely at first position).
498 if (lck
.unique_lock_epoch
!= state
->unique_lock_epoch
) {
499 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
500 state
->watch_instance
= dbwrap_watched_watch_add_instance(rec
);
501 state
->unique_lock_epoch
= lck
.unique_lock_epoch
;
504 subreq
= dbwrap_watched_watch_send(
505 state
, state
->ev
, rec
, state
->watch_instance
, state
->blocker
);
506 if (subreq
== NULL
) {
507 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
508 state
->status
= NT_STATUS_NO_MEMORY
;
511 tevent_req_set_callback(subreq
, g_lock_lock_cb_watch_data_done
, req
);
513 state
->status
= NT_STATUS_EVENT_PENDING
;
516 static void g_lock_lock_cb_watch_data_done(struct tevent_req
*subreq
)
518 struct tevent_req
*req
= tevent_req_callback_data(
519 subreq
, struct tevent_req
);
520 struct g_lock_lock_cb_watch_data_state
*state
= tevent_req_data(
521 req
, struct g_lock_lock_cb_watch_data_state
);
523 uint64_t instance
= 0;
525 status
= dbwrap_watched_watch_recv(
526 subreq
, &instance
, &state
->blockerdead
, &state
->blocker
);
528 if (tevent_req_nterror(req
, status
)) {
529 DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
534 state
->watch_instance
= instance
;
536 status
= dbwrap_do_locked(
537 state
->ctx
->db
, state
->key
, g_lock_lock_cb_watch_data_done_fn
, req
);
538 if (tevent_req_nterror(req
, status
)) {
539 DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status
));
542 if (NT_STATUS_EQUAL(state
->status
, NT_STATUS_EVENT_PENDING
)) {
545 if (tevent_req_nterror(req
, state
->status
)) {
548 tevent_req_done(req
);
551 NTSTATUS
g_lock_lock_cb_watch_data_recv(
552 struct tevent_req
*req
,
554 struct server_id
*blocker
)
556 struct g_lock_lock_cb_watch_data_state
*state
= tevent_req_data(
557 req
, struct g_lock_lock_cb_watch_data_state
);
560 if (tevent_req_is_nterror(req
, &status
)) {
563 if (blockerdead
!= NULL
) {
564 *blockerdead
= state
->blockerdead
;
566 if (blocker
!= NULL
) {
567 *blocker
= state
->blocker
;
573 void g_lock_lock_cb_wake_watchers(struct g_lock_lock_cb_state
*cb_state
)
575 struct g_lock
*lck
= cb_state
->lck
;
577 lck
->unique_data_epoch
= generate_unique_u64(lck
->unique_data_epoch
);
578 cb_state
->modified
= true;
581 static NTSTATUS
g_lock_lock_cb_run_and_store(struct g_lock_lock_cb_state
*cb_state
)
583 struct g_lock
*lck
= cb_state
->lck
;
584 NTSTATUS success_status
= NT_STATUS_OK
;
587 if (cb_state
->cb_fn
!= NULL
) {
589 SMB_ASSERT(lck
->num_shared
== 0);
590 SMB_ASSERT(cb_state
->new_shared
== NULL
);
592 if (cb_state
->ctx
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
593 const char *name
= dbwrap_name(cb_state
->ctx
->db
);
594 dbwrap_lock_order_lock(name
, cb_state
->ctx
->lock_order
);
597 cb_state
->ctx
->busy
= true;
598 cb_state
->cb_fn(cb_state
, cb_state
->cb_private
);
599 cb_state
->ctx
->busy
= false;
601 if (cb_state
->ctx
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
602 const char *name
= dbwrap_name(cb_state
->ctx
->db
);
603 dbwrap_lock_order_unlock(name
, cb_state
->ctx
->lock_order
);
607 if (cb_state
->unlock
) {
609 * Unlocked should wake up watchers.
611 * We no longer need the lock, so
612 * force a wakeup of the next watchers,
613 * even if we don't do any update.
615 dbwrap_watched_watch_reset_alerting(cb_state
->rec
);
616 dbwrap_watched_watch_force_alerting(cb_state
->rec
);
617 if (!cb_state
->modified
) {
619 * The record was not changed at
620 * all, so we can also avoid
621 * storing the lck.unique_lock_epoch
624 return NT_STATUS_WAS_UNLOCKED
;
626 lck
->exclusive
= (struct server_id
) { .pid
= 0 };
627 cb_state
->new_shared
= NULL
;
629 if (lck
->datalen
== 0) {
630 if (!cb_state
->existed
) {
631 return NT_STATUS_WAS_UNLOCKED
;
634 status
= dbwrap_record_delete(cb_state
->rec
);
635 if (!NT_STATUS_IS_OK(status
)) {
636 DBG_WARNING("dbwrap_record_delete() failed: %s\n",
640 return NT_STATUS_WAS_UNLOCKED
;
643 success_status
= NT_STATUS_WAS_UNLOCKED
;
646 status
= g_lock_store(cb_state
->rec
,
648 cb_state
->new_shared
,
650 if (!NT_STATUS_IS_OK(status
)) {
651 DBG_WARNING("g_lock_store() failed: %s\n",
656 return success_status
;
659 struct g_lock_lock_state
{
660 struct tevent_context
*ev
;
661 struct g_lock_ctx
*ctx
;
663 enum g_lock_type type
;
665 g_lock_lock_cb_fn_t cb_fn
;
669 struct g_lock_lock_fn_state
{
670 struct g_lock_lock_state
*req_state
;
671 struct server_id
*dead_blocker
;
673 struct tevent_req
*watch_req
;
674 uint64_t watch_instance
;
678 static int g_lock_lock_state_destructor(struct g_lock_lock_state
*s
);
680 static NTSTATUS
g_lock_trylock(
681 struct db_record
*rec
,
682 struct g_lock_lock_fn_state
*state
,
684 struct server_id
*blocker
)
686 struct g_lock_lock_state
*req_state
= state
->req_state
;
687 struct server_id self
= messaging_server_id(req_state
->ctx
->msg
);
688 enum g_lock_type type
= req_state
->type
;
689 bool retry
= req_state
->retry
;
690 struct g_lock lck
= { .exclusive
.pid
= 0 };
691 struct g_lock_lock_cb_state cb_state
= {
692 .ctx
= req_state
->ctx
,
695 .cb_fn
= req_state
->cb_fn
,
696 .cb_private
= req_state
->cb_private
,
697 .existed
= data
.dsize
!= 0,
698 .update_mem_ctx
= talloc_tos(),
700 struct server_id_buf tmp
;
704 ok
= g_lock_parse(data
.dptr
, data
.dsize
, &lck
);
706 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
707 DBG_DEBUG("g_lock_parse failed\n");
708 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
711 g_lock_cleanup_dead(&lck
, state
->dead_blocker
);
713 lck
.unique_lock_epoch
= generate_unique_u64(lck
.unique_lock_epoch
);
715 if (lck
.exclusive
.pid
!= 0) {
716 bool self_exclusive
= server_id_equal(&self
, &lck
.exclusive
);
718 if (!self_exclusive
) {
719 bool exists
= serverid_exists(&lck
.exclusive
);
721 lck
.exclusive
= (struct server_id
) { .pid
=0 };
725 DBG_DEBUG("%s has an exclusive lock\n",
726 server_id_str_buf(lck
.exclusive
, &tmp
));
728 if (type
== G_LOCK_DOWNGRADE
) {
729 struct server_id_buf tmp2
;
731 dbwrap_watched_watch_remove_instance(rec
,
732 state
->watch_instance
);
734 DBG_DEBUG("%s: Trying to downgrade %s\n",
735 server_id_str_buf(self
, &tmp
),
737 lck
.exclusive
, &tmp2
));
738 return NT_STATUS_NOT_LOCKED
;
741 if (type
== G_LOCK_UPGRADE
) {
744 dbwrap_watched_watch_remove_instance(rec
,
745 state
->watch_instance
);
747 shared_idx
= g_lock_find_shared(&lck
, &self
);
749 if (shared_idx
== -1) {
750 DBG_DEBUG("Trying to upgrade %s "
752 "existing shared lock\n",
755 return NT_STATUS_NOT_LOCKED
;
759 * We're trying to upgrade, and the
760 * exclusive lock is taken by someone
761 * else. This means that someone else
762 * is waiting for us to give up our
763 * shared lock. If we now also wait
764 * for someone to give their shared
765 * lock, we will deadlock.
768 DBG_DEBUG("Trying to upgrade %s while "
769 "someone else is also "
770 "trying to upgrade\n",
771 server_id_str_buf(self
, &tmp
));
772 return NT_STATUS_POSSIBLE_DEADLOCK
;
775 DBG_DEBUG("Waiting for lck.exclusive=%s\n",
776 server_id_str_buf(lck
.exclusive
, &tmp
));
779 * We will return NT_STATUS_LOCK_NOT_GRANTED
780 * and need to monitor the record.
782 * If we don't have a watcher instance yet,
785 if (state
->watch_instance
== 0) {
786 state
->watch_instance
=
787 dbwrap_watched_watch_add_instance(rec
);
790 *blocker
= lck
.exclusive
;
791 return NT_STATUS_LOCK_NOT_GRANTED
;
794 if (type
== G_LOCK_DOWNGRADE
) {
795 DBG_DEBUG("Downgrading %s from WRITE to READ\n",
796 server_id_str_buf(self
, &tmp
));
798 lck
.exclusive
= (struct server_id
) { .pid
= 0 };
803 dbwrap_watched_watch_remove_instance(rec
,
804 state
->watch_instance
);
806 DBG_DEBUG("%s already locked by self\n",
807 server_id_str_buf(self
, &tmp
));
808 return NT_STATUS_WAS_LOCKED
;
811 g_lock_cleanup_shared(&lck
);
813 if (lck
.num_shared
!= 0) {
814 g_lock_get_shared(&lck
, 0, blocker
);
816 DBG_DEBUG("Continue waiting for shared lock %s\n",
817 server_id_str_buf(*blocker
, &tmp
));
820 * We will return NT_STATUS_LOCK_NOT_GRANTED
821 * and need to monitor the record.
823 * If we don't have a watcher instance yet,
826 if (state
->watch_instance
== 0) {
827 state
->watch_instance
=
828 dbwrap_watched_watch_add_instance(rec
);
831 return NT_STATUS_LOCK_NOT_GRANTED
;
835 * Retry after a conflicting lock was released..
836 * All pending readers are gone so we got the lock...
843 if (type
== G_LOCK_UPGRADE
) {
844 ssize_t shared_idx
= g_lock_find_shared(&lck
, &self
);
846 if (shared_idx
== -1) {
847 dbwrap_watched_watch_remove_instance(rec
,
848 state
->watch_instance
);
850 DBG_DEBUG("Trying to upgrade %s without "
851 "existing shared lock\n",
852 server_id_str_buf(self
, &tmp
));
853 return NT_STATUS_NOT_LOCKED
;
856 g_lock_del_shared(&lck
, shared_idx
);
860 if (type
== G_LOCK_WRITE
) {
861 ssize_t shared_idx
= g_lock_find_shared(&lck
, &self
);
863 if (shared_idx
!= -1) {
864 dbwrap_watched_watch_remove_instance(rec
,
865 state
->watch_instance
);
866 DBG_DEBUG("Trying to writelock existing shared %s\n",
867 server_id_str_buf(self
, &tmp
));
868 return NT_STATUS_WAS_LOCKED
;
871 lck
.exclusive
= self
;
873 g_lock_cleanup_shared(&lck
);
875 if (lck
.num_shared
== 0) {
877 * If we store ourself as exclusive writer,
878 * without any pending readers ...
883 if (state
->watch_instance
== 0) {
885 * Here we have lck.num_shared != 0.
887 * We will return NT_STATUS_LOCK_NOT_GRANTED
890 * And don't have a watcher instance yet!
892 * We add it here before g_lock_store()
893 * in order to trigger just one
894 * low level dbwrap_do_locked() call.
896 state
->watch_instance
=
897 dbwrap_watched_watch_add_instance(rec
);
900 status
= g_lock_store(rec
, &lck
, NULL
, NULL
, 0);
901 if (!NT_STATUS_IS_OK(status
)) {
902 DBG_DEBUG("g_lock_store() failed: %s\n",
907 talloc_set_destructor(
908 req_state
, g_lock_lock_state_destructor
);
910 g_lock_get_shared(&lck
, 0, blocker
);
912 DBG_DEBUG("Waiting for %zu shared locks, "
913 "picking blocker %s\n",
915 server_id_str_buf(*blocker
, &tmp
));
917 return NT_STATUS_LOCK_NOT_GRANTED
;
922 g_lock_cleanup_shared(&lck
);
923 cb_state
.new_shared
= &self
;
928 * We got the lock we asked for, so we no
929 * longer need to monitor the record.
931 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
933 status
= g_lock_lock_cb_run_and_store(&cb_state
);
934 if (!NT_STATUS_IS_OK(status
) &&
935 !NT_STATUS_EQUAL(status
, NT_STATUS_WAS_UNLOCKED
))
937 DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n",
942 talloc_set_destructor(req_state
, NULL
);
946 static void g_lock_lock_fn(
947 struct db_record
*rec
,
951 struct g_lock_lock_fn_state
*state
= private_data
;
952 struct server_id blocker
= {0};
955 * We're trying to get a lock and if we are
956 * successful in doing that, we should not
957 * wakeup any other waiters, all they would
958 * find is that we're holding a lock they
959 * are conflicting with.
961 dbwrap_watched_watch_skip_alerting(rec
);
963 state
->status
= g_lock_trylock(rec
, state
, value
, &blocker
);
964 if (!NT_STATUS_IS_OK(state
->status
)) {
965 DBG_DEBUG("g_lock_trylock returned %s\n",
966 nt_errstr(state
->status
));
968 if (!NT_STATUS_EQUAL(state
->status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
972 state
->watch_req
= dbwrap_watched_watch_send(
973 state
->req_state
, state
->req_state
->ev
, rec
, state
->watch_instance
, blocker
);
974 if (state
->watch_req
== NULL
) {
975 state
->status
= NT_STATUS_NO_MEMORY
;
979 static int g_lock_lock_state_destructor(struct g_lock_lock_state
*s
)
981 NTSTATUS status
= g_lock_unlock(s
->ctx
, s
->key
);
982 if (!NT_STATUS_IS_OK(status
)) {
983 DBG_DEBUG("g_lock_unlock failed: %s\n", nt_errstr(status
));
988 static void g_lock_lock_retry(struct tevent_req
*subreq
);
990 struct tevent_req
*g_lock_lock_send(TALLOC_CTX
*mem_ctx
,
991 struct tevent_context
*ev
,
992 struct g_lock_ctx
*ctx
,
994 enum g_lock_type type
,
995 g_lock_lock_cb_fn_t cb_fn
,
998 struct tevent_req
*req
;
999 struct g_lock_lock_state
*state
;
1000 struct g_lock_lock_fn_state fn_state
;
1004 SMB_ASSERT(!ctx
->busy
);
1006 req
= tevent_req_create(mem_ctx
, &state
, struct g_lock_lock_state
);
1014 state
->cb_fn
= cb_fn
;
1015 state
->cb_private
= cb_private
;
1017 fn_state
= (struct g_lock_lock_fn_state
) {
1022 * We allow a cb_fn only for G_LOCK_WRITE for now.
1024 * It's all we currently need and it makes a few things
1025 * easier to implement.
1027 if (unlikely(cb_fn
!= NULL
&& type
!= G_LOCK_WRITE
)) {
1028 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER_6
);
1029 return tevent_req_post(req
, ev
);
1032 status
= dbwrap_do_locked(ctx
->db
, key
, g_lock_lock_fn
, &fn_state
);
1033 if (tevent_req_nterror(req
, status
)) {
1034 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
1036 return tevent_req_post(req
, ev
);
1039 if (NT_STATUS_IS_OK(fn_state
.status
)) {
1040 tevent_req_done(req
);
1041 return tevent_req_post(req
, ev
);
1043 if (!NT_STATUS_EQUAL(fn_state
.status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
1044 tevent_req_nterror(req
, fn_state
.status
);
1045 return tevent_req_post(req
, ev
);
1048 if (tevent_req_nomem(fn_state
.watch_req
, req
)) {
1049 return tevent_req_post(req
, ev
);
1052 ok
= tevent_req_set_endtime(
1055 timeval_current_ofs(5 + generate_random() % 5, 0));
1057 tevent_req_oom(req
);
1058 return tevent_req_post(req
, ev
);
1060 tevent_req_set_callback(fn_state
.watch_req
, g_lock_lock_retry
, req
);
1065 static void g_lock_lock_retry(struct tevent_req
*subreq
)
1067 struct tevent_req
*req
= tevent_req_callback_data(
1068 subreq
, struct tevent_req
);
1069 struct g_lock_lock_state
*state
= tevent_req_data(
1070 req
, struct g_lock_lock_state
);
1071 struct g_lock_lock_fn_state fn_state
;
1072 struct server_id blocker
= { .pid
= 0 };
1073 bool blockerdead
= false;
1075 uint64_t instance
= 0;
1077 status
= dbwrap_watched_watch_recv(subreq
, &instance
, &blockerdead
, &blocker
);
1078 DBG_DEBUG("watch_recv returned %s\n", nt_errstr(status
));
1079 TALLOC_FREE(subreq
);
1081 if (!NT_STATUS_IS_OK(status
) &&
1082 !NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
)) {
1083 tevent_req_nterror(req
, status
);
1087 state
->retry
= true;
1089 fn_state
= (struct g_lock_lock_fn_state
) {
1091 .dead_blocker
= blockerdead
? &blocker
: NULL
,
1092 .watch_instance
= instance
,
1095 status
= dbwrap_do_locked(state
->ctx
->db
, state
->key
,
1096 g_lock_lock_fn
, &fn_state
);
1097 if (tevent_req_nterror(req
, status
)) {
1098 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
1103 if (NT_STATUS_IS_OK(fn_state
.status
)) {
1104 tevent_req_done(req
);
1107 if (!NT_STATUS_EQUAL(fn_state
.status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
1108 tevent_req_nterror(req
, fn_state
.status
);
1112 if (tevent_req_nomem(fn_state
.watch_req
, req
)) {
1116 if (!tevent_req_set_endtime(
1117 fn_state
.watch_req
, state
->ev
,
1118 timeval_current_ofs(5 + generate_random() % 5, 0))) {
1121 tevent_req_set_callback(fn_state
.watch_req
, g_lock_lock_retry
, req
);
1124 NTSTATUS
g_lock_lock_recv(struct tevent_req
*req
)
1126 struct g_lock_lock_state
*state
= tevent_req_data(
1127 req
, struct g_lock_lock_state
);
1128 struct g_lock_ctx
*ctx
= state
->ctx
;
1131 if (tevent_req_is_nterror(req
, &status
)) {
1132 if (NT_STATUS_EQUAL(status
, NT_STATUS_WAS_UNLOCKED
)) {
1133 return NT_STATUS_OK
;
1138 if ((ctx
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) &&
1139 ((state
->type
== G_LOCK_READ
) ||
1140 (state
->type
== G_LOCK_WRITE
))) {
1141 const char *name
= dbwrap_name(ctx
->db
);
1142 dbwrap_lock_order_lock(name
, ctx
->lock_order
);
1145 return NT_STATUS_OK
;
1148 struct g_lock_lock_simple_state
{
1149 struct g_lock_ctx
*ctx
;
1150 struct server_id me
;
1151 enum g_lock_type type
;
1153 g_lock_lock_cb_fn_t cb_fn
;
1157 static void g_lock_lock_simple_fn(
1158 struct db_record
*rec
,
1162 struct g_lock_lock_simple_state
*state
= private_data
;
1163 struct server_id_buf buf
;
1164 struct g_lock lck
= { .exclusive
.pid
= 0 };
1165 struct g_lock_lock_cb_state cb_state
= {
1169 .cb_fn
= state
->cb_fn
,
1170 .cb_private
= state
->cb_private
,
1171 .existed
= value
.dsize
!= 0,
1172 .update_mem_ctx
= talloc_tos(),
1176 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1178 DBG_DEBUG("g_lock_parse failed\n");
1179 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1183 if (lck
.exclusive
.pid
!= 0) {
1184 DBG_DEBUG("locked by %s\n",
1185 server_id_str_buf(lck
.exclusive
, &buf
));
1189 if (state
->type
== G_LOCK_WRITE
) {
1190 if (lck
.num_shared
!= 0) {
1191 DBG_DEBUG("num_shared=%zu\n", lck
.num_shared
);
1194 lck
.exclusive
= state
->me
;
1195 } else if (state
->type
== G_LOCK_READ
) {
1196 g_lock_cleanup_shared(&lck
);
1197 cb_state
.new_shared
= &state
->me
;
1199 smb_panic(__location__
);
1202 lck
.unique_lock_epoch
= generate_unique_u64(lck
.unique_lock_epoch
);
1205 * We are going to store us as owner,
1206 * so we got what we were waiting for.
1208 * So we no longer need to monitor the
1211 dbwrap_watched_watch_skip_alerting(rec
);
1213 state
->status
= g_lock_lock_cb_run_and_store(&cb_state
);
1214 if (!NT_STATUS_IS_OK(state
->status
) &&
1215 !NT_STATUS_EQUAL(state
->status
, NT_STATUS_WAS_UNLOCKED
))
1217 DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n",
1218 nt_errstr(state
->status
));
1225 state
->status
= NT_STATUS_LOCK_NOT_GRANTED
;
1228 NTSTATUS
g_lock_lock(struct g_lock_ctx
*ctx
, TDB_DATA key
,
1229 enum g_lock_type type
, struct timeval timeout
,
1230 g_lock_lock_cb_fn_t cb_fn
,
1234 struct tevent_context
*ev
;
1235 struct tevent_req
*req
;
1239 SMB_ASSERT(!ctx
->busy
);
1242 * We allow a cb_fn only for G_LOCK_WRITE for now.
1244 * It's all we currently need and it makes a few things
1245 * easier to implement.
1247 if (unlikely(cb_fn
!= NULL
&& type
!= G_LOCK_WRITE
)) {
1248 return NT_STATUS_INVALID_PARAMETER_5
;
1251 if ((type
== G_LOCK_READ
) || (type
== G_LOCK_WRITE
)) {
1253 * This is an abstraction violation: Normally we do
1254 * the sync wrappers around async functions with full
1255 * nested event contexts. However, this is used in
1256 * very hot code paths, so avoid the event context
1257 * creation for the good path where there's no lock
1258 * contention. My benchmark gave a factor of 2
1259 * improvement for lock/unlock.
1261 struct g_lock_lock_simple_state state
= {
1263 .me
= messaging_server_id(ctx
->msg
),
1266 .cb_private
= cb_private
,
1268 status
= dbwrap_do_locked(
1269 ctx
->db
, key
, g_lock_lock_simple_fn
, &state
);
1270 if (!NT_STATUS_IS_OK(status
)) {
1271 DBG_DEBUG("dbwrap_do_locked() failed: %s\n",
1276 DBG_DEBUG("status=%s, state.status=%s\n",
1278 nt_errstr(state
.status
));
1280 if (NT_STATUS_IS_OK(state
.status
)) {
1281 if (ctx
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
1282 const char *name
= dbwrap_name(ctx
->db
);
1283 dbwrap_lock_order_lock(name
, ctx
->lock_order
);
1285 return NT_STATUS_OK
;
1287 if (NT_STATUS_EQUAL(state
.status
, NT_STATUS_WAS_UNLOCKED
)) {
1288 /* without dbwrap_lock_order_lock() */
1289 return NT_STATUS_OK
;
1291 if (!NT_STATUS_EQUAL(
1292 state
.status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
1293 return state
.status
;
1296 if (timeval_is_zero(&timeout
)) {
1297 return NT_STATUS_LOCK_NOT_GRANTED
;
1301 * Fall back to the full g_lock_trylock logic,
1302 * g_lock_lock_simple_fn() called above only covers
1303 * the uncontended path.
1307 frame
= talloc_stackframe();
1308 status
= NT_STATUS_NO_MEMORY
;
1310 ev
= samba_tevent_context_init(frame
);
1314 req
= g_lock_lock_send(frame
, ev
, ctx
, key
, type
, cb_fn
, cb_private
);
1318 end
= timeval_current_ofs(timeout
.tv_sec
, timeout
.tv_usec
);
1319 if (!tevent_req_set_endtime(req
, ev
, end
)) {
1322 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
1325 status
= g_lock_lock_recv(req
);
1331 struct g_lock_unlock_state
{
1332 struct server_id self
;
1336 static void g_lock_unlock_fn(
1337 struct db_record
*rec
,
1341 struct g_lock_unlock_state
*state
= private_data
;
1342 struct server_id_buf tmp1
, tmp2
;
1347 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1349 DBG_DEBUG("g_lock_parse() failed\n");
1350 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1354 exclusive
= server_id_equal(&state
->self
, &lck
.exclusive
);
1356 for (i
=0; i
<lck
.num_shared
; i
++) {
1357 struct server_id shared
;
1358 g_lock_get_shared(&lck
, i
, &shared
);
1359 if (server_id_equal(&state
->self
, &shared
)) {
1364 if (i
< lck
.num_shared
) {
1366 DBG_DEBUG("%s both exclusive and shared (%zu)\n",
1367 server_id_str_buf(state
->self
, &tmp1
),
1369 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1372 g_lock_del_shared(&lck
, i
);
1375 DBG_DEBUG("Lock not found, self=%s, lck.exclusive=%s, "
1377 server_id_str_buf(state
->self
, &tmp1
),
1378 server_id_str_buf(lck
.exclusive
, &tmp2
),
1380 state
->status
= NT_STATUS_NOT_FOUND
;
1383 lck
.exclusive
= (struct server_id
) { .pid
= 0 };
1386 if ((lck
.exclusive
.pid
== 0) &&
1387 (lck
.num_shared
== 0) &&
1388 (lck
.datalen
== 0)) {
1389 state
->status
= dbwrap_record_delete(rec
);
1393 if (!exclusive
&& lck
.exclusive
.pid
!= 0) {
1395 * We only had a read lock and there's
1396 * someone waiting for an exclusive lock.
1398 * Don't alert the exclusive lock waiter
1399 * if there are still other read lock holders.
1401 g_lock_cleanup_shared(&lck
);
1402 if (lck
.num_shared
!= 0) {
1403 dbwrap_watched_watch_skip_alerting(rec
);
1407 lck
.unique_lock_epoch
= generate_unique_u64(lck
.unique_lock_epoch
);
1409 state
->status
= g_lock_store(rec
, &lck
, NULL
, NULL
, 0);
1412 NTSTATUS
g_lock_unlock(struct g_lock_ctx
*ctx
, TDB_DATA key
)
1414 struct g_lock_unlock_state state
= {
1415 .self
= messaging_server_id(ctx
->msg
),
1419 SMB_ASSERT(!ctx
->busy
);
1421 status
= dbwrap_do_locked(ctx
->db
, key
, g_lock_unlock_fn
, &state
);
1422 if (!NT_STATUS_IS_OK(status
)) {
1423 DBG_WARNING("dbwrap_do_locked failed: %s\n",
1427 if (!NT_STATUS_IS_OK(state
.status
)) {
1428 DBG_WARNING("g_lock_unlock_fn failed: %s\n",
1429 nt_errstr(state
.status
));
1430 return state
.status
;
1433 if (ctx
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
1434 const char *name
= dbwrap_name(ctx
->db
);
1435 dbwrap_lock_order_unlock(name
, ctx
->lock_order
);
1438 return NT_STATUS_OK
;
1441 struct g_lock_writev_data_state
{
1443 struct server_id self
;
1444 const TDB_DATA
*dbufs
;
1449 static void g_lock_writev_data_fn(
1450 struct db_record
*rec
,
1454 struct g_lock_writev_data_state
*state
= private_data
;
1460 * We're holding an exclusive write lock.
1462 * Now we're updating the content of the record.
1464 * We should not wakeup any other waiters, all they
1465 * would find is that we're still holding a lock they
1466 * are conflicting with.
1468 dbwrap_watched_watch_skip_alerting(rec
);
1470 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1472 DBG_DEBUG("g_lock_parse for %s failed\n",
1473 tdb_data_dbg(state
->key
));
1474 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1478 exclusive
= server_id_equal(&state
->self
, &lck
.exclusive
);
1481 * Make sure we're really exclusive. We are marked as
1482 * exclusive when we are waiting for an exclusive lock
1484 exclusive
&= (lck
.num_shared
== 0);
1487 struct server_id_buf buf1
, buf2
;
1488 DBG_DEBUG("Not locked by us: self=%s, lck.exclusive=%s, "
1489 "lck.num_shared=%zu\n",
1490 server_id_str_buf(state
->self
, &buf1
),
1491 server_id_str_buf(lck
.exclusive
, &buf2
),
1493 state
->status
= NT_STATUS_NOT_LOCKED
;
1497 lck
.unique_data_epoch
= generate_unique_u64(lck
.unique_data_epoch
);
1500 state
->status
= g_lock_store(
1501 rec
, &lck
, NULL
, state
->dbufs
, state
->num_dbufs
);
1504 NTSTATUS
g_lock_writev_data(
1505 struct g_lock_ctx
*ctx
,
1507 const TDB_DATA
*dbufs
,
1510 struct g_lock_writev_data_state state
= {
1512 .self
= messaging_server_id(ctx
->msg
),
1514 .num_dbufs
= num_dbufs
,
1518 SMB_ASSERT(!ctx
->busy
);
1520 status
= dbwrap_do_locked(
1521 ctx
->db
, key
, g_lock_writev_data_fn
, &state
);
1522 if (!NT_STATUS_IS_OK(status
)) {
1523 DBG_WARNING("dbwrap_do_locked failed: %s\n",
1527 if (!NT_STATUS_IS_OK(state
.status
)) {
1528 DBG_WARNING("g_lock_writev_data_fn failed: %s\n",
1529 nt_errstr(state
.status
));
1530 return state
.status
;
1533 return NT_STATUS_OK
;
1536 NTSTATUS
g_lock_write_data(struct g_lock_ctx
*ctx
, TDB_DATA key
,
1537 const uint8_t *buf
, size_t buflen
)
1540 .dptr
= discard_const_p(uint8_t, buf
),
1543 return g_lock_writev_data(ctx
, key
, &dbuf
, 1);
1546 struct g_lock_locks_state
{
1547 int (*fn
)(TDB_DATA key
, void *private_data
);
1551 static int g_lock_locks_fn(struct db_record
*rec
, void *priv
)
1554 struct g_lock_locks_state
*state
= (struct g_lock_locks_state
*)priv
;
1556 key
= dbwrap_record_get_key(rec
);
1557 return state
->fn(key
, state
->private_data
);
1560 int g_lock_locks(struct g_lock_ctx
*ctx
,
1561 int (*fn
)(TDB_DATA key
, void *private_data
),
1564 struct g_lock_locks_state state
;
1568 SMB_ASSERT(!ctx
->busy
);
1571 state
.private_data
= private_data
;
1573 status
= dbwrap_traverse_read(ctx
->db
, g_lock_locks_fn
, &state
, &count
);
1574 if (!NT_STATUS_IS_OK(status
)) {
1580 struct g_lock_dump_state
{
1581 TALLOC_CTX
*mem_ctx
;
1583 void (*fn
)(struct server_id exclusive
,
1585 const struct server_id
*shared
,
1586 const uint8_t *data
,
1588 void *private_data
);
1591 enum dbwrap_req_state req_state
;
1594 static void g_lock_dump_fn(TDB_DATA key
, TDB_DATA data
,
1597 struct g_lock_dump_state
*state
= private_data
;
1598 struct g_lock lck
= (struct g_lock
) { .exclusive
.pid
= 0 };
1599 struct server_id
*shared
= NULL
;
1603 ok
= g_lock_parse(data
.dptr
, data
.dsize
, &lck
);
1605 DBG_DEBUG("g_lock_parse failed for %s\n",
1606 tdb_data_dbg(state
->key
));
1607 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1611 if (lck
.num_shared
> 0) {
1612 shared
= talloc_array(
1613 state
->mem_ctx
, struct server_id
, lck
.num_shared
);
1614 if (shared
== NULL
) {
1615 DBG_DEBUG("talloc failed\n");
1616 state
->status
= NT_STATUS_NO_MEMORY
;
1621 for (i
=0; i
<lck
.num_shared
; i
++) {
1622 g_lock_get_shared(&lck
, i
, &shared
[i
]);
1625 state
->fn(lck
.exclusive
,
1630 state
->private_data
);
1632 TALLOC_FREE(shared
);
1634 state
->status
= NT_STATUS_OK
;
1637 NTSTATUS
g_lock_dump(struct g_lock_ctx
*ctx
, TDB_DATA key
,
1638 void (*fn
)(struct server_id exclusive
,
1640 const struct server_id
*shared
,
1641 const uint8_t *data
,
1643 void *private_data
),
1646 struct g_lock_dump_state state
= {
1647 .mem_ctx
= ctx
, .key
= key
,
1648 .fn
= fn
, .private_data
= private_data
1652 SMB_ASSERT(!ctx
->busy
);
1654 status
= dbwrap_parse_record(ctx
->db
, key
, g_lock_dump_fn
, &state
);
1655 if (!NT_STATUS_IS_OK(status
)) {
1656 DBG_DEBUG("dbwrap_parse_record returned %s\n",
1660 if (!NT_STATUS_IS_OK(state
.status
)) {
1661 DBG_DEBUG("g_lock_dump_fn returned %s\n",
1662 nt_errstr(state
.status
));
1663 return state
.status
;
1665 return NT_STATUS_OK
;
1668 static void g_lock_dump_done(struct tevent_req
*subreq
);
1670 struct tevent_req
*g_lock_dump_send(
1671 TALLOC_CTX
*mem_ctx
,
1672 struct tevent_context
*ev
,
1673 struct g_lock_ctx
*ctx
,
1675 void (*fn
)(struct server_id exclusive
,
1677 const struct server_id
*shared
,
1678 const uint8_t *data
,
1680 void *private_data
),
1683 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
1684 struct g_lock_dump_state
*state
= NULL
;
1686 SMB_ASSERT(!ctx
->busy
);
1688 req
= tevent_req_create(mem_ctx
, &state
, struct g_lock_dump_state
);
1692 state
->mem_ctx
= state
;
1695 state
->private_data
= private_data
;
1697 SMB_ASSERT(!ctx
->busy
);
1699 subreq
= dbwrap_parse_record_send(
1707 if (tevent_req_nomem(subreq
, req
)) {
1708 return tevent_req_post(req
, ev
);
1710 tevent_req_set_callback(subreq
, g_lock_dump_done
, req
);
1714 static void g_lock_dump_done(struct tevent_req
*subreq
)
1716 struct tevent_req
*req
= tevent_req_callback_data(
1717 subreq
, struct tevent_req
);
1718 struct g_lock_dump_state
*state
= tevent_req_data(
1719 req
, struct g_lock_dump_state
);
1722 status
= dbwrap_parse_record_recv(subreq
);
1723 TALLOC_FREE(subreq
);
1724 if (tevent_req_nterror(req
, status
) ||
1725 tevent_req_nterror(req
, state
->status
)) {
1728 tevent_req_done(req
);
1731 NTSTATUS
g_lock_dump_recv(struct tevent_req
*req
)
1733 return tevent_req_simple_recv_ntstatus(req
);
1736 int g_lock_seqnum(struct g_lock_ctx
*ctx
)
1738 return dbwrap_get_seqnum(ctx
->db
);
1741 struct g_lock_watch_data_state
{
1742 struct tevent_context
*ev
;
1743 struct g_lock_ctx
*ctx
;
1745 struct server_id blocker
;
1747 uint64_t unique_lock_epoch
;
1748 uint64_t unique_data_epoch
;
1749 uint64_t watch_instance
;
1753 static void g_lock_watch_data_done(struct tevent_req
*subreq
);
1755 static void g_lock_watch_data_send_fn(
1756 struct db_record
*rec
,
1760 struct tevent_req
*req
= talloc_get_type_abort(
1761 private_data
, struct tevent_req
);
1762 struct g_lock_watch_data_state
*state
= tevent_req_data(
1763 req
, struct g_lock_watch_data_state
);
1764 struct tevent_req
*subreq
= NULL
;
1768 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1770 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1773 state
->unique_lock_epoch
= lck
.unique_lock_epoch
;
1774 state
->unique_data_epoch
= lck
.unique_data_epoch
;
1776 DBG_DEBUG("state->unique_data_epoch=%"PRIu64
"\n", state
->unique_data_epoch
);
1778 subreq
= dbwrap_watched_watch_send(
1779 state
, state
->ev
, rec
, 0, state
->blocker
);
1780 if (subreq
== NULL
) {
1781 state
->status
= NT_STATUS_NO_MEMORY
;
1784 tevent_req_set_callback(subreq
, g_lock_watch_data_done
, req
);
1786 state
->status
= NT_STATUS_EVENT_PENDING
;
1789 struct tevent_req
*g_lock_watch_data_send(
1790 TALLOC_CTX
*mem_ctx
,
1791 struct tevent_context
*ev
,
1792 struct g_lock_ctx
*ctx
,
1794 struct server_id blocker
)
1796 struct tevent_req
*req
= NULL
;
1797 struct g_lock_watch_data_state
*state
= NULL
;
1800 SMB_ASSERT(!ctx
->busy
);
1802 req
= tevent_req_create(
1803 mem_ctx
, &state
, struct g_lock_watch_data_state
);
1809 state
->blocker
= blocker
;
1811 state
->key
= tdb_data_talloc_copy(state
, key
);
1812 if (tevent_req_nomem(state
->key
.dptr
, req
)) {
1813 return tevent_req_post(req
, ev
);
1816 status
= dbwrap_do_locked(
1817 ctx
->db
, key
, g_lock_watch_data_send_fn
, req
);
1818 if (tevent_req_nterror(req
, status
)) {
1819 DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status
));
1820 return tevent_req_post(req
, ev
);
1823 if (NT_STATUS_EQUAL(state
->status
, NT_STATUS_EVENT_PENDING
)) {
1826 if (tevent_req_nterror(req
, state
->status
)) {
1827 return tevent_req_post(req
, ev
);
1829 tevent_req_done(req
);
1830 return tevent_req_post(req
, ev
);
1833 static void g_lock_watch_data_done_fn(
1834 struct db_record
*rec
,
1838 struct tevent_req
*req
= talloc_get_type_abort(
1839 private_data
, struct tevent_req
);
1840 struct g_lock_watch_data_state
*state
= tevent_req_data(
1841 req
, struct g_lock_watch_data_state
);
1842 struct tevent_req
*subreq
= NULL
;
1846 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1848 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
1849 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1853 if (lck
.unique_data_epoch
!= state
->unique_data_epoch
) {
1854 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
1855 DBG_DEBUG("lck.unique_data_epoch=%"PRIu64
", "
1856 "state->unique_data_epoch=%"PRIu64
"\n",
1857 lck
.unique_data_epoch
,
1858 state
->unique_data_epoch
);
1859 state
->status
= NT_STATUS_OK
;
1864 * The lock epoch changed, so we better
1865 * remove ourself from the waiter list
1866 * (most likely the first position)
1867 * and re-add us at the end of the list.
1869 * This gives other lock waiters a change
1872 * Otherwise we'll keep our waiter instance alive,
1873 * keep waiting (most likely at first position).
1875 if (lck
.unique_lock_epoch
!= state
->unique_lock_epoch
) {
1876 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
1877 state
->watch_instance
= dbwrap_watched_watch_add_instance(rec
);
1878 state
->unique_lock_epoch
= lck
.unique_lock_epoch
;
1881 subreq
= dbwrap_watched_watch_send(
1882 state
, state
->ev
, rec
, state
->watch_instance
, state
->blocker
);
1883 if (subreq
== NULL
) {
1884 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
1885 state
->status
= NT_STATUS_NO_MEMORY
;
1888 tevent_req_set_callback(subreq
, g_lock_watch_data_done
, req
);
1890 state
->status
= NT_STATUS_EVENT_PENDING
;
1893 static void g_lock_watch_data_done(struct tevent_req
*subreq
)
1895 struct tevent_req
*req
= tevent_req_callback_data(
1896 subreq
, struct tevent_req
);
1897 struct g_lock_watch_data_state
*state
= tevent_req_data(
1898 req
, struct g_lock_watch_data_state
);
1900 uint64_t instance
= 0;
1902 status
= dbwrap_watched_watch_recv(
1903 subreq
, &instance
, &state
->blockerdead
, &state
->blocker
);
1904 TALLOC_FREE(subreq
);
1905 if (tevent_req_nterror(req
, status
)) {
1906 DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
1911 state
->watch_instance
= instance
;
1913 status
= dbwrap_do_locked(
1914 state
->ctx
->db
, state
->key
, g_lock_watch_data_done_fn
, req
);
1915 if (tevent_req_nterror(req
, status
)) {
1916 DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status
));
1919 if (NT_STATUS_EQUAL(state
->status
, NT_STATUS_EVENT_PENDING
)) {
1922 if (tevent_req_nterror(req
, state
->status
)) {
1925 tevent_req_done(req
);
1928 NTSTATUS
g_lock_watch_data_recv(
1929 struct tevent_req
*req
,
1931 struct server_id
*blocker
)
1933 struct g_lock_watch_data_state
*state
= tevent_req_data(
1934 req
, struct g_lock_watch_data_state
);
1937 if (tevent_req_is_nterror(req
, &status
)) {
1940 if (blockerdead
!= NULL
) {
1941 *blockerdead
= state
->blockerdead
;
1943 if (blocker
!= NULL
) {
1944 *blocker
= state
->blocker
;
1947 return NT_STATUS_OK
;
1950 static void g_lock_wake_watchers_fn(
1951 struct db_record
*rec
,
1955 struct g_lock lck
= { .exclusive
.pid
= 0 };
1959 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1961 DBG_WARNING("g_lock_parse failed\n");
1965 lck
.unique_data_epoch
= generate_unique_u64(lck
.unique_data_epoch
);
1967 status
= g_lock_store(rec
, &lck
, NULL
, NULL
, 0);
1968 if (!NT_STATUS_IS_OK(status
)) {
1969 DBG_WARNING("g_lock_store failed: %s\n", nt_errstr(status
));
1974 void g_lock_wake_watchers(struct g_lock_ctx
*ctx
, TDB_DATA key
)
1978 SMB_ASSERT(!ctx
->busy
);
1980 status
= dbwrap_do_locked(ctx
->db
, key
, g_lock_wake_watchers_fn
, NULL
);
1981 if (!NT_STATUS_IS_OK(status
)) {
1982 DBG_DEBUG("dbwrap_do_locked returned %s\n",