s4-kcc: Fix the list of NCs for DRS replica information
[Samba.git] / lib / tdb2 / tdb.c
blob82b9c323d613bf6c2625d49778370d03f929c8e2
1 /*
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/>.
18 #include "private.h"
19 #ifndef _SAMBA_BUILD_
20 #include <ccan/asprintf/asprintf.h>
21 #include <stdarg.h>
22 #endif
24 static enum TDB_ERROR update_rec_hdr(struct tdb_context *tdb,
25 tdb_off_t off,
26 tdb_len_t keylen,
27 tdb_len_t datalen,
28 struct tdb_used_record *rec,
29 uint64_t h)
31 uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
32 enum TDB_ERROR ecode;
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));
39 return ecode;
42 static enum TDB_ERROR replace_data(struct tdb_context *tdb,
43 struct hash_info *h,
44 struct tdb_data key, struct tdb_data dbuf,
45 tdb_off_t old_off, tdb_len_t old_room,
46 bool growing)
48 tdb_off_t new_off;
49 enum TDB_ERROR ecode;
51 /* Allocate a new record. */
52 new_off = alloc(tdb, key.dsize, dbuf.dsize, h->h, TDB_USED_MAGIC,
53 growing);
54 if (TDB_OFF_IS_ERR(new_off)) {
55 return new_off;
58 /* We didn't like the existing one: remove it. */
59 if (old_off) {
60 tdb->stats.frees++;
61 ecode = add_free_record(tdb, old_off,
62 sizeof(struct tdb_used_record)
63 + key.dsize + old_room,
64 TDB_LOCK_WAIT, true);
65 if (ecode == TDB_SUCCESS)
66 ecode = replace_in_hash(tdb, h, new_off);
67 } else {
68 ecode = add_to_hash(tdb, h, new_off);
70 if (ecode != TDB_SUCCESS) {
71 return ecode;
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) {
77 return ecode;
80 new_off += key.dsize;
81 ecode = tdb->methods->twrite(tdb, new_off, dbuf.dptr, dbuf.dsize);
82 if (ecode != TDB_SUCCESS) {
83 return ecode;
86 if (tdb->flags & TDB_SEQNUM)
87 tdb_inc_seqnum(tdb);
89 return TDB_SUCCESS;
92 static enum TDB_ERROR update_data(struct tdb_context *tdb,
93 tdb_off_t off,
94 struct tdb_data dbuf,
95 tdb_len_t extra)
97 enum TDB_ERROR ecode;
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)
105 tdb_inc_seqnum(tdb);
107 return ecode;
110 enum TDB_ERROR tdb_store(struct tdb_context *tdb,
111 struct tdb_data key, struct tdb_data dbuf, int flag)
113 struct hash_info h;
114 tdb_off_t off;
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) {
126 if (off) {
127 ecode = TDB_ERR_EXISTS;
128 goto out;
130 } else {
131 if (off) {
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,
138 &rec, h.h);
139 if (ecode != TDB_SUCCESS) {
140 goto out;
142 ecode = update_data(tdb,
143 off + sizeof(rec)
144 + key.dsize, dbuf,
145 old_room - dbuf.dsize);
146 if (ecode != TDB_SUCCESS) {
147 goto out;
149 tdb_unlock_hashes(tdb, h.hlock_start,
150 h.hlock_range, F_WRLCK);
151 return tdb->last_error = TDB_SUCCESS;
153 } else {
154 if (flag == TDB_MODIFY) {
155 /* if the record doesn't exist and we
156 are in TDB_MODIFY mode then we should fail
157 the store */
158 ecode = TDB_ERR_NOEXIST;
159 goto out;
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);
166 out:
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)
174 struct hash_info h;
175 tdb_off_t off;
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;
187 if (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,
195 h.h);
196 if (ecode != TDB_SUCCESS) {
197 goto out;
200 off += sizeof(rec) + key.dsize + old_dlen;
201 ecode = update_data(tdb, off, dbuf,
202 rec_extra_padding(&rec));
203 goto out;
206 /* Slow path. */
207 newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
208 if (!newdata) {
209 ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
210 "tdb_append:"
211 " failed to allocate %zu bytes",
212 (size_t)(key.dsize + old_dlen
213 + dbuf.dsize));
214 goto out;
216 ecode = tdb->methods->tread(tdb, off + sizeof(rec) + key.dsize,
217 newdata, old_dlen);
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;
224 } else {
225 newdata = NULL;
226 new_dbuf = dbuf;
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);
232 out_free_newdata:
233 free(newdata);
234 out:
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)
242 tdb_off_t off;
243 struct tdb_used_record rec;
244 struct hash_info h;
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;
252 if (!off) {
253 ecode = TDB_ERR_NOEXIST;
254 } else {
255 data->dsize = rec_data_length(&rec);
256 data->dptr = tdb_alloc_read(tdb, off + sizeof(rec) + key.dsize,
257 data->dsize);
258 if (TDB_PTR_IS_ERR(data->dptr)) {
259 ecode = TDB_PTR_ERR(data->dptr);
260 } else
261 ecode = TDB_SUCCESS;
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)
270 tdb_off_t off;
271 struct tdb_used_record rec;
272 struct hash_info h;
274 off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
275 if (TDB_OFF_IS_ERR(off)) {
276 tdb->last_error = off;
277 return false;
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)
287 tdb_off_t off;
288 struct tdb_used_record rec;
289 struct hash_info h;
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;
297 if (!off) {
298 ecode = TDB_ERR_NOEXIST;
299 goto unlock;
302 ecode = delete_from_hash(tdb, &h);
303 if (ecode != TDB_SUCCESS) {
304 goto unlock;
307 /* Free the deleted entry. */
308 tdb->stats.frees++;
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)
317 tdb_inc_seqnum(tdb);
319 unlock:
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)
326 return tdb->flags;
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,
333 TDB_LOG_USE_ERROR,
334 "tdb_add_flag: internal db");
335 return;
337 switch (flag) {
338 case TDB_NOLOCK:
339 tdb->flags |= TDB_NOLOCK;
340 break;
341 case TDB_NOMMAP:
342 tdb->flags |= TDB_NOMMAP;
343 tdb_munmap(tdb->file);
344 break;
345 case TDB_NOSYNC:
346 tdb->flags |= TDB_NOSYNC;
347 break;
348 case TDB_SEQNUM:
349 tdb->flags |= TDB_SEQNUM;
350 break;
351 case TDB_ALLOW_NESTING:
352 tdb->flags |= TDB_ALLOW_NESTING;
353 break;
354 default:
355 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
356 TDB_LOG_USE_ERROR,
357 "tdb_add_flag: Unknown flag %u",
358 flag);
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,
366 TDB_LOG_USE_ERROR,
367 "tdb_remove_flag: internal db");
368 return;
370 switch (flag) {
371 case TDB_NOLOCK:
372 tdb->flags &= ~TDB_NOLOCK;
373 break;
374 case TDB_NOMMAP:
375 tdb->flags &= ~TDB_NOMMAP;
376 tdb_mmap(tdb);
377 break;
378 case TDB_NOSYNC:
379 tdb->flags &= ~TDB_NOSYNC;
380 break;
381 case TDB_SEQNUM:
382 tdb->flags &= ~TDB_SEQNUM;
383 break;
384 case TDB_ALLOW_NESTING:
385 tdb->flags &= ~TDB_ALLOW_NESTING;
386 break;
387 default:
388 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
389 TDB_LOG_USE_ERROR,
390 "tdb_remove_flag: Unknown flag %u",
391 flag);
395 const char *tdb_errorstr(enum TDB_ERROR ecode)
397 /* Gcc warns if you miss a case in the switch, so use that. */
398 switch (ecode) {
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, ...)
422 char *message;
423 va_list ap;
424 size_t len;
425 /* tdb_open paths care about errno, so save it. */
426 int saved_errno = errno;
428 if (!tdb->log_fn)
429 return ecode;
431 va_start(ap, fmt);
432 len = vasprintf(&message, fmt, ap);
433 va_end(ap);
435 if (len < 0) {
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);
439 } else {
440 tdb->log_fn(tdb, level, message, tdb->log_data);
441 free(message);
443 errno = saved_errno;
444 return ecode;
447 enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
448 TDB_DATA key,
449 enum TDB_ERROR (*parse)(TDB_DATA k,
450 TDB_DATA d,
451 void *data),
452 void *data)
454 tdb_off_t off;
455 struct tdb_used_record rec;
456 struct hash_info h;
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;
464 if (!off) {
465 ecode = TDB_ERR_NOEXIST;
466 } else {
467 const void *dptr;
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);
472 } else {
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)
486 return tdb->name;
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;
494 else
495 tdb->last_error = TDB_SUCCESS;
496 return off;
500 int tdb_fd(const struct tdb_context *tdb)
502 return tdb->file->fd;