Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : trivial database library
5 :
6 : Copyright (C) Andrew Tridgell 1999-2005
7 : Copyright (C) Paul `Rusty' Russell 2000
8 : Copyright (C) Jeremy Allison 2000-2003
9 :
10 : ** NOTE! The following LGPL license applies to the tdb
11 : ** library. This does NOT imply that all of Samba is released
12 : ** under the LGPL
13 :
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.
18 :
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.
23 :
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/>.
26 : */
27 :
28 : #include "tdb_private.h"
29 :
30 : _PUBLIC_ TDB_DATA tdb_null;
31 :
32 : /*
33 : non-blocking increment of the tdb sequence number if the tdb has been opened using
34 : the TDB_SEQNUM flag
35 : */
36 21795733 : _PUBLIC_ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
37 : {
38 21795733 : tdb_off_t seqnum=0;
39 :
40 21795733 : if (!(tdb->flags & TDB_SEQNUM)) {
41 153 : return;
42 : }
43 :
44 : /* we ignore errors from this, as we have no sane way of
45 : dealing with them.
46 : */
47 21795580 : tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
48 21795580 : seqnum++;
49 21795580 : tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum);
50 : }
51 :
52 : /*
53 : increment the tdb sequence number if the tdb has been opened using
54 : the TDB_SEQNUM flag
55 : */
56 88235400 : static void tdb_increment_seqnum(struct tdb_context *tdb)
57 : {
58 88235400 : if (!(tdb->flags & TDB_SEQNUM)) {
59 62007631 : return;
60 : }
61 :
62 23420182 : if (tdb->transaction != NULL) {
63 21795566 : tdb_increment_seqnum_nonblock(tdb);
64 21795566 : return;
65 : }
66 :
67 : #if defined(HAVE___ATOMIC_ADD_FETCH) && defined(HAVE___ATOMIC_ADD_LOAD)
68 1624616 : if (tdb->map_ptr != NULL) {
69 1624616 : uint32_t *pseqnum = (uint32_t *)(
70 1621192 : TDB_SEQNUM_OFS + (char *)tdb->map_ptr);
71 1624616 : __atomic_add_fetch(pseqnum, 1, __ATOMIC_SEQ_CST);
72 1624616 : return;
73 : }
74 : #endif
75 :
76 0 : if (tdb_nest_lock(tdb, TDB_SEQNUM_OFS, F_WRLCK,
77 : TDB_LOCK_WAIT|TDB_LOCK_PROBE) != 0) {
78 0 : return;
79 : }
80 :
81 0 : tdb_increment_seqnum_nonblock(tdb);
82 :
83 0 : tdb_nest_unlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, false);
84 : }
85 :
86 501223970 : static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data)
87 : {
88 501223970 : return memcmp(data.dptr, key.dptr, data.dsize);
89 : }
90 :
91 954852230 : void tdb_chainwalk_init(struct tdb_chainwalk_ctx *ctx, tdb_off_t ptr)
92 : {
93 954852230 : *ctx = (struct tdb_chainwalk_ctx) { .slow_ptr = ptr };
94 951277040 : }
95 :
96 2748991324 : bool tdb_chainwalk_check(struct tdb_context *tdb,
97 : struct tdb_chainwalk_ctx *ctx,
98 : tdb_off_t next_ptr)
99 : {
100 276859032 : int ret;
101 :
102 2748991324 : if (ctx->slow_chase) {
103 1257692170 : ret = tdb_ofs_read(tdb, ctx->slow_ptr, &ctx->slow_ptr);
104 1257692170 : if (ret == -1) {
105 0 : return false;
106 : }
107 : }
108 2748991324 : ctx->slow_chase = !ctx->slow_chase;
109 :
110 2748991324 : if (next_ptr == ctx->slow_ptr) {
111 2 : tdb->ecode = TDB_ERR_CORRUPT;
112 2 : TDB_LOG((tdb, TDB_DEBUG_ERROR,
113 : "tdb_chainwalk_check: circular chain\n"));
114 2 : return false;
115 : }
116 :
117 2472132290 : return true;
118 : }
119 :
120 : /* Returns 0 on fail. On success, return offset of record, and fills
121 : in rec */
122 807856901 : static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
123 : struct tdb_record *r)
124 : {
125 27926741 : tdb_off_t rec_ptr;
126 27926741 : struct tdb_chainwalk_ctx chainwalk;
127 :
128 : /* read in the hash top */
129 807856901 : if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
130 0 : return 0;
131 :
132 807856901 : tdb_chainwalk_init(&chainwalk, rec_ptr);
133 :
134 : /* keep looking until we find the right record */
135 2901522052 : while (rec_ptr) {
136 217947711 : bool ok;
137 :
138 2563710130 : if (tdb_rec_read(tdb, rec_ptr, r) == -1)
139 0 : return 0;
140 :
141 2563710130 : if (!TDB_DEAD(r) && hash==r->full_hash
142 501229107 : && key.dsize==r->key_len
143 501223970 : && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r),
144 : r->key_len, tdb_key_compare,
145 : NULL) == 0) {
146 470044978 : return rec_ptr;
147 : }
148 2093665152 : rec_ptr = r->next;
149 :
150 2093665152 : ok = tdb_chainwalk_check(tdb, &chainwalk, rec_ptr);
151 2093665152 : if (!ok) {
152 1 : return 0;
153 : }
154 : }
155 337811922 : tdb->ecode = TDB_ERR_NOEXIST;
156 337811922 : return 0;
157 : }
158 :
159 : /* As tdb_find, but if you succeed, keep the lock */
160 751011212 : tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype,
161 : struct tdb_record *rec)
162 : {
163 26212773 : uint32_t rec_ptr;
164 :
165 751011212 : if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
166 0 : return 0;
167 751011212 : if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
168 296018079 : tdb_unlock(tdb, BUCKET(hash), locktype);
169 724798439 : return rec_ptr;
170 : }
171 :
172 : static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
173 :
174 : struct tdb_update_hash_state {
175 : const TDB_DATA *dbufs;
176 : int num_dbufs;
177 : tdb_len_t dbufs_len;
178 : };
179 :
180 7379888 : static int tdb_update_hash_cmp(TDB_DATA key, TDB_DATA data, void *private_data)
181 : {
182 7379888 : struct tdb_update_hash_state *state = private_data;
183 7379888 : unsigned char *dptr = data.dptr;
184 315280 : int i;
185 :
186 7379888 : if (state->dbufs_len != data.dsize) {
187 0 : return -1;
188 : }
189 :
190 10229086 : for (i=0; i<state->num_dbufs; i++) {
191 8089753 : TDB_DATA dbuf = state->dbufs[i];
192 8089753 : if( dbuf.dsize > 0) {
193 321412 : int ret;
194 8089391 : ret = memcmp(dptr, dbuf.dptr, dbuf.dsize);
195 8089391 : if (ret != 0) {
196 5343545 : return -1;
197 : }
198 2848836 : dptr += dbuf.dsize;
199 : }
200 : }
201 :
202 2036343 : return 0;
203 : }
204 :
205 : /* update an entry in place - this only works if the new data size
206 : is <= the old data size and the key exists.
207 : on failure return -1.
208 : */
209 56845689 : static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key,
210 : uint32_t hash,
211 : const TDB_DATA *dbufs, int num_dbufs,
212 : tdb_len_t dbufs_len)
213 : {
214 1713968 : struct tdb_record rec;
215 1713968 : tdb_off_t rec_ptr, ofs;
216 1713968 : int i;
217 :
218 : /* find entry */
219 56845689 : if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
220 41028443 : return -1;
221 :
222 : /* it could be an exact duplicate of what is there - this is
223 : * surprisingly common (eg. with a ldb re-index). */
224 15051845 : if (rec.data_len == dbufs_len) {
225 7379888 : struct tdb_update_hash_state state = {
226 : .dbufs = dbufs, .num_dbufs = num_dbufs,
227 : .dbufs_len = dbufs_len
228 : };
229 315280 : int ret;
230 :
231 7379888 : ret = tdb_parse_record(tdb, key, tdb_update_hash_cmp, &state);
232 7379888 : if (ret == 0) {
233 2139333 : return 0;
234 : }
235 : }
236 :
237 : /* must be long enough key, data and tailer */
238 12912512 : if (rec.rec_len < key.dsize + dbufs_len + sizeof(tdb_off_t)) {
239 1306403 : tdb->ecode = TDB_SUCCESS; /* Not really an error */
240 1306403 : return -1;
241 : }
242 :
243 11606109 : ofs = rec_ptr + sizeof(rec) + rec.key_len;
244 :
245 27945874 : for (i=0; i<num_dbufs; i++) {
246 16339765 : TDB_DATA dbuf = dbufs[i];
247 824912 : int ret;
248 :
249 16339765 : ret = tdb->methods->tdb_write(tdb, ofs, dbuf.dptr, dbuf.dsize);
250 16339765 : if (ret == -1) {
251 1713968 : return -1;
252 : }
253 16339765 : ofs += dbuf.dsize;
254 : }
255 :
256 11606109 : if (dbufs_len != rec.data_len) {
257 : /* update size */
258 6365554 : rec.data_len = dbufs_len;
259 6365554 : return tdb_rec_write(tdb, rec_ptr, &rec);
260 : }
261 :
262 5028265 : return 0;
263 : }
264 :
265 : /* find an entry in the database given a key */
266 : /* If an entry doesn't exist tdb_err will be set to
267 : * TDB_ERR_NOEXIST. If a key has no data attached
268 : * then the TDB_DATA will have zero length but
269 : * a non-zero pointer
270 : */
271 150451239 : static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
272 : {
273 12614692 : tdb_off_t rec_ptr;
274 12614692 : struct tdb_record rec;
275 12614692 : TDB_DATA ret;
276 12614692 : uint32_t hash;
277 :
278 : /* find which hash bucket it is in */
279 150451239 : hash = tdb->hash_fn(&key);
280 150451239 : if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
281 96517696 : return tdb_null;
282 :
283 53933543 : ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
284 : rec.data_len);
285 53933543 : ret.dsize = rec.data_len;
286 53933543 : tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
287 53933543 : return ret;
288 : }
289 :
290 150381733 : _PUBLIC_ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
291 : {
292 150381733 : TDB_DATA ret = _tdb_fetch(tdb, key);
293 :
294 12614394 : tdb_trace_1rec_retrec(tdb, "tdb_fetch", key, ret);
295 150381733 : return ret;
296 : }
297 :
298 : /*
299 : * Find an entry in the database and hand the record's data to a parsing
300 : * function. The parsing function is executed under the chain read lock, so it
301 : * should be fast and should not block on other syscalls.
302 : *
303 : * DON'T CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
304 : *
305 : * For mmapped tdb's that do not have a transaction open it points the parsing
306 : * function directly at the mmap area, it avoids the malloc/memcpy in this
307 : * case. If a transaction is open or no mmap is available, it has to do
308 : * malloc/read/parse/free.
309 : *
310 : * This is interesting for all readers of potentially large data structures in
311 : * the tdb records, ldb indexes being one example.
312 : *
313 : * Return -1 if the record was not found.
314 : */
315 :
316 526966924 : _PUBLIC_ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
317 : int (*parser)(TDB_DATA key, TDB_DATA data,
318 : void *private_data),
319 : void *private_data)
320 : {
321 10042862 : tdb_off_t rec_ptr;
322 10042862 : struct tdb_record rec;
323 10042862 : int ret;
324 10042862 : uint32_t hash;
325 :
326 : /* find which hash bucket it is in */
327 526966924 : hash = tdb->hash_fn(&key);
328 :
329 526966924 : if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
330 : /* record not found */
331 2024308 : tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, -1);
332 129898490 : tdb->ecode = TDB_ERR_NOEXIST;
333 129898490 : return -1;
334 : }
335 8018554 : tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0);
336 :
337 397068434 : ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
338 : rec.data_len, parser, private_data);
339 :
340 397068434 : tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
341 :
342 397068434 : return ret;
343 : }
344 :
345 : /* check if an entry in the database exists
346 :
347 : note that 1 is returned if the key is found and 0 is returned if not found
348 : this doesn't match the conventions in the rest of this module, but is
349 : compatible with gdbm
350 : */
351 27514856 : static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
352 : {
353 2595582 : struct tdb_record rec;
354 :
355 27514856 : if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
356 24899418 : return 0;
357 20044 : tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
358 20044 : return 1;
359 : }
360 :
361 79593 : _PUBLIC_ int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
362 : {
363 79593 : uint32_t hash = tdb->hash_fn(&key);
364 2598 : int ret;
365 :
366 79593 : ret = tdb_exists_hash(tdb, key, hash);
367 2598 : tdb_trace_1rec_ret(tdb, "tdb_exists", key, ret);
368 79593 : return ret;
369 : }
370 :
371 : /*
372 : * Move a dead record to the freelist. The hash chain and freelist
373 : * must be locked.
374 : */
375 2989040 : static int tdb_del_dead(struct tdb_context *tdb,
376 : uint32_t last_ptr,
377 : uint32_t rec_ptr,
378 : struct tdb_record *rec,
379 : bool *deleted)
380 : {
381 215275 : int ret;
382 :
383 2989040 : ret = tdb_write_lock_record(tdb, rec_ptr);
384 2989040 : if (ret == -1) {
385 : /* Someone traversing here: Just leave it dead */
386 49410 : return 0;
387 : }
388 2901271 : ret = tdb_write_unlock_record(tdb, rec_ptr);
389 2901271 : if (ret == -1) {
390 0 : return -1;
391 : }
392 2901271 : ret = tdb_ofs_write(tdb, last_ptr, &rec->next);
393 2901271 : if (ret == -1) {
394 0 : return -1;
395 : }
396 :
397 2901271 : *deleted = true;
398 :
399 2901271 : ret = tdb_free(tdb, rec_ptr, rec);
400 2901271 : return ret;
401 : }
402 :
403 : /*
404 : * Walk the hash chain and leave tdb->max_dead_records around. Move
405 : * the rest of dead records to the freelist.
406 : */
407 73761475 : int tdb_trim_dead(struct tdb_context *tdb, uint32_t hash)
408 : {
409 3575190 : struct tdb_chainwalk_ctx chainwalk;
410 3575190 : struct tdb_record rec;
411 3575190 : tdb_off_t last_ptr, rec_ptr;
412 73761475 : bool locked_freelist = false;
413 73761475 : int num_dead = 0;
414 3575190 : int ret;
415 :
416 73761475 : last_ptr = TDB_HASH_TOP(hash);
417 :
418 : /*
419 : * Init chainwalk with the pointer to the hash top. It might
420 : * be that the very first record in the chain is a dead one
421 : * that we have to delete.
422 : */
423 73761475 : tdb_chainwalk_init(&chainwalk, last_ptr);
424 :
425 73761475 : ret = tdb_ofs_read(tdb, last_ptr, &rec_ptr);
426 73761475 : if (ret == -1) {
427 0 : return -1;
428 : }
429 :
430 343346155 : while (rec_ptr != 0) {
431 269584680 : bool deleted = false;
432 38651405 : uint32_t next;
433 :
434 269584680 : ret = tdb_rec_read(tdb, rec_ptr, &rec);
435 269584680 : if (ret == -1) {
436 0 : goto fail;
437 : }
438 :
439 : /*
440 : * Make a copy of rec.next: Further down we might
441 : * delete and put the record on the freelist. Make
442 : * sure that modifications in that code path can't
443 : * break the chainwalk here.
444 : */
445 269584680 : next = rec.next;
446 :
447 269584680 : if (rec.magic == TDB_DEAD_MAGIC) {
448 4440190 : num_dead += 1;
449 :
450 4440190 : if (num_dead > tdb->max_dead_records) {
451 :
452 2989040 : if (!locked_freelist) {
453 : /*
454 : * Lock the freelist only if
455 : * it's really required.
456 : */
457 2916880 : ret = tdb_lock(tdb, -1, F_WRLCK);
458 2916880 : if (ret == -1) {
459 0 : goto fail;
460 : };
461 2731191 : locked_freelist = true;
462 : }
463 :
464 2989040 : ret = tdb_del_dead(
465 : tdb,
466 : last_ptr,
467 : rec_ptr,
468 : &rec,
469 : &deleted);
470 :
471 2989040 : if (ret == -1) {
472 0 : goto fail;
473 : }
474 : }
475 : }
476 :
477 : /*
478 : * Don't do the chainwalk check if "rec_ptr" was
479 : * deleted. We reduced the chain, and the chainwalk
480 : * check might catch up early. Imagine a valid chain
481 : * with just dead records: We never can bump the
482 : * "slow" pointer in chainwalk_check, as there isn't
483 : * anything left to jump to and compare.
484 : */
485 269584680 : if (!deleted) {
486 38474489 : bool ok;
487 :
488 266683409 : last_ptr = rec_ptr;
489 :
490 266683409 : ok = tdb_chainwalk_check(tdb, &chainwalk, next);
491 266683409 : if (!ok) {
492 0 : ret = -1;
493 0 : goto fail;
494 : }
495 : }
496 269584680 : rec_ptr = next;
497 : }
498 70186285 : ret = 0;
499 73761475 : fail:
500 73761475 : if (locked_freelist) {
501 2916880 : tdb_unlock(tdb, -1, F_WRLCK);
502 : }
503 70186285 : return ret;
504 : }
505 :
506 : /* delete an entry in the database given a key */
507 46078192 : static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
508 : {
509 959637 : tdb_off_t rec_ptr;
510 959637 : struct tdb_record rec;
511 959637 : int ret;
512 :
513 46078192 : if (tdb->read_only || tdb->traverse_read) {
514 0 : tdb->ecode = TDB_ERR_RDONLY;
515 0 : return -1;
516 : }
517 :
518 46078192 : rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec);
519 46078192 : if (rec_ptr == 0) {
520 41333172 : return -1;
521 : }
522 :
523 : /*
524 : * Mark the record dead
525 : */
526 3971111 : rec.magic = TDB_DEAD_MAGIC;
527 3971111 : ret = tdb_rec_write(tdb, rec_ptr, &rec);
528 3971111 : if (ret == -1) {
529 0 : goto done;
530 : }
531 :
532 3971111 : tdb_increment_seqnum(tdb);
533 :
534 3971111 : ret = tdb_trim_dead(tdb, hash);
535 3971111 : done:
536 3971111 : if (tdb_unlock(tdb, BUCKET(hash), F_WRLCK) != 0)
537 0 : TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n"));
538 3785383 : return ret;
539 : }
540 :
541 2978156 : _PUBLIC_ int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
542 : {
543 2978156 : uint32_t hash = tdb->hash_fn(&key);
544 161939 : int ret;
545 :
546 2978156 : ret = tdb_delete_hash(tdb, key, hash);
547 161939 : tdb_trace_1rec_ret(tdb, "tdb_delete", key, ret);
548 2978156 : return ret;
549 : }
550 :
551 : /*
552 : * See if we have a dead record around with enough space
553 : */
554 1106182 : tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash,
555 : struct tdb_record *r, tdb_len_t length,
556 : tdb_off_t *p_last_ptr)
557 : {
558 5332 : tdb_off_t rec_ptr, last_ptr;
559 5332 : struct tdb_chainwalk_ctx chainwalk;
560 1106182 : tdb_off_t best_rec_ptr = 0;
561 1106182 : tdb_off_t best_last_ptr = 0;
562 1106182 : struct tdb_record best = { .rec_len = UINT32_MAX };
563 :
564 1106182 : length += sizeof(tdb_off_t); /* tailer */
565 :
566 1106182 : last_ptr = TDB_HASH_TOP(hash);
567 :
568 : /* read in the hash top */
569 1106182 : if (tdb_ofs_read(tdb, last_ptr, &rec_ptr) == -1)
570 0 : return 0;
571 :
572 1106182 : tdb_chainwalk_init(&chainwalk, rec_ptr);
573 :
574 : /* keep looking until we find the right record */
575 2221990 : while (rec_ptr) {
576 1803 : bool ok;
577 :
578 1115808 : if (tdb_rec_read(tdb, rec_ptr, r) == -1)
579 0 : return 0;
580 :
581 1115808 : if (TDB_DEAD(r) && (r->rec_len >= length) &&
582 968525 : (r->rec_len < best.rec_len)) {
583 733931 : best_rec_ptr = rec_ptr;
584 733931 : best_last_ptr = last_ptr;
585 733931 : best = *r;
586 : }
587 1115808 : last_ptr = rec_ptr;
588 1115808 : rec_ptr = r->next;
589 :
590 1115808 : ok = tdb_chainwalk_check(tdb, &chainwalk, rec_ptr);
591 1115808 : if (!ok) {
592 0 : return 0;
593 : }
594 : }
595 :
596 1106182 : if (best.rec_len == UINT32_MAX) {
597 373400 : return 0;
598 : }
599 :
600 728484 : *r = best;
601 728484 : *p_last_ptr = best_last_ptr;
602 728484 : return best_rec_ptr;
603 : }
604 :
605 84280952 : static int _tdb_storev(struct tdb_context *tdb, TDB_DATA key,
606 : const TDB_DATA *dbufs, int num_dbufs,
607 : int flag, uint32_t hash)
608 : {
609 4306952 : struct tdb_record rec;
610 4306952 : tdb_off_t rec_ptr, ofs;
611 4306952 : tdb_len_t rec_len, dbufs_len;
612 4306952 : int i;
613 84280952 : int ret = -1;
614 :
615 84280952 : dbufs_len = 0;
616 :
617 176420263 : for (i=0; i<num_dbufs; i++) {
618 92139311 : size_t dsize = dbufs[i].dsize;
619 :
620 92139311 : if ((dsize != 0) && (dbufs[i].dptr == NULL)) {
621 0 : tdb->ecode = TDB_ERR_EINVAL;
622 0 : goto fail;
623 : }
624 :
625 92139311 : dbufs_len += dsize;
626 92139311 : if (dbufs_len < dsize) {
627 0 : tdb->ecode = TDB_ERR_OOM;
628 0 : goto fail;
629 : }
630 : }
631 :
632 84280952 : rec_len = key.dsize + dbufs_len;
633 84280952 : if ((rec_len < key.dsize) || (rec_len < dbufs_len)) {
634 0 : tdb->ecode = TDB_ERR_OOM;
635 0 : goto fail;
636 : }
637 :
638 : /* check for it existing, on insert. */
639 84280952 : if (flag == TDB_INSERT) {
640 27435263 : if (tdb_exists_hash(tdb, key, hash)) {
641 16451 : tdb->ecode = TDB_ERR_EXISTS;
642 16451 : goto fail;
643 : }
644 : } else {
645 : /* first try in-place update, on modify or replace. */
646 56845689 : if (tdb_update_hash(tdb, key, hash, dbufs, num_dbufs,
647 : dbufs_len) == 0) {
648 13745442 : goto done;
649 : }
650 43100247 : if (tdb->ecode == TDB_ERR_NOEXIST &&
651 : flag == TDB_MODIFY) {
652 : /* if the record doesn't exist and we are in TDB_MODIFY mode then
653 : we should fail the store */
654 211 : goto fail;
655 : }
656 : }
657 : /* reset the error code potentially set by the tdb_update_hash() */
658 70518848 : tdb->ecode = TDB_SUCCESS;
659 :
660 : /* delete any existing record - if it doesn't exist we don't
661 : care. Doing this first reduces fragmentation, and avoids
662 : coalescing with `allocated' block before it's updated. */
663 70518848 : if (flag != TDB_INSERT)
664 43100036 : tdb_delete_hash(tdb, key, hash);
665 :
666 : /* we have to allocate some space */
667 70518848 : rec_ptr = tdb_allocate(tdb, hash, rec_len, &rec);
668 :
669 70518848 : if (rec_ptr == 0) {
670 1 : goto fail;
671 : }
672 :
673 : /* Read hash top into next ptr */
674 70518847 : if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
675 0 : goto fail;
676 :
677 70518847 : rec.key_len = key.dsize;
678 70518847 : rec.data_len = dbufs_len;
679 70518847 : rec.full_hash = hash;
680 70518847 : rec.magic = TDB_MAGIC;
681 :
682 70518847 : ofs = rec_ptr;
683 :
684 : /* write out and point the top of the hash chain at it */
685 70518847 : ret = tdb_rec_write(tdb, ofs, &rec);
686 70518847 : if (ret == -1) {
687 0 : goto fail;
688 : }
689 70518847 : ofs += sizeof(rec);
690 :
691 70518847 : ret = tdb->methods->tdb_write(tdb, ofs, key.dptr, key.dsize);
692 70518847 : if (ret == -1) {
693 0 : goto fail;
694 : }
695 70518847 : ofs += key.dsize;
696 :
697 144077902 : for (i=0; i<num_dbufs; i++) {
698 73559055 : if (dbufs[i].dsize == 0) {
699 890768 : continue;
700 : }
701 :
702 72668287 : ret = tdb->methods->tdb_write(tdb, ofs, dbufs[i].dptr,
703 69235347 : dbufs[i].dsize);
704 72668287 : if (ret == -1) {
705 0 : goto fail;
706 : }
707 72668287 : ofs += dbufs[i].dsize;
708 : }
709 :
710 70518847 : ret = tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr);
711 70518847 : if (ret == -1) {
712 : /* Need to tdb_unallocate() here */
713 0 : goto fail;
714 : }
715 :
716 70518847 : done:
717 84264289 : ret = 0;
718 79974000 : fail:
719 79974210 : if (ret == 0) {
720 84264289 : tdb_increment_seqnum(tdb);
721 : }
722 84280952 : return ret;
723 : }
724 :
725 76666172 : static int _tdb_store(struct tdb_context *tdb, TDB_DATA key,
726 : TDB_DATA dbuf, int flag, uint32_t hash)
727 : {
728 76666172 : return _tdb_storev(tdb, key, &dbuf, 1, flag, hash);
729 : }
730 :
731 : /* store an element in the database, replacing any existing element
732 : with the same key
733 :
734 : return 0 on success, -1 on failure
735 : */
736 76666283 : _PUBLIC_ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
737 : {
738 4160876 : uint32_t hash;
739 4160876 : int ret;
740 :
741 76666283 : if (tdb->read_only || tdb->traverse_read) {
742 111 : tdb->ecode = TDB_ERR_RDONLY;
743 0 : tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, -1);
744 111 : return -1;
745 : }
746 :
747 : /* find which hash bucket it is in */
748 76666172 : hash = tdb->hash_fn(&key);
749 76666172 : if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
750 0 : return -1;
751 :
752 76666172 : ret = _tdb_store(tdb, key, dbuf, flag, hash);
753 4160876 : tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, ret);
754 76666172 : tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
755 76666172 : return ret;
756 : }
757 :
758 7545274 : _PUBLIC_ int tdb_storev(struct tdb_context *tdb, TDB_DATA key,
759 : const TDB_DATA *dbufs, int num_dbufs, int flag)
760 : {
761 145778 : uint32_t hash;
762 145778 : int ret;
763 :
764 7545274 : if (tdb->read_only || tdb->traverse_read) {
765 0 : tdb->ecode = TDB_ERR_RDONLY;
766 0 : tdb_trace_1plusn_rec_flag_ret(tdb, "tdb_storev", key,
767 0 : dbufs, num_dbufs, flag, -1);
768 0 : return -1;
769 : }
770 :
771 : /* find which hash bucket it is in */
772 7545274 : hash = tdb->hash_fn(&key);
773 7545274 : if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
774 0 : return -1;
775 :
776 7545274 : ret = _tdb_storev(tdb, key, dbufs, num_dbufs, flag, hash);
777 145778 : tdb_trace_1plusn_rec_flag_ret(tdb, "tdb_storev", key,
778 145778 : dbufs, num_dbufs, flag, -1);
779 7545274 : tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
780 7545274 : return ret;
781 : }
782 :
783 : /* Append to an entry. Create if not exist. */
784 69506 : _PUBLIC_ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
785 : {
786 298 : uint32_t hash;
787 298 : TDB_DATA dbufs[2];
788 69506 : int ret = -1;
789 :
790 : /* find which hash bucket it is in */
791 69506 : hash = tdb->hash_fn(&key);
792 69506 : if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
793 0 : return -1;
794 :
795 69506 : dbufs[0] = _tdb_fetch(tdb, key);
796 69506 : dbufs[1] = new_dbuf;
797 :
798 69506 : ret = _tdb_storev(tdb, key, dbufs, 2, 0, hash);
799 298 : tdb_trace_2rec_retrec(tdb, "tdb_append", key, dbufs[0], dbufs[1]);
800 :
801 69506 : tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
802 69506 : SAFE_FREE(dbufs[0].dptr);
803 69208 : return ret;
804 : }
805 :
806 :
807 : /*
808 : return the name of the current tdb file
809 : useful for external logging functions
810 : */
811 4173257 : _PUBLIC_ const char *tdb_name(struct tdb_context *tdb)
812 : {
813 4173257 : return tdb->name;
814 : }
815 :
816 : /*
817 : return the underlying file descriptor being used by tdb, or -1
818 : useful for external routines that want to check the device/inode
819 : of the fd
820 : */
821 1622794 : _PUBLIC_ int tdb_fd(struct tdb_context *tdb)
822 : {
823 1622794 : return tdb->fd;
824 : }
825 :
826 : /*
827 : return the current logging function
828 : useful for external tdb routines that wish to log tdb errors
829 : */
830 0 : _PUBLIC_ tdb_log_func tdb_log_fn(struct tdb_context *tdb)
831 : {
832 0 : return tdb->log.log_fn;
833 : }
834 :
835 :
836 : /*
837 : get the tdb sequence number. Only makes sense if the writers opened
838 : with TDB_SEQNUM set. Note that this sequence number will wrap quite
839 : quickly, so it should only be used for a 'has something changed'
840 : test, not for code that relies on the count of the number of changes
841 : made. If you want a counter then use a tdb record.
842 :
843 : The aim of this sequence number is to allow for a very lightweight
844 : test of a possible tdb change.
845 : */
846 196540729 : _PUBLIC_ int tdb_get_seqnum(struct tdb_context *tdb)
847 : {
848 196540729 : tdb_off_t seqnum=0;
849 :
850 196540729 : if (tdb->transaction != NULL) {
851 60782295 : tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
852 60782295 : return seqnum;
853 : }
854 :
855 : #if defined(HAVE___ATOMIC_ADD_FETCH) && defined(HAVE___ATOMIC_ADD_LOAD)
856 135758434 : if (tdb->map_ptr != NULL) {
857 135758434 : uint32_t *pseqnum = (uint32_t *)(
858 130861139 : TDB_SEQNUM_OFS + (char *)tdb->map_ptr);
859 4897295 : uint32_t ret;
860 135758434 : __atomic_load(pseqnum, &ret,__ATOMIC_SEQ_CST);
861 135758434 : return ret;
862 : }
863 : #endif
864 :
865 0 : tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
866 0 : return seqnum;
867 : }
868 :
869 632 : _PUBLIC_ int tdb_hash_size(struct tdb_context *tdb)
870 : {
871 632 : return tdb->hash_size;
872 : }
873 :
874 6 : _PUBLIC_ size_t tdb_map_size(struct tdb_context *tdb)
875 : {
876 6 : return tdb->map_size;
877 : }
878 :
879 6 : _PUBLIC_ int tdb_get_flags(struct tdb_context *tdb)
880 : {
881 6 : return tdb->flags;
882 : }
883 :
884 4 : _PUBLIC_ void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
885 : {
886 4 : if ((flags & TDB_ALLOW_NESTING) &&
887 0 : (flags & TDB_DISALLOW_NESTING)) {
888 0 : tdb->ecode = TDB_ERR_NESTING;
889 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_add_flags: "
890 : "allow_nesting and disallow_nesting are not allowed together!"));
891 0 : return;
892 : }
893 :
894 4 : if (flags & TDB_ALLOW_NESTING) {
895 0 : tdb->flags &= ~TDB_DISALLOW_NESTING;
896 : }
897 4 : if (flags & TDB_DISALLOW_NESTING) {
898 0 : tdb->flags &= ~TDB_ALLOW_NESTING;
899 : }
900 :
901 4 : tdb->flags |= flags;
902 : }
903 :
904 4 : _PUBLIC_ void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
905 : {
906 4 : if ((flags & TDB_ALLOW_NESTING) &&
907 0 : (flags & TDB_DISALLOW_NESTING)) {
908 0 : tdb->ecode = TDB_ERR_NESTING;
909 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_remove_flags: "
910 : "allow_nesting and disallow_nesting are not allowed together!"));
911 0 : return;
912 : }
913 :
914 4 : if ((flags & TDB_NOLOCK) &&
915 0 : (tdb->feature_flags & TDB_FEATURE_FLAG_MUTEX) &&
916 0 : (tdb->mutexes == NULL)) {
917 0 : tdb->ecode = TDB_ERR_LOCK;
918 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_remove_flags: "
919 : "Can not remove NOLOCK flag on mutexed databases"));
920 0 : return;
921 : }
922 :
923 4 : if (flags & TDB_ALLOW_NESTING) {
924 0 : tdb->flags |= TDB_DISALLOW_NESTING;
925 : }
926 4 : if (flags & TDB_DISALLOW_NESTING) {
927 0 : tdb->flags |= TDB_ALLOW_NESTING;
928 : }
929 :
930 4 : tdb->flags &= ~flags;
931 : }
932 :
933 :
934 : /*
935 : enable sequence number handling on an open tdb
936 : */
937 2 : _PUBLIC_ void tdb_enable_seqnum(struct tdb_context *tdb)
938 : {
939 2 : tdb->flags |= TDB_SEQNUM;
940 2 : }
941 :
942 :
943 : /*
944 : add a region of the file to the freelist. Length is the size of the region in bytes,
945 : which includes the free list header that needs to be added
946 : */
947 317 : static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length)
948 : {
949 17 : struct tdb_record rec;
950 317 : if (length <= sizeof(rec)) {
951 : /* the region is not worth adding */
952 103 : return 0;
953 : }
954 206 : if (length + offset > tdb->map_size) {
955 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: adding region beyond end of file\n"));
956 0 : return -1;
957 : }
958 206 : memset(&rec,'\0',sizeof(rec));
959 206 : rec.rec_len = length - sizeof(rec);
960 206 : if (tdb_free(tdb, offset, &rec) == -1) {
961 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: failed to add free record\n"));
962 0 : return -1;
963 : }
964 197 : return 0;
965 : }
966 :
967 : /*
968 : wipe the entire database, deleting all records. This can be done
969 : very fast by using a allrecord lock. The entire data portion of the
970 : file becomes a single entry in the freelist.
971 :
972 : This code carefully steps around the recovery area, leaving it alone
973 : */
974 223 : _PUBLIC_ int tdb_wipe_all(struct tdb_context *tdb)
975 : {
976 12 : uint32_t i;
977 223 : tdb_off_t offset = 0;
978 12 : ssize_t data_len;
979 12 : tdb_off_t recovery_head;
980 223 : tdb_len_t recovery_size = 0;
981 :
982 223 : if (tdb_lockall(tdb) != 0) {
983 58 : return -1;
984 : }
985 :
986 12 : tdb_trace(tdb, "tdb_wipe_all");
987 :
988 : /* see if the tdb has a recovery area, and remember its size
989 : if so. We don't want to lose this as otherwise each
990 : tdb_wipe_all() in a transaction will increase the size of
991 : the tdb by the size of the recovery area */
992 165 : if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
993 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery head\n"));
994 0 : goto failed;
995 : }
996 :
997 165 : if (recovery_head != 0) {
998 5 : struct tdb_record rec;
999 152 : if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
1000 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery record\n"));
1001 0 : return -1;
1002 : }
1003 152 : recovery_size = rec.rec_len + sizeof(rec);
1004 : }
1005 :
1006 : /* wipe the hashes */
1007 199901 : for (i=0;i<tdb->hash_size;i++) {
1008 199736 : if (tdb_ofs_write(tdb, TDB_HASH_TOP(i), &offset) == -1) {
1009 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write hash %d\n", i));
1010 0 : goto failed;
1011 : }
1012 : }
1013 :
1014 : /* wipe the freelist */
1015 165 : if (tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
1016 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write freelist\n"));
1017 0 : goto failed;
1018 : }
1019 :
1020 : /* add all the rest of the file to the freelist, possibly leaving a gap
1021 : for the recovery area */
1022 165 : if (recovery_size == 0) {
1023 : /* the simple case - the whole file can be used as a freelist */
1024 13 : data_len = (tdb->map_size - TDB_DATA_START(tdb->hash_size));
1025 13 : if (tdb_free_region(tdb, TDB_DATA_START(tdb->hash_size), data_len) != 0) {
1026 0 : goto failed;
1027 : }
1028 : } else {
1029 : /* we need to add two freelist entries - one on either
1030 : side of the recovery area
1031 :
1032 : Note that we cannot shift the recovery area during
1033 : this operation. Only the transaction.c code may
1034 : move the recovery area or we risk subtle data
1035 : corruption
1036 : */
1037 152 : data_len = (recovery_head - TDB_DATA_START(tdb->hash_size));
1038 152 : if (tdb_free_region(tdb, TDB_DATA_START(tdb->hash_size), data_len) != 0) {
1039 0 : goto failed;
1040 : }
1041 : /* and the 2nd free list entry after the recovery area - if any */
1042 152 : data_len = tdb->map_size - (recovery_head+recovery_size);
1043 152 : if (tdb_free_region(tdb, recovery_head+recovery_size, data_len) != 0) {
1044 0 : goto failed;
1045 : }
1046 : }
1047 :
1048 165 : tdb_increment_seqnum_nonblock(tdb);
1049 :
1050 165 : if (tdb_unlockall(tdb) != 0) {
1051 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
1052 0 : goto failed;
1053 : }
1054 :
1055 153 : return 0;
1056 :
1057 0 : failed:
1058 0 : tdb_unlockall(tdb);
1059 0 : return -1;
1060 : }
1061 :
1062 : struct traverse_state {
1063 : bool error;
1064 : struct tdb_context *dest_db;
1065 : };
1066 :
1067 : /*
1068 : traverse function for repacking
1069 : */
1070 155560 : static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private_data)
1071 : {
1072 155560 : struct traverse_state *state = (struct traverse_state *)private_data;
1073 155560 : if (tdb_store(state->dest_db, key, data, TDB_INSERT) != 0) {
1074 0 : state->error = true;
1075 0 : return -1;
1076 : }
1077 105042 : return 0;
1078 : }
1079 :
1080 : /*
1081 : repack a tdb
1082 : */
1083 116 : _PUBLIC_ int tdb_repack(struct tdb_context *tdb)
1084 : {
1085 7 : struct tdb_context *tmp_db;
1086 7 : struct traverse_state state;
1087 :
1088 7 : tdb_trace(tdb, "tdb_repack");
1089 :
1090 116 : if (tdb_transaction_start(tdb) != 0) {
1091 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to start transaction\n"));
1092 0 : return -1;
1093 : }
1094 :
1095 116 : tmp_db = tdb_open("tmpdb", tdb_hash_size(tdb), TDB_INTERNAL, O_RDWR|O_CREAT, 0);
1096 116 : if (tmp_db == NULL) {
1097 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to create tmp_db\n"));
1098 0 : tdb_transaction_cancel(tdb);
1099 0 : return -1;
1100 : }
1101 :
1102 116 : state.error = false;
1103 116 : state.dest_db = tmp_db;
1104 :
1105 116 : if (tdb_traverse_read(tdb, repack_traverse, &state) == -1) {
1106 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to traverse copying out\n"));
1107 0 : tdb_transaction_cancel(tdb);
1108 0 : tdb_close(tmp_db);
1109 0 : return -1;
1110 : }
1111 :
1112 116 : if (state.error) {
1113 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Error during traversal\n"));
1114 0 : tdb_transaction_cancel(tdb);
1115 0 : tdb_close(tmp_db);
1116 0 : return -1;
1117 : }
1118 :
1119 116 : if (tdb_wipe_all(tdb) != 0) {
1120 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to wipe database\n"));
1121 0 : tdb_transaction_cancel(tdb);
1122 0 : tdb_close(tmp_db);
1123 0 : return -1;
1124 : }
1125 :
1126 116 : state.error = false;
1127 116 : state.dest_db = tdb;
1128 :
1129 116 : if (tdb_traverse_read(tmp_db, repack_traverse, &state) == -1) {
1130 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to traverse copying back\n"));
1131 0 : tdb_transaction_cancel(tdb);
1132 0 : tdb_close(tmp_db);
1133 0 : return -1;
1134 : }
1135 :
1136 116 : if (state.error) {
1137 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Error during second traversal\n"));
1138 0 : tdb_transaction_cancel(tdb);
1139 0 : tdb_close(tmp_db);
1140 0 : return -1;
1141 : }
1142 :
1143 116 : tdb_close(tmp_db);
1144 :
1145 116 : if (tdb_transaction_commit(tdb) != 0) {
1146 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, __location__ " Failed to commit\n"));
1147 0 : return -1;
1148 : }
1149 :
1150 109 : return 0;
1151 : }
1152 :
1153 : /* Even on files, we can get partial writes due to signals. */
1154 771933 : bool tdb_write_all(int fd, const void *buf, size_t count)
1155 : {
1156 1543866 : while (count) {
1157 20486 : ssize_t ret;
1158 771933 : ret = write(fd, buf, count);
1159 771933 : if (ret < 0)
1160 0 : return false;
1161 771933 : buf = (const char *)buf + ret;
1162 771933 : count -= ret;
1163 : }
1164 751447 : return true;
1165 : }
1166 :
1167 24589029 : bool tdb_add_off_t(tdb_off_t a, tdb_off_t b, tdb_off_t *pret)
1168 : {
1169 24589029 : tdb_off_t ret = a + b;
1170 :
1171 24589029 : if ((ret < a) || (ret < b)) {
1172 0 : return false;
1173 : }
1174 24589029 : *pret = ret;
1175 24589029 : return true;
1176 : }
1177 :
1178 : #ifdef TDB_TRACE
1179 : static void tdb_trace_write(struct tdb_context *tdb, const char *str)
1180 : {
1181 : if (!tdb_write_all(tdb->tracefd, str, strlen(str))) {
1182 : close(tdb->tracefd);
1183 : tdb->tracefd = -1;
1184 : }
1185 : }
1186 :
1187 : static void tdb_trace_start(struct tdb_context *tdb)
1188 : {
1189 : tdb_off_t seqnum=0;
1190 : char msg[sizeof(tdb_off_t) * 4 + 1];
1191 :
1192 : tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
1193 : snprintf(msg, sizeof(msg), "%u ", seqnum);
1194 : tdb_trace_write(tdb, msg);
1195 : }
1196 :
1197 : static void tdb_trace_end(struct tdb_context *tdb)
1198 : {
1199 : tdb_trace_write(tdb, "\n");
1200 : }
1201 :
1202 : static void tdb_trace_end_ret(struct tdb_context *tdb, int ret)
1203 : {
1204 : char msg[sizeof(ret) * 4 + 4];
1205 : snprintf(msg, sizeof(msg), " = %i\n", ret);
1206 : tdb_trace_write(tdb, msg);
1207 : }
1208 :
1209 : static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec)
1210 : {
1211 : char msg[20 + rec.dsize*2], *p;
1212 : unsigned int i;
1213 :
1214 : /* We differentiate zero-length records from non-existent ones. */
1215 : if (rec.dptr == NULL) {
1216 : tdb_trace_write(tdb, " NULL");
1217 : return;
1218 : }
1219 :
1220 : /* snprintf here is purely cargo-cult programming. */
1221 : p = msg;
1222 : p += snprintf(p, sizeof(msg), " %zu:", rec.dsize);
1223 : for (i = 0; i < rec.dsize; i++)
1224 : p += snprintf(p, 2, "%02x", rec.dptr[i]);
1225 :
1226 : tdb_trace_write(tdb, msg);
1227 : }
1228 :
1229 : void tdb_trace(struct tdb_context *tdb, const char *op)
1230 : {
1231 : tdb_trace_start(tdb);
1232 : tdb_trace_write(tdb, op);
1233 : tdb_trace_end(tdb);
1234 : }
1235 :
1236 : void tdb_trace_seqnum(struct tdb_context *tdb, uint32_t seqnum, const char *op)
1237 : {
1238 : char msg[sizeof(tdb_off_t) * 4 + 1];
1239 :
1240 : snprintf(msg, sizeof(msg), "%u ", seqnum);
1241 : tdb_trace_write(tdb, msg);
1242 : tdb_trace_write(tdb, op);
1243 : tdb_trace_end(tdb);
1244 : }
1245 :
1246 : void tdb_trace_open(struct tdb_context *tdb, const char *op,
1247 : unsigned hash_size, unsigned tdb_flags, unsigned open_flags)
1248 : {
1249 : char msg[128];
1250 :
1251 : snprintf(msg, sizeof(msg),
1252 : "%s %u 0x%x 0x%x", op, hash_size, tdb_flags, open_flags);
1253 : tdb_trace_start(tdb);
1254 : tdb_trace_write(tdb, msg);
1255 : tdb_trace_end(tdb);
1256 : }
1257 :
1258 : void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret)
1259 : {
1260 : tdb_trace_start(tdb);
1261 : tdb_trace_write(tdb, op);
1262 : tdb_trace_end_ret(tdb, ret);
1263 : }
1264 :
1265 : void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret)
1266 : {
1267 : tdb_trace_start(tdb);
1268 : tdb_trace_write(tdb, op);
1269 : tdb_trace_write(tdb, " =");
1270 : tdb_trace_record(tdb, ret);
1271 : tdb_trace_end(tdb);
1272 : }
1273 :
1274 : void tdb_trace_1rec(struct tdb_context *tdb, const char *op,
1275 : TDB_DATA rec)
1276 : {
1277 : tdb_trace_start(tdb);
1278 : tdb_trace_write(tdb, op);
1279 : tdb_trace_record(tdb, rec);
1280 : tdb_trace_end(tdb);
1281 : }
1282 :
1283 : void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op,
1284 : TDB_DATA rec, int ret)
1285 : {
1286 : tdb_trace_start(tdb);
1287 : tdb_trace_write(tdb, op);
1288 : tdb_trace_record(tdb, rec);
1289 : tdb_trace_end_ret(tdb, ret);
1290 : }
1291 :
1292 : void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op,
1293 : TDB_DATA rec, TDB_DATA ret)
1294 : {
1295 : tdb_trace_start(tdb);
1296 : tdb_trace_write(tdb, op);
1297 : tdb_trace_record(tdb, rec);
1298 : tdb_trace_write(tdb, " =");
1299 : tdb_trace_record(tdb, ret);
1300 : tdb_trace_end(tdb);
1301 : }
1302 :
1303 : void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op,
1304 : TDB_DATA rec1, TDB_DATA rec2, unsigned flag,
1305 : int ret)
1306 : {
1307 : char msg[1 + sizeof(ret) * 4];
1308 :
1309 : snprintf(msg, sizeof(msg), " %#x", flag);
1310 : tdb_trace_start(tdb);
1311 : tdb_trace_write(tdb, op);
1312 : tdb_trace_record(tdb, rec1);
1313 : tdb_trace_record(tdb, rec2);
1314 : tdb_trace_write(tdb, msg);
1315 : tdb_trace_end_ret(tdb, ret);
1316 : }
1317 :
1318 : void tdb_trace_1plusn_rec_flag_ret(struct tdb_context *tdb, const char *op,
1319 : TDB_DATA rec,
1320 : const TDB_DATA *recs, int num_recs,
1321 : unsigned flag, int ret)
1322 : {
1323 : char msg[1 + sizeof(ret) * 4];
1324 : int i;
1325 :
1326 : snprintf(msg, sizeof(msg), " %#x", flag);
1327 : tdb_trace_start(tdb);
1328 : tdb_trace_write(tdb, op);
1329 : tdb_trace_record(tdb, rec);
1330 : for (i=0; i<num_recs; i++) {
1331 : tdb_trace_record(tdb, recs[i]);
1332 : }
1333 : tdb_trace_write(tdb, msg);
1334 : tdb_trace_end_ret(tdb, ret);
1335 : }
1336 :
1337 : void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op,
1338 : TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret)
1339 : {
1340 : tdb_trace_start(tdb);
1341 : tdb_trace_write(tdb, op);
1342 : tdb_trace_record(tdb, rec1);
1343 : tdb_trace_record(tdb, rec2);
1344 : tdb_trace_write(tdb, " =");
1345 : tdb_trace_record(tdb, ret);
1346 : tdb_trace_end(tdb);
1347 : }
1348 : #endif
|