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 "dbwrap/dbwrap.h"
24 #include "dbwrap/dbwrap_open.h"
25 #include "dbwrap/dbwrap_watch.h"
28 #include "../lib/util/tevent_ntstatus.h"
33 struct db_context
*db
;
34 struct messaging_context
*msg
;
38 * The "g_lock.tdb" file contains records, indexed by the 0-terminated
39 * lockname. The record contains an array of "struct g_lock_rec"
43 #define G_LOCK_REC_LENGTH (SERVER_ID_BUF_LENGTH+1)
45 static void g_lock_rec_put(uint8_t buf
[G_LOCK_REC_LENGTH
],
46 const struct g_lock_rec rec
)
48 SCVAL(buf
, 0, rec
.lock_type
);
49 server_id_put(buf
+1, rec
.pid
);
52 static void g_lock_rec_get(struct g_lock_rec
*rec
,
53 const uint8_t buf
[G_LOCK_REC_LENGTH
])
55 rec
->lock_type
= CVAL(buf
, 0);
56 server_id_get(&rec
->pid
, buf
+1);
66 static bool g_lock_parse(uint8_t *buf
, size_t buflen
, struct g_lock
*lck
)
68 size_t found_recs
, data_ofs
;
70 if (buflen
< sizeof(uint32_t)) {
71 *lck
= (struct g_lock
) {0};
75 found_recs
= IVAL(buf
, 0);
77 buf
+= sizeof(uint32_t);
78 buflen
-= sizeof(uint32_t);
79 if (found_recs
> buflen
/G_LOCK_REC_LENGTH
) {
83 data_ofs
= found_recs
* G_LOCK_REC_LENGTH
;
85 *lck
= (struct g_lock
) {
86 .recsbuf
= buf
, .num_recs
= found_recs
,
87 .data
= buf
+data_ofs
, .datalen
= buflen
-data_ofs
93 static void g_lock_get_rec(struct g_lock
*lck
, size_t i
,
94 struct g_lock_rec
*rec
)
96 if (i
>= lck
->num_recs
) {
99 g_lock_rec_get(rec
, lck
->recsbuf
+ i
*G_LOCK_REC_LENGTH
);
102 static void g_lock_rec_del(struct g_lock
*lck
, size_t i
)
104 if (i
>= lck
->num_recs
) {
108 if (i
< lck
->num_recs
) {
109 uint8_t *recptr
= lck
->recsbuf
+ i
*G_LOCK_REC_LENGTH
;
110 memcpy(recptr
, lck
->recsbuf
+ lck
->num_recs
*G_LOCK_REC_LENGTH
,
115 static NTSTATUS
g_lock_store(struct db_record
*rec
, struct g_lock
*lck
,
116 struct g_lock_rec
*add
)
119 uint8_t addbuf
[G_LOCK_REC_LENGTH
];
121 struct TDB_DATA dbufs
[] = {
122 { .dptr
= sizebuf
, .dsize
= sizeof(sizebuf
) },
123 { .dptr
= lck
->recsbuf
,
124 .dsize
= lck
->num_recs
* G_LOCK_REC_LENGTH
},
126 { .dptr
= lck
->data
, .dsize
= lck
->datalen
}
130 g_lock_rec_put(addbuf
, *add
);
132 dbufs
[2] = (TDB_DATA
) {
133 .dptr
= addbuf
, .dsize
= G_LOCK_REC_LENGTH
139 SIVAL(sizebuf
, 0, lck
->num_recs
);
141 return dbwrap_record_storev(rec
, dbufs
, ARRAY_SIZE(dbufs
), 0);
144 struct g_lock_ctx
*g_lock_ctx_init(TALLOC_CTX
*mem_ctx
,
145 struct messaging_context
*msg
)
147 struct g_lock_ctx
*result
;
148 struct db_context
*backend
;
151 result
= talloc(mem_ctx
, struct g_lock_ctx
);
152 if (result
== NULL
) {
157 db_path
= lock_path(talloc_tos(), "g_lock.tdb");
158 if (db_path
== NULL
) {
163 backend
= db_open(result
, db_path
, 0,
164 TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
165 O_RDWR
|O_CREAT
, 0600,
168 TALLOC_FREE(db_path
);
169 if (backend
== NULL
) {
170 DBG_WARNING("Could not open g_lock.tdb\n");
175 result
->db
= db_open_watched(result
, &backend
, msg
);
176 if (result
->db
== NULL
) {
177 DBG_WARNING("db_open_watched failed\n");
184 static bool g_lock_conflicts(enum g_lock_type l1
, enum g_lock_type l2
)
187 * Only tested write locks so far. Very likely this routine
188 * needs to be fixed for read locks....
190 if ((l1
== G_LOCK_READ
) && (l2
== G_LOCK_READ
)) {
196 static NTSTATUS
g_lock_trylock(struct db_record
*rec
, struct server_id self
,
197 enum g_lock_type type
,
198 struct server_id
*blocker
)
203 struct g_lock_rec mylock
= {0};
205 bool modified
= false;
208 data
= dbwrap_record_get_value(rec
);
210 ok
= g_lock_parse(data
.dptr
, data
.dsize
, &lck
);
212 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
215 if ((type
== G_LOCK_READ
) && (lck
.num_recs
> 0)) {
216 struct g_lock_rec check_rec
;
219 * Read locks can stay around forever if the process
220 * dies. Do a heuristic check for process existence:
221 * Check one random process for existence. Hopefully
222 * this will keep runaway read locks under control.
224 i
= generate_random() % lck
.num_recs
;
226 g_lock_get_rec(&lck
, i
, &check_rec
);
228 if ((check_rec
.lock_type
== G_LOCK_READ
) &&
229 !serverid_exists(&check_rec
.pid
)) {
230 g_lock_rec_del(&lck
, i
);
236 * For the lock upgrade/downgrade case, remove ourselves from
237 * the list. We re-add ourselves later after we checked the
238 * other entries for conflict.
241 for (i
=0; i
<lck
.num_recs
; i
++) {
242 struct g_lock_rec lock
;
244 g_lock_get_rec(&lck
, i
, &lock
);
246 if (serverid_equal(&self
, &lock
.pid
)) {
247 if (lock
.lock_type
== type
) {
248 status
= NT_STATUS_WAS_LOCKED
;
253 g_lock_rec_del(&lck
, i
);
260 * Check for conflicts with everybody else. Not a for-loop
261 * because we remove stale entries in the meantime,
262 * decrementing lck.num_recs.
267 while (i
< lck
.num_recs
) {
268 struct g_lock_rec lock
;
270 g_lock_get_rec(&lck
, i
, &lock
);
272 if (g_lock_conflicts(type
, lock
.lock_type
)) {
273 struct server_id pid
= lock
.pid
;
276 * As the serverid_exists might recurse into
277 * the g_lock code, we use
278 * SERVERID_UNIQUE_ID_NOT_TO_VERIFY to avoid the loop
280 pid
.unique_id
= SERVERID_UNIQUE_ID_NOT_TO_VERIFY
;
282 if (serverid_exists(&pid
)) {
283 status
= NT_STATUS_LOCK_NOT_GRANTED
;
289 * Delete stale conflicting entry
291 g_lock_rec_del(&lck
, i
);
300 mylock
= (struct g_lock_rec
) {
305 status
= NT_STATUS_OK
;
308 NTSTATUS store_status
;
311 * (Re-)add ourselves if needed via non-NULL
312 * g_lock_store argument
315 store_status
= g_lock_store(
318 mylock
.pid
.pid
!= 0 ? &mylock
: NULL
);
320 if (!NT_STATUS_IS_OK(store_status
)) {
321 DBG_WARNING("g_lock_record_store failed: %s\n",
322 nt_errstr(store_status
));
323 status
= store_status
;
329 struct g_lock_lock_state
{
330 struct tevent_context
*ev
;
331 struct g_lock_ctx
*ctx
;
333 enum g_lock_type type
;
336 static void g_lock_lock_retry(struct tevent_req
*subreq
);
338 struct g_lock_lock_fn_state
{
339 struct g_lock_lock_state
*state
;
340 struct server_id self
;
342 struct tevent_req
*watch_req
;
346 static void g_lock_lock_fn(struct db_record
*rec
, void *private_data
)
348 struct g_lock_lock_fn_state
*state
= private_data
;
349 struct server_id blocker
;
351 state
->status
= g_lock_trylock(rec
, state
->self
, state
->state
->type
,
353 if (!NT_STATUS_EQUAL(state
->status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
357 state
->watch_req
= dbwrap_watched_watch_send(
358 state
->state
, state
->state
->ev
, rec
, blocker
);
361 struct tevent_req
*g_lock_lock_send(TALLOC_CTX
*mem_ctx
,
362 struct tevent_context
*ev
,
363 struct g_lock_ctx
*ctx
,
365 enum g_lock_type type
)
367 struct tevent_req
*req
;
368 struct g_lock_lock_state
*state
;
369 struct g_lock_lock_fn_state fn_state
;
372 req
= tevent_req_create(mem_ctx
, &state
, struct g_lock_lock_state
);
381 fn_state
= (struct g_lock_lock_fn_state
) {
382 .state
= state
, .self
= messaging_server_id(ctx
->msg
)
385 status
= dbwrap_do_locked(ctx
->db
, key
, g_lock_lock_fn
, &fn_state
);
386 if (tevent_req_nterror(req
, status
)) {
387 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
389 return tevent_req_post(req
, ev
);
392 if (NT_STATUS_IS_OK(fn_state
.status
)) {
393 tevent_req_done(req
);
394 return tevent_req_post(req
, ev
);
396 if (!NT_STATUS_EQUAL(fn_state
.status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
397 tevent_req_nterror(req
, fn_state
.status
);
398 return tevent_req_post(req
, ev
);
401 if (tevent_req_nomem(fn_state
.watch_req
, req
)) {
402 return tevent_req_post(req
, ev
);
405 if (!tevent_req_set_endtime(
406 fn_state
.watch_req
, state
->ev
,
407 timeval_current_ofs(5 + sys_random() % 5, 0))) {
408 return tevent_req_post(req
, ev
);
410 tevent_req_set_callback(fn_state
.watch_req
, g_lock_lock_retry
, req
);
414 static void g_lock_lock_retry(struct tevent_req
*subreq
)
416 struct tevent_req
*req
= tevent_req_callback_data(
417 subreq
, struct tevent_req
);
418 struct g_lock_lock_state
*state
= tevent_req_data(
419 req
, struct g_lock_lock_state
);
420 struct g_lock_lock_fn_state fn_state
;
423 status
= dbwrap_watched_watch_recv(subreq
, NULL
, NULL
);
424 DBG_DEBUG("watch_recv returned %s\n", nt_errstr(status
));
427 if (!NT_STATUS_IS_OK(status
) &&
428 !NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
)) {
429 tevent_req_nterror(req
, status
);
433 fn_state
= (struct g_lock_lock_fn_state
) {
434 .state
= state
, .self
= messaging_server_id(state
->ctx
->msg
)
437 status
= dbwrap_do_locked(state
->ctx
->db
, state
->key
,
438 g_lock_lock_fn
, &fn_state
);
439 if (tevent_req_nterror(req
, status
)) {
440 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
445 if (NT_STATUS_IS_OK(fn_state
.status
)) {
446 tevent_req_done(req
);
449 if (!NT_STATUS_EQUAL(fn_state
.status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
450 tevent_req_nterror(req
, fn_state
.status
);
454 if (tevent_req_nomem(fn_state
.watch_req
, req
)) {
458 if (!tevent_req_set_endtime(
459 fn_state
.watch_req
, state
->ev
,
460 timeval_current_ofs(5 + sys_random() % 5, 0))) {
463 tevent_req_set_callback(fn_state
.watch_req
, g_lock_lock_retry
, req
);
466 NTSTATUS
g_lock_lock_recv(struct tevent_req
*req
)
468 return tevent_req_simple_recv_ntstatus(req
);
471 NTSTATUS
g_lock_lock(struct g_lock_ctx
*ctx
, TDB_DATA key
,
472 enum g_lock_type type
, struct timeval timeout
)
474 TALLOC_CTX
*frame
= talloc_stackframe();
475 struct tevent_context
*ev
;
476 struct tevent_req
*req
;
478 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
480 ev
= samba_tevent_context_init(frame
);
484 req
= g_lock_lock_send(frame
, ev
, ctx
, key
, type
);
488 end
= timeval_current_ofs(timeout
.tv_sec
, timeout
.tv_usec
);
489 if (!tevent_req_set_endtime(req
, ev
, end
)) {
492 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
495 status
= g_lock_lock_recv(req
);
501 struct g_lock_unlock_state
{
503 struct server_id self
;
507 static void g_lock_unlock_fn(struct db_record
*rec
,
510 struct g_lock_unlock_state
*state
= private_data
;
516 value
= dbwrap_record_get_value(rec
);
518 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
520 DBG_DEBUG("g_lock_get for %s failed\n",
521 hex_encode_talloc(talloc_tos(),
524 state
->status
= NT_STATUS_FILE_INVALID
;
527 for (i
=0; i
<lck
.num_recs
; i
++) {
528 struct g_lock_rec lockrec
;
529 g_lock_get_rec(&lck
, i
, &lockrec
);
530 if (serverid_equal(&state
->self
, &lockrec
.pid
)) {
534 if (i
== lck
.num_recs
) {
535 DBG_DEBUG("Lock not found, num_rec=%zu\n", lck
.num_recs
);
536 state
->status
= NT_STATUS_NOT_FOUND
;
540 g_lock_rec_del(&lck
, i
);
542 if ((lck
.num_recs
== 0) && (lck
.datalen
== 0)) {
543 state
->status
= dbwrap_record_delete(rec
);
546 state
->status
= g_lock_store(rec
, &lck
, NULL
);
549 NTSTATUS
g_lock_unlock(struct g_lock_ctx
*ctx
, TDB_DATA key
)
551 struct g_lock_unlock_state state
= {
552 .self
= messaging_server_id(ctx
->msg
), .key
= key
556 status
= dbwrap_do_locked(ctx
->db
, key
, g_lock_unlock_fn
, &state
);
557 if (!NT_STATUS_IS_OK(status
)) {
558 DBG_WARNING("dbwrap_do_locked failed: %s\n",
562 if (!NT_STATUS_IS_OK(state
.status
)) {
563 DBG_WARNING("g_lock_unlock_fn failed: %s\n",
564 nt_errstr(state
.status
));
571 struct g_lock_write_data_state
{
573 struct server_id self
;
579 static void g_lock_write_data_fn(struct db_record
*rec
,
582 struct g_lock_write_data_state
*state
= private_data
;
588 value
= dbwrap_record_get_value(rec
);
590 ok
= g_lock_parse(value
.dptr
, value
.dsize
, &lck
);
592 DBG_DEBUG("g_lock_parse for %s failed\n",
593 hex_encode_talloc(talloc_tos(),
596 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
599 for (i
=0; i
<lck
.num_recs
; i
++) {
600 struct g_lock_rec lockrec
;
601 g_lock_get_rec(&lck
, i
, &lockrec
);
602 if ((lockrec
.lock_type
== G_LOCK_WRITE
) &&
603 serverid_equal(&state
->self
, &lockrec
.pid
)) {
607 if (i
== lck
.num_recs
) {
608 DBG_DEBUG("Not locked by us\n");
609 state
->status
= NT_STATUS_NOT_LOCKED
;
613 lck
.data
= discard_const_p(uint8_t, state
->data
);
614 lck
.datalen
= state
->datalen
;
615 state
->status
= g_lock_store(rec
, &lck
, NULL
);
618 NTSTATUS
g_lock_write_data(struct g_lock_ctx
*ctx
, TDB_DATA key
,
619 const uint8_t *buf
, size_t buflen
)
621 struct g_lock_write_data_state state
= {
622 .key
= key
, .self
= messaging_server_id(ctx
->msg
),
623 .data
= buf
, .datalen
= buflen
627 status
= dbwrap_do_locked(ctx
->db
, key
,
628 g_lock_write_data_fn
, &state
);
629 if (!NT_STATUS_IS_OK(status
)) {
630 DBG_WARNING("dbwrap_do_locked failed: %s\n",
634 if (!NT_STATUS_IS_OK(state
.status
)) {
635 DBG_WARNING("g_lock_write_data_fn failed: %s\n",
636 nt_errstr(state
.status
));
643 struct g_lock_locks_state
{
644 int (*fn
)(TDB_DATA key
, void *private_data
);
648 static int g_lock_locks_fn(struct db_record
*rec
, void *priv
)
651 struct g_lock_locks_state
*state
= (struct g_lock_locks_state
*)priv
;
653 key
= dbwrap_record_get_key(rec
);
654 return state
->fn(key
, state
->private_data
);
657 int g_lock_locks(struct g_lock_ctx
*ctx
,
658 int (*fn
)(TDB_DATA key
, void *private_data
),
661 struct g_lock_locks_state state
;
666 state
.private_data
= private_data
;
668 status
= dbwrap_traverse_read(ctx
->db
, g_lock_locks_fn
, &state
, &count
);
669 if (!NT_STATUS_IS_OK(status
)) {
675 struct g_lock_dump_state
{
678 void (*fn
)(const struct g_lock_rec
*locks
,
687 static void g_lock_dump_fn(TDB_DATA key
, TDB_DATA data
,
690 struct g_lock_dump_state
*state
= private_data
;
691 struct g_lock_rec
*recs
;
696 ok
= g_lock_parse(data
.dptr
, data
.dsize
, &lck
);
698 DBG_DEBUG("g_lock_parse failed for %s\n",
699 hex_encode_talloc(talloc_tos(),
702 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
706 recs
= talloc_array(state
->mem_ctx
, struct g_lock_rec
, lck
.num_recs
);
708 DBG_DEBUG("talloc failed\n");
709 state
->status
= NT_STATUS_NO_MEMORY
;
713 for (i
=0; i
<lck
.num_recs
; i
++) {
714 g_lock_get_rec(&lck
, i
, &recs
[i
]);
717 state
->fn(recs
, lck
.num_recs
, lck
.data
, lck
.datalen
,
718 state
->private_data
);
722 state
->status
= NT_STATUS_OK
;
725 NTSTATUS
g_lock_dump(struct g_lock_ctx
*ctx
, TDB_DATA key
,
726 void (*fn
)(const struct g_lock_rec
*locks
,
733 struct g_lock_dump_state state
= {
734 .mem_ctx
= ctx
, .key
= key
,
735 .fn
= fn
, .private_data
= private_data
739 status
= dbwrap_parse_record(ctx
->db
, key
, g_lock_dump_fn
, &state
);
740 if (!NT_STATUS_IS_OK(status
)) {
741 DBG_DEBUG("dbwrap_parse_record returned %s\n",
745 if (!NT_STATUS_IS_OK(state
.status
)) {
746 DBG_DEBUG("g_lock_dump_fn returned %s\n",
747 nt_errstr(state
.status
));
753 static bool g_lock_init_all(TALLOC_CTX
*mem_ctx
,
754 struct tevent_context
**pev
,
755 struct messaging_context
**pmsg
,
756 struct g_lock_ctx
**pg_ctx
)
758 struct tevent_context
*ev
= NULL
;
759 struct messaging_context
*msg
= NULL
;
760 struct g_lock_ctx
*g_ctx
= NULL
;
762 ev
= samba_tevent_context_init(mem_ctx
);
764 d_fprintf(stderr
, "ERROR: could not init event context\n");
767 msg
= messaging_init(mem_ctx
, ev
);
769 d_fprintf(stderr
, "ERROR: could not init messaging context\n");
772 g_ctx
= g_lock_ctx_init(mem_ctx
, msg
);
774 d_fprintf(stderr
, "ERROR: could not init g_lock context\n");
789 NTSTATUS
g_lock_do(TDB_DATA key
, enum g_lock_type lock_type
,
790 struct timeval timeout
,
791 void (*fn
)(void *private_data
), void *private_data
)
793 struct tevent_context
*ev
= NULL
;
794 struct messaging_context
*msg
= NULL
;
795 struct g_lock_ctx
*g_ctx
= NULL
;
798 if (!g_lock_init_all(talloc_tos(), &ev
, &msg
, &g_ctx
)) {
799 status
= NT_STATUS_ACCESS_DENIED
;
803 status
= g_lock_lock(g_ctx
, key
, lock_type
, timeout
);
804 if (!NT_STATUS_IS_OK(status
)) {
808 g_lock_unlock(g_ctx
, key
);