2 Trivial Database 2: opening and closing TDBs
3 Copyright (C) Rusty Russell 2010
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 #include <ccan/build_assert/build_assert.h>
22 /* all tdbs, to detect double-opens (fcntl file don't nest!) */
23 static struct tdb_context
*tdbs
= NULL
;
25 static struct tdb_file
*find_file(dev_t device
, ino_t ino
)
27 struct tdb_context
*i
;
29 for (i
= tdbs
; i
; i
= i
->next
) {
30 if (i
->file
->device
== device
&& i
->file
->inode
== ino
) {
38 static bool read_all(int fd
, void *buf
, size_t len
)
42 ret
= read(fd
, buf
, len
);
50 buf
= (char *)buf
+ ret
;
56 static uint64_t random_number(struct tdb_context
*tdb
)
62 fd
= open("/dev/urandom", O_RDONLY
);
64 if (read_all(fd
, &ret
, sizeof(ret
))) {
70 /* FIXME: Untested! Based on Wikipedia protocol description! */
71 fd
= open("/dev/egd-pool", O_RDWR
);
73 /* Command is 1, next byte is size we want to read. */
74 char cmd
[2] = { 1, sizeof(uint64_t) };
75 if (write(fd
, cmd
, sizeof(cmd
)) == sizeof(cmd
)) {
76 char reply
[1 + sizeof(uint64_t)];
77 int r
= read(fd
, reply
, sizeof(reply
));
79 /* Copy at least some bytes. */
80 memcpy(&ret
, reply
+1, r
- 1);
81 if (reply
[0] == sizeof(uint64_t)
82 && r
== sizeof(reply
)) {
91 /* Fallback: pid and time. */
92 gettimeofday(&now
, NULL
);
93 ret
= getpid() * 100132289ULL + now
.tv_sec
* 1000000ULL + now
.tv_usec
;
94 tdb_logerr(tdb
, TDB_SUCCESS
, TDB_LOG_WARNING
,
95 "tdb_open: random from getpid and time");
99 static void tdb2_context_init(struct tdb_context
*tdb
)
101 /* Initialize the TDB2 fields here */
103 tdb
->tdb2
.direct_access
= 0;
104 tdb
->tdb2
.transaction
= NULL
;
105 tdb
->tdb2
.access
= NULL
;
108 struct new_database
{
109 struct tdb_header hdr
;
110 struct tdb_freetable ftable
;
113 /* initialise a new database */
114 static enum TDB_ERROR
tdb_new_database(struct tdb_context
*tdb
,
115 struct tdb_attribute_seed
*seed
,
116 struct tdb_header
*hdr
)
118 /* We make it up in memory, then write it out if not internal */
119 struct new_database newdb
;
120 unsigned int magic_len
;
122 enum TDB_ERROR ecode
;
124 /* Fill in the header */
125 newdb
.hdr
.version
= TDB_VERSION
;
127 newdb
.hdr
.hash_seed
= seed
->seed
;
129 newdb
.hdr
.hash_seed
= random_number(tdb
);
130 newdb
.hdr
.hash_test
= TDB_HASH_MAGIC
;
131 newdb
.hdr
.hash_test
= tdb
->hash_fn(&newdb
.hdr
.hash_test
,
132 sizeof(newdb
.hdr
.hash_test
),
135 newdb
.hdr
.recovery
= 0;
136 newdb
.hdr
.features_used
= newdb
.hdr
.features_offered
= TDB_FEATURE_MASK
;
137 newdb
.hdr
.seqnum
= 0;
138 memset(newdb
.hdr
.reserved
, 0, sizeof(newdb
.hdr
.reserved
));
139 /* Initial hashes are empty. */
140 memset(newdb
.hdr
.hashtable
, 0, sizeof(newdb
.hdr
.hashtable
));
143 newdb
.hdr
.free_table
= offsetof(struct new_database
, ftable
);
144 memset(&newdb
.ftable
, 0, sizeof(newdb
.ftable
));
145 ecode
= set_header(NULL
, &newdb
.ftable
.hdr
, TDB_FTABLE_MAGIC
, 0,
146 sizeof(newdb
.ftable
) - sizeof(newdb
.ftable
.hdr
),
147 sizeof(newdb
.ftable
) - sizeof(newdb
.ftable
.hdr
),
149 if (ecode
!= TDB_SUCCESS
) {
154 memset(newdb
.hdr
.magic_food
, 0, sizeof(newdb
.hdr
.magic_food
));
155 strcpy(newdb
.hdr
.magic_food
, TDB_MAGIC_FOOD
);
157 /* This creates an endian-converted database, as if read from disk */
158 magic_len
= sizeof(newdb
.hdr
.magic_food
);
160 (char *)&newdb
.hdr
+ magic_len
, sizeof(newdb
) - magic_len
);
164 if (tdb
->flags
& TDB_INTERNAL
) {
165 tdb
->file
->map_size
= sizeof(newdb
);
166 tdb
->file
->map_ptr
= malloc(tdb
->file
->map_size
);
167 if (!tdb
->file
->map_ptr
) {
168 return tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
170 " failed to allocate");
172 memcpy(tdb
->file
->map_ptr
, &newdb
, tdb
->file
->map_size
);
175 if (lseek(tdb
->file
->fd
, 0, SEEK_SET
) == -1) {
176 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
178 " failed to seek: %s", strerror(errno
));
181 if (ftruncate(tdb
->file
->fd
, 0) == -1) {
182 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
184 " failed to truncate: %s", strerror(errno
));
187 rlen
= write(tdb
->file
->fd
, &newdb
, sizeof(newdb
));
188 if (rlen
!= sizeof(newdb
)) {
191 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
192 "tdb_new_database: %zi writing header: %s",
193 rlen
, strerror(errno
));
198 static enum TDB_ERROR
tdb_new_file(struct tdb_context
*tdb
)
200 tdb
->file
= malloc(sizeof(*tdb
->file
));
202 return tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
203 "tdb_open: cannot alloc tdb_file structure");
204 tdb
->file
->num_lockrecs
= 0;
205 tdb
->file
->lockrecs
= NULL
;
206 tdb
->file
->allrecord_lock
.count
= 0;
207 tdb
->file
->refcnt
= 1;
208 tdb
->file
->map_ptr
= NULL
;
212 enum TDB_ERROR
tdb_set_attribute(struct tdb_context
*tdb
,
213 const union tdb_attribute
*attr
)
215 switch (attr
->base
.attr
) {
216 case TDB_ATTRIBUTE_LOG
:
217 tdb
->log_fn
= attr
->log
.fn
;
218 tdb
->log_data
= attr
->log
.data
;
220 case TDB_ATTRIBUTE_HASH
:
221 case TDB_ATTRIBUTE_SEED
:
222 case TDB_ATTRIBUTE_OPENHOOK
:
223 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
224 return tdb
->last_error
225 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
228 " cannot set %s after opening",
229 attr
->base
.attr
== TDB_ATTRIBUTE_HASH
230 ? "TDB_ATTRIBUTE_HASH"
231 : attr
->base
.attr
== TDB_ATTRIBUTE_SEED
232 ? "TDB_ATTRIBUTE_SEED"
233 : attr
->base
.attr
== TDB_ATTRIBUTE_OPENHOOK
234 ? "TDB_ATTRIBUTE_OPENHOOK"
235 : "TDB_ATTRIBUTE_TDB1_HASHSIZE");
236 case TDB_ATTRIBUTE_STATS
:
237 return tdb
->last_error
238 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
241 " cannot set TDB_ATTRIBUTE_STATS");
242 case TDB_ATTRIBUTE_FLOCK
:
243 tdb
->lock_fn
= attr
->flock
.lock
;
244 tdb
->unlock_fn
= attr
->flock
.unlock
;
245 tdb
->lock_data
= attr
->flock
.data
;
248 return tdb
->last_error
249 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
252 " unknown attribute type %u",
258 enum TDB_ERROR
tdb_get_attribute(struct tdb_context
*tdb
,
259 union tdb_attribute
*attr
)
261 switch (attr
->base
.attr
) {
262 case TDB_ATTRIBUTE_LOG
:
264 return tdb
->last_error
= TDB_ERR_NOEXIST
;
265 attr
->log
.fn
= tdb
->log_fn
;
266 attr
->log
.data
= tdb
->log_data
;
268 case TDB_ATTRIBUTE_HASH
:
269 attr
->hash
.fn
= tdb
->hash_fn
;
270 attr
->hash
.data
= tdb
->hash_data
;
272 case TDB_ATTRIBUTE_SEED
:
273 if (tdb
->flags
& TDB_VERSION1
)
274 return tdb
->last_error
275 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
278 " cannot get TDB_ATTRIBUTE_SEED"
280 attr
->seed
.seed
= tdb
->hash_seed
;
282 case TDB_ATTRIBUTE_OPENHOOK
:
284 return tdb
->last_error
= TDB_ERR_NOEXIST
;
285 attr
->openhook
.fn
= tdb
->openhook
;
286 attr
->openhook
.data
= tdb
->openhook_data
;
288 case TDB_ATTRIBUTE_STATS
: {
289 size_t size
= attr
->stats
.size
;
290 if (size
> tdb
->stats
.size
)
291 size
= tdb
->stats
.size
;
292 memcpy(&attr
->stats
, &tdb
->stats
, size
);
295 case TDB_ATTRIBUTE_FLOCK
:
296 attr
->flock
.lock
= tdb
->lock_fn
;
297 attr
->flock
.unlock
= tdb
->unlock_fn
;
298 attr
->flock
.data
= tdb
->lock_data
;
300 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
301 if (!(tdb
->flags
& TDB_VERSION1
))
302 return tdb
->last_error
303 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
306 " cannot get TDB_ATTRIBUTE_TDB1_HASHSIZE"
308 attr
->tdb1_hashsize
.hsize
= tdb
->tdb1
.header
.hash_size
;
311 return tdb
->last_error
312 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
315 " unknown attribute type %u",
318 attr
->base
.next
= NULL
;
322 void tdb_unset_attribute(struct tdb_context
*tdb
,
323 enum tdb_attribute_type type
)
326 case TDB_ATTRIBUTE_LOG
:
329 case TDB_ATTRIBUTE_OPENHOOK
:
330 tdb
->openhook
= NULL
;
332 case TDB_ATTRIBUTE_HASH
:
333 case TDB_ATTRIBUTE_SEED
:
334 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
335 tdb_logerr(tdb
, TDB_ERR_EINVAL
, TDB_LOG_USE_ERROR
,
336 "tdb_unset_attribute: cannot unset %s after opening",
337 type
== TDB_ATTRIBUTE_HASH
338 ? "TDB_ATTRIBUTE_HASH"
339 : type
== TDB_ATTRIBUTE_SEED
340 ? "TDB_ATTRIBUTE_SEED"
341 : "TDB_ATTRIBUTE_TDB1_HASHSIZE");
343 case TDB_ATTRIBUTE_STATS
:
344 tdb_logerr(tdb
, TDB_ERR_EINVAL
,
346 "tdb_unset_attribute:"
347 "cannot unset TDB_ATTRIBUTE_STATS");
349 case TDB_ATTRIBUTE_FLOCK
:
350 tdb
->lock_fn
= tdb_fcntl_lock
;
351 tdb
->unlock_fn
= tdb_fcntl_unlock
;
354 tdb_logerr(tdb
, TDB_ERR_EINVAL
,
356 "tdb_unset_attribute: unknown attribute type %u",
361 static bool is_tdb1(struct tdb1_header
*hdr
, const void *buf
, ssize_t rlen
)
363 /* This code assumes we've tried to read entire tdb1 header. */
364 BUILD_ASSERT(sizeof(*hdr
) <= sizeof(struct tdb_header
));
366 if (rlen
< (ssize_t
)sizeof(*hdr
)) {
370 memcpy(hdr
, buf
, sizeof(*hdr
));
371 if (strcmp(hdr
->magic_food
, TDB_MAGIC_FOOD
) != 0)
374 return hdr
->version
== TDB1_VERSION
375 || hdr
->version
== TDB1_BYTEREV(TDB1_VERSION
);
378 struct tdb_context
*tdb_open(const char *name
, int tdb_flags
,
379 int open_flags
, mode_t mode
,
380 union tdb_attribute
*attr
)
382 struct tdb_context
*tdb
;
388 struct tdb_header hdr
;
389 struct tdb_attribute_seed
*seed
= NULL
;
390 struct tdb_attribute_tdb1_hashsize
*hsize_attr
= NULL
;
391 struct tdb_attribute_tdb1_max_dead
*maxsize_attr
= NULL
;
393 enum TDB_ERROR ecode
;
396 tdb
= malloc(sizeof(*tdb
) + (name
? strlen(name
) + 1 : 0));
402 /* Set name immediately for logging functions. */
404 tdb
->name
= strcpy((char *)(tdb
+ 1), name
);
408 tdb
->flags
= tdb_flags
;
410 tdb
->open_flags
= open_flags
;
411 tdb
->last_error
= TDB_SUCCESS
;
413 tdb
->openhook
= NULL
;
414 tdb
->lock_fn
= tdb_fcntl_lock
;
415 tdb
->unlock_fn
= tdb_fcntl_unlock
;
416 tdb
->hash_fn
= tdb_jenkins_hash
;
417 memset(&tdb
->stats
, 0, sizeof(tdb
->stats
));
418 tdb
->stats
.base
.attr
= TDB_ATTRIBUTE_STATS
;
419 tdb
->stats
.size
= sizeof(tdb
->stats
);
422 switch (attr
->base
.attr
) {
423 case TDB_ATTRIBUTE_HASH
:
424 tdb
->hash_fn
= attr
->hash
.fn
;
425 tdb
->hash_data
= attr
->hash
.data
;
427 case TDB_ATTRIBUTE_SEED
:
430 case TDB_ATTRIBUTE_OPENHOOK
:
431 tdb
->openhook
= attr
->openhook
.fn
;
432 tdb
->openhook_data
= attr
->openhook
.data
;
434 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
435 hsize_attr
= &attr
->tdb1_hashsize
;
437 case TDB_ATTRIBUTE_TDB1_MAX_DEAD
:
438 maxsize_attr
= &attr
->tdb1_max_dead
;
441 /* These are set as normal. */
442 ecode
= tdb_set_attribute(tdb
, attr
);
443 if (ecode
!= TDB_SUCCESS
)
446 attr
= attr
->base
.next
;
449 if (tdb_flags
& ~(TDB_INTERNAL
| TDB_NOLOCK
| TDB_NOMMAP
| TDB_CONVERT
450 | TDB_NOSYNC
| TDB_SEQNUM
| TDB_ALLOW_NESTING
451 | TDB_RDONLY
| TDB_VERSION1
)) {
452 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
, TDB_LOG_USE_ERROR
,
453 "tdb_open: unknown flags %u", tdb_flags
);
458 if (!(tdb_flags
& TDB_VERSION1
) ||
459 (!(tdb_flags
& TDB_INTERNAL
) && !(open_flags
& O_CREAT
))) {
460 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
462 "tdb_open: can only use"
463 " TDB_ATTRIBUTE_TDB1_HASHSIZE when"
464 " creating a TDB_VERSION1 tdb");
470 if (tdb_flags
& TDB_VERSION1
) {
471 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
474 " cannot set TDB_ATTRIBUTE_SEED"
477 } else if (!(tdb_flags
& TDB_INTERNAL
)
478 && !(open_flags
& O_CREAT
)) {
479 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
482 " cannot set TDB_ATTRIBUTE_SEED"
483 " without O_CREAT.");
488 if ((open_flags
& O_ACCMODE
) == O_WRONLY
) {
489 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
, TDB_LOG_USE_ERROR
,
490 "tdb_open: can't open tdb %s write-only",
495 if ((open_flags
& O_ACCMODE
) == O_RDONLY
) {
497 tdb
->flags
|= TDB_RDONLY
;
499 if (tdb_flags
& TDB_RDONLY
) {
500 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
502 "tdb_open: can't use TDB_RDONLY"
503 " without O_RDONLY");
509 /* internal databases don't need any of the rest. */
510 if (tdb
->flags
& TDB_INTERNAL
) {
511 tdb
->flags
|= (TDB_NOLOCK
| TDB_NOMMAP
);
512 ecode
= tdb_new_file(tdb
);
513 if (ecode
!= TDB_SUCCESS
) {
517 if (tdb
->flags
& TDB_VERSION1
)
518 ecode
= tdb1_new_database(tdb
, hsize_attr
, maxsize_attr
);
520 ecode
= tdb_new_database(tdb
, seed
, &hdr
);
521 if (ecode
== TDB_SUCCESS
) {
522 tdb_convert(tdb
, &hdr
.hash_seed
,
523 sizeof(hdr
.hash_seed
));
524 tdb
->hash_seed
= hdr
.hash_seed
;
525 tdb2_context_init(tdb
);
526 tdb_ftable_init(tdb
);
529 if (ecode
!= TDB_SUCCESS
) {
535 if (stat(name
, &st
) != -1)
536 tdb
->file
= find_file(st
.st_dev
, st
.st_ino
);
541 if ((fd
= open(name
, open_flags
, mode
)) == -1) {
542 /* errno set by open(2) */
544 tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
545 "tdb_open: could not open file %s: %s",
546 name
, strerror(errno
));
550 /* on exec, don't inherit the fd */
551 v
= fcntl(fd
, F_GETFD
, 0);
552 fcntl(fd
, F_SETFD
, v
| FD_CLOEXEC
);
554 if (fstat(fd
, &st
) == -1) {
556 tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
557 "tdb_open: could not stat open %s: %s",
558 name
, strerror(errno
));
563 ecode
= tdb_new_file(tdb
);
564 if (ecode
!= TDB_SUCCESS
) {
570 tdb
->file
->device
= st
.st_dev
;
571 tdb
->file
->inode
= st
.st_ino
;
572 tdb
->file
->map_ptr
= NULL
;
573 tdb
->file
->map_size
= 0;
576 /* ensure there is only one process initialising at once */
577 ecode
= tdb_lock_open(tdb
, openlock
, TDB_LOCK_WAIT
|TDB_LOCK_NOCHECK
);
578 if (ecode
!= TDB_SUCCESS
) {
583 /* call their open hook if they gave us one. */
585 ecode
= tdb
->openhook(tdb
->file
->fd
, tdb
->openhook_data
);
586 if (ecode
!= TDB_SUCCESS
) {
587 tdb_logerr(tdb
, ecode
, TDB_LOG_ERROR
,
588 "tdb_open: open hook failed");
591 open_flags
|= O_CREAT
;
594 /* If they used O_TRUNC, read will return 0. */
595 rlen
= pread(tdb
->file
->fd
, &hdr
, sizeof(hdr
), 0);
596 if (rlen
== 0 && (open_flags
& O_CREAT
)) {
597 if (tdb
->flags
& TDB_VERSION1
) {
598 ecode
= tdb1_new_database(tdb
, hsize_attr
, maxsize_attr
);
599 if (ecode
!= TDB_SUCCESS
)
603 ecode
= tdb_new_database(tdb
, seed
, &hdr
);
604 if (ecode
!= TDB_SUCCESS
) {
607 } else if (rlen
< 0) {
608 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
609 "tdb_open: error %s reading %s",
610 strerror(errno
), name
);
612 } else if (rlen
< sizeof(hdr
)
613 || strcmp(hdr
.magic_food
, TDB_MAGIC_FOOD
) != 0) {
614 if (is_tdb1(&tdb
->tdb1
.header
, &hdr
, rlen
)) {
615 ecode
= tdb1_open(tdb
, maxsize_attr
);
620 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
621 "tdb_open: %s is not a tdb file", name
);
625 if (hdr
.version
!= TDB_VERSION
) {
626 if (hdr
.version
== bswap_64(TDB_VERSION
))
627 tdb
->flags
|= TDB_CONVERT
;
629 if (is_tdb1(&tdb
->tdb1
.header
, &hdr
, rlen
)) {
630 ecode
= tdb1_open(tdb
, maxsize_attr
);
636 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
638 " %s is unknown version 0x%llx",
639 name
, (long long)hdr
.version
);
642 } else if (tdb
->flags
& TDB_CONVERT
) {
643 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
645 " %s does not need TDB_CONVERT",
650 if (tdb
->flags
& TDB_VERSION1
) {
651 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
653 " %s does not need TDB_VERSION1",
658 tdb2_context_init(tdb
);
660 tdb_convert(tdb
, &hdr
, sizeof(hdr
));
661 tdb
->hash_seed
= hdr
.hash_seed
;
662 hash_test
= TDB_HASH_MAGIC
;
663 hash_test
= tdb_hash(tdb
, &hash_test
, sizeof(hash_test
));
664 if (hdr
.hash_test
!= hash_test
) {
665 /* wrong hash variant */
666 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
668 " %s uses a different hash function",
673 /* Clear any features we don't understand. */
674 if ((open_flags
& O_ACCMODE
) != O_RDONLY
) {
675 hdr
.features_used
&= TDB_FEATURE_MASK
;
676 ecode
= tdb_write_convert(tdb
, offsetof(struct tdb_header
,
679 sizeof(hdr
.features_used
));
680 if (ecode
!= TDB_SUCCESS
)
685 if (tdb
->flags
& TDB_VERSION1
) {
686 /* if needed, run recovery */
687 if (tdb1_transaction_recover(tdb
) == -1) {
688 ecode
= tdb
->last_error
;
693 tdb_unlock_open(tdb
, openlock
);
695 /* This makes sure we have current map_size and mmap. */
696 if (tdb
->flags
& TDB_VERSION1
) {
697 ecode
= tdb1_probe_length(tdb
);
699 ecode
= tdb
->tdb2
.io
->oob(tdb
, tdb
->file
->map_size
+ 1, true);
701 if (unlikely(ecode
!= TDB_SUCCESS
))
704 if (!(tdb
->flags
& TDB_VERSION1
)) {
705 /* Now it's fully formed, recover if necessary. */
706 berr
= tdb_needs_recovery(tdb
);
707 if (unlikely(berr
!= false)) {
709 ecode
= TDB_OFF_TO_ERR(berr
);
712 ecode
= tdb_lock_and_recover(tdb
);
713 if (ecode
!= TDB_SUCCESS
) {
718 ecode
= tdb_ftable_init(tdb
);
719 if (ecode
!= TDB_SUCCESS
) {
729 /* Map ecode to some logical errno. */
730 switch (TDB_ERR_TO_OFF(ecode
)) {
731 case TDB_ERR_TO_OFF(TDB_ERR_CORRUPT
):
732 case TDB_ERR_TO_OFF(TDB_ERR_IO
):
735 case TDB_ERR_TO_OFF(TDB_ERR_LOCK
):
736 saved_errno
= EWOULDBLOCK
;
738 case TDB_ERR_TO_OFF(TDB_ERR_OOM
):
739 saved_errno
= ENOMEM
;
741 case TDB_ERR_TO_OFF(TDB_ERR_EINVAL
):
742 saved_errno
= EINVAL
;
745 saved_errno
= EINVAL
;
754 tdb_lock_cleanup(tdb
);
755 if (--tdb
->file
->refcnt
== 0) {
756 assert(tdb
->file
->num_lockrecs
== 0);
757 if (tdb
->file
->map_ptr
) {
758 if (tdb
->flags
& TDB_INTERNAL
) {
759 free(tdb
->file
->map_ptr
);
761 tdb_munmap(tdb
->file
);
763 if (close(tdb
->file
->fd
) != 0)
764 tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
765 "tdb_open: failed to close tdb fd"
766 " on error: %s", strerror(errno
));
767 free(tdb
->file
->lockrecs
);
777 int tdb_close(struct tdb_context
*tdb
)
780 struct tdb_context
**i
;
782 tdb_trace(tdb
, "tdb_close");
784 if (tdb
->flags
& TDB_VERSION1
) {
785 if (tdb
->tdb1
.transaction
) {
786 tdb1_transaction_cancel(tdb
);
789 if (tdb
->tdb2
.transaction
) {
790 tdb_transaction_cancel(tdb
);
794 if (tdb
->file
->map_ptr
) {
795 if (tdb
->flags
& TDB_INTERNAL
)
796 free(tdb
->file
->map_ptr
);
798 tdb_munmap(tdb
->file
);
801 tdb_lock_cleanup(tdb
);
802 if (--tdb
->file
->refcnt
== 0) {
803 ret
= close(tdb
->file
->fd
);
804 free(tdb
->file
->lockrecs
);
809 /* Remove from tdbs list */
810 for (i
= &tdbs
; *i
; i
= &(*i
)->next
) {
825 void tdb_foreach_(int (*fn
)(struct tdb_context
*, void *), void *p
)
827 struct tdb_context
*i
;
829 for (i
= tdbs
; i
; i
= i
->next
) {