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 ntdb_context
*tdbs
= NULL
;
25 static struct ntdb_file
*find_file(dev_t device
, ino_t ino
)
27 struct ntdb_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 ntdb_context
*ntdb
)
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 ntdb_logerr(ntdb
, NTDB_SUCCESS
, NTDB_LOG_WARNING
,
95 "ntdb_open: random from getpid and time");
99 static void ntdb_context_init(struct ntdb_context
*ntdb
)
101 /* Initialize the NTDB fields here */
103 ntdb
->direct_access
= 0;
104 ntdb
->transaction
= NULL
;
108 struct new_database
{
109 struct ntdb_header hdr
;
110 struct ntdb_freetable ftable
;
111 struct ntdb_free_record remainder
;
114 /* initialise a new database */
115 static enum NTDB_ERROR
ntdb_new_database(struct ntdb_context
*ntdb
,
116 struct ntdb_attribute_seed
*seed
,
117 struct ntdb_header
*hdr
)
119 /* We make it up in memory, then write it out if not internal */
120 struct new_database
*newdb
;
121 unsigned int magic_len
;
123 size_t dbsize
, remaindersize
;
124 enum NTDB_ERROR ecode
;
126 /* Always make db a multiple of NTDB_PGSIZE */
127 dbsize
= (sizeof(*newdb
) + NTDB_PGSIZE
-1) & ~(NTDB_PGSIZE
-1);
128 remaindersize
= dbsize
- sizeof(*newdb
);
129 newdb
= malloc(dbsize
);
131 return ntdb_logerr(ntdb
, NTDB_ERR_OOM
, NTDB_LOG_ERROR
,
132 "ntdb_new_database: failed to allocate");
135 /* Fill in the header */
136 newdb
->hdr
.version
= NTDB_VERSION
;
138 newdb
->hdr
.hash_seed
= seed
->seed
;
140 newdb
->hdr
.hash_seed
= random_number(ntdb
);
141 newdb
->hdr
.hash_test
= NTDB_HASH_MAGIC
;
142 newdb
->hdr
.hash_test
= ntdb
->hash_fn(&newdb
->hdr
.hash_test
,
143 sizeof(newdb
->hdr
.hash_test
),
144 newdb
->hdr
.hash_seed
,
146 newdb
->hdr
.recovery
= 0;
147 newdb
->hdr
.features_used
= newdb
->hdr
.features_offered
= NTDB_FEATURE_MASK
;
148 newdb
->hdr
.seqnum
= 0;
149 newdb
->hdr
.capabilities
= 0;
150 memset(newdb
->hdr
.reserved
, 0, sizeof(newdb
->hdr
.reserved
));
151 /* Initial hashes are empty. */
152 memset(newdb
->hdr
.hashtable
, 0, sizeof(newdb
->hdr
.hashtable
));
155 newdb
->hdr
.free_table
= offsetof(struct new_database
, ftable
);
156 memset(&newdb
->ftable
, 0, sizeof(newdb
->ftable
));
157 ecode
= set_header(NULL
, &newdb
->ftable
.hdr
, NTDB_FTABLE_MAGIC
, 0,
158 sizeof(newdb
->ftable
) - sizeof(newdb
->ftable
.hdr
),
159 sizeof(newdb
->ftable
) - sizeof(newdb
->ftable
.hdr
),
161 if (ecode
!= NTDB_SUCCESS
) {
165 /* Rest of database is a free record, containing junk. */
166 newdb
->remainder
.ftable_and_len
167 = (remaindersize
+ sizeof(newdb
->remainder
)
168 - sizeof(struct ntdb_used_record
));
169 newdb
->remainder
.next
= 0;
170 newdb
->remainder
.magic_and_prev
171 = (NTDB_FREE_MAGIC
<< (64-NTDB_OFF_UPPER_STEAL
))
172 | offsetof(struct new_database
, remainder
);
173 memset(&newdb
->remainder
+ 1, 0x43, remaindersize
);
175 /* Put in our single free entry. */
176 newdb
->ftable
.buckets
[size_to_bucket(remaindersize
)] =
177 offsetof(struct new_database
, remainder
);
180 memset(newdb
->hdr
.magic_food
, 0, sizeof(newdb
->hdr
.magic_food
));
181 strcpy(newdb
->hdr
.magic_food
, NTDB_MAGIC_FOOD
);
183 /* This creates an endian-converted database, as if read from disk */
184 magic_len
= sizeof(newdb
->hdr
.magic_food
);
186 (char *)&newdb
->hdr
+ magic_len
,
187 sizeof(*newdb
) - magic_len
);
191 if (ntdb
->flags
& NTDB_INTERNAL
) {
192 ntdb
->file
->map_size
= dbsize
;
193 ntdb
->file
->map_ptr
= newdb
;
196 if (lseek(ntdb
->file
->fd
, 0, SEEK_SET
) == -1) {
197 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
199 " failed to seek: %s", strerror(errno
));
203 if (ftruncate(ntdb
->file
->fd
, 0) == -1) {
204 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
206 " failed to truncate: %s", strerror(errno
));
210 rlen
= write(ntdb
->file
->fd
, newdb
, dbsize
);
211 if (rlen
!= dbsize
) {
214 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
215 "ntdb_new_database: %zi writing header: %s",
216 rlen
, strerror(errno
));
225 static enum NTDB_ERROR
ntdb_new_file(struct ntdb_context
*ntdb
)
227 ntdb
->file
= malloc(sizeof(*ntdb
->file
));
229 return ntdb_logerr(ntdb
, NTDB_ERR_OOM
, NTDB_LOG_ERROR
,
230 "ntdb_open: cannot alloc ntdb_file structure");
231 ntdb
->file
->num_lockrecs
= 0;
232 ntdb
->file
->lockrecs
= NULL
;
233 ntdb
->file
->allrecord_lock
.count
= 0;
234 ntdb
->file
->refcnt
= 1;
235 ntdb
->file
->map_ptr
= NULL
;
239 _PUBLIC_
enum NTDB_ERROR
ntdb_set_attribute(struct ntdb_context
*ntdb
,
240 const union ntdb_attribute
*attr
)
242 switch (attr
->base
.attr
) {
243 case NTDB_ATTRIBUTE_LOG
:
244 ntdb
->log_fn
= attr
->log
.fn
;
245 ntdb
->log_data
= attr
->log
.data
;
247 case NTDB_ATTRIBUTE_HASH
:
248 case NTDB_ATTRIBUTE_SEED
:
249 case NTDB_ATTRIBUTE_OPENHOOK
:
250 return ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
252 "ntdb_set_attribute:"
253 " cannot set %s after opening",
254 attr
->base
.attr
== NTDB_ATTRIBUTE_HASH
255 ? "NTDB_ATTRIBUTE_HASH"
256 : attr
->base
.attr
== NTDB_ATTRIBUTE_SEED
257 ? "NTDB_ATTRIBUTE_SEED"
258 : "NTDB_ATTRIBUTE_OPENHOOK");
259 case NTDB_ATTRIBUTE_STATS
:
260 return ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
262 "ntdb_set_attribute:"
263 " cannot set NTDB_ATTRIBUTE_STATS");
264 case NTDB_ATTRIBUTE_FLOCK
:
265 ntdb
->lock_fn
= attr
->flock
.lock
;
266 ntdb
->unlock_fn
= attr
->flock
.unlock
;
267 ntdb
->lock_data
= attr
->flock
.data
;
270 return ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
272 "ntdb_set_attribute:"
273 " unknown attribute type %u",
279 _PUBLIC_
enum NTDB_ERROR
ntdb_get_attribute(struct ntdb_context
*ntdb
,
280 union ntdb_attribute
*attr
)
282 switch (attr
->base
.attr
) {
283 case NTDB_ATTRIBUTE_LOG
:
285 return NTDB_ERR_NOEXIST
;
286 attr
->log
.fn
= ntdb
->log_fn
;
287 attr
->log
.data
= ntdb
->log_data
;
289 case NTDB_ATTRIBUTE_HASH
:
290 attr
->hash
.fn
= ntdb
->hash_fn
;
291 attr
->hash
.data
= ntdb
->hash_data
;
293 case NTDB_ATTRIBUTE_SEED
:
294 attr
->seed
.seed
= ntdb
->hash_seed
;
296 case NTDB_ATTRIBUTE_OPENHOOK
:
298 return NTDB_ERR_NOEXIST
;
299 attr
->openhook
.fn
= ntdb
->openhook
;
300 attr
->openhook
.data
= ntdb
->openhook_data
;
302 case NTDB_ATTRIBUTE_STATS
: {
303 size_t size
= attr
->stats
.size
;
304 if (size
> ntdb
->stats
.size
)
305 size
= ntdb
->stats
.size
;
306 memcpy(&attr
->stats
, &ntdb
->stats
, size
);
309 case NTDB_ATTRIBUTE_FLOCK
:
310 attr
->flock
.lock
= ntdb
->lock_fn
;
311 attr
->flock
.unlock
= ntdb
->unlock_fn
;
312 attr
->flock
.data
= ntdb
->lock_data
;
315 return ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
317 "ntdb_get_attribute:"
318 " unknown attribute type %u",
321 attr
->base
.next
= NULL
;
325 _PUBLIC_
void ntdb_unset_attribute(struct ntdb_context
*ntdb
,
326 enum ntdb_attribute_type type
)
329 case NTDB_ATTRIBUTE_LOG
:
332 case NTDB_ATTRIBUTE_OPENHOOK
:
333 ntdb
->openhook
= NULL
;
335 case NTDB_ATTRIBUTE_HASH
:
336 case NTDB_ATTRIBUTE_SEED
:
337 ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
, NTDB_LOG_USE_ERROR
,
338 "ntdb_unset_attribute: cannot unset %s after opening",
339 type
== NTDB_ATTRIBUTE_HASH
340 ? "NTDB_ATTRIBUTE_HASH"
341 : "NTDB_ATTRIBUTE_SEED");
343 case NTDB_ATTRIBUTE_STATS
:
344 ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
346 "ntdb_unset_attribute:"
347 "cannot unset NTDB_ATTRIBUTE_STATS");
349 case NTDB_ATTRIBUTE_FLOCK
:
350 ntdb
->lock_fn
= ntdb_fcntl_lock
;
351 ntdb
->unlock_fn
= ntdb_fcntl_unlock
;
354 ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
356 "ntdb_unset_attribute: unknown attribute type %u",
361 /* The top three bits of the capability tell us whether it matters. */
362 enum NTDB_ERROR
unknown_capability(struct ntdb_context
*ntdb
, const char *caller
,
365 if (type
& NTDB_CAP_NOOPEN
) {
366 return ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
367 "%s: file has unknown capability %llu",
368 caller
, type
& NTDB_CAP_NOOPEN
);
371 if ((type
& NTDB_CAP_NOWRITE
) && !(ntdb
->flags
& NTDB_RDONLY
)) {
372 return ntdb_logerr(ntdb
, NTDB_ERR_RDONLY
, NTDB_LOG_ERROR
,
373 "%s: file has unknown capability %llu"
374 " (cannot write to it)",
375 caller
, type
& NTDB_CAP_NOOPEN
);
378 if (type
& NTDB_CAP_NOCHECK
) {
379 ntdb
->flags
|= NTDB_CANT_CHECK
;
384 static enum NTDB_ERROR
capabilities_ok(struct ntdb_context
*ntdb
,
385 ntdb_off_t capabilities
)
387 ntdb_off_t off
, next
;
388 enum NTDB_ERROR ecode
= NTDB_SUCCESS
;
389 const struct ntdb_capability
*cap
;
391 /* Check capability list. */
392 for (off
= capabilities
; off
&& ecode
== NTDB_SUCCESS
; off
= next
) {
393 cap
= ntdb_access_read(ntdb
, off
, sizeof(*cap
), true);
394 if (NTDB_PTR_IS_ERR(cap
)) {
395 return NTDB_PTR_ERR(cap
);
398 switch (cap
->type
& NTDB_CAP_TYPE_MASK
) {
399 /* We don't understand any capabilities (yet). */
401 ecode
= unknown_capability(ntdb
, "ntdb_open", cap
->type
);
404 ntdb_access_release(ntdb
, cap
);
409 _PUBLIC_
struct ntdb_context
*ntdb_open(const char *name
, int ntdb_flags
,
410 int open_flags
, mode_t mode
,
411 union ntdb_attribute
*attr
)
413 struct ntdb_context
*ntdb
;
419 struct ntdb_header hdr
;
420 struct ntdb_attribute_seed
*seed
= NULL
;
422 enum NTDB_ERROR ecode
;
425 ntdb
= malloc(sizeof(*ntdb
) + strlen(name
) + 1);
431 /* Set name immediately for logging functions. */
432 ntdb
->name
= strcpy((char *)(ntdb
+ 1), name
);
433 ntdb
->flags
= ntdb_flags
;
435 ntdb
->open_flags
= open_flags
;
437 ntdb
->openhook
= NULL
;
438 ntdb
->lock_fn
= ntdb_fcntl_lock
;
439 ntdb
->unlock_fn
= ntdb_fcntl_unlock
;
440 ntdb
->hash_fn
= ntdb_jenkins_hash
;
441 memset(&ntdb
->stats
, 0, sizeof(ntdb
->stats
));
442 ntdb
->stats
.base
.attr
= NTDB_ATTRIBUTE_STATS
;
443 ntdb
->stats
.size
= sizeof(ntdb
->stats
);
446 switch (attr
->base
.attr
) {
447 case NTDB_ATTRIBUTE_HASH
:
448 ntdb
->hash_fn
= attr
->hash
.fn
;
449 ntdb
->hash_data
= attr
->hash
.data
;
451 case NTDB_ATTRIBUTE_SEED
:
454 case NTDB_ATTRIBUTE_OPENHOOK
:
455 ntdb
->openhook
= attr
->openhook
.fn
;
456 ntdb
->openhook_data
= attr
->openhook
.data
;
459 /* These are set as normal. */
460 ecode
= ntdb_set_attribute(ntdb
, attr
);
461 if (ecode
!= NTDB_SUCCESS
)
464 attr
= attr
->base
.next
;
467 if (ntdb_flags
& ~(NTDB_INTERNAL
| NTDB_NOLOCK
| NTDB_NOMMAP
| NTDB_CONVERT
468 | NTDB_NOSYNC
| NTDB_SEQNUM
| NTDB_ALLOW_NESTING
470 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
, NTDB_LOG_USE_ERROR
,
471 "ntdb_open: unknown flags %u", ntdb_flags
);
476 if (!(ntdb_flags
& NTDB_INTERNAL
) && !(open_flags
& O_CREAT
)) {
477 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
480 " cannot set NTDB_ATTRIBUTE_SEED"
481 " without O_CREAT.");
486 if ((open_flags
& O_ACCMODE
) == O_WRONLY
) {
487 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
, NTDB_LOG_USE_ERROR
,
488 "ntdb_open: can't open ntdb %s write-only",
493 if ((open_flags
& O_ACCMODE
) == O_RDONLY
) {
495 ntdb
->flags
|= NTDB_RDONLY
;
497 if (ntdb_flags
& NTDB_RDONLY
) {
498 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_EINVAL
,
500 "ntdb_open: can't use NTDB_RDONLY"
501 " without O_RDONLY");
507 /* internal databases don't need any of the rest. */
508 if (ntdb
->flags
& NTDB_INTERNAL
) {
509 ntdb
->flags
|= (NTDB_NOLOCK
| NTDB_NOMMAP
);
510 ecode
= ntdb_new_file(ntdb
);
511 if (ecode
!= NTDB_SUCCESS
) {
515 ecode
= ntdb_new_database(ntdb
, seed
, &hdr
);
516 if (ecode
== NTDB_SUCCESS
) {
517 ntdb_convert(ntdb
, &hdr
.hash_seed
,
518 sizeof(hdr
.hash_seed
));
519 ntdb
->hash_seed
= hdr
.hash_seed
;
520 ntdb_context_init(ntdb
);
521 ntdb_ftable_init(ntdb
);
523 if (ecode
!= NTDB_SUCCESS
) {
529 if (stat(name
, &st
) != -1)
530 ntdb
->file
= find_file(st
.st_dev
, st
.st_ino
);
533 ecode
= ntdb_new_file(ntdb
);
534 if (ecode
!= NTDB_SUCCESS
) {
538 /* Set this now, as ntdb_nest_lock examines it. */
539 ntdb
->file
->map_size
= 0;
541 if ((ntdb
->file
->fd
= open(name
, open_flags
, mode
)) == -1) {
542 enum ntdb_log_level lvl
;
543 /* errno set by open(2) */
546 /* Probing for files like this is a common pattern. */
547 if (!(open_flags
& O_CREAT
) && errno
== ENOENT
) {
548 lvl
= NTDB_LOG_WARNING
;
550 lvl
= NTDB_LOG_ERROR
;
552 ntdb_logerr(ntdb
, NTDB_ERR_IO
, lvl
,
553 "ntdb_open: could not open file %s: %s",
554 name
, strerror(errno
));
559 /* ensure there is only one process initialising at once:
560 * do it immediately to reduce the create/openlock race. */
561 ecode
= ntdb_lock_open(ntdb
, openlock
,
562 NTDB_LOCK_WAIT
|NTDB_LOCK_NOCHECK
);
563 if (ecode
!= NTDB_SUCCESS
) {
568 /* on exec, don't inherit the fd */
569 v
= fcntl(ntdb
->file
->fd
, F_GETFD
, 0);
570 fcntl(ntdb
->file
->fd
, F_SETFD
, v
| FD_CLOEXEC
);
572 if (fstat(ntdb
->file
->fd
, &st
) == -1) {
574 ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
575 "ntdb_open: could not stat open %s: %s",
576 name
, strerror(errno
));
580 ntdb
->file
->device
= st
.st_dev
;
581 ntdb
->file
->inode
= st
.st_ino
;
583 /* ensure there is only one process initialising at once */
584 ecode
= ntdb_lock_open(ntdb
, openlock
,
585 NTDB_LOCK_WAIT
|NTDB_LOCK_NOCHECK
);
586 if (ecode
!= NTDB_SUCCESS
) {
592 /* call their open hook if they gave us one. */
593 if (ntdb
->openhook
) {
594 ecode
= ntdb
->openhook(ntdb
->file
->fd
, ntdb
->openhook_data
);
595 if (ecode
!= NTDB_SUCCESS
) {
596 ntdb_logerr(ntdb
, ecode
, NTDB_LOG_ERROR
,
597 "ntdb_open: open hook failed");
600 open_flags
|= O_CREAT
;
603 /* If they used O_TRUNC, read will return 0. */
604 rlen
= pread(ntdb
->file
->fd
, &hdr
, sizeof(hdr
), 0);
605 if (rlen
== 0 && (open_flags
& O_CREAT
)) {
606 ecode
= ntdb_new_database(ntdb
, seed
, &hdr
);
607 if (ecode
!= NTDB_SUCCESS
) {
610 } else if (rlen
< 0) {
611 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
612 "ntdb_open: error %s reading %s",
613 strerror(errno
), name
);
615 } else if (rlen
< sizeof(hdr
)
616 || strcmp(hdr
.magic_food
, NTDB_MAGIC_FOOD
) != 0) {
617 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
618 "ntdb_open: %s is not a ntdb file", name
);
622 if (hdr
.version
!= NTDB_VERSION
) {
623 if (hdr
.version
== bswap_64(NTDB_VERSION
))
624 ntdb
->flags
|= NTDB_CONVERT
;
627 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
629 " %s is unknown version 0x%llx",
630 name
, (long long)hdr
.version
);
633 } else if (ntdb
->flags
& NTDB_CONVERT
) {
634 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
636 " %s does not need NTDB_CONVERT",
641 ntdb_context_init(ntdb
);
643 ntdb_convert(ntdb
, &hdr
, sizeof(hdr
));
644 ntdb
->hash_seed
= hdr
.hash_seed
;
645 hash_test
= NTDB_HASH_MAGIC
;
646 hash_test
= ntdb_hash(ntdb
, &hash_test
, sizeof(hash_test
));
647 if (hdr
.hash_test
!= hash_test
) {
648 /* wrong hash variant */
649 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
651 " %s uses a different hash function",
656 ecode
= capabilities_ok(ntdb
, hdr
.capabilities
);
657 if (ecode
!= NTDB_SUCCESS
) {
661 /* Clear any features we don't understand. */
662 if ((open_flags
& O_ACCMODE
) != O_RDONLY
) {
663 hdr
.features_used
&= NTDB_FEATURE_MASK
;
664 ecode
= ntdb_write_convert(ntdb
, offsetof(struct ntdb_header
,
667 sizeof(hdr
.features_used
));
668 if (ecode
!= NTDB_SUCCESS
)
672 ntdb_unlock_open(ntdb
, openlock
);
674 /* This makes sure we have current map_size and mmap. */
675 ecode
= ntdb
->io
->oob(ntdb
, ntdb
->file
->map_size
, 1, true);
676 if (unlikely(ecode
!= NTDB_SUCCESS
))
679 if (ntdb
->file
->map_size
% NTDB_PGSIZE
!= 0) {
680 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
682 " %s size %llu isn't a multiple of %u",
683 name
, (long long)ntdb
->file
->map_size
,
688 /* Now it's fully formed, recover if necessary. */
689 berr
= ntdb_needs_recovery(ntdb
);
690 if (unlikely(berr
!= false)) {
692 ecode
= NTDB_OFF_TO_ERR(berr
);
695 ecode
= ntdb_lock_and_recover(ntdb
);
696 if (ecode
!= NTDB_SUCCESS
) {
701 ecode
= ntdb_ftable_init(ntdb
);
702 if (ecode
!= NTDB_SUCCESS
) {
711 /* Map ecode to some logical errno. */
712 switch (NTDB_ERR_TO_OFF(ecode
)) {
713 case NTDB_ERR_TO_OFF(NTDB_ERR_CORRUPT
):
714 case NTDB_ERR_TO_OFF(NTDB_ERR_IO
):
717 case NTDB_ERR_TO_OFF(NTDB_ERR_LOCK
):
718 saved_errno
= EWOULDBLOCK
;
720 case NTDB_ERR_TO_OFF(NTDB_ERR_OOM
):
721 saved_errno
= ENOMEM
;
723 case NTDB_ERR_TO_OFF(NTDB_ERR_EINVAL
):
724 saved_errno
= EINVAL
;
727 saved_errno
= EINVAL
;
733 close(ntdb
->tracefd
);
736 ntdb_lock_cleanup(ntdb
);
737 if (--ntdb
->file
->refcnt
== 0) {
738 assert(ntdb
->file
->num_lockrecs
== 0);
739 if (ntdb
->file
->map_ptr
) {
740 if (ntdb
->flags
& NTDB_INTERNAL
) {
741 free(ntdb
->file
->map_ptr
);
743 ntdb_munmap(ntdb
->file
);
745 if (ntdb
->file
->fd
!= -1 && close(ntdb
->file
->fd
) != 0)
746 ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
747 "ntdb_open: failed to close ntdb fd"
748 " on error: %s", strerror(errno
));
749 free(ntdb
->file
->lockrecs
);
759 _PUBLIC_
int ntdb_close(struct ntdb_context
*ntdb
)
762 struct ntdb_context
**i
;
764 ntdb_trace(ntdb
, "ntdb_close");
766 if (ntdb
->transaction
) {
767 ntdb_transaction_cancel(ntdb
);
770 if (ntdb
->file
->map_ptr
) {
771 if (ntdb
->flags
& NTDB_INTERNAL
)
772 free(ntdb
->file
->map_ptr
);
774 ntdb_munmap(ntdb
->file
);
777 ntdb_lock_cleanup(ntdb
);
778 if (--ntdb
->file
->refcnt
== 0) {
779 ret
= close(ntdb
->file
->fd
);
780 free(ntdb
->file
->lockrecs
);
785 /* Remove from tdbs list */
786 for (i
= &tdbs
; *i
; i
= &(*i
)->next
) {
794 close(ntdb
->tracefd
);
801 _PUBLIC_
void ntdb_foreach_(int (*fn
)(struct ntdb_context
*, void *), void *p
)
803 struct ntdb_context
*i
;
805 for (i
= tdbs
; i
; i
= i
->next
) {