]> git.samba.org - kai/samba.git/blob - lib/tdb2/tdb.c
tdb2: unify tdb1_get_seqnum/tdb1_increment_seqnum into tdb_get_seqnum/tdb_inc_seqnum
[kai/samba.git] / lib / tdb2 / tdb.c
1  /*
2    Trivial Database 2: fetch, store and misc routines.
3    Copyright (C) Rusty Russell 2010
4
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.
9
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.
14
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/>.
17 */
18 #include "private.h"
19 #ifndef _SAMBA_BUILD_
20 #include <ccan/asprintf/asprintf.h>
21 #include <stdarg.h>
22 #endif
23
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)
30 {
31         uint64_t dataroom = rec_data_length(rec) + rec_extra_padding(rec);
32         enum TDB_ERROR ecode;
33
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));
38         }
39         return ecode;
40 }
41
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)
47 {
48         tdb_off_t new_off;
49         enum TDB_ERROR ecode;
50
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;
56         }
57
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);
69         }
70         if (ecode != TDB_SUCCESS) {
71                 return ecode;
72         }
73
74         new_off += sizeof(struct tdb_used_record);
75         ecode = tdb->tdb2.io->twrite(tdb, new_off, key.dptr, key.dsize);
76         if (ecode != TDB_SUCCESS) {
77                 return ecode;
78         }
79
80         new_off += key.dsize;
81         ecode = tdb->tdb2.io->twrite(tdb, new_off, dbuf.dptr, dbuf.dsize);
82         if (ecode != TDB_SUCCESS) {
83                 return ecode;
84         }
85
86         if (tdb->flags & TDB_SEQNUM)
87                 tdb_inc_seqnum(tdb);
88
89         return TDB_SUCCESS;
90 }
91
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)
96 {
97         enum TDB_ERROR ecode;
98
99         ecode = tdb->tdb2.io->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->tdb2.io->twrite(tdb, off + dbuf.dsize, "", 1);
103         }
104         if (tdb->flags & TDB_SEQNUM)
105                 tdb_inc_seqnum(tdb);
106
107         return ecode;
108 }
109
110 enum TDB_ERROR tdb_store(struct tdb_context *tdb,
111                          struct tdb_data key, struct tdb_data dbuf, int flag)
112 {
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;
118
119         if (tdb->flags & TDB_VERSION1) {
120                 if (tdb1_store(tdb, key, dbuf, flag) == -1)
121                         return tdb->last_error;
122                 return TDB_SUCCESS;
123         }
124
125         off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
126         if (TDB_OFF_IS_ERR(off)) {
127                 return tdb->last_error = off;
128         }
129
130         /* Now we have lock on this hash bucket. */
131         if (flag == TDB_INSERT) {
132                 if (off) {
133                         ecode = TDB_ERR_EXISTS;
134                         goto out;
135                 }
136         } else {
137                 if (off) {
138                         old_room = rec_data_length(&rec)
139                                 + rec_extra_padding(&rec);
140                         if (old_room >= dbuf.dsize) {
141                                 /* Can modify in-place.  Easy! */
142                                 ecode = update_rec_hdr(tdb, off,
143                                                        key.dsize, dbuf.dsize,
144                                                        &rec, h.h);
145                                 if (ecode != TDB_SUCCESS) {
146                                         goto out;
147                                 }
148                                 ecode = update_data(tdb,
149                                                     off + sizeof(rec)
150                                                     + key.dsize, dbuf,
151                                                     old_room - dbuf.dsize);
152                                 if (ecode != TDB_SUCCESS) {
153                                         goto out;
154                                 }
155                                 tdb_unlock_hashes(tdb, h.hlock_start,
156                                                   h.hlock_range, F_WRLCK);
157                                 return tdb->last_error = TDB_SUCCESS;
158                         }
159                 } else {
160                         if (flag == TDB_MODIFY) {
161                                 /* if the record doesn't exist and we
162                                    are in TDB_MODIFY mode then we should fail
163                                    the store */
164                                 ecode = TDB_ERR_NOEXIST;
165                                 goto out;
166                         }
167                 }
168         }
169
170         /* If we didn't use the old record, this implies we're growing. */
171         ecode = replace_data(tdb, &h, key, dbuf, off, old_room, off);
172 out:
173         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
174         return tdb->last_error = ecode;
175 }
176
177 enum TDB_ERROR tdb_append(struct tdb_context *tdb,
178                           struct tdb_data key, struct tdb_data dbuf)
179 {
180         struct hash_info h;
181         tdb_off_t off;
182         struct tdb_used_record rec;
183         tdb_len_t old_room = 0, old_dlen;
184         unsigned char *newdata;
185         struct tdb_data new_dbuf;
186         enum TDB_ERROR ecode;
187
188         if (tdb->flags & TDB_VERSION1) {
189                 if (tdb1_append(tdb, key, dbuf) == -1)
190                         return tdb->last_error;
191                 return TDB_SUCCESS;
192         }
193
194         off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
195         if (TDB_OFF_IS_ERR(off)) {
196                 return tdb->last_error = off;
197         }
198
199         if (off) {
200                 old_dlen = rec_data_length(&rec);
201                 old_room = old_dlen + rec_extra_padding(&rec);
202
203                 /* Fast path: can append in place. */
204                 if (rec_extra_padding(&rec) >= dbuf.dsize) {
205                         ecode = update_rec_hdr(tdb, off, key.dsize,
206                                                old_dlen + dbuf.dsize, &rec,
207                                                h.h);
208                         if (ecode != TDB_SUCCESS) {
209                                 goto out;
210                         }
211
212                         off += sizeof(rec) + key.dsize + old_dlen;
213                         ecode = update_data(tdb, off, dbuf,
214                                             rec_extra_padding(&rec));
215                         goto out;
216                 }
217
218                 /* Slow path. */
219                 newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
220                 if (!newdata) {
221                         ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
222                                            "tdb_append:"
223                                            " failed to allocate %zu bytes",
224                                            (size_t)(key.dsize + old_dlen
225                                                     + dbuf.dsize));
226                         goto out;
227                 }
228                 ecode = tdb->tdb2.io->tread(tdb, off + sizeof(rec) + key.dsize,
229                                             newdata, old_dlen);
230                 if (ecode != TDB_SUCCESS) {
231                         goto out_free_newdata;
232                 }
233                 memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
234                 new_dbuf.dptr = newdata;
235                 new_dbuf.dsize = old_dlen + dbuf.dsize;
236         } else {
237                 newdata = NULL;
238                 new_dbuf = dbuf;
239         }
240
241         /* If they're using tdb_append(), it implies they're growing record. */
242         ecode = replace_data(tdb, &h, key, new_dbuf, off, old_room, true);
243
244 out_free_newdata:
245         free(newdata);
246 out:
247         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
248         return tdb->last_error = ecode;
249 }
250
251 enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
252                          struct tdb_data *data)
253 {
254         tdb_off_t off;
255         struct tdb_used_record rec;
256         struct hash_info h;
257         enum TDB_ERROR ecode;
258
259         if (tdb->flags & TDB_VERSION1)
260                 return tdb1_fetch(tdb, key, data);
261
262         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
263         if (TDB_OFF_IS_ERR(off)) {
264                 return tdb->last_error = off;
265         }
266
267         if (!off) {
268                 ecode = TDB_ERR_NOEXIST;
269         } else {
270                 data->dsize = rec_data_length(&rec);
271                 data->dptr = tdb_alloc_read(tdb, off + sizeof(rec) + key.dsize,
272                                             data->dsize);
273                 if (TDB_PTR_IS_ERR(data->dptr)) {
274                         ecode = TDB_PTR_ERR(data->dptr);
275                 } else
276                         ecode = TDB_SUCCESS;
277         }
278
279         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
280         return tdb->last_error = ecode;
281 }
282
283 bool tdb_exists(struct tdb_context *tdb, TDB_DATA key)
284 {
285         tdb_off_t off;
286         struct tdb_used_record rec;
287         struct hash_info h;
288
289         if (tdb->flags & TDB_VERSION1) {
290                 return tdb1_exists(tdb, key);
291         }
292
293         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
294         if (TDB_OFF_IS_ERR(off)) {
295                 tdb->last_error = off;
296                 return false;
297         }
298         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
299
300         tdb->last_error = TDB_SUCCESS;
301         return off ? true : false;
302 }
303
304 enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key)
305 {
306         tdb_off_t off;
307         struct tdb_used_record rec;
308         struct hash_info h;
309         enum TDB_ERROR ecode;
310
311         if (tdb->flags & TDB_VERSION1) {
312                 if (tdb1_delete(tdb, key) == -1)
313                         return tdb->last_error;
314                 return TDB_SUCCESS;
315         }
316
317         off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
318         if (TDB_OFF_IS_ERR(off)) {
319                 return tdb->last_error = off;
320         }
321
322         if (!off) {
323                 ecode = TDB_ERR_NOEXIST;
324                 goto unlock;
325         }
326
327         ecode = delete_from_hash(tdb, &h);
328         if (ecode != TDB_SUCCESS) {
329                 goto unlock;
330         }
331
332         /* Free the deleted entry. */
333         tdb->stats.frees++;
334         ecode = add_free_record(tdb, off,
335                                 sizeof(struct tdb_used_record)
336                                 + rec_key_length(&rec)
337                                 + rec_data_length(&rec)
338                                 + rec_extra_padding(&rec),
339                                 TDB_LOCK_WAIT, true);
340
341         if (tdb->flags & TDB_SEQNUM)
342                 tdb_inc_seqnum(tdb);
343
344 unlock:
345         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
346         return tdb->last_error = ecode;
347 }
348
349 unsigned int tdb_get_flags(struct tdb_context *tdb)
350 {
351         return tdb->flags;
352 }
353
354 static bool inside_transaction(const struct tdb_context *tdb)
355 {
356         if (tdb->flags & TDB_VERSION1)
357                 return tdb->tdb1.transaction != NULL;
358         else
359                 return tdb->tdb2.transaction != NULL;
360 }
361
362 static bool readonly_changable(struct tdb_context *tdb, const char *caller)
363 {
364         if (inside_transaction(tdb)) {
365                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
366                                              TDB_LOG_USE_ERROR,
367                                              "%s: can't change"
368                                              " TDB_RDONLY inside transaction",
369                                              caller);
370                 return false;
371         }
372
373         if (tdb->file->allrecord_lock.count != 0
374             || tdb->file->num_lockrecs != 0) {
375                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
376                                              TDB_LOG_USE_ERROR,
377                                              "%s: can't change"
378                                              " TDB_RDONLY holding locks",
379                                              caller);
380                 return false;
381         }
382         return true;
383 }
384
385 void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
386 {
387         if (tdb->flags & TDB_INTERNAL) {
388                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
389                                              TDB_LOG_USE_ERROR,
390                                              "tdb_add_flag: internal db");
391                 return;
392         }
393         switch (flag) {
394         case TDB_NOLOCK:
395                 tdb->flags |= TDB_NOLOCK;
396                 break;
397         case TDB_NOMMAP:
398                 tdb->flags |= TDB_NOMMAP;
399                 tdb_munmap(tdb->file);
400                 break;
401         case TDB_NOSYNC:
402                 tdb->flags |= TDB_NOSYNC;
403                 break;
404         case TDB_SEQNUM:
405                 tdb->flags |= TDB_SEQNUM;
406                 break;
407         case TDB_ALLOW_NESTING:
408                 tdb->flags |= TDB_ALLOW_NESTING;
409                 break;
410         case TDB_RDONLY:
411                 if (readonly_changable(tdb, "tdb_add_flag"))
412                         tdb->flags |= TDB_RDONLY;
413                 break;
414         default:
415                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
416                                              TDB_LOG_USE_ERROR,
417                                              "tdb_add_flag: Unknown flag %u",
418                                              flag);
419         }
420 }
421
422 void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
423 {
424         if (tdb->flags & TDB_INTERNAL) {
425                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
426                                              TDB_LOG_USE_ERROR,
427                                              "tdb_remove_flag: internal db");
428                 return;
429         }
430         switch (flag) {
431         case TDB_NOLOCK:
432                 tdb->flags &= ~TDB_NOLOCK;
433                 break;
434         case TDB_NOMMAP:
435                 tdb->flags &= ~TDB_NOMMAP;
436                 tdb_mmap(tdb);
437                 break;
438         case TDB_NOSYNC:
439                 tdb->flags &= ~TDB_NOSYNC;
440                 break;
441         case TDB_SEQNUM:
442                 tdb->flags &= ~TDB_SEQNUM;
443                 break;
444         case TDB_ALLOW_NESTING:
445                 tdb->flags &= ~TDB_ALLOW_NESTING;
446                 break;
447         case TDB_RDONLY:
448                 if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) {
449                         tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
450                                                      TDB_LOG_USE_ERROR,
451                                                      "tdb_remove_flag: can't"
452                                                      " remove TDB_RDONLY on tdb"
453                                                      " opened with O_RDONLY");
454                         break;
455                 }
456                 if (readonly_changable(tdb, "tdb_remove_flag"))
457                         tdb->flags &= ~TDB_RDONLY;
458                 break;
459         default:
460                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
461                                              TDB_LOG_USE_ERROR,
462                                              "tdb_remove_flag: Unknown flag %u",
463                                              flag);
464         }
465 }
466
467 const char *tdb_errorstr(enum TDB_ERROR ecode)
468 {
469         /* Gcc warns if you miss a case in the switch, so use that. */
470         switch (ecode) {
471         case TDB_SUCCESS: return "Success";
472         case TDB_ERR_CORRUPT: return "Corrupt database";
473         case TDB_ERR_IO: return "IO Error";
474         case TDB_ERR_LOCK: return "Locking error";
475         case TDB_ERR_OOM: return "Out of memory";
476         case TDB_ERR_EXISTS: return "Record exists";
477         case TDB_ERR_EINVAL: return "Invalid parameter";
478         case TDB_ERR_NOEXIST: return "Record does not exist";
479         case TDB_ERR_RDONLY: return "write not permitted";
480         }
481         return "Invalid error code";
482 }
483
484 enum TDB_ERROR tdb_error(struct tdb_context *tdb)
485 {
486         return tdb->last_error;
487 }
488
489 enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
490                                enum TDB_ERROR ecode,
491                                enum tdb_log_level level,
492                                const char *fmt, ...)
493 {
494         char *message;
495         va_list ap;
496         size_t len;
497         /* tdb_open paths care about errno, so save it. */
498         int saved_errno = errno;
499
500         if (!tdb->log_fn)
501                 return ecode;
502
503         va_start(ap, fmt);
504         len = vasprintf(&message, fmt, ap);
505         va_end(ap);
506
507         if (len < 0) {
508                 tdb->log_fn(tdb, TDB_LOG_ERROR, TDB_ERR_OOM,
509                             "out of memory formatting message:", tdb->log_data);
510                 tdb->log_fn(tdb, level, ecode, fmt, tdb->log_data);
511         } else {
512                 tdb->log_fn(tdb, level, ecode, message, tdb->log_data);
513                 free(message);
514         }
515         errno = saved_errno;
516         return ecode;
517 }
518
519 enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
520                                  TDB_DATA key,
521                                  enum TDB_ERROR (*parse)(TDB_DATA k,
522                                                          TDB_DATA d,
523                                                          void *data),
524                                  void *data)
525 {
526         tdb_off_t off;
527         struct tdb_used_record rec;
528         struct hash_info h;
529         enum TDB_ERROR ecode;
530
531         if (tdb->flags & TDB_VERSION1) {
532                 return tdb->last_error = tdb1_parse_record(tdb, key, parse,
533                                                            data);
534         }
535
536         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
537         if (TDB_OFF_IS_ERR(off)) {
538                 return tdb->last_error = off;
539         }
540
541         if (!off) {
542                 ecode = TDB_ERR_NOEXIST;
543         } else {
544                 const void *dptr;
545                 dptr = tdb_access_read(tdb, off + sizeof(rec) + key.dsize,
546                                        rec_data_length(&rec), false);
547                 if (TDB_PTR_IS_ERR(dptr)) {
548                         ecode = TDB_PTR_ERR(dptr);
549                 } else {
550                         TDB_DATA d = tdb_mkdata(dptr, rec_data_length(&rec));
551
552                         ecode = parse(key, d, data);
553                         tdb_access_release(tdb, dptr);
554                 }
555         }
556
557         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
558         return tdb->last_error = ecode;
559 }
560
561 const char *tdb_name(const struct tdb_context *tdb)
562 {
563         return tdb->name;
564 }
565
566 int64_t tdb_get_seqnum(struct tdb_context *tdb)
567 {
568         tdb_off_t off;
569
570         if (tdb->flags & TDB_VERSION1) {
571                 tdb1_off_t val;
572                 tdb->last_error = TDB_SUCCESS;
573                 val = tdb1_get_seqnum(tdb);
574
575                 if (tdb->last_error != TDB_SUCCESS)
576                         return tdb->last_error;
577                 else
578                         return val;
579         }
580
581         off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
582         if (TDB_OFF_IS_ERR(off))
583                 tdb->last_error = off;
584         else
585                 tdb->last_error = TDB_SUCCESS;
586         return off;
587 }
588
589
590 int tdb_fd(const struct tdb_context *tdb)
591 {
592         return tdb->file->fd;
593 }