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
)
85 NTSTATUS
dbwrap_record_storev(struct db_record
*rec
,
86 const TDB_DATA
*dbufs
, int num_dbufs
, int flags
)
90 status
= rec
->storev(rec
, dbufs
, num_dbufs
, flags
);
91 if (!NT_STATUS_IS_OK(status
)) {
97 NTSTATUS
dbwrap_record_store(struct db_record
*rec
, TDB_DATA data
, int flags
)
99 return dbwrap_record_storev(rec
, &data
, 1, flags
);
102 NTSTATUS
dbwrap_record_delete(struct db_record
*rec
)
106 status
= rec
->delete_rec(rec
);
107 if (!NT_STATUS_IS_OK(status
)) {
113 static void debug_lock_order(int level
, struct db_context
*dbs
[])
116 DEBUG(level
, ("lock order: "));
117 for (i
=0; i
<DBWRAP_LOCK_ORDER_MAX
; i
++) {
118 DEBUGADD(level
, (" %d:%s", i
+ 1, dbs
[i
] ? dbs
[i
]->name
: "<none>"));
120 DEBUGADD(level
, ("\n"));
123 static void dbwrap_lock_order_lock(struct db_context
*db
,
124 struct db_context
***lockptr
)
126 static struct db_context
*locked_dbs
[DBWRAP_LOCK_ORDER_MAX
];
129 DBG_INFO("check lock order %d for %s\n", (int)db
->lock_order
,
132 if (!DBWRAP_LOCK_ORDER_VALID(db
->lock_order
)) {
133 DBG_ERR("Invalid lock order %d of %s\n",
134 (int)db
->lock_order
, db
->name
);
135 smb_panic("lock order violation");
138 for (idx
=db
->lock_order
-1; idx
<DBWRAP_LOCK_ORDER_MAX
; idx
++) {
139 if (locked_dbs
[idx
] != NULL
) {
140 DBG_ERR("Lock order violation: Trying %s at %d while "
141 "%s at %d is locked\n",
142 db
->name
, (int)db
->lock_order
,
143 locked_dbs
[idx
]->name
, idx
+ 1);
144 debug_lock_order(0, locked_dbs
);
145 smb_panic("lock order violation");
149 locked_dbs
[db
->lock_order
-1] = db
;
150 *lockptr
= &locked_dbs
[db
->lock_order
-1];
152 debug_lock_order(10, locked_dbs
);
155 static void dbwrap_lock_order_unlock(struct db_context
*db
,
156 struct db_context
**lockptr
)
158 DBG_INFO("release lock order %d for %s\n",
159 (int)db
->lock_order
, db
->name
);
161 if (*lockptr
== NULL
) {
162 DBG_ERR("db %s at order %d unlocked\n", db
->name
,
163 (int)db
->lock_order
);
164 smb_panic("lock order violation");
167 if (*lockptr
!= db
) {
168 DBG_ERR("locked db at lock order %d is %s, expected %s\n",
169 (int)(*lockptr
)->lock_order
, (*lockptr
)->name
,
171 smb_panic("lock order violation");
177 struct dbwrap_lock_order_state
{
178 struct db_context
*db
;
179 struct db_context
**lockptr
;
182 static int dbwrap_lock_order_state_destructor(
183 struct dbwrap_lock_order_state
*s
)
185 dbwrap_lock_order_unlock(s
->db
, s
->lockptr
);
189 static struct dbwrap_lock_order_state
*dbwrap_check_lock_order(
190 struct db_context
*db
, TALLOC_CTX
*mem_ctx
)
192 struct dbwrap_lock_order_state
*state
;
194 state
= talloc(mem_ctx
, struct dbwrap_lock_order_state
);
196 DBG_WARNING("talloc failed\n");
201 dbwrap_lock_order_lock(db
, &state
->lockptr
);
202 talloc_set_destructor(state
, dbwrap_lock_order_state_destructor
);
207 static struct db_record
*dbwrap_fetch_locked_internal(
208 struct db_context
*db
, TALLOC_CTX
*mem_ctx
, TDB_DATA key
,
209 struct db_record
*(*db_fn
)(struct db_context
*db
, TALLOC_CTX
*mem_ctx
,
212 struct db_record
*rec
;
213 struct dbwrap_lock_order_state
*lock_order
= NULL
;
215 if (db
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
216 lock_order
= dbwrap_check_lock_order(db
, mem_ctx
);
217 if (lock_order
== NULL
) {
221 rec
= db_fn(db
, mem_ctx
, key
);
223 TALLOC_FREE(lock_order
);
226 (void)talloc_steal(rec
, lock_order
);
231 struct db_record
*dbwrap_fetch_locked(struct db_context
*db
,
235 return dbwrap_fetch_locked_internal(db
, mem_ctx
, key
,
239 struct db_record
*dbwrap_try_fetch_locked(struct db_context
*db
,
243 return dbwrap_fetch_locked_internal(
246 ? db
->try_fetch_locked
: db
->fetch_locked
);
249 struct db_context
*dbwrap_record_get_db(struct db_record
*rec
)
254 struct dbwrap_fetch_state
{
259 static void dbwrap_fetch_parser(TDB_DATA key
, TDB_DATA data
,
262 struct dbwrap_fetch_state
*state
=
263 (struct dbwrap_fetch_state
*)private_data
;
265 state
->data
.dsize
= data
.dsize
;
266 state
->data
.dptr
= (uint8_t *)talloc_memdup(state
->mem_ctx
, data
.dptr
,
270 NTSTATUS
dbwrap_fetch(struct db_context
*db
, TALLOC_CTX
*mem_ctx
,
271 TDB_DATA key
, TDB_DATA
*value
)
273 struct dbwrap_fetch_state state
;
277 return NT_STATUS_INVALID_PARAMETER
;
280 state
.mem_ctx
= mem_ctx
;
282 status
= dbwrap_parse_record(db
, key
, dbwrap_fetch_parser
, &state
);
283 if (!NT_STATUS_IS_OK(status
)) {
286 if ((state
.data
.dsize
!= 0) && (state
.data
.dptr
== NULL
)) {
287 return NT_STATUS_NO_MEMORY
;
293 bool dbwrap_exists(struct db_context
*db
, TDB_DATA key
)
296 if (db
->exists
!= NULL
) {
297 result
= db
->exists(db
, key
);
299 result
= dbwrap_fallback_exists(db
,key
);
301 return (result
== 1);
304 struct dbwrap_store_state
{
310 static void dbwrap_store_fn(struct db_record
*rec
, void *private_data
)
312 struct dbwrap_store_state
*state
= private_data
;
313 state
->status
= dbwrap_record_store(rec
, state
->data
, state
->flags
);
316 NTSTATUS
dbwrap_store(struct db_context
*db
, TDB_DATA key
,
317 TDB_DATA data
, int flags
)
319 struct dbwrap_store_state state
= { .data
= data
, .flags
= flags
};
322 status
= dbwrap_do_locked(db
, key
, dbwrap_store_fn
, &state
);
323 if (!NT_STATUS_IS_OK(status
)) {
330 struct dbwrap_delete_state
{
334 static void dbwrap_delete_fn(struct db_record
*rec
, void *private_data
)
336 struct dbwrap_delete_state
*state
= private_data
;
337 state
->status
= dbwrap_record_delete(rec
);
340 NTSTATUS
dbwrap_delete(struct db_context
*db
, TDB_DATA key
)
342 struct dbwrap_delete_state state
;
345 status
= dbwrap_do_locked(db
, key
, dbwrap_delete_fn
, &state
);
346 if (!NT_STATUS_IS_OK(status
)) {
353 NTSTATUS
dbwrap_traverse(struct db_context
*db
,
354 int (*f
)(struct db_record
*, void*),
358 int ret
= db
->traverse(db
, f
, private_data
);
361 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
371 NTSTATUS
dbwrap_traverse_read(struct db_context
*db
,
372 int (*f
)(struct db_record
*, void*),
376 int ret
= db
->traverse_read(db
, f
, private_data
);
379 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
389 static void dbwrap_null_parser(TDB_DATA key
, TDB_DATA val
, void* data
)
394 NTSTATUS
dbwrap_parse_record(struct db_context
*db
, TDB_DATA key
,
395 void (*parser
)(TDB_DATA key
, TDB_DATA data
,
399 if (parser
== NULL
) {
400 parser
= dbwrap_null_parser
;
402 return db
->parse_record(db
, key
, parser
, private_data
);
405 struct dbwrap_parse_record_state
{
406 struct db_context
*db
;
411 static void dbwrap_parse_record_done(struct tevent_req
*subreq
);
413 struct tevent_req
*dbwrap_parse_record_send(
415 struct tevent_context
*ev
,
416 struct db_context
*db
,
418 void (*parser
)(TDB_DATA key
, TDB_DATA data
, void *private_data
),
420 enum dbwrap_req_state
*req_state
)
422 struct tevent_req
*req
= NULL
;
423 struct tevent_req
*subreq
= NULL
;
424 struct dbwrap_parse_record_state
*state
= NULL
;
427 req
= tevent_req_create(mem_ctx
, &state
, struct dbwrap_parse_record_state
);
429 *req_state
= DBWRAP_REQ_ERROR
;
433 *state
= (struct dbwrap_parse_record_state
) {
437 if (parser
== NULL
) {
438 parser
= dbwrap_null_parser
;
441 *req_state
= DBWRAP_REQ_INIT
;
443 if (db
->parse_record_send
== NULL
) {
445 * Backend doesn't implement async version, call sync one
447 status
= db
->parse_record(db
, key
, parser
, private_data
);
448 if (tevent_req_nterror(req
, status
)) {
449 *req_state
= DBWRAP_REQ_DONE
;
450 return tevent_req_post(req
, ev
);
453 *req_state
= DBWRAP_REQ_DONE
;
454 tevent_req_done(req
);
455 return tevent_req_post(req
, ev
);
459 * Copy the key into our state ensuring the key data buffer is always
460 * available to the all dbwrap backend over the entire lifetime of the
461 * async request. Otherwise the caller might have free'd the key buffer.
463 if (key
.dsize
> sizeof(state
->_keybuf
)) {
464 state
->key
.dptr
= talloc_memdup(state
, key
.dptr
, key
.dsize
);
465 if (tevent_req_nomem(state
->key
.dptr
, req
)) {
466 return tevent_req_post(req
, ev
);
469 memcpy(state
->_keybuf
, key
.dptr
, key
.dsize
);
470 state
->key
.dptr
= state
->_keybuf
;
472 state
->key
.dsize
= key
.dsize
;
474 subreq
= db
->parse_record_send(state
,
481 if (tevent_req_nomem(subreq
, req
)) {
482 *req_state
= DBWRAP_REQ_ERROR
;
483 return tevent_req_post(req
, ev
);
486 tevent_req_set_callback(subreq
,
487 dbwrap_parse_record_done
,
492 static void dbwrap_parse_record_done(struct tevent_req
*subreq
)
494 struct tevent_req
*req
= tevent_req_callback_data(
495 subreq
, struct tevent_req
);
496 struct dbwrap_parse_record_state
*state
= tevent_req_data(
497 req
, struct dbwrap_parse_record_state
);
500 status
= state
->db
->parse_record_recv(subreq
);
502 if (!NT_STATUS_IS_OK(status
)) {
503 tevent_req_nterror(req
, status
);
507 tevent_req_done(req
);
511 NTSTATUS
dbwrap_parse_record_recv(struct tevent_req
*req
)
513 return tevent_req_simple_recv_ntstatus(req
);
516 NTSTATUS
dbwrap_do_locked(struct db_context
*db
, TDB_DATA key
,
517 void (*fn
)(struct db_record
*rec
,
521 struct db_record
*rec
;
523 if (db
->do_locked
!= NULL
) {
524 struct db_context
**lockptr
;
527 if (db
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
528 dbwrap_lock_order_lock(db
, &lockptr
);
531 status
= db
->do_locked(db
, key
, fn
, private_data
);
533 if (db
->lock_order
!= DBWRAP_LOCK_ORDER_NONE
) {
534 dbwrap_lock_order_unlock(db
, lockptr
);
540 rec
= dbwrap_fetch_locked(db
, db
, key
);
542 return NT_STATUS_NO_MEMORY
;
545 fn(rec
, private_data
);
552 int dbwrap_wipe(struct db_context
*db
)
554 if (db
->wipe
== NULL
) {
555 return dbwrap_fallback_wipe(db
);
560 int dbwrap_check(struct db_context
*db
)
562 if (db
->check
== NULL
) {
563 return dbwrap_fallback_check(db
);
565 return db
->check(db
);
568 int dbwrap_get_seqnum(struct db_context
*db
)
570 return db
->get_seqnum(db
);
573 int dbwrap_transaction_start(struct db_context
*db
)
575 if (!db
->persistent
) {
577 * dbwrap_ctdb has two different data models for persistent
578 * and non-persistent databases. Transactions are supported
579 * only for the persistent databases. This check is here to
580 * prevent breakages of the cluster case, autobuild at this
581 * point only tests non-clustered Samba. Before removing this
582 * check, please make sure that this facility has also been
583 * added to dbwrap_ctdb.
587 DEBUG(1, ("transactions not supported on non-persistent "
588 "database %s\n", db
->name
));
591 return db
->transaction_start(db
);
594 NTSTATUS
dbwrap_transaction_start_nonblock(struct db_context
*db
)
596 if (db
->transaction_start_nonblock
) {
597 return db
->transaction_start_nonblock(db
);
599 return dbwrap_transaction_start(db
) == 0 ? NT_STATUS_OK
600 : NT_STATUS_UNSUCCESSFUL
;
604 int dbwrap_transaction_commit(struct db_context
*db
)
606 return db
->transaction_commit(db
);
609 int dbwrap_transaction_cancel(struct db_context
*db
)
611 return db
->transaction_cancel(db
);
614 size_t dbwrap_db_id(struct db_context
*db
, uint8_t *id
, size_t idlen
)
616 return db
->id(db
, id
, idlen
);
619 bool dbwrap_is_persistent(struct db_context
*db
)
621 return db
->persistent
;
624 const char *dbwrap_name(struct db_context
*db
)
629 static ssize_t
tdb_data_buf(const TDB_DATA
*dbufs
, int num_dbufs
,
630 uint8_t *buf
, size_t buflen
)
636 for (i
=0; i
<num_dbufs
; i
++) {
637 size_t thislen
= dbufs
[i
].dsize
;
640 tmp
= needed
+ thislen
;
647 if (needed
<= buflen
) {
648 memcpy(p
, dbufs
[i
].dptr
, thislen
);
657 TDB_DATA
dbwrap_merge_dbufs(TALLOC_CTX
*mem_ctx
,
658 const TDB_DATA
*dbufs
, int num_dbufs
)
660 ssize_t len
= tdb_data_buf(dbufs
, num_dbufs
, NULL
, 0);
664 return (TDB_DATA
) {0};
667 buf
= talloc_array(mem_ctx
, uint8_t, len
);
669 return (TDB_DATA
) {0};
672 tdb_data_buf(dbufs
, num_dbufs
, buf
, len
);
674 return (TDB_DATA
) { .dptr
= buf
, .dsize
= len
};