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