]> git.ozlabs.org Git - ccan/blob - ccan/tdb/tdb.c
Automate rerunning when we have to back off: gives accurate(ish) timings.
[ccan] / ccan / tdb / tdb.c
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 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 void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
37 {
38         tdb_off_t seqnum=0;
39         
40         if (!(tdb->flags & TDB_SEQNUM)) {
41                 return;
42         }
43
44         /* we ignore errors from this, as we have no sane way of
45            dealing with them.
46         */
47         tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
48         seqnum++;
49         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 static void tdb_increment_seqnum(struct tdb_context *tdb)
57 {
58         if (!(tdb->flags & TDB_SEQNUM)) {
59                 return;
60         }
61
62         if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) {
63                 return;
64         }
65
66         tdb_increment_seqnum_nonblock(tdb);
67
68         tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1);
69 }
70
71 static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data)
72 {
73         return memcmp(data.dptr, key.dptr, data.dsize);
74 }
75
76 /* Returns 0 on fail.  On success, return offset of record, and fills
77    in rec */
78 static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
79                         struct list_struct *r)
80 {
81         tdb_off_t rec_ptr;
82         
83         /* read in the hash top */
84         if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
85                 return 0;
86
87         /* keep looking until we find the right record */
88         while (rec_ptr) {
89                 if (tdb_rec_read(tdb, rec_ptr, r) == -1)
90                         return 0;
91
92                 if (!TDB_DEAD(r) && hash==r->full_hash
93                     && key.dsize==r->key_len
94                     && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r),
95                                       r->key_len, tdb_key_compare,
96                                       NULL) == 0) {
97                         return rec_ptr;
98                 }
99                 rec_ptr = r->next;
100         }
101         return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
102 }
103
104 /* As tdb_find, but if you succeed, keep the lock */
105 tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype,
106                            struct list_struct *rec)
107 {
108         uint32_t rec_ptr;
109
110         if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
111                 return 0;
112         if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
113                 tdb_unlock(tdb, BUCKET(hash), locktype);
114         return rec_ptr;
115 }
116
117
118 /* update an entry in place - this only works if the new data size
119    is <= the old data size and the key exists.
120    on failure return -1.
121 */
122 static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, TDB_DATA dbuf)
123 {
124         struct list_struct rec;
125         tdb_off_t rec_ptr;
126
127         /* find entry */
128         if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
129                 return -1;
130
131         /* must be long enough key, data and tailer */
132         if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) {
133                 tdb->ecode = TDB_SUCCESS; /* Not really an error */
134                 return -1;
135         }
136
137         if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
138                       dbuf.dptr, dbuf.dsize) == -1)
139                 return -1;
140
141         if (dbuf.dsize != rec.data_len) {
142                 /* update size */
143                 rec.data_len = dbuf.dsize;
144                 return tdb_rec_write(tdb, rec_ptr, &rec);
145         }
146  
147         return 0;
148 }
149
150 /* find an entry in the database given a key */
151 /* If an entry doesn't exist tdb_err will be set to
152  * TDB_ERR_NOEXIST. If a key has no data attached
153  * then the TDB_DATA will have zero length but
154  * a non-zero pointer
155  */
156 static TDB_DATA do_tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
157 {
158         tdb_off_t rec_ptr;
159         struct list_struct rec;
160         TDB_DATA ret;
161         uint32_t hash;
162
163         /* find which hash bucket it is in */
164         hash = tdb->hash_fn(&key);
165         if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
166                 return tdb_null;
167         }
168         ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
169                                   rec.data_len);
170         ret.dsize = rec.data_len;
171         tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
172         return ret;
173 }
174
175 TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
176 {
177         TDB_DATA ret = do_tdb_fetch(tdb, key);
178
179         tdb_trace_1rec_retrec(tdb, "tdb_fetch", key, ret);
180         return ret;
181 }
182
183 /*
184  * Find an entry in the database and hand the record's data to a parsing
185  * function. The parsing function is executed under the chain read lock, so it
186  * should be fast and should not block on other syscalls.
187  *
188  * DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
189  *
190  * For mmapped tdb's that do not have a transaction open it points the parsing
191  * function directly at the mmap area, it avoids the malloc/memcpy in this
192  * case. If a transaction is open or no mmap is available, it has to do
193  * malloc/read/parse/free.
194  *
195  * This is interesting for all readers of potentially large data structures in
196  * the tdb records, ldb indexes being one example.
197  */
198
199 int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
200                      int (*parser)(TDB_DATA key, TDB_DATA data,
201                                    void *private_data),
202                      void *private_data)
203 {
204         tdb_off_t rec_ptr;
205         struct list_struct rec;
206         int ret;
207         uint32_t hash;
208
209         /* find which hash bucket it is in */
210         hash = tdb->hash_fn(&key);
211
212         if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
213                 tdb_trace_1rec_ret(tdb, "tdb_parse_record", key,
214                                    -TDB_ERR_NOEXIST);
215                 return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
216         }
217         tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0);
218
219         ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
220                              rec.data_len, parser, private_data);
221
222         tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
223
224         return ret;
225 }
226
227 /* check if an entry in the database exists 
228
229    note that 1 is returned if the key is found and 0 is returned if not found
230    this doesn't match the conventions in the rest of this module, but is
231    compatible with gdbm
232 */
233 static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
234 {
235         struct list_struct rec;
236         
237         if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
238                 return 0;
239         tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
240         return 1;
241 }
242
243 int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
244 {
245         uint32_t hash = tdb->hash_fn(&key);
246         int ret;
247
248         ret = tdb_exists_hash(tdb, key, hash);
249         tdb_trace_1rec_ret(tdb, "tdb_exists", key, ret);
250         return ret;
251 }
252
253 /* actually delete an entry in the database given the offset */
254 int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec)
255 {
256         tdb_off_t last_ptr, i;
257         struct list_struct lastrec;
258
259         if (tdb->read_only || tdb->traverse_read) return -1;
260
261         if (tdb->traverse_write != 0 || 
262             tdb_write_lock_record(tdb, rec_ptr) == -1) {
263                 /* Someone traversing here: mark it as dead */
264                 rec->magic = TDB_DEAD_MAGIC;
265                 return tdb_rec_write(tdb, rec_ptr, rec);
266         }
267         if (tdb_write_unlock_record(tdb, rec_ptr) != 0)
268                 return -1;
269
270         /* find previous record in hash chain */
271         if (tdb_ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
272                 return -1;
273         for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
274                 if (tdb_rec_read(tdb, i, &lastrec) == -1)
275                         return -1;
276
277         /* unlink it: next ptr is at start of record. */
278         if (last_ptr == 0)
279                 last_ptr = TDB_HASH_TOP(rec->full_hash);
280         if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1)
281                 return -1;
282
283         /* recover the space */
284         if (tdb_free(tdb, rec_ptr, rec) == -1)
285                 return -1;
286         return 0;
287 }
288
289 static int tdb_count_dead(struct tdb_context *tdb, uint32_t hash)
290 {
291         int res = 0;
292         tdb_off_t rec_ptr;
293         struct list_struct rec;
294         
295         /* read in the hash top */
296         if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
297                 return 0;
298
299         while (rec_ptr) {
300                 if (tdb_rec_read(tdb, rec_ptr, &rec) == -1)
301                         return 0;
302
303                 if (rec.magic == TDB_DEAD_MAGIC) {
304                         res += 1;
305                 }
306                 rec_ptr = rec.next;
307         }
308         return res;
309 }
310
311 /*
312  * Purge all DEAD records from a hash chain
313  */
314 static int tdb_purge_dead(struct tdb_context *tdb, uint32_t hash)
315 {
316         int res = -1;
317         struct list_struct rec;
318         tdb_off_t rec_ptr;
319
320         if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
321                 return -1;
322         }
323         
324         /* read in the hash top */
325         if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
326                 goto fail;
327
328         while (rec_ptr) {
329                 tdb_off_t next;
330
331                 if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) {
332                         goto fail;
333                 }
334
335                 next = rec.next;
336
337                 if (rec.magic == TDB_DEAD_MAGIC
338                     && tdb_do_delete(tdb, rec_ptr, &rec) == -1) {
339                         goto fail;
340                 }
341                 rec_ptr = next;
342         }
343         res = 0;
344  fail:
345         tdb_unlock(tdb, -1, F_WRLCK);
346         return res;
347 }
348
349 /* delete an entry in the database given a key */
350 static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
351 {
352         tdb_off_t rec_ptr;
353         struct list_struct rec;
354         int ret;
355
356         if (tdb->max_dead_records != 0) {
357
358                 /*
359                  * Allow for some dead records per hash chain, mainly for
360                  * tdb's with a very high create/delete rate like locking.tdb.
361                  */
362
363                 if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
364                         return -1;
365
366                 if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) {
367                         /*
368                          * Don't let the per-chain freelist grow too large,
369                          * delete all existing dead records
370                          */
371                         tdb_purge_dead(tdb, hash);
372                 }
373
374                 if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
375                         tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
376                         return -1;
377                 }
378
379                 /*
380                  * Just mark the record as dead.
381                  */
382                 rec.magic = TDB_DEAD_MAGIC;
383                 ret = tdb_rec_write(tdb, rec_ptr, &rec);
384         }
385         else {
386                 if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK,
387                                                    &rec)))
388                         return -1;
389
390                 ret = tdb_do_delete(tdb, rec_ptr, &rec);
391         }
392
393         if (ret == 0) {
394                 tdb_increment_seqnum(tdb);
395         }
396
397         if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
398                 TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n"));
399         return ret;
400 }
401
402 int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
403 {
404         uint32_t hash = tdb->hash_fn(&key);
405         int ret;
406
407         ret = tdb_delete_hash(tdb, key, hash);
408         tdb_trace_1rec_ret(tdb, "tdb_delete", key, ret);
409         return ret;
410 }
411
412 /*
413  * See if we have a dead record around with enough space
414  */
415 static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash,
416                                struct list_struct *r, tdb_len_t length)
417 {
418         tdb_off_t rec_ptr;
419         
420         /* read in the hash top */
421         if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
422                 return 0;
423
424         /* keep looking until we find the right record */
425         while (rec_ptr) {
426                 if (tdb_rec_read(tdb, rec_ptr, r) == -1)
427                         return 0;
428
429                 if (TDB_DEAD(r) && r->rec_len >= length) {
430                         /*
431                          * First fit for simple coding, TODO: change to best
432                          * fit
433                          */
434                         return rec_ptr;
435                 }
436                 rec_ptr = r->next;
437         }
438         return 0;
439 }
440
441 static int _tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
442                       int flag, uint32_t hash)
443 {
444         struct list_struct rec;
445         tdb_off_t rec_ptr;
446         char *p = NULL;
447         int ret = -1;
448
449         /* check for it existing, on insert. */
450         if (flag == TDB_INSERT) {
451                 if (tdb_exists_hash(tdb, key, hash)) {
452                         tdb->ecode = TDB_ERR_EXISTS;
453                         goto fail;
454                 }
455         } else {
456                 /* first try in-place update, on modify or replace. */
457                 if (tdb_update_hash(tdb, key, hash, dbuf) == 0) {
458                         goto done;
459                 }
460                 if (tdb->ecode == TDB_ERR_NOEXIST &&
461                     flag == TDB_MODIFY) {
462                         /* if the record doesn't exist and we are in TDB_MODIFY mode then
463                          we should fail the store */
464                         goto fail;
465                 }
466         }
467         /* reset the error code potentially set by the tdb_update() */
468         tdb->ecode = TDB_SUCCESS;
469
470         /* delete any existing record - if it doesn't exist we don't
471            care.  Doing this first reduces fragmentation, and avoids
472            coalescing with `allocated' block before it's updated. */
473         if (flag != TDB_INSERT)
474                 tdb_delete_hash(tdb, key, hash);
475
476         /* Copy key+value *before* allocating free space in case malloc
477            fails and we are left with a dead spot in the tdb. */
478
479         if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
480                 tdb->ecode = TDB_ERR_OOM;
481                 goto fail;
482         }
483
484         memcpy(p, key.dptr, key.dsize);
485         if (dbuf.dsize)
486                 memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
487
488         if (tdb->max_dead_records != 0) {
489                 /*
490                  * Allow for some dead records per hash chain, look if we can
491                  * find one that can hold the new record. We need enough space
492                  * for key, data and tailer. If we find one, we don't have to
493                  * consult the central freelist.
494                  */
495                 rec_ptr = tdb_find_dead(
496                         tdb, hash, &rec,
497                         key.dsize + dbuf.dsize + sizeof(tdb_off_t));
498
499                 if (rec_ptr != 0) {
500                         rec.key_len = key.dsize;
501                         rec.data_len = dbuf.dsize;
502                         rec.full_hash = hash;
503                         rec.magic = TDB_MAGIC;
504                         if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
505                             || tdb->methods->tdb_write(
506                                     tdb, rec_ptr + sizeof(rec),
507                                     p, key.dsize + dbuf.dsize) == -1) {
508                                 goto fail;
509                         }
510                         goto done;
511                 }
512         }
513
514         /*
515          * We have to allocate some space from the freelist, so this means we
516          * have to lock it. Use the chance to purge all the DEAD records from
517          * the hash chain under the freelist lock.
518          */
519
520         if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
521                 goto fail;
522         }
523
524         if ((tdb->max_dead_records != 0)
525             && (tdb_purge_dead(tdb, hash) == -1)) {
526                 tdb_unlock(tdb, -1, F_WRLCK);
527                 goto fail;
528         }
529
530         /* we have to allocate some space */
531         rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec);
532
533         tdb_unlock(tdb, -1, F_WRLCK);
534
535         if (rec_ptr == 0) {
536                 goto fail;
537         }
538
539         /* Read hash top into next ptr */
540         if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
541                 goto fail;
542
543         rec.key_len = key.dsize;
544         rec.data_len = dbuf.dsize;
545         rec.full_hash = hash;
546         rec.magic = TDB_MAGIC;
547
548         /* write out and point the top of the hash chain at it */
549         if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
550             || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
551             || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
552                 /* Need to tdb_unallocate() here */
553                 goto fail;
554         }
555
556  done:
557         ret = 0;
558  fail:
559         if (ret == 0) {
560                 tdb_increment_seqnum(tdb);
561         }
562
563         SAFE_FREE(p); 
564         return ret;
565 }
566
567 /* store an element in the database, replacing any existing element
568    with the same key 
569
570    return 0 on success, -1 on failure
571 */
572 int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
573 {
574         uint32_t hash;
575         int ret;
576
577         if (tdb->read_only || tdb->traverse_read) {
578                 tdb->ecode = TDB_ERR_RDONLY;
579                 tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag,
580                                         -TDB_ERR_RDONLY);
581                 return -1;
582         }
583
584         /* find which hash bucket it is in */
585         hash = tdb->hash_fn(&key);
586         if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
587                 return -1;
588
589         ret = _tdb_store(tdb, key, dbuf, flag, hash);
590         tdb_trace_2rec_flag_ret(tdb, "tdb_store", key, dbuf, flag, ret);
591         tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
592         return ret;
593 }
594
595
596 /* Append to an entry. Create if not exist. */
597 int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
598 {
599         uint32_t hash;
600         TDB_DATA dbuf;
601         int ret = -1;
602
603         /* find which hash bucket it is in */
604         hash = tdb->hash_fn(&key);
605         if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
606                 return -1;
607
608         dbuf = do_tdb_fetch(tdb, key);
609
610         if (dbuf.dptr == NULL) {
611                 dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
612         } else {
613                 unsigned int new_len = dbuf.dsize + new_dbuf.dsize;
614                 unsigned char *new_dptr;
615
616                 /* realloc '0' is special: don't do that. */
617                 if (new_len == 0)
618                         new_len = 1;
619                 new_dptr = (unsigned char *)realloc(dbuf.dptr, new_len);
620                 if (new_dptr == NULL) {
621                         free(dbuf.dptr);
622                 }
623                 dbuf.dptr = new_dptr;
624         }
625
626         if (dbuf.dptr == NULL) {
627                 tdb->ecode = TDB_ERR_OOM;
628                 goto failed;
629         }
630
631         memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize);
632         dbuf.dsize += new_dbuf.dsize;
633
634         ret = _tdb_store(tdb, key, dbuf, 0, hash);
635         tdb_trace_2rec_retrec(tdb, "tdb_append", key, new_dbuf, dbuf);
636         
637 failed:
638         tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
639         SAFE_FREE(dbuf.dptr);
640         return ret;
641 }
642
643
644 /*
645   return the name of the current tdb file
646   useful for external logging functions
647 */
648 const char *tdb_name(struct tdb_context *tdb)
649 {
650         return tdb->name;
651 }
652
653 /*
654   return the underlying file descriptor being used by tdb, or -1
655   useful for external routines that want to check the device/inode
656   of the fd
657 */
658 int tdb_fd(struct tdb_context *tdb)
659 {
660         return tdb->fd;
661 }
662
663 /*
664   return the current logging function
665   useful for external tdb routines that wish to log tdb errors
666 */
667 tdb_log_func tdb_log_fn(struct tdb_context *tdb)
668 {
669         return tdb->log.log_fn;
670 }
671
672
673 /*
674   get the tdb sequence number. Only makes sense if the writers opened
675   with TDB_SEQNUM set. Note that this sequence number will wrap quite
676   quickly, so it should only be used for a 'has something changed'
677   test, not for code that relies on the count of the number of changes
678   made. If you want a counter then use a tdb record.
679
680   The aim of this sequence number is to allow for a very lightweight
681   test of a possible tdb change.
682 */
683 int tdb_get_seqnum(struct tdb_context *tdb)
684 {
685         tdb_off_t seqnum=0;
686
687         tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
688         tdb_trace_ret(tdb, "tdb_get_seqnum", seqnum);
689         return seqnum;
690 }
691
692 int tdb_hash_size(struct tdb_context *tdb)
693 {
694         return tdb->header.hash_size;
695 }
696
697 size_t tdb_map_size(struct tdb_context *tdb)
698 {
699         return tdb->map_size;
700 }
701
702 int tdb_get_flags(struct tdb_context *tdb)
703 {
704         return tdb->flags;
705 }
706
707 void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
708 {
709         tdb->flags |= flags;
710 }
711
712 void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
713 {
714         tdb->flags &= ~flags;
715 }
716
717
718 /*
719   enable sequence number handling on an open tdb
720 */
721 void tdb_enable_seqnum(struct tdb_context *tdb)
722 {
723         tdb->flags |= TDB_SEQNUM;
724 }
725
726
727 /*
728   add a region of the file to the freelist. Length is the size of the region in bytes, 
729   which includes the free list header that needs to be added
730  */
731 static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length)
732 {
733         struct list_struct rec;
734         if (length <= sizeof(rec)) {
735                 /* the region is not worth adding */
736                 return 0;
737         }
738         if (length + offset > tdb->map_size) {
739                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: adding region beyond end of file\n"));
740                 return -1;              
741         }
742         memset(&rec,'\0',sizeof(rec));
743         rec.rec_len = length - sizeof(rec);
744         if (tdb_free(tdb, offset, &rec) == -1) {
745                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: failed to add free record\n"));
746                 return -1;
747         }
748         return 0;
749 }
750
751 /*
752   wipe the entire database, deleting all records. This can be done
753   very fast by using a global lock. The entire data portion of the
754   file becomes a single entry in the freelist.
755
756   This code carefully steps around the recovery area, leaving it alone
757  */
758 int tdb_wipe_all(struct tdb_context *tdb)
759 {
760         int i;
761         tdb_off_t offset = 0;
762         ssize_t data_len;
763         tdb_off_t recovery_head;
764         tdb_len_t recovery_size = 0;
765
766         if (tdb_lockall(tdb) != 0) {
767                 return -1;
768         }
769
770         tdb_trace(tdb, "tdb_wipe_all");
771
772         /* see if the tdb has a recovery area, and remember its size
773            if so. We don't want to lose this as otherwise each
774            tdb_wipe_all() in a transaction will increase the size of
775            the tdb by the size of the recovery area */
776         if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
777                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery head\n"));
778                 goto failed;
779         }
780
781         if (recovery_head != 0) {
782                 struct list_struct rec;
783                 if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
784                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_wipe_all: failed to read recovery record\n"));
785                         return -1;
786                 }       
787                 recovery_size = rec.rec_len + sizeof(rec);
788         }
789
790         /* wipe the hashes */
791         for (i=0;i<tdb->header.hash_size;i++) {
792                 if (tdb_ofs_write(tdb, TDB_HASH_TOP(i), &offset) == -1) {
793                         TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write hash %d\n", i));
794                         goto failed;
795                 }
796         }
797
798         /* wipe the freelist */
799         if (tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
800                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write freelist\n"));
801                 goto failed;
802         }
803
804         /* add all the rest of the file to the freelist, possibly leaving a gap 
805            for the recovery area */
806         if (recovery_size == 0) {
807                 /* the simple case - the whole file can be used as a freelist */
808                 data_len = (tdb->map_size - TDB_DATA_START(tdb->header.hash_size));
809                 if (tdb_free_region(tdb, TDB_DATA_START(tdb->header.hash_size), data_len) != 0) {
810                         goto failed;
811                 }
812         } else {
813                 /* we need to add two freelist entries - one on either
814                    side of the recovery area 
815
816                    Note that we cannot shift the recovery area during
817                    this operation. Only the transaction.c code may
818                    move the recovery area or we risk subtle data
819                    corruption
820                 */
821                 data_len = (recovery_head - TDB_DATA_START(tdb->header.hash_size));
822                 if (tdb_free_region(tdb, TDB_DATA_START(tdb->header.hash_size), data_len) != 0) {
823                         goto failed;
824                 }
825                 /* and the 2nd free list entry after the recovery area - if any */
826                 data_len = tdb->map_size - (recovery_head+recovery_size);
827                 if (tdb_free_region(tdb, recovery_head+recovery_size, data_len) != 0) {
828                         goto failed;
829                 }
830         }
831
832         if (tdb_unlockall(tdb) != 0) {
833                 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
834                 goto failed;
835         }
836
837         return 0;
838
839 failed:
840         tdb_unlockall(tdb);
841         return -1;
842 }
843
844 #ifdef TDB_TRACE
845 static void tdb_trace_write(struct tdb_context *tdb, const char *str)
846 {
847         if (write(tdb->tracefd, str, strlen(str)) != strlen(str)) {
848                 close(tdb->tracefd);
849                 tdb->tracefd = -1;
850         }
851 }
852
853 static void tdb_trace_start(struct tdb_context *tdb)
854 {
855         tdb_off_t seqnum=0;
856         char msg[sizeof(tdb_off_t) * 4];
857
858         tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
859         sprintf(msg, "%u ", seqnum);
860         tdb_trace_write(tdb, msg);
861 }
862
863 static void tdb_trace_end(struct tdb_context *tdb)
864 {
865         tdb_trace_write(tdb, "\n");
866 }
867
868 static void tdb_trace_end_ret(struct tdb_context *tdb, int ret)
869 {
870         char msg[sizeof(ret) * 4];
871         sprintf(msg, " = %i\n", ret);
872         tdb_trace_write(tdb, msg);
873 }
874
875 static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec)
876 {
877         char msg[20];
878         unsigned int i;
879
880         /* We differentiate zero-length records from non-existent ones. */
881         if (rec.dptr == NULL) {
882                 tdb_trace_write(tdb, " NULL");
883                 return;
884         }
885         sprintf(msg, " %zu:", rec.dsize);
886         tdb_trace_write(tdb, msg);
887         for (i = 0; i < rec.dsize; i++) {
888                 sprintf(msg, "%02x", rec.dptr[i]);
889                 tdb_trace_write(tdb, msg);
890         }
891 }
892
893 void tdb_trace(struct tdb_context *tdb, const char *op)
894 {
895         tdb_trace_start(tdb);
896         tdb_trace_write(tdb, op);
897         tdb_trace_end(tdb);
898 }
899
900 void tdb_trace_open(struct tdb_context *tdb, const char *op,
901                     unsigned hash_size, unsigned tdb_flags, unsigned open_flags)
902 {
903         char msg[128];
904
905         sprintf(msg, "%s %u %#x %#x", op, hash_size, tdb_flags, open_flags);
906         tdb_trace_start(tdb);
907         tdb_trace_write(tdb, msg);
908         tdb_trace_end(tdb);
909 }
910
911 void tdb_trace_ret(struct tdb_context *tdb, const char *op, int ret)
912 {
913         tdb_trace_start(tdb);
914         tdb_trace_write(tdb, op);
915         tdb_trace_end_ret(tdb, ret);
916 }
917
918 void tdb_trace_retrec(struct tdb_context *tdb, const char *op, TDB_DATA ret)
919 {
920         tdb_trace_start(tdb);
921         tdb_trace_write(tdb, op);
922         tdb_trace_write(tdb, " =");
923         tdb_trace_record(tdb, ret);
924         tdb_trace_end(tdb);
925 }
926
927 void tdb_trace_1rec(struct tdb_context *tdb, const char *op,
928                     TDB_DATA rec)
929 {
930         tdb_trace_start(tdb);
931         tdb_trace_write(tdb, op);
932         tdb_trace_record(tdb, rec);
933         tdb_trace_end(tdb);
934 }
935
936 void tdb_trace_1rec_ret(struct tdb_context *tdb, const char *op,
937                         TDB_DATA rec, int ret)
938 {
939         tdb_trace_start(tdb);
940         tdb_trace_write(tdb, op);
941         tdb_trace_record(tdb, rec);
942         tdb_trace_end_ret(tdb, ret);
943 }
944
945 void tdb_trace_1rec_retrec(struct tdb_context *tdb, const char *op,
946                            TDB_DATA rec, TDB_DATA ret)
947 {
948         tdb_trace_start(tdb);
949         tdb_trace_write(tdb, op);
950         tdb_trace_record(tdb, rec);
951         tdb_trace_write(tdb, " =");
952         tdb_trace_record(tdb, ret);
953         tdb_trace_end(tdb);
954 }
955
956 void tdb_trace_2rec_flag_ret(struct tdb_context *tdb, const char *op,
957                              TDB_DATA rec1, TDB_DATA rec2, unsigned flag,
958                              int ret)
959 {
960         char msg[sizeof(ret) * 4];
961
962         sprintf(msg, " %#x", flag); 
963         tdb_trace_start(tdb);
964         tdb_trace_write(tdb, op);
965         tdb_trace_record(tdb, rec1);
966         tdb_trace_record(tdb, rec2);
967         tdb_trace_write(tdb, msg);
968         tdb_trace_end_ret(tdb, ret);
969 }
970
971 void tdb_trace_2rec_retrec(struct tdb_context *tdb, const char *op,
972                            TDB_DATA rec1, TDB_DATA rec2, TDB_DATA ret)
973 {
974         tdb_trace_start(tdb);
975         tdb_trace_write(tdb, op);
976         tdb_trace_record(tdb, rec1);
977         tdb_trace_record(tdb, rec2);
978         tdb_trace_write(tdb, " =");
979         tdb_trace_record(tdb, ret);
980         tdb_trace_end(tdb);
981 }
982 #endif