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 newdb
.hdr
.capabilities
= 0;
139 memset(newdb
.hdr
.reserved
, 0, sizeof(newdb
.hdr
.reserved
));
140 /* Initial hashes are empty. */
141 memset(newdb
.hdr
.hashtable
, 0, sizeof(newdb
.hdr
.hashtable
));
144 newdb
.hdr
.free_table
= offsetof(struct new_database
, ftable
);
145 memset(&newdb
.ftable
, 0, sizeof(newdb
.ftable
));
146 ecode
= set_header(NULL
, &newdb
.ftable
.hdr
, TDB_FTABLE_MAGIC
, 0,
147 sizeof(newdb
.ftable
) - sizeof(newdb
.ftable
.hdr
),
148 sizeof(newdb
.ftable
) - sizeof(newdb
.ftable
.hdr
),
150 if (ecode
!= TDB_SUCCESS
) {
155 memset(newdb
.hdr
.magic_food
, 0, sizeof(newdb
.hdr
.magic_food
));
156 strcpy(newdb
.hdr
.magic_food
, TDB_MAGIC_FOOD
);
158 /* This creates an endian-converted database, as if read from disk */
159 magic_len
= sizeof(newdb
.hdr
.magic_food
);
161 (char *)&newdb
.hdr
+ magic_len
, sizeof(newdb
) - magic_len
);
165 if (tdb
->flags
& TDB_INTERNAL
) {
166 tdb
->file
->map_size
= sizeof(newdb
);
167 tdb
->file
->map_ptr
= malloc(tdb
->file
->map_size
);
168 if (!tdb
->file
->map_ptr
) {
169 return tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
171 " failed to allocate");
173 memcpy(tdb
->file
->map_ptr
, &newdb
, tdb
->file
->map_size
);
176 if (lseek(tdb
->file
->fd
, 0, SEEK_SET
) == -1) {
177 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
179 " failed to seek: %s", strerror(errno
));
182 if (ftruncate(tdb
->file
->fd
, 0) == -1) {
183 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
185 " failed to truncate: %s", strerror(errno
));
188 rlen
= write(tdb
->file
->fd
, &newdb
, sizeof(newdb
));
189 if (rlen
!= sizeof(newdb
)) {
192 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
193 "tdb_new_database: %zi writing header: %s",
194 rlen
, strerror(errno
));
199 static enum TDB_ERROR
tdb_new_file(struct tdb_context
*tdb
)
201 tdb
->file
= malloc(sizeof(*tdb
->file
));
203 return tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
204 "tdb_open: cannot alloc tdb_file structure");
205 tdb
->file
->num_lockrecs
= 0;
206 tdb
->file
->lockrecs
= NULL
;
207 tdb
->file
->allrecord_lock
.count
= 0;
208 tdb
->file
->refcnt
= 1;
209 tdb
->file
->map_ptr
= NULL
;
213 _PUBLIC_
enum TDB_ERROR
tdb_set_attribute(struct tdb_context
*tdb
,
214 const union tdb_attribute
*attr
)
216 switch (attr
->base
.attr
) {
217 case TDB_ATTRIBUTE_LOG
:
218 tdb
->log_fn
= attr
->log
.fn
;
219 tdb
->log_data
= attr
->log
.data
;
221 case TDB_ATTRIBUTE_HASH
:
222 case TDB_ATTRIBUTE_SEED
:
223 case TDB_ATTRIBUTE_OPENHOOK
:
224 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
225 return tdb
->last_error
226 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
229 " cannot set %s after opening",
230 attr
->base
.attr
== TDB_ATTRIBUTE_HASH
231 ? "TDB_ATTRIBUTE_HASH"
232 : attr
->base
.attr
== TDB_ATTRIBUTE_SEED
233 ? "TDB_ATTRIBUTE_SEED"
234 : attr
->base
.attr
== TDB_ATTRIBUTE_OPENHOOK
235 ? "TDB_ATTRIBUTE_OPENHOOK"
236 : "TDB_ATTRIBUTE_TDB1_HASHSIZE");
237 case TDB_ATTRIBUTE_STATS
:
238 return tdb
->last_error
239 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
242 " cannot set TDB_ATTRIBUTE_STATS");
243 case TDB_ATTRIBUTE_FLOCK
:
244 tdb
->lock_fn
= attr
->flock
.lock
;
245 tdb
->unlock_fn
= attr
->flock
.unlock
;
246 tdb
->lock_data
= attr
->flock
.data
;
249 return tdb
->last_error
250 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
253 " unknown attribute type %u",
259 _PUBLIC_
enum TDB_ERROR
tdb_get_attribute(struct tdb_context
*tdb
,
260 union tdb_attribute
*attr
)
262 switch (attr
->base
.attr
) {
263 case TDB_ATTRIBUTE_LOG
:
265 return tdb
->last_error
= TDB_ERR_NOEXIST
;
266 attr
->log
.fn
= tdb
->log_fn
;
267 attr
->log
.data
= tdb
->log_data
;
269 case TDB_ATTRIBUTE_HASH
:
270 attr
->hash
.fn
= tdb
->hash_fn
;
271 attr
->hash
.data
= tdb
->hash_data
;
273 case TDB_ATTRIBUTE_SEED
:
274 if (tdb
->flags
& TDB_VERSION1
)
275 return tdb
->last_error
276 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
279 " cannot get TDB_ATTRIBUTE_SEED"
281 attr
->seed
.seed
= tdb
->hash_seed
;
283 case TDB_ATTRIBUTE_OPENHOOK
:
285 return tdb
->last_error
= TDB_ERR_NOEXIST
;
286 attr
->openhook
.fn
= tdb
->openhook
;
287 attr
->openhook
.data
= tdb
->openhook_data
;
289 case TDB_ATTRIBUTE_STATS
: {
290 size_t size
= attr
->stats
.size
;
291 if (size
> tdb
->stats
.size
)
292 size
= tdb
->stats
.size
;
293 memcpy(&attr
->stats
, &tdb
->stats
, size
);
296 case TDB_ATTRIBUTE_FLOCK
:
297 attr
->flock
.lock
= tdb
->lock_fn
;
298 attr
->flock
.unlock
= tdb
->unlock_fn
;
299 attr
->flock
.data
= tdb
->lock_data
;
301 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
302 if (!(tdb
->flags
& TDB_VERSION1
))
303 return tdb
->last_error
304 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
307 " cannot get TDB_ATTRIBUTE_TDB1_HASHSIZE"
309 attr
->tdb1_hashsize
.hsize
= tdb
->tdb1
.header
.hash_size
;
312 return tdb
->last_error
313 = tdb_logerr(tdb
, TDB_ERR_EINVAL
,
316 " unknown attribute type %u",
319 attr
->base
.next
= NULL
;
323 _PUBLIC_
void tdb_unset_attribute(struct tdb_context
*tdb
,
324 enum tdb_attribute_type type
)
327 case TDB_ATTRIBUTE_LOG
:
330 case TDB_ATTRIBUTE_OPENHOOK
:
331 tdb
->openhook
= NULL
;
333 case TDB_ATTRIBUTE_HASH
:
334 case TDB_ATTRIBUTE_SEED
:
335 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
336 tdb_logerr(tdb
, TDB_ERR_EINVAL
, TDB_LOG_USE_ERROR
,
337 "tdb_unset_attribute: cannot unset %s after opening",
338 type
== TDB_ATTRIBUTE_HASH
339 ? "TDB_ATTRIBUTE_HASH"
340 : type
== TDB_ATTRIBUTE_SEED
341 ? "TDB_ATTRIBUTE_SEED"
342 : "TDB_ATTRIBUTE_TDB1_HASHSIZE");
344 case TDB_ATTRIBUTE_STATS
:
345 tdb_logerr(tdb
, TDB_ERR_EINVAL
,
347 "tdb_unset_attribute:"
348 "cannot unset TDB_ATTRIBUTE_STATS");
350 case TDB_ATTRIBUTE_FLOCK
:
351 tdb
->lock_fn
= tdb_fcntl_lock
;
352 tdb
->unlock_fn
= tdb_fcntl_unlock
;
355 tdb_logerr(tdb
, TDB_ERR_EINVAL
,
357 "tdb_unset_attribute: unknown attribute type %u",
362 static bool is_tdb1(struct tdb1_header
*hdr
, const void *buf
, ssize_t rlen
)
364 /* This code assumes we've tried to read entire tdb1 header. */
365 BUILD_ASSERT(sizeof(*hdr
) <= sizeof(struct tdb_header
));
367 if (rlen
< (ssize_t
)sizeof(*hdr
)) {
371 memcpy(hdr
, buf
, sizeof(*hdr
));
372 if (strcmp(hdr
->magic_food
, TDB_MAGIC_FOOD
) != 0)
375 return hdr
->version
== TDB1_VERSION
376 || hdr
->version
== TDB1_BYTEREV(TDB1_VERSION
);
379 /* The top three bits of the capability tell us whether it matters. */
380 enum TDB_ERROR
unknown_capability(struct tdb_context
*tdb
, const char *caller
,
383 if (type
& TDB_CAP_NOOPEN
) {
384 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
385 "%s: file has unknown capability %llu",
386 caller
, type
& TDB_CAP_NOOPEN
);
389 if ((type
& TDB_CAP_NOWRITE
) && !(tdb
->flags
& TDB_RDONLY
)) {
390 return tdb_logerr(tdb
, TDB_ERR_RDONLY
, TDB_LOG_ERROR
,
391 "%s: file has unknown capability %llu"
392 " (cannot write to it)",
393 caller
, type
& TDB_CAP_NOOPEN
);
396 if (type
& TDB_CAP_NOCHECK
) {
397 tdb
->flags
|= TDB_CANT_CHECK
;
402 static enum TDB_ERROR
capabilities_ok(struct tdb_context
*tdb
,
403 tdb_off_t capabilities
)
406 enum TDB_ERROR ecode
= TDB_SUCCESS
;
407 const struct tdb_capability
*cap
;
409 /* Check capability list. */
410 for (off
= capabilities
; off
&& ecode
== TDB_SUCCESS
; off
= next
) {
411 cap
= tdb_access_read(tdb
, off
, sizeof(*cap
), true);
412 if (TDB_PTR_IS_ERR(cap
)) {
413 return TDB_PTR_ERR(cap
);
416 switch (cap
->type
& TDB_CAP_TYPE_MASK
) {
417 /* We don't understand any capabilities (yet). */
419 ecode
= unknown_capability(tdb
, "tdb_open", cap
->type
);
422 tdb_access_release(tdb
, cap
);
427 _PUBLIC_
struct tdb_context
*tdb_open(const char *name
, int tdb_flags
,
428 int open_flags
, mode_t mode
,
429 union tdb_attribute
*attr
)
431 struct tdb_context
*tdb
;
437 struct tdb_header hdr
;
438 struct tdb_attribute_seed
*seed
= NULL
;
439 struct tdb_attribute_tdb1_hashsize
*hsize_attr
= NULL
;
440 struct tdb_attribute_tdb1_max_dead
*maxsize_attr
= NULL
;
442 enum TDB_ERROR ecode
;
445 tdb
= malloc(sizeof(*tdb
) + (name
? strlen(name
) + 1 : 0));
451 /* Set name immediately for logging functions. */
453 tdb
->name
= strcpy((char *)(tdb
+ 1), name
);
457 tdb
->flags
= tdb_flags
;
459 tdb
->open_flags
= open_flags
;
460 tdb
->last_error
= TDB_SUCCESS
;
462 tdb
->openhook
= NULL
;
463 tdb
->lock_fn
= tdb_fcntl_lock
;
464 tdb
->unlock_fn
= tdb_fcntl_unlock
;
465 tdb
->hash_fn
= tdb_jenkins_hash
;
466 memset(&tdb
->stats
, 0, sizeof(tdb
->stats
));
467 tdb
->stats
.base
.attr
= TDB_ATTRIBUTE_STATS
;
468 tdb
->stats
.size
= sizeof(tdb
->stats
);
471 switch (attr
->base
.attr
) {
472 case TDB_ATTRIBUTE_HASH
:
473 tdb
->hash_fn
= attr
->hash
.fn
;
474 tdb
->hash_data
= attr
->hash
.data
;
476 case TDB_ATTRIBUTE_SEED
:
479 case TDB_ATTRIBUTE_OPENHOOK
:
480 tdb
->openhook
= attr
->openhook
.fn
;
481 tdb
->openhook_data
= attr
->openhook
.data
;
483 case TDB_ATTRIBUTE_TDB1_HASHSIZE
:
484 hsize_attr
= &attr
->tdb1_hashsize
;
486 case TDB_ATTRIBUTE_TDB1_MAX_DEAD
:
487 maxsize_attr
= &attr
->tdb1_max_dead
;
490 /* These are set as normal. */
491 ecode
= tdb_set_attribute(tdb
, attr
);
492 if (ecode
!= TDB_SUCCESS
)
495 attr
= attr
->base
.next
;
498 if (tdb_flags
& ~(TDB_INTERNAL
| TDB_NOLOCK
| TDB_NOMMAP
| TDB_CONVERT
499 | TDB_NOSYNC
| TDB_SEQNUM
| TDB_ALLOW_NESTING
500 | TDB_RDONLY
| TDB_VERSION1
)) {
501 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
, TDB_LOG_USE_ERROR
,
502 "tdb_open: unknown flags %u", tdb_flags
);
507 if (!(tdb_flags
& TDB_VERSION1
) ||
508 (!(tdb_flags
& TDB_INTERNAL
) && !(open_flags
& O_CREAT
))) {
509 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
511 "tdb_open: can only use"
512 " TDB_ATTRIBUTE_TDB1_HASHSIZE when"
513 " creating a TDB_VERSION1 tdb");
519 if (tdb_flags
& TDB_VERSION1
) {
520 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
523 " cannot set TDB_ATTRIBUTE_SEED"
526 } else if (!(tdb_flags
& TDB_INTERNAL
)
527 && !(open_flags
& O_CREAT
)) {
528 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
531 " cannot set TDB_ATTRIBUTE_SEED"
532 " without O_CREAT.");
537 if ((open_flags
& O_ACCMODE
) == O_WRONLY
) {
538 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
, TDB_LOG_USE_ERROR
,
539 "tdb_open: can't open tdb %s write-only",
544 if ((open_flags
& O_ACCMODE
) == O_RDONLY
) {
546 tdb
->flags
|= TDB_RDONLY
;
548 if (tdb_flags
& TDB_RDONLY
) {
549 ecode
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
551 "tdb_open: can't use TDB_RDONLY"
552 " without O_RDONLY");
558 /* internal databases don't need any of the rest. */
559 if (tdb
->flags
& TDB_INTERNAL
) {
560 tdb
->flags
|= (TDB_NOLOCK
| TDB_NOMMAP
);
561 ecode
= tdb_new_file(tdb
);
562 if (ecode
!= TDB_SUCCESS
) {
566 if (tdb
->flags
& TDB_VERSION1
)
567 ecode
= tdb1_new_database(tdb
, hsize_attr
, maxsize_attr
);
569 ecode
= tdb_new_database(tdb
, seed
, &hdr
);
570 if (ecode
== TDB_SUCCESS
) {
571 tdb_convert(tdb
, &hdr
.hash_seed
,
572 sizeof(hdr
.hash_seed
));
573 tdb
->hash_seed
= hdr
.hash_seed
;
574 tdb2_context_init(tdb
);
575 tdb_ftable_init(tdb
);
578 if (ecode
!= TDB_SUCCESS
) {
584 if (stat(name
, &st
) != -1)
585 tdb
->file
= find_file(st
.st_dev
, st
.st_ino
);
590 if ((fd
= open(name
, open_flags
, mode
)) == -1) {
591 /* errno set by open(2) */
593 tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
594 "tdb_open: could not open file %s: %s",
595 name
, strerror(errno
));
599 /* on exec, don't inherit the fd */
600 v
= fcntl(fd
, F_GETFD
, 0);
601 fcntl(fd
, F_SETFD
, v
| FD_CLOEXEC
);
603 if (fstat(fd
, &st
) == -1) {
605 tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
606 "tdb_open: could not stat open %s: %s",
607 name
, strerror(errno
));
612 ecode
= tdb_new_file(tdb
);
613 if (ecode
!= TDB_SUCCESS
) {
619 tdb
->file
->device
= st
.st_dev
;
620 tdb
->file
->inode
= st
.st_ino
;
621 tdb
->file
->map_ptr
= NULL
;
622 tdb
->file
->map_size
= 0;
625 /* ensure there is only one process initialising at once */
626 ecode
= tdb_lock_open(tdb
, openlock
, TDB_LOCK_WAIT
|TDB_LOCK_NOCHECK
);
627 if (ecode
!= TDB_SUCCESS
) {
632 /* call their open hook if they gave us one. */
634 ecode
= tdb
->openhook(tdb
->file
->fd
, tdb
->openhook_data
);
635 if (ecode
!= TDB_SUCCESS
) {
636 tdb_logerr(tdb
, ecode
, TDB_LOG_ERROR
,
637 "tdb_open: open hook failed");
640 open_flags
|= O_CREAT
;
643 /* If they used O_TRUNC, read will return 0. */
644 rlen
= pread(tdb
->file
->fd
, &hdr
, sizeof(hdr
), 0);
645 if (rlen
== 0 && (open_flags
& O_CREAT
)) {
646 if (tdb
->flags
& TDB_VERSION1
) {
647 ecode
= tdb1_new_database(tdb
, hsize_attr
, maxsize_attr
);
648 if (ecode
!= TDB_SUCCESS
)
652 ecode
= tdb_new_database(tdb
, seed
, &hdr
);
653 if (ecode
!= TDB_SUCCESS
) {
656 } else if (rlen
< 0) {
657 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
658 "tdb_open: error %s reading %s",
659 strerror(errno
), name
);
661 } else if (rlen
< sizeof(hdr
)
662 || strcmp(hdr
.magic_food
, TDB_MAGIC_FOOD
) != 0) {
663 if (is_tdb1(&tdb
->tdb1
.header
, &hdr
, rlen
)) {
664 ecode
= tdb1_open(tdb
, maxsize_attr
);
669 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
670 "tdb_open: %s is not a tdb file", name
);
674 if (hdr
.version
!= TDB_VERSION
) {
675 if (hdr
.version
== bswap_64(TDB_VERSION
))
676 tdb
->flags
|= TDB_CONVERT
;
678 if (is_tdb1(&tdb
->tdb1
.header
, &hdr
, rlen
)) {
679 ecode
= tdb1_open(tdb
, maxsize_attr
);
685 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
687 " %s is unknown version 0x%llx",
688 name
, (long long)hdr
.version
);
691 } else if (tdb
->flags
& TDB_CONVERT
) {
692 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
694 " %s does not need TDB_CONVERT",
699 /* This is a version2 tdb. */
700 if (tdb
->flags
& TDB_VERSION1
) {
701 tdb
->flags
&= ~TDB_VERSION1
;
704 tdb2_context_init(tdb
);
706 tdb_convert(tdb
, &hdr
, sizeof(hdr
));
707 tdb
->hash_seed
= hdr
.hash_seed
;
708 hash_test
= TDB_HASH_MAGIC
;
709 hash_test
= tdb_hash(tdb
, &hash_test
, sizeof(hash_test
));
710 if (hdr
.hash_test
!= hash_test
) {
711 /* wrong hash variant */
712 ecode
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
714 " %s uses a different hash function",
719 ecode
= capabilities_ok(tdb
, hdr
.capabilities
);
720 if (ecode
!= TDB_SUCCESS
) {
724 /* Clear any features we don't understand. */
725 if ((open_flags
& O_ACCMODE
) != O_RDONLY
) {
726 hdr
.features_used
&= TDB_FEATURE_MASK
;
727 ecode
= tdb_write_convert(tdb
, offsetof(struct tdb_header
,
730 sizeof(hdr
.features_used
));
731 if (ecode
!= TDB_SUCCESS
)
736 if (tdb
->flags
& TDB_VERSION1
) {
737 /* if needed, run recovery */
738 if (tdb1_transaction_recover(tdb
) == -1) {
739 ecode
= tdb
->last_error
;
744 tdb_unlock_open(tdb
, openlock
);
746 /* This makes sure we have current map_size and mmap. */
747 if (tdb
->flags
& TDB_VERSION1
) {
748 ecode
= tdb1_probe_length(tdb
);
750 ecode
= tdb
->tdb2
.io
->oob(tdb
, tdb
->file
->map_size
, 1, true);
752 if (unlikely(ecode
!= TDB_SUCCESS
))
755 if (!(tdb
->flags
& TDB_VERSION1
)) {
756 /* Now it's fully formed, recover if necessary. */
757 berr
= tdb_needs_recovery(tdb
);
758 if (unlikely(berr
!= false)) {
760 ecode
= TDB_OFF_TO_ERR(berr
);
763 ecode
= tdb_lock_and_recover(tdb
);
764 if (ecode
!= TDB_SUCCESS
) {
769 ecode
= tdb_ftable_init(tdb
);
770 if (ecode
!= TDB_SUCCESS
) {
780 /* Map ecode to some logical errno. */
781 switch (TDB_ERR_TO_OFF(ecode
)) {
782 case TDB_ERR_TO_OFF(TDB_ERR_CORRUPT
):
783 case TDB_ERR_TO_OFF(TDB_ERR_IO
):
786 case TDB_ERR_TO_OFF(TDB_ERR_LOCK
):
787 saved_errno
= EWOULDBLOCK
;
789 case TDB_ERR_TO_OFF(TDB_ERR_OOM
):
790 saved_errno
= ENOMEM
;
792 case TDB_ERR_TO_OFF(TDB_ERR_EINVAL
):
793 saved_errno
= EINVAL
;
796 saved_errno
= EINVAL
;
805 tdb_lock_cleanup(tdb
);
806 if (--tdb
->file
->refcnt
== 0) {
807 assert(tdb
->file
->num_lockrecs
== 0);
808 if (tdb
->file
->map_ptr
) {
809 if (tdb
->flags
& TDB_INTERNAL
) {
810 free(tdb
->file
->map_ptr
);
812 tdb_munmap(tdb
->file
);
814 if (close(tdb
->file
->fd
) != 0)
815 tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
816 "tdb_open: failed to close tdb fd"
817 " on error: %s", strerror(errno
));
818 free(tdb
->file
->lockrecs
);
828 _PUBLIC_
int tdb_close(struct tdb_context
*tdb
)
831 struct tdb_context
**i
;
833 tdb_trace(tdb
, "tdb_close");
835 if (tdb
->flags
& TDB_VERSION1
) {
836 if (tdb
->tdb1
.transaction
) {
837 tdb1_transaction_cancel(tdb
);
840 if (tdb
->tdb2
.transaction
) {
841 tdb_transaction_cancel(tdb
);
845 if (tdb
->file
->map_ptr
) {
846 if (tdb
->flags
& TDB_INTERNAL
)
847 free(tdb
->file
->map_ptr
);
849 tdb_munmap(tdb
->file
);
852 tdb_lock_cleanup(tdb
);
853 if (--tdb
->file
->refcnt
== 0) {
854 ret
= close(tdb
->file
->fd
);
855 free(tdb
->file
->lockrecs
);
860 /* Remove from tdbs list */
861 for (i
= &tdbs
; *i
; i
= &(*i
)->next
) {
876 _PUBLIC_
void tdb_foreach_(int (*fn
)(struct tdb_context
*, void *), void *p
)
878 struct tdb_context
*i
;
880 for (i
= tdbs
; i
; i
= i
->next
) {