2 #include <ccan/asprintf/asprintf.h>
6 struct tdb_data tdb_null = { .dptr = NULL, .dsize = 0 };
8 static enum TDB_ERROR update_rec_hdr(struct tdb_context *tdb,
12 struct tdb_used_record *rec,
15 uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
18 ecode = set_header(tdb, rec, TDB_USED_MAGIC, keylen, datalen,
19 keylen + dataroom, h);
20 if (ecode == TDB_SUCCESS) {
21 ecode = tdb_write_convert(tdb, off, rec, sizeof(*rec));
26 static enum TDB_ERROR replace_data(struct tdb_context *tdb,
28 struct tdb_data key, struct tdb_data dbuf,
29 tdb_off_t old_off, tdb_len_t old_room,
35 /* Allocate a new record. */
36 new_off = alloc(tdb, key.dsize, dbuf.dsize, h->h, TDB_USED_MAGIC,
38 if (TDB_OFF_IS_ERR(new_off)) {
42 /* We didn't like the existing one: remove it. */
44 add_stat(tdb, frees, 1);
45 ecode = add_free_record(tdb, old_off,
46 sizeof(struct tdb_used_record)
47 + 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 /* FIXME: tdb_increment_seqnum(tdb); */
73 static enum TDB_ERROR update_data(struct tdb_context *tdb,
80 ecode = tdb->methods->twrite(tdb, off, dbuf.dptr, dbuf.dsize);
81 if (ecode == TDB_SUCCESS && extra) {
82 /* Put a zero in; future versions may append other data. */
83 ecode = tdb->methods->twrite(tdb, off + dbuf.dsize, "", 1);
88 enum TDB_ERROR tdb_store(struct tdb_context *tdb,
89 struct tdb_data key, struct tdb_data dbuf, int flag)
93 tdb_len_t old_room = 0;
94 struct tdb_used_record rec;
97 off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
98 if (TDB_OFF_IS_ERR(off)) {
102 /* Now we have lock on this hash bucket. */
103 if (flag == TDB_INSERT) {
105 ecode = TDB_ERR_EXISTS;
110 old_room = rec_data_length(&rec)
111 + rec_extra_padding(&rec);
112 if (old_room >= dbuf.dsize) {
113 /* Can modify in-place. Easy! */
114 ecode = update_rec_hdr(tdb, off,
115 key.dsize, dbuf.dsize,
117 if (ecode != TDB_SUCCESS) {
120 ecode = update_data(tdb,
123 old_room - dbuf.dsize);
124 if (ecode != TDB_SUCCESS) {
127 tdb_unlock_hashes(tdb, h.hlock_start,
128 h.hlock_range, F_WRLCK);
132 if (flag == TDB_MODIFY) {
133 /* if the record doesn't exist and we
134 are in TDB_MODIFY mode then we should fail
136 ecode = TDB_ERR_NOEXIST;
142 /* If we didn't use the old record, this implies we're growing. */
143 ecode = replace_data(tdb, &h, key, dbuf, off, old_room, off);
145 tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
149 enum TDB_ERROR tdb_append(struct tdb_context *tdb,
150 struct tdb_data key, struct tdb_data dbuf)
154 struct tdb_used_record rec;
155 tdb_len_t old_room = 0, old_dlen;
156 unsigned char *newdata;
157 struct tdb_data new_dbuf;
158 enum TDB_ERROR ecode;
160 off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
161 if (TDB_OFF_IS_ERR(off)) {
166 old_dlen = rec_data_length(&rec);
167 old_room = old_dlen + rec_extra_padding(&rec);
169 /* Fast path: can append in place. */
170 if (rec_extra_padding(&rec) >= dbuf.dsize) {
171 ecode = update_rec_hdr(tdb, off, key.dsize,
172 old_dlen + dbuf.dsize, &rec,
174 if (ecode != TDB_SUCCESS) {
178 off += sizeof(rec) + key.dsize + old_dlen;
179 ecode = update_data(tdb, off, dbuf,
180 rec_extra_padding(&rec));
185 newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
187 ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
189 " failed to allocate %zu bytes",
190 (size_t)(key.dsize + old_dlen
194 ecode = tdb->methods->tread(tdb, off + sizeof(rec) + key.dsize,
196 if (ecode != TDB_SUCCESS) {
197 goto out_free_newdata;
199 memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
200 new_dbuf.dptr = newdata;
201 new_dbuf.dsize = old_dlen + dbuf.dsize;
207 /* If they're using tdb_append(), it implies they're growing record. */
208 ecode = replace_data(tdb, &h, key, new_dbuf, off, old_room, true);
213 tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
217 enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
218 struct tdb_data *data)
221 struct tdb_used_record rec;
223 enum TDB_ERROR ecode;
225 off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
226 if (TDB_OFF_IS_ERR(off)) {
231 ecode = TDB_ERR_NOEXIST;
233 data->dsize = rec_data_length(&rec);
234 data->dptr = tdb_alloc_read(tdb, off + sizeof(rec) + key.dsize,
236 if (TDB_PTR_IS_ERR(data->dptr)) {
237 ecode = TDB_PTR_ERR(data->dptr);
242 tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
246 enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key)
249 struct tdb_used_record rec;
251 enum TDB_ERROR ecode;
253 off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
254 if (TDB_OFF_IS_ERR(off)) {
259 ecode = TDB_ERR_NOEXIST;
263 ecode = delete_from_hash(tdb, &h);
264 if (ecode != TDB_SUCCESS) {
268 /* Free the deleted entry. */
269 add_stat(tdb, frees, 1);
270 ecode = add_free_record(tdb, off,
271 sizeof(struct tdb_used_record)
272 + rec_key_length(&rec)
273 + rec_data_length(&rec)
274 + rec_extra_padding(&rec));
277 tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
281 unsigned int tdb_get_flags(struct tdb_context *tdb)
286 void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
288 if (tdb->flags & TDB_INTERNAL) {
289 tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
290 "tdb_add_flag: internal db");
295 tdb->flags |= TDB_NOLOCK;
298 tdb->flags |= TDB_NOMMAP;
299 tdb_munmap(tdb->file);
302 tdb->flags |= TDB_NOSYNC;
305 tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
306 "tdb_add_flag: Unknown flag %u", flag);
310 void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
312 if (tdb->flags & TDB_INTERNAL) {
313 tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
314 "tdb_remove_flag: internal db");
319 tdb->flags &= ~TDB_NOLOCK;
322 tdb->flags &= ~TDB_NOMMAP;
326 tdb->flags &= ~TDB_NOSYNC;
329 tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
330 "tdb_remove_flag: Unknown flag %u", flag);
334 const char *tdb_errorstr(enum TDB_ERROR ecode)
336 /* Gcc warns if you miss a case in the switch, so use that. */
338 case TDB_SUCCESS: return "Success";
339 case TDB_ERR_CORRUPT: return "Corrupt database";
340 case TDB_ERR_IO: return "IO Error";
341 case TDB_ERR_LOCK: return "Locking error";
342 case TDB_ERR_OOM: return "Out of memory";
343 case TDB_ERR_EXISTS: return "Record exists";
344 case TDB_ERR_EINVAL: return "Invalid parameter";
345 case TDB_ERR_NOEXIST: return "Record does not exist";
346 case TDB_ERR_RDONLY: return "write not permitted";
348 return "Invalid error code";
351 enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
352 enum TDB_ERROR ecode,
353 enum tdb_log_level level,
354 const char *fmt, ...)
359 /* tdb_open paths care about errno, so save it. */
360 int saved_errno = errno;
366 len = vasprintf(&message, fmt, ap);
370 tdb->logfn(tdb, TDB_LOG_ERROR, tdb->log_private,
371 "out of memory formatting message:");
372 tdb->logfn(tdb, level, tdb->log_private, fmt);
374 tdb->logfn(tdb, level, tdb->log_private, message);