c9c95c602c252cd4bf586372b2759c49f2fe53d6
[mat/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         off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
189         if (TDB_OFF_IS_ERR(off)) {
190                 return tdb->last_error = off;
191         }
192
193         if (off) {
194                 old_dlen = rec_data_length(&rec);
195                 old_room = old_dlen + rec_extra_padding(&rec);
196
197                 /* Fast path: can append in place. */
198                 if (rec_extra_padding(&rec) >= dbuf.dsize) {
199                         ecode = update_rec_hdr(tdb, off, key.dsize,
200                                                old_dlen + dbuf.dsize, &rec,
201                                                h.h);
202                         if (ecode != TDB_SUCCESS) {
203                                 goto out;
204                         }
205
206                         off += sizeof(rec) + key.dsize + old_dlen;
207                         ecode = update_data(tdb, off, dbuf,
208                                             rec_extra_padding(&rec));
209                         goto out;
210                 }
211
212                 /* Slow path. */
213                 newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
214                 if (!newdata) {
215                         ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
216                                            "tdb_append:"
217                                            " failed to allocate %zu bytes",
218                                            (size_t)(key.dsize + old_dlen
219                                                     + dbuf.dsize));
220                         goto out;
221                 }
222                 ecode = tdb->tdb2.io->tread(tdb, off + sizeof(rec) + key.dsize,
223                                             newdata, old_dlen);
224                 if (ecode != TDB_SUCCESS) {
225                         goto out_free_newdata;
226                 }
227                 memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
228                 new_dbuf.dptr = newdata;
229                 new_dbuf.dsize = old_dlen + dbuf.dsize;
230         } else {
231                 newdata = NULL;
232                 new_dbuf = dbuf;
233         }
234
235         /* If they're using tdb_append(), it implies they're growing record. */
236         ecode = replace_data(tdb, &h, key, new_dbuf, off, old_room, true);
237
238 out_free_newdata:
239         free(newdata);
240 out:
241         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
242         return tdb->last_error = ecode;
243 }
244
245 enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
246                          struct tdb_data *data)
247 {
248         tdb_off_t off;
249         struct tdb_used_record rec;
250         struct hash_info h;
251         enum TDB_ERROR ecode;
252
253         if (tdb->flags & TDB_VERSION1)
254                 return tdb1_fetch(tdb, key, data);
255
256         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
257         if (TDB_OFF_IS_ERR(off)) {
258                 return tdb->last_error = off;
259         }
260
261         if (!off) {
262                 ecode = TDB_ERR_NOEXIST;
263         } else {
264                 data->dsize = rec_data_length(&rec);
265                 data->dptr = tdb_alloc_read(tdb, off + sizeof(rec) + key.dsize,
266                                             data->dsize);
267                 if (TDB_PTR_IS_ERR(data->dptr)) {
268                         ecode = TDB_PTR_ERR(data->dptr);
269                 } else
270                         ecode = TDB_SUCCESS;
271         }
272
273         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
274         return tdb->last_error = ecode;
275 }
276
277 bool tdb_exists(struct tdb_context *tdb, TDB_DATA key)
278 {
279         tdb_off_t off;
280         struct tdb_used_record rec;
281         struct hash_info h;
282
283         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
284         if (TDB_OFF_IS_ERR(off)) {
285                 tdb->last_error = off;
286                 return false;
287         }
288         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
289
290         tdb->last_error = TDB_SUCCESS;
291         return off ? true : false;
292 }
293
294 enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key)
295 {
296         tdb_off_t off;
297         struct tdb_used_record rec;
298         struct hash_info h;
299         enum TDB_ERROR ecode;
300
301         off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
302         if (TDB_OFF_IS_ERR(off)) {
303                 return tdb->last_error = off;
304         }
305
306         if (!off) {
307                 ecode = TDB_ERR_NOEXIST;
308                 goto unlock;
309         }
310
311         ecode = delete_from_hash(tdb, &h);
312         if (ecode != TDB_SUCCESS) {
313                 goto unlock;
314         }
315
316         /* Free the deleted entry. */
317         tdb->stats.frees++;
318         ecode = add_free_record(tdb, off,
319                                 sizeof(struct tdb_used_record)
320                                 + rec_key_length(&rec)
321                                 + rec_data_length(&rec)
322                                 + rec_extra_padding(&rec),
323                                 TDB_LOCK_WAIT, true);
324
325         if (tdb->flags & TDB_SEQNUM)
326                 tdb_inc_seqnum(tdb);
327
328 unlock:
329         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
330         return tdb->last_error = ecode;
331 }
332
333 unsigned int tdb_get_flags(struct tdb_context *tdb)
334 {
335         return tdb->flags;
336 }
337
338 static bool inside_transaction(const struct tdb_context *tdb)
339 {
340         if (tdb->flags & TDB_VERSION1)
341                 return tdb->tdb1.transaction != NULL;
342         else
343                 return tdb->tdb2.transaction != NULL;
344 }
345
346 static bool readonly_changable(struct tdb_context *tdb, const char *caller)
347 {
348         if (inside_transaction(tdb)) {
349                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
350                                              TDB_LOG_USE_ERROR,
351                                              "%s: can't change"
352                                              " TDB_RDONLY inside transaction",
353                                              caller);
354                 return false;
355         }
356
357         if (tdb->file->allrecord_lock.count != 0
358             || tdb->file->num_lockrecs != 0) {
359                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
360                                              TDB_LOG_USE_ERROR,
361                                              "%s: can't change"
362                                              " TDB_RDONLY holding locks",
363                                              caller);
364                 return false;
365         }
366         return true;
367 }
368
369 void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
370 {
371         if (tdb->flags & TDB_INTERNAL) {
372                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
373                                              TDB_LOG_USE_ERROR,
374                                              "tdb_add_flag: internal db");
375                 return;
376         }
377         switch (flag) {
378         case TDB_NOLOCK:
379                 tdb->flags |= TDB_NOLOCK;
380                 break;
381         case TDB_NOMMAP:
382                 tdb->flags |= TDB_NOMMAP;
383                 tdb_munmap(tdb->file);
384                 break;
385         case TDB_NOSYNC:
386                 tdb->flags |= TDB_NOSYNC;
387                 break;
388         case TDB_SEQNUM:
389                 tdb->flags |= TDB_SEQNUM;
390                 break;
391         case TDB_ALLOW_NESTING:
392                 tdb->flags |= TDB_ALLOW_NESTING;
393                 break;
394         case TDB_RDONLY:
395                 if (readonly_changable(tdb, "tdb_add_flag"))
396                         tdb->flags |= TDB_RDONLY;
397                 break;
398         default:
399                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
400                                              TDB_LOG_USE_ERROR,
401                                              "tdb_add_flag: Unknown flag %u",
402                                              flag);
403         }
404 }
405
406 void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
407 {
408         if (tdb->flags & TDB_INTERNAL) {
409                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
410                                              TDB_LOG_USE_ERROR,
411                                              "tdb_remove_flag: internal db");
412                 return;
413         }
414         switch (flag) {
415         case TDB_NOLOCK:
416                 tdb->flags &= ~TDB_NOLOCK;
417                 break;
418         case TDB_NOMMAP:
419                 tdb->flags &= ~TDB_NOMMAP;
420                 tdb_mmap(tdb);
421                 break;
422         case TDB_NOSYNC:
423                 tdb->flags &= ~TDB_NOSYNC;
424                 break;
425         case TDB_SEQNUM:
426                 tdb->flags &= ~TDB_SEQNUM;
427                 break;
428         case TDB_ALLOW_NESTING:
429                 tdb->flags &= ~TDB_ALLOW_NESTING;
430                 break;
431         case TDB_RDONLY:
432                 if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) {
433                         tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
434                                                      TDB_LOG_USE_ERROR,
435                                                      "tdb_remove_flag: can't"
436                                                      " remove TDB_RDONLY on tdb"
437                                                      " opened with O_RDONLY");
438                         break;
439                 }
440                 if (readonly_changable(tdb, "tdb_remove_flag"))
441                         tdb->flags &= ~TDB_RDONLY;
442                 break;
443         default:
444                 tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
445                                              TDB_LOG_USE_ERROR,
446                                              "tdb_remove_flag: Unknown flag %u",
447                                              flag);
448         }
449 }
450
451 const char *tdb_errorstr(enum TDB_ERROR ecode)
452 {
453         /* Gcc warns if you miss a case in the switch, so use that. */
454         switch (ecode) {
455         case TDB_SUCCESS: return "Success";
456         case TDB_ERR_CORRUPT: return "Corrupt database";
457         case TDB_ERR_IO: return "IO Error";
458         case TDB_ERR_LOCK: return "Locking error";
459         case TDB_ERR_OOM: return "Out of memory";
460         case TDB_ERR_EXISTS: return "Record exists";
461         case TDB_ERR_EINVAL: return "Invalid parameter";
462         case TDB_ERR_NOEXIST: return "Record does not exist";
463         case TDB_ERR_RDONLY: return "write not permitted";
464         }
465         return "Invalid error code";
466 }
467
468 enum TDB_ERROR tdb_error(struct tdb_context *tdb)
469 {
470         return tdb->last_error;
471 }
472
473 enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb,
474                                enum TDB_ERROR ecode,
475                                enum tdb_log_level level,
476                                const char *fmt, ...)
477 {
478         char *message;
479         va_list ap;
480         size_t len;
481         /* tdb_open paths care about errno, so save it. */
482         int saved_errno = errno;
483
484         if (!tdb->log_fn)
485                 return ecode;
486
487         va_start(ap, fmt);
488         len = vasprintf(&message, fmt, ap);
489         va_end(ap);
490
491         if (len < 0) {
492                 tdb->log_fn(tdb, TDB_LOG_ERROR, TDB_ERR_OOM,
493                             "out of memory formatting message:", tdb->log_data);
494                 tdb->log_fn(tdb, level, ecode, fmt, tdb->log_data);
495         } else {
496                 tdb->log_fn(tdb, level, ecode, message, tdb->log_data);
497                 free(message);
498         }
499         errno = saved_errno;
500         return ecode;
501 }
502
503 enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
504                                  TDB_DATA key,
505                                  enum TDB_ERROR (*parse)(TDB_DATA k,
506                                                          TDB_DATA d,
507                                                          void *data),
508                                  void *data)
509 {
510         tdb_off_t off;
511         struct tdb_used_record rec;
512         struct hash_info h;
513         enum TDB_ERROR ecode;
514
515         off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
516         if (TDB_OFF_IS_ERR(off)) {
517                 return tdb->last_error = off;
518         }
519
520         if (!off) {
521                 ecode = TDB_ERR_NOEXIST;
522         } else {
523                 const void *dptr;
524                 dptr = tdb_access_read(tdb, off + sizeof(rec) + key.dsize,
525                                        rec_data_length(&rec), false);
526                 if (TDB_PTR_IS_ERR(dptr)) {
527                         ecode = TDB_PTR_ERR(dptr);
528                 } else {
529                         TDB_DATA d = tdb_mkdata(dptr, rec_data_length(&rec));
530
531                         ecode = parse(key, d, data);
532                         tdb_access_release(tdb, dptr);
533                 }
534         }
535
536         tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
537         return tdb->last_error = ecode;
538 }
539
540 const char *tdb_name(const struct tdb_context *tdb)
541 {
542         return tdb->name;
543 }
544
545 int64_t tdb_get_seqnum(struct tdb_context *tdb)
546 {
547         tdb_off_t off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
548         if (TDB_OFF_IS_ERR(off))
549                 tdb->last_error = off;
550         else
551                 tdb->last_error = TDB_SUCCESS;
552         return off;
553 }
554
555
556 int tdb_fd(const struct tdb_context *tdb)
557 {
558         return tdb->file->fd;
559 }