2 Unix SMB/CIFS implementation.
4 trivial database library
6 Copyright (C) Andrew Tridgell 1999-2005
7 Copyright (C) Paul `Rusty' Russell 2000
8 Copyright (C) Jeremy Allison 2000-2003
10 ** NOTE! The following LGPL license applies to the tdb
11 ** library. This does NOT imply that all of Samba is released
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Lesser General Public
16 License as published by the Free Software Foundation; either
17 version 3 of the License, or (at your option) any later version.
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Lesser General Public License for more details.
24 You should have received a copy of the GNU Lesser General Public
25 License along with this library; if not, see <http://www.gnu.org/licenses/>.
28 #include "tdb_private.h"
30 #define TDB_NEXT_LOCK_ERR ((tdb_off_t)-1)
32 /* Uses traverse lock: 0 = finish, TDB_NEXT_LOCK_ERR = error,
33 other = record offset */
34 static tdb_off_t
tdb_next_lock(struct tdb_context
*tdb
, struct tdb_traverse_lock
*tlock
,
35 struct tdb_record
*rec
)
37 int want_next
= (tlock
->off
!= 0);
39 /* Lock each chain from the start one. */
40 for (; tlock
->list
< tdb
->hash_size
; tlock
->list
++) {
41 if (!tlock
->off
&& tlock
->list
!= 0) {
42 /* this is an optimisation for the common case where
43 the hash chain is empty, which is particularly
44 common for the use of tdb with ldb, where large
45 hashes are used. In that case we spend most of our
46 time in tdb_brlock(), locking empty hash chains.
48 To avoid this, we do an unlocked pre-check to see
49 if the hash chain is empty before starting to look
50 inside it. If it is empty then we can avoid that
51 hash chain. If it isn't empty then we can't believe
52 the value we get back, as we read it without a
53 lock, so instead we get the lock and re-fetch the
56 Notice that not doing this optimisation on the
57 first hash chain is critical. We must guarantee
58 that we have done at least one fcntl lock at the
59 start of a search to guarantee that memory is
60 coherent on SMP systems. If records are added by
61 others during the search then that's OK, and we
62 could possibly miss those with this trick, but we
63 could miss them anyway without this trick, so the
64 semantics don't change.
66 With a non-indexed ldb search this trick gains us a
67 factor of around 80 in speed on a linux 2.6.x
68 system (testing using ldbtest).
70 tdb
->methods
->next_hash_chain(tdb
, &tlock
->list
);
71 if (tlock
->list
== tdb
->hash_size
) {
76 if (tdb_lock(tdb
, tlock
->list
, tlock
->lock_rw
) == -1)
77 return TDB_NEXT_LOCK_ERR
;
79 /* No previous record? Start at top of chain. */
81 if (tdb_ofs_read(tdb
, TDB_HASH_TOP(tlock
->list
),
85 /* Otherwise unlock the previous record. */
86 if (tdb_unlock_record(tdb
, tlock
->off
) != 0)
91 /* We have offset of old record: grab next */
92 if (tdb_rec_read(tdb
, tlock
->off
, rec
) == -1)
94 tlock
->off
= rec
->next
;
97 /* Iterate through chain */
99 if (tdb_rec_read(tdb
, tlock
->off
, rec
) == -1)
102 /* Detect infinite loops. From "Shlomi Yaakobovich" <Shlomi@exanet.com>. */
103 if (tlock
->off
== rec
->next
) {
104 tdb
->ecode
= TDB_ERR_CORRUPT
;
105 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_next_lock: loop detected.\n"));
109 if (!TDB_DEAD(rec
)) {
110 /* Woohoo: we found one! */
111 if (tdb_lock_record(tdb
, tlock
->off
) != 0)
116 tlock
->off
= rec
->next
;
118 tdb_unlock(tdb
, tlock
->list
, tlock
->lock_rw
);
121 /* We finished iteration without finding anything */
122 tdb
->ecode
= TDB_SUCCESS
;
127 if (tdb_unlock(tdb
, tlock
->list
, tlock
->lock_rw
) != 0)
128 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_next_lock: On error unlock failed!\n"));
129 return TDB_NEXT_LOCK_ERR
;
132 /* traverse the entire database - calling fn(tdb, key, data) on each element.
133 return -1 on error or the record count traversed
134 if fn is NULL then it is not called
135 a non-zero return value from fn() indicates that the traversal should stop
137 static int tdb_traverse_internal(struct tdb_context
*tdb
,
138 tdb_traverse_func fn
, void *private_data
,
139 struct tdb_traverse_lock
*tl
)
142 struct tdb_record rec
;
143 int ret
= 0, count
= 0;
148 key
.dptr
= malloc(recbuf_len
);
149 if (key
.dptr
== NULL
) {
153 /* This was in the initialization, above, but the IRIX compiler
154 * did not like it. crh
156 tl
->next
= tdb
->travlocks
.next
;
158 /* fcntl locks don't stack: beware traverse inside traverse */
159 tdb
->travlocks
.next
= tl
;
161 /* tdb_next_lock places locks on the record returned, and its chain */
162 while ((off
= tdb_next_lock(tdb
, tl
, &rec
)) != 0) {
166 if (off
== TDB_NEXT_LOCK_ERR
) {
171 full_len
= rec
.key_len
+ rec
.data_len
;
173 if (full_len
> recbuf_len
) {
174 recbuf_len
= full_len
;
177 * No realloc, we don't need the old data and thus can
178 * do without the memcpy
181 key
.dptr
= malloc(recbuf_len
);
183 if (key
.dptr
== NULL
) {
185 if (tdb_unlock(tdb
, tl
->list
, tl
->lock_rw
)
189 if (tdb_unlock_record(tdb
, tl
->off
) != 0) {
190 TDB_LOG((tdb
, TDB_DEBUG_FATAL
,
191 "tdb_traverse: malloc "
192 "failed and unlock_record "
200 /* now read the full record */
201 nread
= tdb
->methods
->tdb_read(tdb
, tl
->off
+ sizeof(rec
),
202 key
.dptr
, full_len
, 0);
205 if (tdb_unlock(tdb
, tl
->list
, tl
->lock_rw
) != 0)
207 if (tdb_unlock_record(tdb
, tl
->off
) != 0)
208 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
211 key
.dsize
= rec
.key_len
;
212 dbuf
.dptr
= key
.dptr
+ rec
.key_len
;
213 dbuf
.dsize
= rec
.data_len
;
215 tdb_trace_1rec_retrec(tdb
, "traverse", key
, dbuf
);
217 /* Drop chain lock, call out */
218 if (tdb_unlock(tdb
, tl
->list
, tl
->lock_rw
) != 0) {
222 if (fn
&& fn(tdb
, key
, dbuf
, private_data
)) {
223 /* They want us to terminate traversal */
224 tdb_trace_ret(tdb
, "tdb_traverse_end", count
);
225 if (tdb_unlock_record(tdb
, tl
->off
) != 0) {
226 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_traverse: unlock_record failed!\n"));;
232 tdb_trace(tdb
, "tdb_traverse_end");
235 tdb
->travlocks
.next
= tl
->next
;
244 a read style traverse - temporarily marks each record read only
246 _PUBLIC_
int tdb_traverse_read(struct tdb_context
*tdb
,
247 tdb_traverse_func fn
, void *private_data
)
249 struct tdb_traverse_lock tl
= { NULL
, 0, 0, F_RDLCK
};
252 tdb
->traverse_read
++;
253 tdb_trace(tdb
, "tdb_traverse_read_start");
254 ret
= tdb_traverse_internal(tdb
, fn
, private_data
, &tl
);
255 tdb
->traverse_read
--;
261 a write style traverse - needs to get the transaction lock to
264 WARNING: The data buffer given to the callback fn does NOT meet the
265 alignment guarantees malloc gives you.
267 _PUBLIC_
int tdb_traverse(struct tdb_context
*tdb
,
268 tdb_traverse_func fn
, void *private_data
)
270 struct tdb_traverse_lock tl
= { NULL
, 0, 0, F_WRLCK
};
271 enum tdb_lock_flags lock_flags
;
274 if (tdb
->read_only
|| tdb
->traverse_read
) {
275 return tdb_traverse_read(tdb
, fn
, private_data
);
278 lock_flags
= TDB_LOCK_WAIT
;
280 if (tdb
->allrecord_lock
.count
!= 0) {
282 * This avoids a deadlock between tdb_lockall() and
283 * tdb_traverse(). See
284 * https://bugzilla.samba.org/show_bug.cgi?id=11381
286 lock_flags
= TDB_LOCK_NOWAIT
;
289 if (tdb_transaction_lock(tdb
, F_WRLCK
, lock_flags
)) {
293 tdb
->traverse_write
++;
294 tdb_trace(tdb
, "tdb_traverse_start");
295 ret
= tdb_traverse_internal(tdb
, fn
, private_data
, &tl
);
296 tdb
->traverse_write
--;
298 tdb_transaction_unlock(tdb
, F_WRLCK
);
304 /* find the first entry in the database and return its key */
305 _PUBLIC_ TDB_DATA
tdb_firstkey(struct tdb_context
*tdb
)
308 struct tdb_record rec
;
311 /* release any old lock */
312 if (tdb_unlock_record(tdb
, tdb
->travlocks
.off
) != 0)
314 tdb
->travlocks
.off
= tdb
->travlocks
.list
= 0;
315 tdb
->travlocks
.lock_rw
= F_RDLCK
;
317 /* Grab first record: locks chain and returned record. */
318 off
= tdb_next_lock(tdb
, &tdb
->travlocks
, &rec
);
319 if (off
== 0 || off
== TDB_NEXT_LOCK_ERR
) {
320 tdb_trace_retrec(tdb
, "tdb_firstkey", tdb_null
);
323 /* now read the key */
324 key
.dsize
= rec
.key_len
;
325 key
.dptr
=tdb_alloc_read(tdb
,tdb
->travlocks
.off
+sizeof(rec
),key
.dsize
);
327 tdb_trace_retrec(tdb
, "tdb_firstkey", key
);
329 /* Unlock the hash chain of the record we just read. */
330 if (tdb_unlock(tdb
, tdb
->travlocks
.list
, tdb
->travlocks
.lock_rw
) != 0)
331 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
335 /* find the next entry in the database, returning its key */
336 _PUBLIC_ TDB_DATA
tdb_nextkey(struct tdb_context
*tdb
, TDB_DATA oldkey
)
339 TDB_DATA key
= tdb_null
;
340 struct tdb_record rec
;
341 unsigned char *k
= NULL
;
344 /* Is locked key the old key? If so, traverse will be reliable. */
345 if (tdb
->travlocks
.off
) {
346 if (tdb_lock(tdb
,tdb
->travlocks
.list
,tdb
->travlocks
.lock_rw
))
348 if (tdb_rec_read(tdb
, tdb
->travlocks
.off
, &rec
) == -1
349 || !(k
= tdb_alloc_read(tdb
,tdb
->travlocks
.off
+sizeof(rec
),
351 || memcmp(k
, oldkey
.dptr
, oldkey
.dsize
) != 0) {
352 /* No, it wasn't: unlock it and start from scratch */
353 if (tdb_unlock_record(tdb
, tdb
->travlocks
.off
) != 0) {
354 tdb_trace_1rec_retrec(tdb
, "tdb_nextkey",
359 if (tdb_unlock(tdb
, tdb
->travlocks
.list
, tdb
->travlocks
.lock_rw
) != 0) {
363 tdb
->travlocks
.off
= 0;
369 if (!tdb
->travlocks
.off
) {
370 /* No previous element: do normal find, and lock record */
371 tdb
->travlocks
.off
= tdb_find_lock_hash(tdb
, oldkey
, tdb
->hash_fn(&oldkey
), tdb
->travlocks
.lock_rw
, &rec
);
372 if (!tdb
->travlocks
.off
) {
373 tdb_trace_1rec_retrec(tdb
, "tdb_nextkey", oldkey
, tdb_null
);
376 tdb
->travlocks
.list
= BUCKET(rec
.full_hash
);
377 if (tdb_lock_record(tdb
, tdb
->travlocks
.off
) != 0) {
378 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno
)));
382 oldlist
= tdb
->travlocks
.list
;
384 /* Grab next record: locks chain and returned record,
385 unlocks old record */
386 off
= tdb_next_lock(tdb
, &tdb
->travlocks
, &rec
);
387 if (off
!= TDB_NEXT_LOCK_ERR
&& off
!= 0) {
388 key
.dsize
= rec
.key_len
;
389 key
.dptr
= tdb_alloc_read(tdb
, tdb
->travlocks
.off
+sizeof(rec
),
391 /* Unlock the chain of this new record */
392 if (tdb_unlock(tdb
, tdb
->travlocks
.list
, tdb
->travlocks
.lock_rw
) != 0)
393 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
395 /* Unlock the chain of old record */
396 if (tdb_unlock(tdb
, oldlist
, tdb
->travlocks
.lock_rw
) != 0)
397 TDB_LOG((tdb
, TDB_DEBUG_FATAL
, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
398 tdb_trace_1rec_retrec(tdb
, "tdb_nextkey", oldkey
, key
);
402 _PUBLIC_
int tdb_traverse_chain(struct tdb_context
*tdb
,
404 tdb_traverse_func fn
,
408 struct tdb_chainwalk_ctx chainwalk
;
412 if (chain
>= tdb
->hash_size
) {
413 tdb
->ecode
= TDB_ERR_EINVAL
;
417 if (tdb
->traverse_read
!= 0) {
418 tdb
->ecode
= TDB_ERR_LOCK
;
422 ret
= tdb_lock(tdb
, chain
, F_RDLCK
);
427 tdb
->traverse_read
+= 1;
429 ret
= tdb_ofs_read(tdb
, TDB_HASH_TOP(chain
), &rec_ptr
);
434 tdb_chainwalk_init(&chainwalk
, rec_ptr
);
436 while (rec_ptr
!= 0) {
437 struct tdb_record rec
;
440 ret
= tdb_rec_read(tdb
, rec_ptr
, &rec
);
445 if (!TDB_DEAD(&rec
)) {
446 /* no overflow checks, tdb_rec_read checked it */
447 tdb_off_t key_ofs
= rec_ptr
+ sizeof(rec
);
448 size_t full_len
= rec
.key_len
+ rec
.data_len
;
451 TDB_DATA key
= { .dsize
= rec
.key_len
};
452 TDB_DATA data
= { .dsize
= rec
.data_len
};
454 if ((tdb
->transaction
== NULL
) &&
455 (tdb
->map_ptr
!= NULL
)) {
456 ret
= tdb_oob(tdb
, key_ofs
, full_len
, 0);
460 key
.dptr
= (uint8_t *)tdb
->map_ptr
+ key_ofs
;
462 buf
= tdb_alloc_read(tdb
, key_ofs
, full_len
);
468 data
.dptr
= key
.dptr
+ key
.dsize
;
470 ret
= fn(tdb
, key
, data
, private_data
);
482 ok
= tdb_chainwalk_check(tdb
, &chainwalk
, rec_ptr
);
487 tdb
->traverse_read
-= 1;
488 tdb_unlock(tdb
, chain
, F_RDLCK
);
492 tdb
->traverse_read
-= 1;
493 tdb_unlock(tdb
, chain
, F_RDLCK
);
497 _PUBLIC_
int tdb_traverse_key_chain(struct tdb_context
*tdb
,
499 tdb_traverse_func fn
,
502 uint32_t hash
, chain
;
505 hash
= tdb
->hash_fn(&key
);
506 chain
= BUCKET(hash
);
507 ret
= tdb_traverse_chain(tdb
, chain
, fn
, private_data
);