tdb2: make TDB1 code use tdb2's TDB_ERROR and tdb_logerr()
[Samba/gbeck.git] / lib / tdb2 / tdb1_tdb.c
blob802311504508cca01c040cadc00849983897ba30
1 /*
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
12 ** under the LGPL
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 "tdb1_private.h"
30 TDB1_DATA tdb1_null;
33 non-blocking increment of the tdb sequence number if the tdb has been opened using
34 the TDB1_SEQNUM flag
36 void tdb1_increment_seqnum_nonblock(struct tdb1_context *tdb)
38 tdb1_off_t seqnum=0;
40 if (!(tdb->flags & TDB1_SEQNUM)) {
41 return;
44 /* we ignore errors from this, as we have no sane way of
45 dealing with them.
47 tdb1_ofs_read(tdb, TDB1_SEQNUM_OFS, &seqnum);
48 seqnum++;
49 tdb1_ofs_write(tdb, TDB1_SEQNUM_OFS, &seqnum);
53 increment the tdb sequence number if the tdb has been opened using
54 the TDB1_SEQNUM flag
56 static void tdb1_increment_seqnum(struct tdb1_context *tdb)
58 if (!(tdb->flags & TDB1_SEQNUM)) {
59 return;
62 if (tdb1_nest_lock(tdb, TDB1_SEQNUM_OFS, F_WRLCK,
63 TDB1_LOCK_WAIT|TDB1_LOCK_PROBE) != 0) {
64 return;
67 tdb1_increment_seqnum_nonblock(tdb);
69 tdb1_nest_unlock(tdb, TDB1_SEQNUM_OFS, F_WRLCK);
72 static int tdb1_key_compare(TDB1_DATA key, TDB1_DATA data, void *private_data)
74 return memcmp(data.dptr, key.dptr, data.dsize);
77 /* Returns 0 on fail. On success, return offset of record, and fills
78 in rec */
79 static tdb1_off_t tdb1_find(struct tdb1_context *tdb, TDB1_DATA key, uint32_t hash,
80 struct tdb1_record *r)
82 tdb1_off_t rec_ptr;
84 /* read in the hash top */
85 if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1)
86 return 0;
88 /* keep looking until we find the right record */
89 while (rec_ptr) {
90 if (tdb1_rec_read(tdb, rec_ptr, r) == -1)
91 return 0;
93 if (!TDB1_DEAD(r) && hash==r->full_hash
94 && key.dsize==r->key_len
95 && tdb1_parse_data(tdb, key, rec_ptr + sizeof(*r),
96 r->key_len, tdb1_key_compare,
97 NULL) == 0) {
98 return rec_ptr;
100 /* detect tight infinite loop */
101 if (rec_ptr == r->next) {
102 tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT,
103 TDB_LOG_ERROR,
104 "tdb1_find: loop detected.");
105 return 0;
107 rec_ptr = r->next;
109 tdb->last_error = TDB_ERR_NOEXIST;
110 return 0;
113 /* As tdb1_find, but if you succeed, keep the lock */
114 tdb1_off_t tdb1_find_lock_hash(struct tdb1_context *tdb, TDB1_DATA key, uint32_t hash, int locktype,
115 struct tdb1_record *rec)
117 uint32_t rec_ptr;
119 if (tdb1_lock(tdb, TDB1_BUCKET(hash), locktype) == -1)
120 return 0;
121 if (!(rec_ptr = tdb1_find(tdb, key, hash, rec)))
122 tdb1_unlock(tdb, TDB1_BUCKET(hash), locktype);
123 return rec_ptr;
126 static TDB1_DATA _tdb1_fetch(struct tdb1_context *tdb, TDB1_DATA key);
128 /* update an entry in place - this only works if the new data size
129 is <= the old data size and the key exists.
130 on failure return -1.
132 static int tdb1_update_hash(struct tdb1_context *tdb, TDB1_DATA key, uint32_t hash, TDB1_DATA dbuf)
134 struct tdb1_record rec;
135 tdb1_off_t rec_ptr;
137 /* find entry */
138 if (!(rec_ptr = tdb1_find(tdb, key, hash, &rec)))
139 return -1;
141 /* it could be an exact duplicate of what is there - this is
142 * surprisingly common (eg. with a ldb re-index). */
143 if (rec.key_len == key.dsize &&
144 rec.data_len == dbuf.dsize &&
145 rec.full_hash == hash) {
146 TDB1_DATA data = _tdb1_fetch(tdb, key);
147 if (data.dsize == dbuf.dsize &&
148 memcmp(data.dptr, dbuf.dptr, data.dsize) == 0) {
149 if (data.dptr) {
150 free(data.dptr);
152 return 0;
154 if (data.dptr) {
155 free(data.dptr);
159 /* must be long enough key, data and tailer */
160 if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb1_off_t)) {
161 tdb->last_error = TDB_SUCCESS; /* Not really an error */
162 return -1;
165 if (tdb->methods->tdb1_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
166 dbuf.dptr, dbuf.dsize) == -1)
167 return -1;
169 if (dbuf.dsize != rec.data_len) {
170 /* update size */
171 rec.data_len = dbuf.dsize;
172 return tdb1_rec_write(tdb, rec_ptr, &rec);
175 return 0;
178 /* find an entry in the database given a key */
179 /* If an entry doesn't exist tdb1_err will be set to
180 * TDB_ERR_NOEXIST. If a key has no data attached
181 * then the TDB1_DATA will have zero length but
182 * a non-zero pointer
184 static TDB1_DATA _tdb1_fetch(struct tdb1_context *tdb, TDB1_DATA key)
186 tdb1_off_t rec_ptr;
187 struct tdb1_record rec;
188 TDB1_DATA ret;
189 uint32_t hash;
191 /* find which hash bucket it is in */
192 hash = tdb->hash_fn(&key);
193 if (!(rec_ptr = tdb1_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
194 return tdb1_null;
196 ret.dptr = tdb1_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
197 rec.data_len);
198 ret.dsize = rec.data_len;
199 tdb1_unlock(tdb, TDB1_BUCKET(rec.full_hash), F_RDLCK);
200 return ret;
203 TDB1_DATA tdb1_fetch(struct tdb1_context *tdb, TDB1_DATA key)
205 TDB1_DATA ret = _tdb1_fetch(tdb, key);
207 return ret;
211 * Find an entry in the database and hand the record's data to a parsing
212 * function. The parsing function is executed under the chain read lock, so it
213 * should be fast and should not block on other syscalls.
215 * DON'T CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
217 * For mmapped tdb's that do not have a transaction open it points the parsing
218 * function directly at the mmap area, it avoids the malloc/memcpy in this
219 * case. If a transaction is open or no mmap is available, it has to do
220 * malloc/read/parse/free.
222 * This is interesting for all readers of potentially large data structures in
223 * the tdb records, ldb indexes being one example.
225 * Return -1 if the record was not found.
228 int tdb1_parse_record(struct tdb1_context *tdb, TDB1_DATA key,
229 int (*parser)(TDB1_DATA key, TDB1_DATA data,
230 void *private_data),
231 void *private_data)
233 tdb1_off_t rec_ptr;
234 struct tdb1_record rec;
235 int ret;
236 uint32_t hash;
238 /* find which hash bucket it is in */
239 hash = tdb->hash_fn(&key);
241 if (!(rec_ptr = tdb1_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
242 /* record not found */
243 tdb->last_error = TDB_ERR_NOEXIST;
244 return -1;
247 ret = tdb1_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
248 rec.data_len, parser, private_data);
250 tdb1_unlock(tdb, TDB1_BUCKET(rec.full_hash), F_RDLCK);
252 return ret;
255 /* check if an entry in the database exists
257 note that 1 is returned if the key is found and 0 is returned if not found
258 this doesn't match the conventions in the rest of this module, but is
259 compatible with gdbm
261 static int tdb1_exists_hash(struct tdb1_context *tdb, TDB1_DATA key, uint32_t hash)
263 struct tdb1_record rec;
265 if (tdb1_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
266 return 0;
267 tdb1_unlock(tdb, TDB1_BUCKET(rec.full_hash), F_RDLCK);
268 return 1;
271 int tdb1_exists(struct tdb1_context *tdb, TDB1_DATA key)
273 uint32_t hash = tdb->hash_fn(&key);
274 int ret;
276 ret = tdb1_exists_hash(tdb, key, hash);
277 return ret;
280 /* actually delete an entry in the database given the offset */
281 int tdb1_do_delete(struct tdb1_context *tdb, tdb1_off_t rec_ptr, struct tdb1_record *rec)
283 tdb1_off_t last_ptr, i;
284 struct tdb1_record lastrec;
286 if (tdb->read_only || tdb->traverse_read) return -1;
288 if (((tdb->traverse_write != 0) && (!TDB1_DEAD(rec))) ||
289 tdb1_write_lock_record(tdb, rec_ptr) == -1) {
290 /* Someone traversing here: mark it as dead */
291 rec->magic = TDB1_DEAD_MAGIC;
292 return tdb1_rec_write(tdb, rec_ptr, rec);
294 if (tdb1_write_unlock_record(tdb, rec_ptr) != 0)
295 return -1;
297 /* find previous record in hash chain */
298 if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(rec->full_hash), &i) == -1)
299 return -1;
300 for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
301 if (tdb1_rec_read(tdb, i, &lastrec) == -1)
302 return -1;
304 /* unlink it: next ptr is at start of record. */
305 if (last_ptr == 0)
306 last_ptr = TDB1_HASH_TOP(rec->full_hash);
307 if (tdb1_ofs_write(tdb, last_ptr, &rec->next) == -1)
308 return -1;
310 /* recover the space */
311 if (tdb1_free(tdb, rec_ptr, rec) == -1)
312 return -1;
313 return 0;
316 static int tdb1_count_dead(struct tdb1_context *tdb, uint32_t hash)
318 int res = 0;
319 tdb1_off_t rec_ptr;
320 struct tdb1_record rec;
322 /* read in the hash top */
323 if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1)
324 return 0;
326 while (rec_ptr) {
327 if (tdb1_rec_read(tdb, rec_ptr, &rec) == -1)
328 return 0;
330 if (rec.magic == TDB1_DEAD_MAGIC) {
331 res += 1;
333 rec_ptr = rec.next;
335 return res;
339 * Purge all DEAD records from a hash chain
341 static int tdb1_purge_dead(struct tdb1_context *tdb, uint32_t hash)
343 int res = -1;
344 struct tdb1_record rec;
345 tdb1_off_t rec_ptr;
347 if (tdb1_lock(tdb, -1, F_WRLCK) == -1) {
348 return -1;
351 /* read in the hash top */
352 if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1)
353 goto fail;
355 while (rec_ptr) {
356 tdb1_off_t next;
358 if (tdb1_rec_read(tdb, rec_ptr, &rec) == -1) {
359 goto fail;
362 next = rec.next;
364 if (rec.magic == TDB1_DEAD_MAGIC
365 && tdb1_do_delete(tdb, rec_ptr, &rec) == -1) {
366 goto fail;
368 rec_ptr = next;
370 res = 0;
371 fail:
372 tdb1_unlock(tdb, -1, F_WRLCK);
373 return res;
376 /* delete an entry in the database given a key */
377 static int tdb1_delete_hash(struct tdb1_context *tdb, TDB1_DATA key, uint32_t hash)
379 tdb1_off_t rec_ptr;
380 struct tdb1_record rec;
381 int ret;
383 if (tdb->max_dead_records != 0) {
386 * Allow for some dead records per hash chain, mainly for
387 * tdb's with a very high create/delete rate like locking.tdb.
390 if (tdb1_lock(tdb, TDB1_BUCKET(hash), F_WRLCK) == -1)
391 return -1;
393 if (tdb1_count_dead(tdb, hash) >= tdb->max_dead_records) {
395 * Don't let the per-chain freelist grow too large,
396 * delete all existing dead records
398 tdb1_purge_dead(tdb, hash);
401 if (!(rec_ptr = tdb1_find(tdb, key, hash, &rec))) {
402 tdb1_unlock(tdb, TDB1_BUCKET(hash), F_WRLCK);
403 return -1;
407 * Just mark the record as dead.
409 rec.magic = TDB1_DEAD_MAGIC;
410 ret = tdb1_rec_write(tdb, rec_ptr, &rec);
412 else {
413 if (!(rec_ptr = tdb1_find_lock_hash(tdb, key, hash, F_WRLCK,
414 &rec)))
415 return -1;
417 ret = tdb1_do_delete(tdb, rec_ptr, &rec);
420 if (ret == 0) {
421 tdb1_increment_seqnum(tdb);
424 if (tdb1_unlock(tdb, TDB1_BUCKET(rec.full_hash), F_WRLCK) != 0)
425 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
426 "tdb1_delete: WARNING tdb1_unlock failed!");
427 return ret;
430 int tdb1_delete(struct tdb1_context *tdb, TDB1_DATA key)
432 uint32_t hash = tdb->hash_fn(&key);
433 int ret;
435 ret = tdb1_delete_hash(tdb, key, hash);
436 return ret;
440 * See if we have a dead record around with enough space
442 static tdb1_off_t tdb1_find_dead(struct tdb1_context *tdb, uint32_t hash,
443 struct tdb1_record *r, tdb1_len_t length)
445 tdb1_off_t rec_ptr;
447 /* read in the hash top */
448 if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1)
449 return 0;
451 /* keep looking until we find the right record */
452 while (rec_ptr) {
453 if (tdb1_rec_read(tdb, rec_ptr, r) == -1)
454 return 0;
456 if (TDB1_DEAD(r) && r->rec_len >= length) {
458 * First fit for simple coding, TODO: change to best
459 * fit
461 return rec_ptr;
463 rec_ptr = r->next;
465 return 0;
468 static int _tdb1_store(struct tdb1_context *tdb, TDB1_DATA key,
469 TDB1_DATA dbuf, int flag, uint32_t hash)
471 struct tdb1_record rec;
472 tdb1_off_t rec_ptr;
473 char *p = NULL;
474 int ret = -1;
476 /* check for it existing, on insert. */
477 if (flag == TDB1_INSERT) {
478 if (tdb1_exists_hash(tdb, key, hash)) {
479 tdb->last_error = TDB_ERR_EXISTS;
480 goto fail;
482 } else {
483 /* first try in-place update, on modify or replace. */
484 if (tdb1_update_hash(tdb, key, hash, dbuf) == 0) {
485 goto done;
487 if (tdb->last_error == TDB_ERR_NOEXIST &&
488 flag == TDB1_MODIFY) {
489 /* if the record doesn't exist and we are in TDB1_MODIFY mode then
490 we should fail the store */
491 goto fail;
494 /* reset the error code potentially set by the tdb1_update() */
495 tdb->last_error = TDB_SUCCESS;
497 /* delete any existing record - if it doesn't exist we don't
498 care. Doing this first reduces fragmentation, and avoids
499 coalescing with `allocated' block before it's updated. */
500 if (flag != TDB1_INSERT)
501 tdb1_delete_hash(tdb, key, hash);
503 /* Copy key+value *before* allocating free space in case malloc
504 fails and we are left with a dead spot in the tdb. */
506 if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
507 tdb->last_error = TDB_ERR_OOM;
508 goto fail;
511 memcpy(p, key.dptr, key.dsize);
512 if (dbuf.dsize)
513 memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
515 if (tdb->max_dead_records != 0) {
517 * Allow for some dead records per hash chain, look if we can
518 * find one that can hold the new record. We need enough space
519 * for key, data and tailer. If we find one, we don't have to
520 * consult the central freelist.
522 rec_ptr = tdb1_find_dead(
523 tdb, hash, &rec,
524 key.dsize + dbuf.dsize + sizeof(tdb1_off_t));
526 if (rec_ptr != 0) {
527 rec.key_len = key.dsize;
528 rec.data_len = dbuf.dsize;
529 rec.full_hash = hash;
530 rec.magic = TDB1_MAGIC;
531 if (tdb1_rec_write(tdb, rec_ptr, &rec) == -1
532 || tdb->methods->tdb1_write(
533 tdb, rec_ptr + sizeof(rec),
534 p, key.dsize + dbuf.dsize) == -1) {
535 goto fail;
537 goto done;
542 * We have to allocate some space from the freelist, so this means we
543 * have to lock it. Use the chance to purge all the DEAD records from
544 * the hash chain under the freelist lock.
547 if (tdb1_lock(tdb, -1, F_WRLCK) == -1) {
548 goto fail;
551 if ((tdb->max_dead_records != 0)
552 && (tdb1_purge_dead(tdb, hash) == -1)) {
553 tdb1_unlock(tdb, -1, F_WRLCK);
554 goto fail;
557 /* we have to allocate some space */
558 rec_ptr = tdb1_allocate(tdb, key.dsize + dbuf.dsize, &rec);
560 tdb1_unlock(tdb, -1, F_WRLCK);
562 if (rec_ptr == 0) {
563 goto fail;
566 /* Read hash top into next ptr */
567 if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec.next) == -1)
568 goto fail;
570 rec.key_len = key.dsize;
571 rec.data_len = dbuf.dsize;
572 rec.full_hash = hash;
573 rec.magic = TDB1_MAGIC;
575 /* write out and point the top of the hash chain at it */
576 if (tdb1_rec_write(tdb, rec_ptr, &rec) == -1
577 || tdb->methods->tdb1_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
578 || tdb1_ofs_write(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1) {
579 /* Need to tdb1_unallocate() here */
580 goto fail;
583 done:
584 ret = 0;
585 fail:
586 if (ret == 0) {
587 tdb1_increment_seqnum(tdb);
590 SAFE_FREE(p);
591 return ret;
594 /* store an element in the database, replacing any existing element
595 with the same key
597 return 0 on success, -1 on failure
599 int tdb1_store(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA dbuf, int flag)
601 uint32_t hash;
602 int ret;
604 if (tdb->read_only || tdb->traverse_read) {
605 tdb->last_error = TDB_ERR_RDONLY;
606 return -1;
609 /* find which hash bucket it is in */
610 hash = tdb->hash_fn(&key);
611 if (tdb1_lock(tdb, TDB1_BUCKET(hash), F_WRLCK) == -1)
612 return -1;
614 ret = _tdb1_store(tdb, key, dbuf, flag, hash);
615 tdb1_unlock(tdb, TDB1_BUCKET(hash), F_WRLCK);
616 return ret;
619 /* Append to an entry. Create if not exist. */
620 int tdb1_append(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA new_dbuf)
622 uint32_t hash;
623 TDB1_DATA dbuf;
624 int ret = -1;
626 /* find which hash bucket it is in */
627 hash = tdb->hash_fn(&key);
628 if (tdb1_lock(tdb, TDB1_BUCKET(hash), F_WRLCK) == -1)
629 return -1;
631 dbuf = _tdb1_fetch(tdb, key);
633 if (dbuf.dptr == NULL) {
634 dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
635 } else {
636 unsigned int new_len = dbuf.dsize + new_dbuf.dsize;
637 unsigned char *new_dptr;
639 /* realloc '0' is special: don't do that. */
640 if (new_len == 0)
641 new_len = 1;
642 new_dptr = (unsigned char *)realloc(dbuf.dptr, new_len);
643 if (new_dptr == NULL) {
644 free(dbuf.dptr);
646 dbuf.dptr = new_dptr;
649 if (dbuf.dptr == NULL) {
650 tdb->last_error = TDB_ERR_OOM;
651 goto failed;
654 memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize);
655 dbuf.dsize += new_dbuf.dsize;
657 ret = _tdb1_store(tdb, key, dbuf, 0, hash);
659 failed:
660 tdb1_unlock(tdb, TDB1_BUCKET(hash), F_WRLCK);
661 SAFE_FREE(dbuf.dptr);
662 return ret;
667 get the tdb sequence number. Only makes sense if the writers opened
668 with TDB1_SEQNUM set. Note that this sequence number will wrap quite
669 quickly, so it should only be used for a 'has something changed'
670 test, not for code that relies on the count of the number of changes
671 made. If you want a counter then use a tdb record.
673 The aim of this sequence number is to allow for a very lightweight
674 test of a possible tdb change.
676 int tdb1_get_seqnum(struct tdb1_context *tdb)
678 tdb1_off_t seqnum=0;
680 tdb1_ofs_read(tdb, TDB1_SEQNUM_OFS, &seqnum);
681 return seqnum;
684 int tdb1_hash_size(struct tdb1_context *tdb)
686 return tdb->header.hash_size;
691 add a region of the file to the freelist. Length is the size of the region in bytes,
692 which includes the free list header that needs to be added
694 static int tdb1_free_region(struct tdb1_context *tdb, tdb1_off_t offset, ssize_t length)
696 struct tdb1_record rec;
697 if (length <= sizeof(rec)) {
698 /* the region is not worth adding */
699 return 0;
701 if (length + offset > tdb->map_size) {
702 tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
703 "tdb1_free_region: adding region beyond"
704 " end of file");
705 return -1;
707 memset(&rec,'\0',sizeof(rec));
708 rec.rec_len = length - sizeof(rec);
709 if (tdb1_free(tdb, offset, &rec) == -1) {
710 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
711 "tdb1_free_region: failed to add free record");
712 return -1;
714 return 0;
718 wipe the entire database, deleting all records. This can be done
719 very fast by using a allrecord lock. The entire data portion of the
720 file becomes a single entry in the freelist.
722 This code carefully steps around the recovery area, leaving it alone
724 int tdb1_wipe_all(struct tdb1_context *tdb)
726 int i;
727 tdb1_off_t offset = 0;
728 ssize_t data_len;
729 tdb1_off_t recovery_head;
730 tdb1_len_t recovery_size = 0;
732 if (tdb1_lockall(tdb) != 0) {
733 return -1;
737 /* see if the tdb has a recovery area, and remember its size
738 if so. We don't want to lose this as otherwise each
739 tdb1_wipe_all() in a transaction will increase the size of
740 the tdb by the size of the recovery area */
741 if (tdb1_ofs_read(tdb, TDB1_RECOVERY_HEAD, &recovery_head) == -1) {
742 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
743 "tdb1_wipe_all: failed to read recovery head");
744 goto failed;
747 if (recovery_head != 0) {
748 struct tdb1_record rec;
749 if (tdb->methods->tdb1_read(tdb, recovery_head, &rec, sizeof(rec), TDB1_DOCONV()) == -1) {
750 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
751 "tdb1_wipe_all: failed to read recovery record");
752 return -1;
754 recovery_size = rec.rec_len + sizeof(rec);
757 /* wipe the hashes */
758 for (i=0;i<tdb->header.hash_size;i++) {
759 if (tdb1_ofs_write(tdb, TDB1_HASH_TOP(i), &offset) == -1) {
760 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
761 "tdb1_wipe_all: failed to write hash %d", i);
762 goto failed;
766 /* wipe the freelist */
767 if (tdb1_ofs_write(tdb, TDB1_FREELIST_TOP, &offset) == -1) {
768 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
769 "tdb1_wipe_all: failed to write freelist");
770 goto failed;
773 /* add all the rest of the file to the freelist, possibly leaving a gap
774 for the recovery area */
775 if (recovery_size == 0) {
776 /* the simple case - the whole file can be used as a freelist */
777 data_len = (tdb->map_size - TDB1_DATA_START(tdb->header.hash_size));
778 if (tdb1_free_region(tdb, TDB1_DATA_START(tdb->header.hash_size), data_len) != 0) {
779 goto failed;
781 } else {
782 /* we need to add two freelist entries - one on either
783 side of the recovery area
785 Note that we cannot shift the recovery area during
786 this operation. Only the transaction.c code may
787 move the recovery area or we risk subtle data
788 corruption
790 data_len = (recovery_head - TDB1_DATA_START(tdb->header.hash_size));
791 if (tdb1_free_region(tdb, TDB1_DATA_START(tdb->header.hash_size), data_len) != 0) {
792 goto failed;
794 /* and the 2nd free list entry after the recovery area - if any */
795 data_len = tdb->map_size - (recovery_head+recovery_size);
796 if (tdb1_free_region(tdb, recovery_head+recovery_size, data_len) != 0) {
797 goto failed;
801 if (tdb1_unlockall(tdb) != 0) {
802 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
803 "tdb1_wipe_all: failed to unlock");
804 goto failed;
807 return 0;
809 failed:
810 tdb1_unlockall(tdb);
811 return -1;
814 struct traverse_state {
815 enum TDB_ERROR error;
816 struct tdb1_context *dest_db;
820 traverse function for repacking
822 static int repack_traverse(struct tdb1_context *tdb, TDB1_DATA key, TDB1_DATA data, void *private_data)
824 struct traverse_state *state = (struct traverse_state *)private_data;
825 if (tdb1_store(state->dest_db, key, data, TDB1_INSERT) != 0) {
826 state->error = state->dest_db->last_error;
827 return -1;
829 return 0;
833 repack a tdb
835 int tdb1_repack(struct tdb1_context *tdb)
837 struct tdb1_context *tmp_db;
838 struct traverse_state state;
840 if (tdb1_transaction_start(tdb) != 0) {
841 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
842 __location__ " Failed to start transaction");
843 return -1;
846 tmp_db = tdb1_open("tmpdb", tdb1_hash_size(tdb), TDB1_INTERNAL, O_RDWR|O_CREAT, 0);
847 if (tmp_db == NULL) {
848 tdb->last_error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
849 __location__ " Failed to create tmp_db");
850 tdb1_transaction_cancel(tdb);
851 return -1;
854 state.error = TDB_SUCCESS;
855 state.dest_db = tmp_db;
857 if (tdb1_traverse_read(tdb, repack_traverse, &state) == -1) {
858 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
859 __location__ " Failed to traverse copying out");
860 tdb1_transaction_cancel(tdb);
861 tdb1_close(tmp_db);
862 return -1;
865 if (state.error != TDB_SUCCESS) {
866 tdb->last_error = tdb_logerr(tdb, state.error, TDB_LOG_ERROR,
867 __location__ " Error during traversal");
868 tdb1_transaction_cancel(tdb);
869 tdb1_close(tmp_db);
870 return -1;
873 if (tdb1_wipe_all(tdb) != 0) {
874 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
875 __location__ " Failed to wipe database\n");
876 tdb1_transaction_cancel(tdb);
877 tdb1_close(tmp_db);
878 return -1;
881 state.error = TDB_SUCCESS;
882 state.dest_db = tdb;
884 if (tdb1_traverse_read(tmp_db, repack_traverse, &state) == -1) {
885 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
886 __location__ " Failed to traverse copying back");
887 tdb1_transaction_cancel(tdb);
888 tdb1_close(tmp_db);
889 return -1;
892 if (state.error) {
893 tdb->last_error = tdb_logerr(tdb, state.error, TDB_LOG_ERROR,
894 __location__ " Error during second traversal");
895 tdb1_transaction_cancel(tdb);
896 tdb1_close(tmp_db);
897 return -1;
900 tdb1_close(tmp_db);
902 if (tdb1_transaction_commit(tdb) != 0) {
903 tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
904 __location__ " Failed to commit");
905 return -1;
908 return 0;
911 /* Even on files, we can get partial writes due to signals. */
912 bool tdb1_write_all(int fd, const void *buf, size_t count)
914 while (count) {
915 ssize_t ret;
916 ret = write(fd, buf, count);
917 if (ret < 0)
918 return false;
919 buf = (const char *)buf + ret;
920 count -= ret;
922 return true;