2 Unix SMB/CIFS implementation.
4 trivial database library
6 Copyright (C) Andrew Tridgell 1999-2005
7 Copyright (C) Paul `Rusty' Russell 2000
8 Copyright (C) Jeremy Allison 2000-2003
10 ** NOTE! The following LGPL license applies to the tdb
11 ** library. This does NOT imply that all of Samba is released
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Lesser General Public
16 License as published by the Free Software Foundation; either
17 version 3 of the License, or (at your option) any later version.
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Lesser General Public License for more details.
24 You should have received a copy of the GNU Lesser General Public
25 License along with this library; if not, see <http://www.gnu.org/licenses/>.
28 #include "tdb_private.h"
33 non-blocking increment of the tdb sequence number if the tdb has been opened using
36 void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)
40 if (!(tdb->flags & TDB_SEQNUM)) {
44 tdb_trace(tdb, "tdb_increment_seqnum_nonblock");
46 /* we ignore errors from this, as we have no sane way of
49 tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
51 tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum);
55 increment the tdb sequence number if the tdb has been opened using
58 static void tdb_increment_seqnum(struct tdb_context *tdb)
60 if (!(tdb->flags & TDB_SEQNUM)) {
64 if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) {
68 tdb_trace(tdb, "tdb_increment_seqnum");
70 tdb_increment_seqnum_nonblock(tdb);
72 tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1);
75 static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data)
77 return memcmp(data.dptr, key.dptr, data.dsize);
80 /* Returns 0 on fail. On success, return offset of record, and fills
82 static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
83 struct list_struct *r)
87 /* read in the hash top */
88 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
91 /* keep looking until we find the right record */
93 if (tdb_rec_read(tdb, rec_ptr, r) == -1)
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,
105 return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
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)
114 if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
116 if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
117 tdb_unlock(tdb, BUCKET(hash), locktype);
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.
126 static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, TDB_DATA dbuf)
128 struct list_struct rec;
132 if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
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 */
141 if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
142 dbuf.dptr, dbuf.dsize) == -1)
145 if (dbuf.dsize != rec.data_len) {
147 rec.data_len = dbuf.dsize;
148 return tdb_rec_write(tdb, rec_ptr, &rec);
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
160 TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
163 struct list_struct rec;
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");
175 ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_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");
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.
192 * DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
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.
199 * This is interesting for all readers of potentially large data structures in
200 * the tdb records, ldb indexes being one example.
203 int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
204 int (*parser)(TDB_DATA key, TDB_DATA data,
209 struct list_struct rec;
213 /* find which hash bucket it is in */
214 hash = tdb->hash_fn(&key);
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);
223 tdb_trace(tdb, "tdb_parse_record ");
224 tdb_trace_record(tdb, key);
225 tdb_trace(tdb, "= %u\n", rec.data_len);
227 ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
228 rec.data_len, parser, private_data);
230 tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
235 /* check if an entry in the database exists
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
241 static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
243 struct list_struct rec;
245 if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
247 tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
251 int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
253 uint32_t hash = tdb->hash_fn(&key);
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);
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)
266 tdb_off_t last_ptr, i;
267 struct list_struct lastrec;
269 if (tdb->read_only || tdb->traverse_read) return -1;
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);
277 if (tdb_write_unlock_record(tdb, rec_ptr) != 0)
280 /* find previous record in hash chain */
281 if (tdb_ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
283 for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
284 if (tdb_rec_read(tdb, i, &lastrec) == -1)
287 /* unlink it: next ptr is at start of record. */
289 last_ptr = TDB_HASH_TOP(rec->full_hash);
290 if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1)
293 /* recover the space */
294 if (tdb_free(tdb, rec_ptr, rec) == -1)
299 static int tdb_count_dead(struct tdb_context *tdb, uint32_t hash)
303 struct list_struct rec;
305 /* read in the hash top */
306 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
310 if (tdb_rec_read(tdb, rec_ptr, &rec) == -1)
313 if (rec.magic == TDB_DEAD_MAGIC) {
322 * Purge all DEAD records from a hash chain
324 static int tdb_purge_dead(struct tdb_context *tdb, uint32_t hash)
327 struct list_struct rec;
330 if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
334 /* read in the hash top */
335 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
341 if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) {
347 if (rec.magic == TDB_DEAD_MAGIC
348 && tdb_do_delete(tdb, rec_ptr, &rec) == -1) {
355 tdb_unlock(tdb, -1, F_WRLCK);
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)
363 struct list_struct rec;
366 if (tdb->max_dead_records != 0) {
369 * Allow for some dead records per hash chain, mainly for
370 * tdb's with a very high create/delete rate like locking.tdb.
373 if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
376 if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) {
378 * Don't let the per-chain freelist grow too large,
379 * delete all existing dead records
381 tdb_purge_dead(tdb, hash);
384 if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
385 tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
390 * Just mark the record as dead.
392 rec.magic = TDB_DEAD_MAGIC;
393 ret = tdb_rec_write(tdb, rec_ptr, &rec);
396 if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK,
400 ret = tdb_do_delete(tdb, rec_ptr, &rec);
404 tdb_increment_seqnum(tdb);
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"));
412 int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
414 uint32_t hash = tdb->hash_fn(&key);
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);
425 * See if we have a dead record around with enough space
427 static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash,
428 struct list_struct *r, tdb_len_t length)
432 /* read in the hash top */
433 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
436 /* keep looking until we find the right record */
438 if (tdb_rec_read(tdb, rec_ptr, r) == -1)
441 if (TDB_DEAD(r) && r->rec_len >= length) {
443 * First fit for simple coding, TODO: change to best
453 /* store an element in the database, replacing any existing element
456 return 0 on success, -1 on failure
458 int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
460 struct list_struct rec;
466 if (tdb->read_only || tdb->traverse_read) {
467 tdb->ecode = TDB_ERR_RDONLY;
471 /* find which hash bucket it is in */
472 hash = tdb->hash_fn(&key);
473 if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
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);
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");
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");
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");
502 /* reset the error code potentially set by the tdb_update() */
503 tdb->ecode = TDB_SUCCESS;
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);
511 /* Copy key+value *before* allocating free space in case malloc
512 fails and we are left with a dead spot in the tdb. */
514 if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
515 tdb->ecode = TDB_ERR_OOM;
519 memcpy(p, key.dptr, key.dsize);
521 memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
523 if (tdb->max_dead_records != 0) {
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.
530 rec_ptr = tdb_find_dead(
532 key.dsize + dbuf.dsize + sizeof(tdb_off_t));
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) {
546 tdb_trace(tdb, "= fromdead\n");
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.
556 if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
560 if ((tdb->max_dead_records != 0)
561 && (tdb_purge_dead(tdb, hash) == -1)) {
562 tdb_unlock(tdb, -1, F_WRLCK);
566 /* we have to allocate some space */
567 rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec);
569 tdb_unlock(tdb, -1, F_WRLCK);
575 /* Read hash top into next ptr */
576 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
579 rec.key_len = key.dsize;
580 rec.data_len = dbuf.dsize;
581 rec.full_hash = hash;
582 rec.magic = TDB_MAGIC;
584 tdb_trace(tdb, "= allocate\n");
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 */
598 tdb_increment_seqnum(tdb);
602 tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
607 /* Append to an entry. Create if not exist. */
608 int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
614 /* find which hash bucket it is in */
615 hash = tdb->hash_fn(&key);
616 if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
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");
625 if (dbuf.dptr == NULL) {
626 dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
628 unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr,
629 dbuf.dsize + new_dbuf.dsize);
630 if (new_dptr == NULL) {
633 dbuf.dptr = new_dptr;
636 if (dbuf.dptr == NULL) {
637 tdb->ecode = TDB_ERR_OOM;
641 memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize);
642 dbuf.dsize += new_dbuf.dsize;
644 ret = tdb_store(tdb, key, dbuf, 0);
647 tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
648 SAFE_FREE(dbuf.dptr);
654 return the name of the current tdb file
655 useful for external logging functions
657 const char *tdb_name(struct tdb_context *tdb)
663 return the underlying file descriptor being used by tdb, or -1
664 useful for external routines that want to check the device/inode
667 int tdb_fd(struct tdb_context *tdb)
673 return the current logging function
674 useful for external tdb routines that wish to log tdb errors
676 tdb_log_func tdb_log_fn(struct tdb_context *tdb)
678 return tdb->log.log_fn;
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.
689 The aim of this sequence number is to allow for a very lightweight
690 test of a possible tdb change.
692 int tdb_get_seqnum(struct tdb_context *tdb)
696 tdb_trace(tdb, "tdb_get_seqnum\n");
697 tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
701 int tdb_hash_size(struct tdb_context *tdb)
703 return tdb->header.hash_size;
706 size_t tdb_map_size(struct tdb_context *tdb)
708 return tdb->map_size;
711 int tdb_get_flags(struct tdb_context *tdb)
716 void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
721 void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
723 tdb->flags &= ~flags;
728 enable sequence number handling on an open tdb
730 void tdb_enable_seqnum(struct tdb_context *tdb)
732 tdb->flags |= TDB_SEQNUM;
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
740 static int tdb_free_region(struct tdb_context *tdb, tdb_off_t offset, ssize_t length)
742 struct list_struct rec;
743 if (length <= sizeof(rec)) {
744 /* the region is not worth adding */
747 if (length + offset > tdb->map_size) {
748 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_free_region: adding region beyond end of file\n"));
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"));
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.
765 This code carefully steps around the recovery area, leaving it alone
767 int tdb_wipe_all(struct tdb_context *tdb)
770 tdb_off_t offset = 0;
772 tdb_off_t recovery_head;
773 tdb_len_t recovery_size = 0;
775 if (tdb_lockall(tdb) != 0) {
779 tdb_trace(tdb, "tdb_wipe_all\n");
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"));
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"));
796 recovery_size = rec.rec_len + sizeof(rec);
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));
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"));
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) {
822 /* we need to add two freelist entries - one on either
823 side of the recovery area
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
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) {
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) {
841 if (tdb_unlockall(tdb) != 0) {
842 TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
856 void tdb_trace(const struct tdb_context *tdb, const char *fmt, ...)
863 len = vsprintf(msg, fmt, args);
866 write(tdb->tracefd, msg, len);
869 void tdb_trace_record(const struct tdb_context *tdb, TDB_DATA rec)
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);