3 #include <ccan/asprintf/asprintf.h>
7 static enum TDB_ERROR
update_rec_hdr(struct tdb_context
*tdb
,
11 struct tdb_used_record
*rec
,
14 uint64_t dataroom
= rec_data_length(rec
) + rec_extra_padding(rec
);
17 ecode
= set_header(tdb
, rec
, TDB_USED_MAGIC
, keylen
, datalen
,
18 keylen
+ dataroom
, h
);
19 if (ecode
== TDB_SUCCESS
) {
20 ecode
= tdb_write_convert(tdb
, off
, rec
, sizeof(*rec
));
25 static enum TDB_ERROR
replace_data(struct tdb_context
*tdb
,
27 struct tdb_data key
, struct tdb_data dbuf
,
28 tdb_off_t old_off
, tdb_len_t old_room
,
34 /* Allocate a new record. */
35 new_off
= alloc(tdb
, key
.dsize
, dbuf
.dsize
, h
->h
, TDB_USED_MAGIC
,
37 if (TDB_OFF_IS_ERR(new_off
)) {
41 /* We didn't like the existing one: remove it. */
44 ecode
= add_free_record(tdb
, old_off
,
45 sizeof(struct tdb_used_record
)
46 + key
.dsize
+ old_room
,
48 if (ecode
== TDB_SUCCESS
)
49 ecode
= replace_in_hash(tdb
, h
, new_off
);
51 ecode
= add_to_hash(tdb
, h
, new_off
);
53 if (ecode
!= TDB_SUCCESS
) {
57 new_off
+= sizeof(struct tdb_used_record
);
58 ecode
= tdb
->methods
->twrite(tdb
, new_off
, key
.dptr
, key
.dsize
);
59 if (ecode
!= TDB_SUCCESS
) {
64 ecode
= tdb
->methods
->twrite(tdb
, new_off
, dbuf
.dptr
, dbuf
.dsize
);
65 if (ecode
!= TDB_SUCCESS
) {
69 if (tdb
->flags
& TDB_SEQNUM
)
75 static enum TDB_ERROR
update_data(struct tdb_context
*tdb
,
82 ecode
= tdb
->methods
->twrite(tdb
, off
, dbuf
.dptr
, dbuf
.dsize
);
83 if (ecode
== TDB_SUCCESS
&& extra
) {
84 /* Put a zero in; future versions may append other data. */
85 ecode
= tdb
->methods
->twrite(tdb
, off
+ dbuf
.dsize
, "", 1);
87 if (tdb
->flags
& TDB_SEQNUM
)
93 enum TDB_ERROR
tdb_store(struct tdb_context
*tdb
,
94 struct tdb_data key
, struct tdb_data dbuf
, int flag
)
98 tdb_len_t old_room
= 0;
99 struct tdb_used_record rec
;
100 enum TDB_ERROR ecode
;
102 off
= find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
);
103 if (TDB_OFF_IS_ERR(off
)) {
104 return tdb
->last_error
= off
;
107 /* Now we have lock on this hash bucket. */
108 if (flag
== TDB_INSERT
) {
110 ecode
= TDB_ERR_EXISTS
;
115 old_room
= rec_data_length(&rec
)
116 + rec_extra_padding(&rec
);
117 if (old_room
>= dbuf
.dsize
) {
118 /* Can modify in-place. Easy! */
119 ecode
= update_rec_hdr(tdb
, off
,
120 key
.dsize
, dbuf
.dsize
,
122 if (ecode
!= TDB_SUCCESS
) {
125 ecode
= update_data(tdb
,
128 old_room
- dbuf
.dsize
);
129 if (ecode
!= TDB_SUCCESS
) {
132 tdb_unlock_hashes(tdb
, h
.hlock_start
,
133 h
.hlock_range
, F_WRLCK
);
134 return tdb
->last_error
= TDB_SUCCESS
;
137 if (flag
== TDB_MODIFY
) {
138 /* if the record doesn't exist and we
139 are in TDB_MODIFY mode then we should fail
141 ecode
= TDB_ERR_NOEXIST
;
147 /* If we didn't use the old record, this implies we're growing. */
148 ecode
= replace_data(tdb
, &h
, key
, dbuf
, off
, old_room
, off
);
150 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_WRLCK
);
151 return tdb
->last_error
= ecode
;
154 enum TDB_ERROR
tdb_append(struct tdb_context
*tdb
,
155 struct tdb_data key
, struct tdb_data dbuf
)
159 struct tdb_used_record rec
;
160 tdb_len_t old_room
= 0, old_dlen
;
161 unsigned char *newdata
;
162 struct tdb_data new_dbuf
;
163 enum TDB_ERROR ecode
;
165 off
= find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
);
166 if (TDB_OFF_IS_ERR(off
)) {
167 return tdb
->last_error
= off
;
171 old_dlen
= rec_data_length(&rec
);
172 old_room
= old_dlen
+ rec_extra_padding(&rec
);
174 /* Fast path: can append in place. */
175 if (rec_extra_padding(&rec
) >= dbuf
.dsize
) {
176 ecode
= update_rec_hdr(tdb
, off
, key
.dsize
,
177 old_dlen
+ dbuf
.dsize
, &rec
,
179 if (ecode
!= TDB_SUCCESS
) {
183 off
+= sizeof(rec
) + key
.dsize
+ old_dlen
;
184 ecode
= update_data(tdb
, off
, dbuf
,
185 rec_extra_padding(&rec
));
190 newdata
= malloc(key
.dsize
+ old_dlen
+ dbuf
.dsize
);
192 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
194 " failed to allocate %zu bytes",
195 (size_t)(key
.dsize
+ old_dlen
199 ecode
= tdb
->methods
->tread(tdb
, off
+ sizeof(rec
) + key
.dsize
,
201 if (ecode
!= TDB_SUCCESS
) {
202 goto out_free_newdata
;
204 memcpy(newdata
+ old_dlen
, dbuf
.dptr
, dbuf
.dsize
);
205 new_dbuf
.dptr
= newdata
;
206 new_dbuf
.dsize
= old_dlen
+ dbuf
.dsize
;
212 /* If they're using tdb_append(), it implies they're growing record. */
213 ecode
= replace_data(tdb
, &h
, key
, new_dbuf
, off
, old_room
, true);
218 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_WRLCK
);
219 return tdb
->last_error
= ecode
;
222 enum TDB_ERROR
tdb_fetch(struct tdb_context
*tdb
, struct tdb_data key
,
223 struct tdb_data
*data
)
226 struct tdb_used_record rec
;
228 enum TDB_ERROR ecode
;
230 off
= find_and_lock(tdb
, key
, F_RDLCK
, &h
, &rec
, NULL
);
231 if (TDB_OFF_IS_ERR(off
)) {
232 return tdb
->last_error
= off
;
236 ecode
= TDB_ERR_NOEXIST
;
238 data
->dsize
= rec_data_length(&rec
);
239 data
->dptr
= tdb_alloc_read(tdb
, off
+ sizeof(rec
) + key
.dsize
,
241 if (TDB_PTR_IS_ERR(data
->dptr
)) {
242 ecode
= TDB_PTR_ERR(data
->dptr
);
247 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_RDLCK
);
248 return tdb
->last_error
= ecode
;
251 bool tdb_exists(struct tdb_context
*tdb
, TDB_DATA key
)
254 struct tdb_used_record rec
;
257 off
= find_and_lock(tdb
, key
, F_RDLCK
, &h
, &rec
, NULL
);
258 if (TDB_OFF_IS_ERR(off
)) {
259 tdb
->last_error
= off
;
262 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_RDLCK
);
264 tdb
->last_error
= TDB_SUCCESS
;
265 return off
? true : false;
268 enum TDB_ERROR
tdb_delete(struct tdb_context
*tdb
, struct tdb_data key
)
271 struct tdb_used_record rec
;
273 enum TDB_ERROR ecode
;
275 off
= find_and_lock(tdb
, key
, F_WRLCK
, &h
, &rec
, NULL
);
276 if (TDB_OFF_IS_ERR(off
)) {
277 return tdb
->last_error
= off
;
281 ecode
= TDB_ERR_NOEXIST
;
285 ecode
= delete_from_hash(tdb
, &h
);
286 if (ecode
!= TDB_SUCCESS
) {
290 /* Free the deleted entry. */
292 ecode
= add_free_record(tdb
, off
,
293 sizeof(struct tdb_used_record
)
294 + rec_key_length(&rec
)
295 + rec_data_length(&rec
)
296 + rec_extra_padding(&rec
),
297 TDB_LOCK_WAIT
, true);
299 if (tdb
->flags
& TDB_SEQNUM
)
303 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_WRLCK
);
304 return tdb
->last_error
= ecode
;
307 unsigned int tdb_get_flags(struct tdb_context
*tdb
)
312 void tdb_add_flag(struct tdb_context
*tdb
, unsigned flag
)
314 if (tdb
->flags
& TDB_INTERNAL
) {
315 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
317 "tdb_add_flag: internal db");
322 tdb
->flags
|= TDB_NOLOCK
;
325 tdb
->flags
|= TDB_NOMMAP
;
326 tdb_munmap(tdb
->file
);
329 tdb
->flags
|= TDB_NOSYNC
;
332 tdb
->flags
|= TDB_SEQNUM
;
334 case TDB_ALLOW_NESTING
:
335 tdb
->flags
|= TDB_ALLOW_NESTING
;
338 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
340 "tdb_add_flag: Unknown flag %u",
345 void tdb_remove_flag(struct tdb_context
*tdb
, unsigned flag
)
347 if (tdb
->flags
& TDB_INTERNAL
) {
348 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
350 "tdb_remove_flag: internal db");
355 tdb
->flags
&= ~TDB_NOLOCK
;
358 tdb
->flags
&= ~TDB_NOMMAP
;
362 tdb
->flags
&= ~TDB_NOSYNC
;
365 tdb
->flags
&= ~TDB_SEQNUM
;
367 case TDB_ALLOW_NESTING
:
368 tdb
->flags
&= ~TDB_ALLOW_NESTING
;
371 tdb
->last_error
= tdb_logerr(tdb
, TDB_ERR_EINVAL
,
373 "tdb_remove_flag: Unknown flag %u",
378 const char *tdb_errorstr(enum TDB_ERROR ecode
)
380 /* Gcc warns if you miss a case in the switch, so use that. */
382 case TDB_SUCCESS
: return "Success";
383 case TDB_ERR_CORRUPT
: return "Corrupt database";
384 case TDB_ERR_IO
: return "IO Error";
385 case TDB_ERR_LOCK
: return "Locking error";
386 case TDB_ERR_OOM
: return "Out of memory";
387 case TDB_ERR_EXISTS
: return "Record exists";
388 case TDB_ERR_EINVAL
: return "Invalid parameter";
389 case TDB_ERR_NOEXIST
: return "Record does not exist";
390 case TDB_ERR_RDONLY
: return "write not permitted";
392 return "Invalid error code";
395 enum TDB_ERROR
tdb_error(struct tdb_context
*tdb
)
397 return tdb
->last_error
;
400 enum TDB_ERROR COLD
tdb_logerr(struct tdb_context
*tdb
,
401 enum TDB_ERROR ecode
,
402 enum tdb_log_level level
,
403 const char *fmt
, ...)
408 /* tdb_open paths care about errno, so save it. */
409 int saved_errno
= errno
;
415 len
= vasprintf(&message
, fmt
, ap
);
419 tdb
->log_fn(tdb
, TDB_LOG_ERROR
,
420 "out of memory formatting message:", tdb
->log_data
);
421 tdb
->log_fn(tdb
, level
, fmt
, tdb
->log_data
);
423 tdb
->log_fn(tdb
, level
, message
, tdb
->log_data
);
430 enum TDB_ERROR
tdb_parse_record_(struct tdb_context
*tdb
,
432 enum TDB_ERROR (*parse
)(TDB_DATA k
,
438 struct tdb_used_record rec
;
440 enum TDB_ERROR ecode
;
442 off
= find_and_lock(tdb
, key
, F_RDLCK
, &h
, &rec
, NULL
);
443 if (TDB_OFF_IS_ERR(off
)) {
444 return tdb
->last_error
= off
;
448 ecode
= TDB_ERR_NOEXIST
;
451 dptr
= tdb_access_read(tdb
, off
+ sizeof(rec
) + key
.dsize
,
452 rec_data_length(&rec
), false);
453 if (TDB_PTR_IS_ERR(dptr
)) {
454 ecode
= TDB_PTR_ERR(dptr
);
456 TDB_DATA d
= tdb_mkdata(dptr
, rec_data_length(&rec
));
458 ecode
= parse(key
, d
, data
);
459 tdb_access_release(tdb
, dptr
);
463 tdb_unlock_hashes(tdb
, h
.hlock_start
, h
.hlock_range
, F_RDLCK
);
464 return tdb
->last_error
= ecode
;
467 const char *tdb_name(const struct tdb_context
*tdb
)
472 int64_t tdb_get_seqnum(struct tdb_context
*tdb
)
474 tdb_off_t off
= tdb_read_off(tdb
, offsetof(struct tdb_header
, seqnum
));
475 if (TDB_OFF_IS_ERR(off
))
476 tdb
->last_error
= off
;
478 tdb
->last_error
= TDB_SUCCESS
;
483 int tdb_fd(const struct tdb_context
*tdb
)
485 return tdb
->file
->fd
;