25ce9b46626d3a6765da6947e7deba22e3ea67f3
[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->methods->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->methods->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->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);
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         off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
120         if (TDB_OFF_IS_ERR(off)) {
121                 return tdb->last_error = off;
122         }
123
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;
129                 }
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;
141                                 }
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;
148                                 }
149                                 tdb_unlock_hashes(tdb, h.hlock_start,
150                                                   h.hlock_range, F_WRLCK);
151                                 return tdb->last_error = TDB_SUCCESS;
152                         }
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;
160                         }
161                 }
162         }
163
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;
169 }
170
171 enum TDB_ERROR tdb_append(struct tdb_context *tdb,
172                           struct tdb_data key, struct tdb_data dbuf)
173 {
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;
181
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;
185         }
186
187         if (off) {
188                 old_dlen = rec_data_length(&rec);
189                 old_room = old_dlen + rec_extra_padding(&rec);
190
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;
198                         }
199
200                         off += sizeof(rec) + key.dsize + old_dlen;
201                         ecode = update_data(tdb, off, dbuf,
202                                             rec_extra_padding(&rec));
203                         goto out;
204                 }
205
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;
215                 }
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;
220                 }
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;
227         }
228
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);
231
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;
237 }
238
239 enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
240                          struct tdb_data *data)
241 {
242         tdb_off_t off;
243         struct tdb_used_record rec;
244         struct hash_info h;
245         enum TDB_ERROR ecode;
246
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;
250         }
251
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;
262         }
263
264         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
265         return tdb->last_error = ecode;
266 }
267
268 bool tdb_exists(struct tdb_context *tdb, TDB_DATA key)
269 {
270         tdb_off_t off;
271         struct tdb_used_record rec;
272         struct hash_info h;
273
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;
278         }
279         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
280
281         tdb->last_error = TDB_SUCCESS;
282         return off ? true : false;
283 }
284
285 enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key)
286 {
287         tdb_off_t off;
288         struct tdb_used_record rec;
289         struct hash_info h;
290         enum TDB_ERROR ecode;
291
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;
295         }
296
297         if (!off) {
298                 ecode = TDB_ERR_NOEXIST;
299                 goto unlock;
300         }
301
302         ecode = delete_from_hash(tdb, &h);
303         if (ecode != TDB_SUCCESS) {
304                 goto unlock;
305         }
306
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);
315
316         if (tdb->flags & TDB_SEQNUM)
317                 tdb_inc_seqnum(tdb);
318
319 unlock:
320         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
321         return tdb->last_error = ecode;
322 }
323
324 unsigned int tdb_get_flags(struct tdb_context *tdb)
325 {
326         return tdb->flags;
327 }
328
329 static bool readonly_changable(struct tdb_context *tdb, const char *caller)
330 {
331         if (tdb->transaction) {
332                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
333                                              TDB_LOG_USE_ERROR,
334                                              "%s: can't change"
335                                              " TDB_RDONLY inside transaction",
336                                              caller);
337                 return false;
338         }
339
340         if (tdb->file->allrecord_lock.count != 0
341             || tdb->file->num_lockrecs != 0) {
342                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
343                                              TDB_LOG_USE_ERROR,
344                                              "%s: can't change"
345                                              " TDB_RDONLY holding locks",
346                                              caller);
347                 return false;
348         }
349         return true;
350 }
351
352 void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
353 {
354         if (tdb->flags & TDB_INTERNAL) {
355                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
356                                              TDB_LOG_USE_ERROR,
357                                              "tdb_add_flag: internal db");
358                 return;
359         }
360         switch (flag) {
361         case TDB_NOLOCK:
362                 tdb->flags |= TDB_NOLOCK;
363                 break;
364         case TDB_NOMMAP:
365                 tdb->flags |= TDB_NOMMAP;
366                 tdb_munmap(tdb->file);
367                 break;
368         case TDB_NOSYNC:
369                 tdb->flags |= TDB_NOSYNC;
370                 break;
371         case TDB_SEQNUM:
372                 tdb->flags |= TDB_SEQNUM;
373                 break;
374         case TDB_ALLOW_NESTING:
375                 tdb->flags |= TDB_ALLOW_NESTING;
376                 break;
377         case TDB_RDONLY:
378                 if (readonly_changable(tdb, "tdb_add_flag"))
379                         tdb->flags |= TDB_RDONLY;
380                 break;
381         default:
382                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
383                                              TDB_LOG_USE_ERROR,
384                                              "tdb_add_flag: Unknown flag %u",
385                                              flag);
386         }
387 }
388
389 void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
390 {
391         if (tdb->flags & TDB_INTERNAL) {
392                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
393                                              TDB_LOG_USE_ERROR,
394                                              "tdb_remove_flag: internal db");
395                 return;
396         }
397         switch (flag) {
398         case TDB_NOLOCK:
399                 tdb->flags &= ~TDB_NOLOCK;
400                 break;
401         case TDB_NOMMAP:
402                 tdb->flags &= ~TDB_NOMMAP;
403                 tdb_mmap(tdb);
404                 break;
405         case TDB_NOSYNC:
406                 tdb->flags &= ~TDB_NOSYNC;
407                 break;
408         case TDB_SEQNUM:
409                 tdb->flags &= ~TDB_SEQNUM;
410                 break;
411         case TDB_ALLOW_NESTING:
412                 tdb->flags &= ~TDB_ALLOW_NESTING;
413                 break;
414         case TDB_RDONLY:
415                 if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) {
416                         tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
417                                                      TDB_LOG_USE_ERROR,
418                                                      "tdb_remove_flag: can't"
419                                                      " remove TDB_RDONLY on tdb"
420                                                      " opened with O_RDONLY");
421                         break;
422                 }
423                 if (readonly_changable(tdb, "tdb_remove_flag"))
424                         tdb->flags &= ~TDB_RDONLY;
425                 break;
426         default:
427                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
428                                              TDB_LOG_USE_ERROR,
429                                              "tdb_remove_flag: Unknown flag %u",
430                                              flag);
431         }
432 }
433
434 const char *tdb_errorstr(enum TDB_ERROR ecode)
435 {
436         /* Gcc warns if you miss a case in the switch, so use that. */
437         switch (ecode) {
438         case TDB_SUCCESS: return "Success";
439         case TDB_ERR_CORRUPT: return "Corrupt database";
440         case TDB_ERR_IO: return "IO Error";
441         case TDB_ERR_LOCK: return "Locking error";
442         case TDB_ERR_OOM: return "Out of memory";
443         case TDB_ERR_EXISTS: return "Record exists";
444         case TDB_ERR_EINVAL: return "Invalid parameter";
445         case TDB_ERR_NOEXIST: return "Record does not exist";
446         case TDB_ERR_RDONLY: return "write not permitted";
447         }
448         return "Invalid error code";
449 }
450
451 enum TDB_ERROR tdb_error(struct tdb_context *tdb)
452 {
453         return tdb->last_error;
454 }
455
456 enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
457                                enum TDB_ERROR ecode,
458                                enum tdb_log_level level,
459                                const char *fmt, ...)
460 {
461         char *message;
462         va_list ap;
463         size_t len;
464         /* tdb_open paths care about errno, so save it. */
465         int saved_errno = errno;
466
467         if (!tdb->log_fn)
468                 return ecode;
469
470         va_start(ap, fmt);
471         len = vasprintf(&message, fmt, ap);
472         va_end(ap);
473
474         if (len < 0) {
475                 tdb->log_fn(tdb, TDB_LOG_ERROR, TDB_ERR_OOM,
476                             "out of memory formatting message:", tdb->log_data);
477                 tdb->log_fn(tdb, level, ecode, fmt, tdb->log_data);
478         } else {
479                 tdb->log_fn(tdb, level, ecode, message, tdb->log_data);
480                 free(message);
481         }
482         errno = saved_errno;
483         return ecode;
484 }
485
486 enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
487                                  TDB_DATA key,
488                                  enum TDB_ERROR (*parse)(TDB_DATA k,
489                                                          TDB_DATA d,
490                                                          void *data),
491                                  void *data)
492 {
493         tdb_off_t off;
494         struct tdb_used_record rec;
495         struct hash_info h;
496         enum TDB_ERROR ecode;
497
498         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
499         if (TDB_OFF_IS_ERR(off)) {
500                 return tdb->last_error = off;
501         }
502
503         if (!off) {
504                 ecode = TDB_ERR_NOEXIST;
505         } else {
506                 const void *dptr;
507                 dptr = tdb_access_read(tdb, off + sizeof(rec) + key.dsize,
508                                        rec_data_length(&rec), false);
509                 if (TDB_PTR_IS_ERR(dptr)) {
510                         ecode = TDB_PTR_ERR(dptr);
511                 } else {
512                         TDB_DATA d = tdb_mkdata(dptr, rec_data_length(&rec));
513
514                         ecode = parse(key, d, data);
515                         tdb_access_release(tdb, dptr);
516                 }
517         }
518
519         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
520         return tdb->last_error = ecode;
521 }
522
523 const char *tdb_name(const struct tdb_context *tdb)
524 {
525         return tdb->name;
526 }
527
528 int64_t tdb_get_seqnum(struct tdb_context *tdb)
529 {
530         tdb_off_t off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
531         if (TDB_OFF_IS_ERR(off))
532                 tdb->last_error = off;
533         else
534                 tdb->last_error = TDB_SUCCESS;
535         return off;
536 }
537
538
539 int tdb_fd(const struct tdb_context *tdb)
540 {
541         return tdb->file->fd;
542 }