s4:libcli/rap: the caller should get talloc children from call->ndr_pull_{param,data}
[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 HAVE_LIBREPLACE
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 TDB_OFF_TO_ERR(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 = TDB_OFF_TO_ERR(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 = TDB_OFF_TO_ERR(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 = TDB_OFF_TO_ERR(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 = TDB_OFF_TO_ERR(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 = TDB_OFF_TO_ERR(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         return true;
373 }
374
375 void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
376 {
377         if (tdb->flags & TDB_INTERNAL) {
378                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
379                                              TDB_LOG_USE_ERROR,
380                                              "tdb_add_flag: internal db");
381                 return;
382         }
383         switch (flag) {
384         case TDB_NOLOCK:
385                 tdb->flags |= TDB_NOLOCK;
386                 break;
387         case TDB_NOMMAP:
388                 tdb->flags |= TDB_NOMMAP;
389                 tdb_munmap(tdb->file);
390                 break;
391         case TDB_NOSYNC:
392                 tdb->flags |= TDB_NOSYNC;
393                 break;
394         case TDB_SEQNUM:
395                 tdb->flags |= TDB_SEQNUM;
396                 break;
397         case TDB_ALLOW_NESTING:
398                 tdb->flags |= TDB_ALLOW_NESTING;
399                 break;
400         case TDB_RDONLY:
401                 if (readonly_changable(tdb, "tdb_add_flag"))
402                         tdb->flags |= TDB_RDONLY;
403                 break;
404         default:
405                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
406                                              TDB_LOG_USE_ERROR,
407                                              "tdb_add_flag: Unknown flag %u",
408                                              flag);
409         }
410 }
411
412 void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
413 {
414         if (tdb->flags & TDB_INTERNAL) {
415                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
416                                              TDB_LOG_USE_ERROR,
417                                              "tdb_remove_flag: internal db");
418                 return;
419         }
420         switch (flag) {
421         case TDB_NOLOCK:
422                 tdb->flags &= ~TDB_NOLOCK;
423                 break;
424         case TDB_NOMMAP:
425                 tdb->flags &= ~TDB_NOMMAP;
426                 tdb_mmap(tdb);
427                 break;
428         case TDB_NOSYNC:
429                 tdb->flags &= ~TDB_NOSYNC;
430                 break;
431         case TDB_SEQNUM:
432                 tdb->flags &= ~TDB_SEQNUM;
433                 break;
434         case TDB_ALLOW_NESTING:
435                 tdb->flags &= ~TDB_ALLOW_NESTING;
436                 break;
437         case TDB_RDONLY:
438                 if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) {
439                         tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
440                                                      TDB_LOG_USE_ERROR,
441                                                      "tdb_remove_flag: can't"
442                                                      " remove TDB_RDONLY on tdb"
443                                                      " opened with O_RDONLY");
444                         break;
445                 }
446                 if (readonly_changable(tdb, "tdb_remove_flag"))
447                         tdb->flags &= ~TDB_RDONLY;
448                 break;
449         default:
450                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
451                                              TDB_LOG_USE_ERROR,
452                                              "tdb_remove_flag: Unknown flag %u",
453                                              flag);
454         }
455 }
456
457 const char *tdb_errorstr(enum TDB_ERROR ecode)
458 {
459         /* Gcc warns if you miss a case in the switch, so use that. */
460         switch (TDB_ERR_TO_OFF(ecode)) {
461         case TDB_ERR_TO_OFF(TDB_SUCCESS): return "Success";
462         case TDB_ERR_TO_OFF(TDB_ERR_CORRUPT): return "Corrupt database";
463         case TDB_ERR_TO_OFF(TDB_ERR_IO): return "IO Error";
464         case TDB_ERR_TO_OFF(TDB_ERR_LOCK): return "Locking error";
465         case TDB_ERR_TO_OFF(TDB_ERR_OOM): return "Out of memory";
466         case TDB_ERR_TO_OFF(TDB_ERR_EXISTS): return "Record exists";
467         case TDB_ERR_TO_OFF(TDB_ERR_EINVAL): return "Invalid parameter";
468         case TDB_ERR_TO_OFF(TDB_ERR_NOEXIST): return "Record does not exist";
469         case TDB_ERR_TO_OFF(TDB_ERR_RDONLY): return "write not permitted";
470         }
471         return "Invalid error code";
472 }
473
474 enum TDB_ERROR tdb_error(struct tdb_context *tdb)
475 {
476         return tdb->last_error;
477 }
478
479 enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
480                                enum TDB_ERROR ecode,
481                                enum tdb_log_level level,
482                                const char *fmt, ...)
483 {
484         char *message;
485         va_list ap;
486         size_t len;
487         /* tdb_open paths care about errno, so save it. */
488         int saved_errno = errno;
489
490         if (!tdb->log_fn)
491                 return ecode;
492
493         va_start(ap, fmt);
494         len = vasprintf(&message, fmt, ap);
495         va_end(ap);
496
497         if (len < 0) {
498                 tdb->log_fn(tdb, TDB_LOG_ERROR, TDB_ERR_OOM,
499                             "out of memory formatting message:", tdb->log_data);
500                 tdb->log_fn(tdb, level, ecode, fmt, tdb->log_data);
501         } else {
502                 tdb->log_fn(tdb, level, ecode, message, tdb->log_data);
503                 free(message);
504         }
505         errno = saved_errno;
506         return ecode;
507 }
508
509 enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
510                                  TDB_DATA key,
511                                  enum TDB_ERROR (*parse)(TDB_DATA k,
512                                                          TDB_DATA d,
513                                                          void *data),
514                                  void *data)
515 {
516         tdb_off_t off;
517         struct tdb_used_record rec;
518         struct hash_info h;
519         enum TDB_ERROR ecode;
520
521         if (tdb->flags & TDB_VERSION1) {
522                 return tdb->last_error = tdb1_parse_record(tdb, key, parse,
523                                                            data);
524         }
525
526         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
527         if (TDB_OFF_IS_ERR(off)) {
528                 return tdb->last_error = TDB_OFF_TO_ERR(off);
529         }
530
531         if (!off) {
532                 ecode = TDB_ERR_NOEXIST;
533         } else {
534                 const void *dptr;
535                 dptr = tdb_access_read(tdb, off + sizeof(rec) + key.dsize,
536                                        rec_data_length(&rec), false);
537                 if (TDB_PTR_IS_ERR(dptr)) {
538                         ecode = TDB_PTR_ERR(dptr);
539                 } else {
540                         TDB_DATA d = tdb_mkdata(dptr, rec_data_length(&rec));
541
542                         ecode = parse(key, d, data);
543                         tdb_access_release(tdb, dptr);
544                 }
545         }
546
547         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
548         return tdb->last_error = ecode;
549 }
550
551 const char *tdb_name(const struct tdb_context *tdb)
552 {
553         return tdb->name;
554 }
555
556 int64_t tdb_get_seqnum(struct tdb_context *tdb)
557 {
558         tdb_off_t off;
559
560         if (tdb->flags & TDB_VERSION1) {
561                 tdb1_off_t val;
562                 tdb->last_error = TDB_SUCCESS;
563                 val = tdb1_get_seqnum(tdb);
564
565                 if (tdb->last_error != TDB_SUCCESS)
566                         return TDB_ERR_TO_OFF(tdb->last_error);
567                 else
568                         return val;
569         }
570
571         off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
572         if (TDB_OFF_IS_ERR(off))
573                 tdb->last_error = TDB_OFF_TO_ERR(off);
574         else
575                 tdb->last_error = TDB_SUCCESS;
576         return off;
577 }
578
579
580 int tdb_fd(const struct tdb_context *tdb)
581 {
582         return tdb->file->fd;
583 }
584
585 struct traverse_state {
586         enum TDB_ERROR error;
587         struct tdb_context *dest_db;
588 };
589
590 /*
591   traverse function for repacking
592  */
593 static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data,
594                            struct traverse_state *state)
595 {
596         state->error = tdb_store(state->dest_db, key, data, TDB_INSERT);
597         if (state->error != TDB_SUCCESS) {
598                 return -1;
599         }
600         return 0;
601 }
602
603 enum TDB_ERROR tdb_repack(struct tdb_context *tdb)
604 {
605         struct tdb_context *tmp_db;
606         struct traverse_state state;
607
608         state.error = tdb_transaction_start(tdb);
609         if (state.error != TDB_SUCCESS) {
610                 return state.error;
611         }
612
613         tmp_db = tdb_open("tmpdb", TDB_INTERNAL, O_RDWR|O_CREAT, 0, NULL);
614         if (tmp_db == NULL) {
615                 state.error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
616                                          __location__
617                                          " Failed to create tmp_db");
618                 tdb_transaction_cancel(tdb);
619                 return tdb->last_error = state.error;
620         }
621
622         state.dest_db = tmp_db;
623         if (tdb_traverse(tdb, repack_traverse, &state) < 0) {
624                 goto fail;
625         }
626
627         state.error = tdb_wipe_all(tdb);
628         if (state.error != TDB_SUCCESS) {
629                 goto fail;
630         }
631
632         state.dest_db = tdb;
633         if (tdb_traverse(tmp_db, repack_traverse, &state) < 0) {
634                 goto fail;
635         }
636
637         tdb_close(tmp_db);
638         return tdb_transaction_commit(tdb);
639
640 fail:
641         tdb_transaction_cancel(tdb);
642         tdb_close(tmp_db);
643         return state.error;
644 }