2 Trivial Database 2: fetch, store and misc routines.
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/>.
20 #include <ccan/asprintf/asprintf.h>
24 static enum TDB_ERROR
update_rec_hdr(struct tdb_context
*tdb
,
28 struct tdb_used_record
*rec
,
31 uint64_t dataroom
= rec_data_length(rec
) + rec_extra_padding(rec
);
34 ecode
= set_header(tdb
, rec
, TDB_USED_MAGIC
, keylen
, datalen
,
35 keylen
+ dataroom
, h
);
36 if (ecode
== TDB_SUCCESS
) {
37 ecode
= tdb_write_convert(tdb
, off
, rec
, sizeof(*rec
));
42 static enum TDB_ERROR
replace_data(struct tdb_context
*tdb
,
44 struct tdb_data key
, struct tdb_data dbuf
,
45 tdb_off_t old_off
, tdb_len_t old_room
,
51 /* Allocate a new record. */
52 new_off
= alloc(tdb
, key
.dsize
, dbuf
.dsize
, h
->h
, TDB_USED_MAGIC
,
54 if (TDB_OFF_IS_ERR(new_off
)) {
58 /* We didn't like the existing one: remove it. */
61 ecode
= add_free_record(tdb
, old_off
,
62 sizeof(struct tdb_used_record
)
63 + key
.dsize
+ old_room
,
65 if (ecode
== TDB_SUCCESS
)
66 ecode
= replace_in_hash(tdb
, h
, new_off
);
68 ecode
= add_to_hash(tdb
, h
, new_off
);
70 if (ecode
!= TDB_SUCCESS
) {
74 new_off
+= sizeof(struct tdb_used_record
);
75 ecode
= tdb
->methods
->twrite(tdb
, new_off
, key
.dptr
, key
.dsize
);
76 if (ecode
!= TDB_SUCCESS
) {
81 ecode
= tdb
->methods
->twrite(tdb
, new_off
, dbuf
.dptr
, dbuf
.dsize
);
82 if (ecode
!= TDB_SUCCESS
) {
86 if (tdb
->flags
& TDB_SEQNUM
)
92 static enum TDB_ERROR
update_data(struct tdb_context
*tdb
,
99 ecode
= tdb
->methods
->twrite(tdb
, off
, dbuf
.dptr
, dbuf
.dsize
);
100 if (ecode
== TDB_SUCCESS
&& extra
) {
101 /* Put a zero in; future versions may append other data. */
102 ecode
= tdb
->methods
->twrite(tdb
, off
+ dbuf
.dsize
, "", 1);
104 if (tdb
->flags
& TDB_SEQNUM
)
110 enum TDB_ERROR
tdb_store(struct tdb_context
*tdb
,
111 struct tdb_data key
, struct tdb_data dbuf
, int flag
)
115 tdb_len_t old_room
= 0;
116 struct tdb_used_record rec
;
117 enum TDB_ERROR ecode
;
119 off
= find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
);
120 if (TDB_OFF_IS_ERR(off
)) {
121 return tdb
->last_error
= off
;
124 /* Now we have lock on this hash bucket. */
125 if (flag
== TDB_INSERT
) {
127 ecode
= TDB_ERR_EXISTS
;
132 old_room
= rec_data_length(&rec
)
133 + rec_extra_padding(&rec
);
134 if (old_room
>= dbuf
.dsize
) {
135 /* Can modify in-place. Easy! */
136 ecode
= update_rec_hdr(tdb
, off
,
137 key
.dsize
, dbuf
.dsize
,
139 if (ecode
!= TDB_SUCCESS
) {
142 ecode
= update_data(tdb
,
145 old_room
- dbuf
.dsize
);
146 if (ecode
!= TDB_SUCCESS
) {
149 tdb_unlock_hashes(tdb
, h
.hlock_start
,
150 h
.hlock_range
, F_WRLCK
);
151 return tdb
->last_error
= TDB_SUCCESS
;
154 if (flag
== TDB_MODIFY
) {
155 /* if the record doesn't exist and we
156 are in TDB_MODIFY mode then we should fail
158 ecode
= TDB_ERR_NOEXIST
;
164 /* If we didn't use the old record, this implies we're growing. */
165 ecode
= replace_data(tdb
, &h
, key
, dbuf
, off
, old_room
, off
);
167 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_WRLCK
);
168 return tdb
->last_error
= ecode
;
171 enum TDB_ERROR
tdb_append(struct tdb_context
*tdb
,
172 struct tdb_data key
, struct tdb_data dbuf
)
176 struct tdb_used_record rec
;
177 tdb_len_t old_room
= 0, old_dlen
;
178 unsigned char *newdata
;
179 struct tdb_data new_dbuf
;
180 enum TDB_ERROR ecode
;
182 off
= find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
);
183 if (TDB_OFF_IS_ERR(off
)) {
184 return tdb
->last_error
= off
;
188 old_dlen
= rec_data_length(&rec
);
189 old_room
= old_dlen
+ rec_extra_padding(&rec
);
191 /* Fast path: can append in place. */
192 if (rec_extra_padding(&rec
) >= dbuf
.dsize
) {
193 ecode
= update_rec_hdr(tdb
, off
, key
.dsize
,
194 old_dlen
+ dbuf
.dsize
, &rec
,
196 if (ecode
!= TDB_SUCCESS
) {
200 off
+= sizeof(rec
) + key
.dsize
+ old_dlen
;
201 ecode
= update_data(tdb
, off
, dbuf
,
202 rec_extra_padding(&rec
));
207 newdata
= malloc(key
.dsize
+ old_dlen
+ dbuf
.dsize
);
209 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
211 " failed to allocate %zu bytes",
212 (size_t)(key
.dsize
+ old_dlen
216 ecode
= tdb
->methods
->tread(tdb
, off
+ sizeof(rec
) + key
.dsize
,
218 if (ecode
!= TDB_SUCCESS
) {
219 goto out_free_newdata
;
221 memcpy(newdata
+ old_dlen
, dbuf
.dptr
, dbuf
.dsize
);
222 new_dbuf
.dptr
= newdata
;
223 new_dbuf
.dsize
= old_dlen
+ dbuf
.dsize
;
229 /* If they're using tdb_append(), it implies they're growing record. */
230 ecode
= replace_data(tdb
, &h
, key
, new_dbuf
, off
, old_room
, true);
235 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_WRLCK
);
236 return tdb
->last_error
= ecode
;
239 enum TDB_ERROR
tdb_fetch(struct tdb_context
*tdb
, struct tdb_data key
,
240 struct tdb_data
*data
)
243 struct tdb_used_record rec
;
245 enum TDB_ERROR ecode
;
247 off
= find_and_lock(tdb
, key
, F_RDLCK
, &h
, &rec
, NULL
);
248 if (TDB_OFF_IS_ERR(off
)) {
249 return tdb
->last_error
= off
;
253 ecode
= TDB_ERR_NOEXIST
;
255 data
->dsize
= rec_data_length(&rec
);
256 data
->dptr
= tdb_alloc_read(tdb
, off
+ sizeof(rec
) + key
.dsize
,
258 if (TDB_PTR_IS_ERR(data
->dptr
)) {
259 ecode
= TDB_PTR_ERR(data
->dptr
);
264 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_RDLCK
);
265 return tdb
->last_error
= ecode
;
268 bool tdb_exists(struct tdb_context
*tdb
, TDB_DATA key
)
271 struct tdb_used_record rec
;
274 off
= find_and_lock(tdb
, key
, F_RDLCK
, &h
, &rec
, NULL
);
275 if (TDB_OFF_IS_ERR(off
)) {
276 tdb
->last_error
= off
;
279 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_RDLCK
);
281 tdb
->last_error
= TDB_SUCCESS
;
282 return off
? true : false;
285 enum TDB_ERROR
tdb_delete(struct tdb_context
*tdb
, struct tdb_data key
)
288 struct tdb_used_record rec
;
290 enum TDB_ERROR ecode
;
292 off
= find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
);
293 if (TDB_OFF_IS_ERR(off
)) {
294 return tdb
->last_error
= off
;
298 ecode
= TDB_ERR_NOEXIST
;
302 ecode
= delete_from_hash(tdb
, &h
);
303 if (ecode
!= TDB_SUCCESS
) {
307 /* Free the deleted entry. */
309 ecode
= add_free_record(tdb
, off
,
310 sizeof(struct tdb_used_record
)
311 + rec_key_length(&rec
)
312 + rec_data_length(&rec
)
313 + rec_extra_padding(&rec
),
314 TDB_LOCK_WAIT
, true);
316 if (tdb
->flags
& TDB_SEQNUM
)
320 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_WRLCK
);
321 return tdb
->last_error
= ecode
;
324 unsigned int tdb_get_flags(struct tdb_context
*tdb
)
329 void tdb_add_flag(struct tdb_context
*tdb
, unsigned flag
)
331 if (tdb
->flags
& TDB_INTERNAL
) {
332 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
334 "tdb_add_flag: internal db");
339 tdb
->flags
|= TDB_NOLOCK
;
342 tdb
->flags
|= TDB_NOMMAP
;
343 tdb_munmap(tdb
->file
);
346 tdb
->flags
|= TDB_NOSYNC
;
349 tdb
->flags
|= TDB_SEQNUM
;
351 case TDB_ALLOW_NESTING
:
352 tdb
->flags
|= TDB_ALLOW_NESTING
;
355 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
357 "tdb_add_flag: Unknown flag %u",
362 void tdb_remove_flag(struct tdb_context
*tdb
, unsigned flag
)
364 if (tdb
->flags
& TDB_INTERNAL
) {
365 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
367 "tdb_remove_flag: internal db");
372 tdb
->flags
&= ~TDB_NOLOCK
;
375 tdb
->flags
&= ~TDB_NOMMAP
;
379 tdb
->flags
&= ~TDB_NOSYNC
;
382 tdb
->flags
&= ~TDB_SEQNUM
;
384 case TDB_ALLOW_NESTING
:
385 tdb
->flags
&= ~TDB_ALLOW_NESTING
;
388 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
390 "tdb_remove_flag: Unknown flag %u",
395 const char *tdb_errorstr(enum TDB_ERROR ecode
)
397 /* Gcc warns if you miss a case in the switch, so use that. */
399 case TDB_SUCCESS
: return "Success";
400 case TDB_ERR_CORRUPT
: return "Corrupt database";
401 case TDB_ERR_IO
: return "IO Error";
402 case TDB_ERR_LOCK
: return "Locking error";
403 case TDB_ERR_OOM
: return "Out of memory";
404 case TDB_ERR_EXISTS
: return "Record exists";
405 case TDB_ERR_EINVAL
: return "Invalid parameter";
406 case TDB_ERR_NOEXIST
: return "Record does not exist";
407 case TDB_ERR_RDONLY
: return "write not permitted";
409 return "Invalid error code";
412 enum TDB_ERROR
tdb_error(struct tdb_context
*tdb
)
414 return tdb
->last_error
;
417 enum TDB_ERROR COLD
tdb_logerr(struct tdb_context
*tdb
,
418 enum TDB_ERROR ecode
,
419 enum tdb_log_level level
,
420 const char *fmt
, ...)
425 /* tdb_open paths care about errno, so save it. */
426 int saved_errno
= errno
;
432 len
= vasprintf(&message
, fmt
, ap
);
436 tdb
->log_fn(tdb
, TDB_LOG_ERROR
,
437 "out of memory formatting message:", tdb
->log_data
);
438 tdb
->log_fn(tdb
, level
, fmt
, tdb
->log_data
);
440 tdb
->log_fn(tdb
, level
, message
, tdb
->log_data
);
447 enum TDB_ERROR
tdb_parse_record_(struct tdb_context
*tdb
,
449 enum TDB_ERROR (*parse
)(TDB_DATA k
,
455 struct tdb_used_record rec
;
457 enum TDB_ERROR ecode
;
459 off
= find_and_lock(tdb
, key
, F_RDLCK
, &h
, &rec
, NULL
);
460 if (TDB_OFF_IS_ERR(off
)) {
461 return tdb
->last_error
= off
;
465 ecode
= TDB_ERR_NOEXIST
;
468 dptr
= tdb_access_read(tdb
, off
+ sizeof(rec
) + key
.dsize
,
469 rec_data_length(&rec
), false);
470 if (TDB_PTR_IS_ERR(dptr
)) {
471 ecode
= TDB_PTR_ERR(dptr
);
473 TDB_DATA d
= tdb_mkdata(dptr
, rec_data_length(&rec
));
475 ecode
= parse(key
, d
, data
);
476 tdb_access_release(tdb
, dptr
);
480 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_RDLCK
);
481 return tdb
->last_error
= ecode
;
484 const char *tdb_name(const struct tdb_context
*tdb
)
489 int64_t tdb_get_seqnum(struct tdb_context
*tdb
)
491 tdb_off_t off
= tdb_read_off(tdb
, offsetof(struct tdb_header
, seqnum
));
492 if (TDB_OFF_IS_ERR(off
))
493 tdb
->last_error
= off
;
495 tdb
->last_error
= TDB_SUCCESS
;
500 int tdb_fd(const struct tdb_context
*tdb
)
502 return tdb
->file
->fd
;