2 Unix SMB/CIFS implementation.
3 Database interface wrapper
4 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2006
6 Major code contributions from Aleksey Fedoseev (fedoseev@ru.ibm.com)
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "lib/util/debug.h"
24 #include "lib/util/fault.h"
25 #include "lib/util/talloc_stack.h"
26 #include "dbwrap/dbwrap.h"
27 #include "dbwrap/dbwrap_private.h"
28 #include "lib/util/util_tdb.h"
29 #include "lib/util/tevent_ntstatus.h"
32 * Fall back using fetch if no genuine exists operation is provided
35 static int dbwrap_fallback_exists(struct db_context
*db
, TDB_DATA key
)
37 NTSTATUS status
= dbwrap_parse_record(db
, key
, NULL
, NULL
);
38 return NT_STATUS_IS_OK(status
) ? 1 : 0;
41 static int delete_record(struct db_record
*rec
, void *data
)
43 NTSTATUS status
= dbwrap_record_delete(rec
);
44 return NT_STATUS_IS_OK(status
) ? 0 : -1;
48 * Fallback wipe implementation using traverse and delete if no genuine
49 * wipe operation is provided
51 static int dbwrap_fallback_wipe(struct db_context
*db
)
53 NTSTATUS status
= dbwrap_trans_traverse(db
, delete_record
, NULL
);
54 return NT_STATUS_IS_OK(status
) ? 0 : -1;
57 static int do_nothing(struct db_record
*rec
, void *unused
)
63 * Fallback check operation: just traverse.
65 static int dbwrap_fallback_check(struct db_context
*db
)
67 NTSTATUS status
= dbwrap_traverse_read(db
, do_nothing
, NULL
, NULL
);
68 return NT_STATUS_IS_OK(status
) ? 0 : -1;
72 * Wrapper functions for the backend methods
75 TDB_DATA
dbwrap_record_get_key(const struct db_record
*rec
)
80 TDB_DATA
dbwrap_record_get_value(const struct db_record
*rec
)
82 SMB_ASSERT(rec
->value_valid
);
86 NTSTATUS
dbwrap_record_storev(struct db_record
*rec
,
87 const TDB_DATA
*dbufs
, int num_dbufs
, int flags
)
92 * Invalidate before rec->storev() is called, give
93 * rec->storev() the chance to re-validate rec->value.
95 rec
->value_valid
= false;
97 status
= rec
->storev(rec
, dbufs
, num_dbufs
, flags
);
98 if (!NT_STATUS_IS_OK(status
)) {
104 NTSTATUS
dbwrap_record_store(struct db_record
*rec
, TDB_DATA data
, int flags
)
106 return dbwrap_record_storev(rec
, &data
, 1, flags
);
109 NTSTATUS
dbwrap_record_delete(struct db_record
*rec
)
113 status
= rec
->delete_rec(rec
);
114 if (!NT_STATUS_IS_OK(status
)) {
118 rec
->value
= tdb_null
;
123 struct dbwrap_merge_dbs_state
{
124 struct db_context
*to
;
128 /* Copy a single record to the db_context passed in private_data */
129 static int dbwrap_merge_dbs_copy_record(struct db_record
*rec
,
132 struct dbwrap_merge_dbs_state
*state
= private_data
;
134 TDB_DATA data
= dbwrap_record_get_value(rec
);
135 TDB_DATA key
= dbwrap_record_get_key(rec
);
136 NTSTATUS status
= dbwrap_store(state
->to
, key
, data
, state
->flags
);
138 return NT_STATUS_IS_OK(status
) ? 0 : 1;
142 dbwrap_merge_dbs(struct db_context
*to
, struct db_context
*from
, int flags
)
144 struct dbwrap_merge_dbs_state state
= {.to
= to
, .flags
= flags
};
146 return dbwrap_traverse(from
,
147 dbwrap_merge_dbs_copy_record
,
152 const char *locked_dbs
[DBWRAP_LOCK_ORDER_MAX
];
154 static void debug_lock_order(int level
)
157 DEBUG(level
, ("lock order:"));
158 for (i
=0; i
<DBWRAP_LOCK_ORDER_MAX
; i
++) {
162 locked_dbs
[i
] ? locked_dbs
[i
] : "<none>"));
164 DEBUGADD(level
, ("\n"));
167 void dbwrap_lock_order_lock(const char *db_name
,
168 enum dbwrap_lock_order lock_order
)
172 DBG_INFO("check lock order %d for %s\n",
176 if (!DBWRAP_LOCK_ORDER_VALID(lock_order
)) {
177 DBG_ERR("Invalid lock order %d of %s\n",
180 smb_panic("lock order violation");
183 for (idx
=lock_order
-1; idx
<DBWRAP_LOCK_ORDER_MAX
; idx
++) {
184 if (locked_dbs
[idx
] != NULL
) {
185 DBG_ERR("Lock order violation: Trying %s at %d while "
186 "%s at %d is locked\n",
192 smb_panic("lock order violation");
196 locked_dbs
[lock_order
-1] = db_name
;
198 debug_lock_order(10);
201 void dbwrap_lock_order_unlock(const char *db_name
,
202 enum dbwrap_lock_order lock_order
)
204 DBG_INFO("release lock order %d for %s\n",
208 if (!DBWRAP_LOCK_ORDER_VALID(lock_order
)) {
209 DBG_ERR("Invalid lock order %d of %s\n",
212 smb_panic("lock order violation");
215 if (locked_dbs
[lock_order
-1] == NULL
) {
216 DBG_ERR("db %s at order %d unlocked\n",
219 smb_panic("lock order violation");
222 if (locked_dbs
[lock_order
-1] != db_name
) {
223 DBG_ERR("locked db at lock order %d is %s, expected %s\n",
225 locked_dbs
[lock_order
-1],
227 smb_panic("lock order violation");
230 locked_dbs
[lock_order
-1] = NULL
;
233 struct dbwrap_lock_order_state
{
234 struct db_context
*db
;
237 static int dbwrap_lock_order_state_destructor(
238 struct dbwrap_lock_order_state
*s
)
240 struct db_context
*db
= s
->db
;
241 dbwrap_lock_order_unlock(db
->name
, db
->lock_order
);
245 static struct dbwrap_lock_order_state
*dbwrap_check_lock_order(
246 struct db_context
*db
, TALLOC_CTX
*mem_ctx
)
248 struct dbwrap_lock_order_state
*state
;
250 state
= talloc(mem_ctx
, struct dbwrap_lock_order_state
);
252 DBG_WARNING("talloc failed\n");
257 dbwrap_lock_order_lock(db
->name
, db
->lock_order
);
258 talloc_set_destructor(state
, dbwrap_lock_order_state_destructor
);
263 static struct db_record
*dbwrap_fetch_locked_internal(
264 struct db_context
*db
, TALLOC_CTX
*mem_ctx
, TDB_DATA key
,
265 struct db_record
*(*db_fn
)(struct db_context
*db
, TALLOC_CTX
*mem_ctx
,
268 struct db_record
*rec
;
269 struct dbwrap_lock_order_state
*lock_order
= NULL
;
271 if (db
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
272 lock_order
= dbwrap_check_lock_order(db
, mem_ctx
);
273 if (lock_order
== NULL
) {
277 rec
= db_fn(db
, mem_ctx
, key
);
279 TALLOC_FREE(lock_order
);
282 (void)talloc_steal(rec
, lock_order
);
287 struct db_record
*dbwrap_fetch_locked(struct db_context
*db
,
291 return dbwrap_fetch_locked_internal(db
, mem_ctx
, key
,
295 struct db_context
*dbwrap_record_get_db(struct db_record
*rec
)
300 struct dbwrap_fetch_state
{
305 static void dbwrap_fetch_parser(TDB_DATA key
, TDB_DATA data
,
308 struct dbwrap_fetch_state
*state
=
309 (struct dbwrap_fetch_state
*)private_data
;
311 state
->data
.dsize
= data
.dsize
;
312 state
->data
.dptr
= (uint8_t *)talloc_memdup(state
->mem_ctx
, data
.dptr
,
316 NTSTATUS
dbwrap_fetch(struct db_context
*db
, TALLOC_CTX
*mem_ctx
,
317 TDB_DATA key
, TDB_DATA
*value
)
319 struct dbwrap_fetch_state state
;
323 return NT_STATUS_INVALID_PARAMETER
;
326 state
.mem_ctx
= mem_ctx
;
328 status
= dbwrap_parse_record(db
, key
, dbwrap_fetch_parser
, &state
);
329 if (!NT_STATUS_IS_OK(status
)) {
332 if ((state
.data
.dsize
!= 0) && (state
.data
.dptr
== NULL
)) {
333 return NT_STATUS_NO_MEMORY
;
339 bool dbwrap_exists(struct db_context
*db
, TDB_DATA key
)
342 if (db
->exists
!= NULL
) {
343 result
= db
->exists(db
, key
);
345 result
= dbwrap_fallback_exists(db
,key
);
347 return (result
== 1);
350 struct dbwrap_store_state
{
356 static void dbwrap_store_fn(
357 struct db_record
*rec
,
361 struct dbwrap_store_state
*state
= private_data
;
362 state
->status
= dbwrap_record_store(rec
, state
->data
, state
->flags
);
365 NTSTATUS
dbwrap_store(struct db_context
*db
, TDB_DATA key
,
366 TDB_DATA data
, int flags
)
368 struct dbwrap_store_state state
= { .data
= data
, .flags
= flags
};
371 status
= dbwrap_do_locked(db
, key
, dbwrap_store_fn
, &state
);
372 if (!NT_STATUS_IS_OK(status
)) {
379 struct dbwrap_delete_state
{
383 static void dbwrap_delete_fn(
384 struct db_record
*rec
,
388 struct dbwrap_delete_state
*state
= private_data
;
389 state
->status
= dbwrap_record_delete(rec
);
392 NTSTATUS
dbwrap_delete(struct db_context
*db
, TDB_DATA key
)
394 struct dbwrap_delete_state state
= { .status
= NT_STATUS_NOT_FOUND
};
397 status
= dbwrap_do_locked(db
, key
, dbwrap_delete_fn
, &state
);
398 if (!NT_STATUS_IS_OK(status
)) {
405 NTSTATUS
dbwrap_traverse(struct db_context
*db
,
406 int (*f
)(struct db_record
*, void*),
410 int ret
= db
->traverse(db
, f
, private_data
);
413 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
423 NTSTATUS
dbwrap_traverse_read(struct db_context
*db
,
424 int (*f
)(struct db_record
*, void*),
428 int ret
= db
->traverse_read(db
, f
, private_data
);
431 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
441 static void dbwrap_null_parser(TDB_DATA key
, TDB_DATA val
, void* data
)
446 NTSTATUS
dbwrap_parse_record(struct db_context
*db
, TDB_DATA key
,
447 void (*parser
)(TDB_DATA key
, TDB_DATA data
,
451 if (parser
== NULL
) {
452 parser
= dbwrap_null_parser
;
454 return db
->parse_record(db
, key
, parser
, private_data
);
457 struct dbwrap_parse_record_state
{
458 struct db_context
*db
;
463 static void dbwrap_parse_record_done(struct tevent_req
*subreq
);
465 struct tevent_req
*dbwrap_parse_record_send(
467 struct tevent_context
*ev
,
468 struct db_context
*db
,
470 void (*parser
)(TDB_DATA key
, TDB_DATA data
, void *private_data
),
472 enum dbwrap_req_state
*req_state
)
474 struct tevent_req
*req
= NULL
;
475 struct tevent_req
*subreq
= NULL
;
476 struct dbwrap_parse_record_state
*state
= NULL
;
479 req
= tevent_req_create(mem_ctx
, &state
, struct dbwrap_parse_record_state
);
481 *req_state
= DBWRAP_REQ_ERROR
;
485 *state
= (struct dbwrap_parse_record_state
) {
489 if (parser
== NULL
) {
490 parser
= dbwrap_null_parser
;
493 *req_state
= DBWRAP_REQ_INIT
;
495 if (db
->parse_record_send
== NULL
) {
497 * Backend doesn't implement async version, call sync one
499 status
= db
->parse_record(db
, key
, parser
, private_data
);
500 if (tevent_req_nterror(req
, status
)) {
501 *req_state
= DBWRAP_REQ_DONE
;
502 return tevent_req_post(req
, ev
);
505 *req_state
= DBWRAP_REQ_DONE
;
506 tevent_req_done(req
);
507 return tevent_req_post(req
, ev
);
511 * Copy the key into our state ensuring the key data buffer is always
512 * available to all the dbwrap backends over the entire lifetime of the
513 * async request. Otherwise the caller might have free'd the key buffer.
515 if (key
.dsize
> sizeof(state
->_keybuf
)) {
516 state
->key
.dptr
= talloc_memdup(state
, key
.dptr
, key
.dsize
);
517 if (tevent_req_nomem(state
->key
.dptr
, req
)) {
518 return tevent_req_post(req
, ev
);
521 memcpy(state
->_keybuf
, key
.dptr
, key
.dsize
);
522 state
->key
.dptr
= state
->_keybuf
;
524 state
->key
.dsize
= key
.dsize
;
526 subreq
= db
->parse_record_send(state
,
533 if (tevent_req_nomem(subreq
, req
)) {
534 *req_state
= DBWRAP_REQ_ERROR
;
535 return tevent_req_post(req
, ev
);
538 tevent_req_set_callback(subreq
,
539 dbwrap_parse_record_done
,
544 static void dbwrap_parse_record_done(struct tevent_req
*subreq
)
546 struct tevent_req
*req
= tevent_req_callback_data(
547 subreq
, struct tevent_req
);
548 struct dbwrap_parse_record_state
*state
= tevent_req_data(
549 req
, struct dbwrap_parse_record_state
);
552 status
= state
->db
->parse_record_recv(subreq
);
554 if (!NT_STATUS_IS_OK(status
)) {
555 tevent_req_nterror(req
, status
);
559 tevent_req_done(req
);
562 NTSTATUS
dbwrap_parse_record_recv(struct tevent_req
*req
)
564 return tevent_req_simple_recv_ntstatus(req
);
567 NTSTATUS
dbwrap_do_locked(struct db_context
*db
, TDB_DATA key
,
568 void (*fn
)(struct db_record
*rec
,
573 struct db_record
*rec
;
575 if (db
->do_locked
!= NULL
) {
578 if (db
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
579 dbwrap_lock_order_lock(db
->name
, db
->lock_order
);
582 status
= db
->do_locked(db
, key
, fn
, private_data
);
584 if (db
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
585 dbwrap_lock_order_unlock(db
->name
, db
->lock_order
);
591 rec
= dbwrap_fetch_locked(db
, db
, key
);
593 return NT_STATUS_NO_MEMORY
;
597 * Invalidate rec->value, nobody shall assume it's set from
598 * within dbwrap_do_locked().
600 rec
->value_valid
= false;
602 fn(rec
, rec
->value
, private_data
);
609 int dbwrap_wipe(struct db_context
*db
)
611 if (db
->wipe
== NULL
) {
612 return dbwrap_fallback_wipe(db
);
617 int dbwrap_check(struct db_context
*db
)
619 if (db
->check
== NULL
) {
620 return dbwrap_fallback_check(db
);
622 return db
->check(db
);
625 int dbwrap_get_seqnum(struct db_context
*db
)
627 return db
->get_seqnum(db
);
630 int dbwrap_transaction_start(struct db_context
*db
)
632 if (!db
->persistent
) {
634 * dbwrap_ctdb has two different data models for persistent
635 * and non-persistent databases. Transactions are supported
636 * only for the persistent databases. This check is here to
637 * prevent breakages of the cluster case, autobuild at this
638 * point only tests non-clustered Samba. Before removing this
639 * check, please make sure that this facility has also been
640 * added to dbwrap_ctdb.
644 DEBUG(1, ("transactions not supported on non-persistent "
645 "database %s\n", db
->name
));
648 return db
->transaction_start(db
);
651 NTSTATUS
dbwrap_transaction_start_nonblock(struct db_context
*db
)
653 if (db
->transaction_start_nonblock
) {
654 return db
->transaction_start_nonblock(db
);
656 return dbwrap_transaction_start(db
) == 0 ? NT_STATUS_OK
657 : NT_STATUS_UNSUCCESSFUL
;
661 int dbwrap_transaction_commit(struct db_context
*db
)
663 return db
->transaction_commit(db
);
666 int dbwrap_transaction_cancel(struct db_context
*db
)
668 return db
->transaction_cancel(db
);
671 size_t dbwrap_db_id(struct db_context
*db
, uint8_t *id
, size_t idlen
)
673 return db
->id(db
, id
, idlen
);
676 bool dbwrap_is_persistent(struct db_context
*db
)
678 return db
->persistent
;
681 const char *dbwrap_name(struct db_context
*db
)
686 static ssize_t
tdb_data_buf(const TDB_DATA
*dbufs
, int num_dbufs
,
687 uint8_t *buf
, size_t buflen
)
693 for (i
=0; i
<num_dbufs
; i
++) {
694 size_t thislen
= dbufs
[i
].dsize
;
697 if (needed
< thislen
) {
702 if (p
!= NULL
&& (thislen
!= 0) && (needed
<= buflen
)) {
703 memcpy(p
, dbufs
[i
].dptr
, thislen
);
712 NTSTATUS
dbwrap_merge_dbufs(TDB_DATA
*buf
, TALLOC_CTX
*mem_ctx
,
713 const TDB_DATA
*dbufs
, int num_dbufs
)
715 ssize_t len
= tdb_data_buf(dbufs
, num_dbufs
, NULL
, 0);
718 return NT_STATUS_INVALID_PARAMETER
;
721 if (buf
->dsize
!= len
) {
724 tmp
= talloc_realloc(mem_ctx
, buf
->dptr
, uint8_t, len
);
725 if (tmp
== NULL
&& len
!= 0) {
726 return NT_STATUS_NO_MEMORY
;
733 tdb_data_buf(dbufs
, num_dbufs
, buf
->dptr
, buf
->dsize
);