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
, 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 */
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
;
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
,
106 .data
= data_len
== 0 ? NULL
: buf
+ shared_len
,
112 static void g_lock_get_shared(const struct g_lock
*lck
,
114 struct server_id
*shared
)
116 if (i
>= lck
->num_shared
) {
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
) {
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
,
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
) {
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) {
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(
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
) {
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");
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
) {
242 TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
|TDB_VOLATILE
,
247 TALLOC_FREE(db_path
);
248 if (backend
== NULL
) {
249 DBG_WARNING("Could not open g_lock.tdb\n");
253 ctx
= g_lock_ctx_init_backend(mem_ctx
, msg
, &backend
);
257 static void g_lock_cleanup_dead(
259 struct server_id
*dead_blocker
)
262 struct server_id_buf tmp
;
264 if (dead_blocker
== NULL
) {
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) {
278 struct server_id shared
;
280 g_lock_get_shared(lck
, 0, &shared
);
281 shared_died
= server_id_equal(dead_blocker
, &shared
);
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(
293 const struct server_id
*self
)
297 for (i
=0; i
<lck
->num_shared
; i
++) {
298 struct server_id shared
;
301 g_lock_get_shared(lck
, i
, &shared
);
303 same
= server_id_equal(self
, &shared
);
312 static void g_lock_cleanup_shared(struct g_lock
*lck
)
315 struct server_id check
;
318 if (lck
->num_shared
== 0) {
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
);
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
;
344 struct server_id
*new_shared
;
345 g_lock_lock_cb_fn_t cb_fn
;
347 TALLOC_CTX
*update_mem_ctx
;
348 TDB_DATA updated_data
;
354 NTSTATUS
g_lock_lock_cb_dump(struct g_lock_lock_cb_state
*cb_state
,
355 void (*fn
)(struct server_id exclusive
,
357 const struct server_id
*shared
,
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);
378 NTSTATUS
g_lock_lock_cb_writev(struct g_lock_lock_cb_state
*cb_state
,
379 const TDB_DATA
*dbufs
,
384 status
= dbwrap_merge_dbufs(&cb_state
->updated_data
,
385 cb_state
->update_mem_ctx
,
387 if (!NT_STATUS_IS_OK(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
;
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
;
407 struct server_id blocker
;
409 uint64_t unique_lock_epoch
;
410 uint64_t unique_data_epoch
;
411 uint64_t watch_instance
;
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(
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
);
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
);
457 static void g_lock_lock_cb_watch_data_done_fn(
458 struct db_record
*rec
,
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
;
470 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
472 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
473 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
;
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
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
;
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
);
524 uint64_t instance
= 0;
526 status
= dbwrap_watched_watch_recv(
527 subreq
, &instance
, &state
->blockerdead
, &state
->blocker
);
529 if (tevent_req_nterror(req
, status
)) {
530 DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
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
));
543 if (NT_STATUS_EQUAL(state
->status
, NT_STATUS_EVENT_PENDING
)) {
546 if (tevent_req_nterror(req
, state
->status
)) {
549 tevent_req_done(req
);
552 NTSTATUS
g_lock_lock_cb_watch_data_recv(
553 struct tevent_req
*req
,
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
);
561 if (tevent_req_is_nterror(req
, &status
)) {
564 if (blockerdead
!= NULL
) {
565 *blockerdead
= state
->blockerdead
;
567 if (blocker
!= NULL
) {
568 *blocker
= state
->blocker
;
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
;
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
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",
641 return NT_STATUS_WAS_UNLOCKED
;
644 success_status
= NT_STATUS_WAS_UNLOCKED
;
647 status
= g_lock_store(cb_state
->rec
,
649 cb_state
->new_shared
,
651 if (!NT_STATUS_IS_OK(status
)) {
652 DBG_WARNING("g_lock_store() failed: %s\n",
657 return success_status
;
660 struct g_lock_lock_state
{
661 struct tevent_context
*ev
;
662 struct g_lock_ctx
*ctx
;
664 enum g_lock_type type
;
666 g_lock_lock_cb_fn_t cb_fn
;
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
;
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
,
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
,
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
;
705 ok
= g_lock_parse(data
.dptr
, data
.dsize
, &lck
);
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
);
722 lck
.exclusive
= (struct server_id
) { .pid
=0 };
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
),
738 lck
.exclusive
, &tmp2
));
739 return NT_STATUS_NOT_LOCKED
;
742 if (type
== G_LOCK_UPGRADE
) {
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 "
753 "existing shared lock\n",
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,
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 };
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,
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...
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
);
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 ...
884 if (state
->watch_instance
== 0) {
886 * Here we have lck.num_shared != 0.
888 * We will return NT_STATUS_LOCK_NOT_GRANTED
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",
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",
916 server_id_str_buf(*blocker
, &tmp
));
918 return NT_STATUS_LOCK_NOT_GRANTED
;
923 g_lock_cleanup_shared(&lck
);
924 cb_state
.new_shared
= &self
;
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",
943 talloc_set_destructor(req_state
, NULL
);
947 static void g_lock_lock_fn(
948 struct db_record
*rec
,
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
)) {
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
));
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
,
995 enum g_lock_type type
,
996 g_lock_lock_cb_fn_t cb_fn
,
999 struct tevent_req
*req
;
1000 struct g_lock_lock_state
*state
;
1001 struct g_lock_lock_fn_state fn_state
;
1005 SMB_ASSERT(!ctx
->busy
);
1007 req
= tevent_req_create(mem_ctx
, &state
, struct g_lock_lock_state
);
1015 state
->cb_fn
= cb_fn
;
1016 state
->cb_private
= cb_private
;
1018 fn_state
= (struct g_lock_lock_fn_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",
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(
1056 timeval_current_ofs(5 + generate_random() % 5, 0));
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
);
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;
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
);
1088 state
->retry
= true;
1090 fn_state
= (struct g_lock_lock_fn_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",
1104 if (NT_STATUS_IS_OK(fn_state
.status
)) {
1105 tevent_req_done(req
);
1108 if (!NT_STATUS_EQUAL(fn_state
.status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
1109 tevent_req_nterror(req
, fn_state
.status
);
1113 if (tevent_req_nomem(fn_state
.watch_req
, req
)) {
1117 if (!tevent_req_set_endtime(
1118 fn_state
.watch_req
, state
->ev
,
1119 timeval_current_ofs(5 + generate_random() % 5, 0))) {
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
;
1132 if (tevent_req_is_nterror(req
, &status
)) {
1133 if (NT_STATUS_EQUAL(status
, NT_STATUS_WAS_UNLOCKED
)) {
1134 return NT_STATUS_OK
;
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
;
1154 g_lock_lock_cb_fn_t cb_fn
;
1158 static void g_lock_lock_simple_fn(
1159 struct db_record
*rec
,
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
= {
1170 .cb_fn
= state
->cb_fn
,
1171 .cb_private
= state
->cb_private
,
1172 .existed
= value
.dsize
!= 0,
1173 .update_mem_ctx
= talloc_tos(),
1177 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1179 DBG_DEBUG("g_lock_parse failed\n");
1180 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
1184 if (lck
.exclusive
.pid
!= 0) {
1185 DBG_DEBUG("locked by %s\n",
1186 server_id_str_buf(lck
.exclusive
, &buf
));
1190 if (state
->type
== G_LOCK_WRITE
) {
1191 if (lck
.num_shared
!= 0) {
1192 DBG_DEBUG("num_shared=%zu\n", lck
.num_shared
);
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
;
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
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
));
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
,
1235 struct tevent_context
*ev
;
1236 struct tevent_req
*req
;
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
= {
1264 .me
= messaging_server_id(ctx
->msg
),
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",
1277 DBG_DEBUG("status=%s, state.status=%s\n",
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
);
1315 req
= g_lock_lock_send(frame
, ev
, ctx
, key
, type
, cb_fn
, cb_private
);
1319 end
= timeval_current_ofs(timeout
.tv_sec
, timeout
.tv_usec
);
1320 if (!tevent_req_set_endtime(req
, ev
, end
)) {
1323 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
1326 status
= g_lock_lock_recv(req
);
1332 struct g_lock_unlock_state
{
1333 struct server_id self
;
1337 static void g_lock_unlock_fn(
1338 struct db_record
*rec
,
1342 struct g_lock_unlock_state
*state
= private_data
;
1343 struct server_id_buf tmp1
, tmp2
;
1348 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1350 DBG_DEBUG("g_lock_parse() failed\n");
1351 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
)) {
1365 if (i
< lck
.num_shared
) {
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
;
1373 g_lock_del_shared(&lck
, i
);
1376 DBG_DEBUG("Lock not found, self=%s, lck.exclusive=%s, "
1378 server_id_str_buf(state
->self
, &tmp1
),
1379 server_id_str_buf(lck
.exclusive
, &tmp2
),
1381 state
->status
= NT_STATUS_NOT_FOUND
;
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
);
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
),
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",
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
{
1444 struct server_id self
;
1445 const TDB_DATA
*dbufs
;
1450 static void g_lock_writev_data_fn(
1451 struct db_record
*rec
,
1455 struct g_lock_writev_data_state
*state
= private_data
;
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
);
1473 DBG_DEBUG("g_lock_parse for %s failed\n",
1474 tdb_data_dbg(state
->key
));
1475 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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);
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
),
1494 state
->status
= NT_STATUS_NOT_LOCKED
;
1498 lck
.unique_data_epoch
= generate_unique_u64(lck
.unique_data_epoch
);
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
,
1508 const TDB_DATA
*dbufs
,
1511 struct g_lock_writev_data_state state
= {
1513 .self
= messaging_server_id(ctx
->msg
),
1515 .num_dbufs
= num_dbufs
,
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",
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
)
1541 .dptr
= discard_const_p(uint8_t, buf
),
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
);
1552 static int g_lock_locks_fn(struct db_record
*rec
, void *priv
)
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
),
1565 struct g_lock_locks_state state
;
1569 SMB_ASSERT(!ctx
->busy
);
1572 state
.private_data
= private_data
;
1574 status
= dbwrap_traverse_read(ctx
->db
,
1578 if (!NT_STATUS_IS_OK(status
)) {
1584 int g_lock_locks(struct g_lock_ctx
*ctx
,
1585 int (*fn
)(TDB_DATA key
, void *private_data
),
1588 struct g_lock_locks_state state
;
1592 SMB_ASSERT(!ctx
->busy
);
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
)) {
1604 struct g_lock_dump_state
{
1605 TALLOC_CTX
*mem_ctx
;
1607 void (*fn
)(struct server_id exclusive
,
1609 const struct server_id
*shared
,
1610 const uint8_t *data
,
1612 void *private_data
);
1615 enum dbwrap_req_state req_state
;
1618 static void g_lock_dump_fn(TDB_DATA key
, TDB_DATA 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
;
1627 ok
= g_lock_parse(data
.dptr
, data
.dsize
, &lck
);
1629 DBG_DEBUG("g_lock_parse failed for %s\n",
1630 tdb_data_dbg(state
->key
));
1631 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
;
1645 for (i
=0; i
<lck
.num_shared
; i
++) {
1646 g_lock_get_shared(&lck
, i
, &shared
[i
]);
1649 state
->fn(lck
.exclusive
,
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
,
1664 const struct server_id
*shared
,
1665 const uint8_t *data
,
1667 void *private_data
),
1670 struct g_lock_dump_state state
= {
1671 .mem_ctx
= ctx
, .key
= key
,
1672 .fn
= fn
, .private_data
= private_data
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",
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
,
1699 void (*fn
)(struct server_id exclusive
,
1701 const struct server_id
*shared
,
1702 const uint8_t *data
,
1704 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
);
1716 state
->mem_ctx
= state
;
1719 state
->private_data
= private_data
;
1721 SMB_ASSERT(!ctx
->busy
);
1723 subreq
= dbwrap_parse_record_send(
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
);
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
);
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
)) {
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
;
1769 struct server_id blocker
;
1771 uint64_t unique_lock_epoch
;
1772 uint64_t unique_data_epoch
;
1773 uint64_t watch_instance
;
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
,
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
;
1792 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1794 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
;
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
,
1818 struct server_id blocker
)
1820 struct tevent_req
*req
= NULL
;
1821 struct g_lock_watch_data_state
*state
= NULL
;
1824 SMB_ASSERT(!ctx
->busy
);
1826 req
= tevent_req_create(
1827 mem_ctx
, &state
, struct g_lock_watch_data_state
);
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
)) {
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
,
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
;
1870 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1872 dbwrap_watched_watch_remove_instance(rec
, state
->watch_instance
);
1873 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
;
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
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
;
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
);
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",
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
));
1943 if (NT_STATUS_EQUAL(state
->status
, NT_STATUS_EVENT_PENDING
)) {
1946 if (tevent_req_nterror(req
, state
->status
)) {
1949 tevent_req_done(req
);
1952 NTSTATUS
g_lock_watch_data_recv(
1953 struct tevent_req
*req
,
1955 struct server_id
*blocker
)
1957 struct g_lock_watch_data_state
*state
= tevent_req_data(
1958 req
, struct g_lock_watch_data_state
);
1961 if (tevent_req_is_nterror(req
, &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
,
1979 struct g_lock lck
= { .exclusive
.pid
= 0 };
1983 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
1985 DBG_WARNING("g_lock_parse failed\n");
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
));
1998 void g_lock_wake_watchers(struct g_lock_ctx
*ctx
, TDB_DATA key
)
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",