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