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