2 Unix SMB/CIFS implementation.
4 trivial database library
6 Copyright (C) Andrew Tridgell 1999-2005
7 Copyright (C) Paul `Rusty' Russell 2000
8 Copyright (C) Jeremy Allison 2000-2003
9 Copyright (C) Rusty Russell 2010
11 ** NOTE! The following LGPL license applies to the tdb
12 ** library. This does NOT imply that all of Samba is released
15 This library is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Lesser General Public
17 License as published by the Free Software Foundation; either
18 version 3 of the License, or (at your option) any later version.
20 This library is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 Lesser General Public License for more details.
25 You should have received a copy of the GNU Lesser General Public
26 License along with this library; if not, see <http://www.gnu.org/licenses/>.
30 #include <ccan/likely/likely.h>
32 void tdb_munmap(struct tdb_context *tdb)
34 if (tdb->flags & TDB_INTERNAL)
38 munmap(tdb->map_ptr, tdb->map_size);
43 void tdb_mmap(struct tdb_context *tdb)
45 if (tdb->flags & TDB_INTERNAL)
48 if (tdb->flags & TDB_NOMMAP)
51 tdb->map_ptr = mmap(NULL, tdb->map_size, tdb->mmap_flags,
52 MAP_SHARED, tdb->fd, 0);
55 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
57 if (tdb->map_ptr == MAP_FAILED) {
59 tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING,
60 "tdb_mmap failed for size %lld (%s)",
61 (long long)tdb->map_size, strerror(errno));
65 /* check for an out of bounds access - if it is out of bounds then
66 see if the database has been expanded by someone else and expand
68 note that "len" is the minimum length needed for the db
70 static enum TDB_ERROR tdb_oob(struct tdb_context *tdb, tdb_off_t len,
76 /* We can't hold pointers during this: we could unmap! */
77 assert(!tdb->direct_access
78 || (tdb->flags & TDB_NOLOCK)
79 || tdb_has_expansion_lock(tdb));
81 if (len <= tdb->map_size)
83 if (tdb->flags & TDB_INTERNAL) {
85 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
86 "tdb_oob len %lld beyond internal"
89 (long long)tdb->map_size);
94 ecode = tdb_lock_expand(tdb, F_RDLCK);
95 if (ecode != TDB_SUCCESS) {
99 if (fstat(tdb->fd, &st) != 0) {
100 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
101 "Failed to fstat file: %s", strerror(errno));
102 tdb_unlock_expand(tdb, F_RDLCK);
106 tdb_unlock_expand(tdb, F_RDLCK);
108 if (st.st_size < (size_t)len) {
110 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
111 "tdb_oob len %zu beyond eof at %zu",
112 (size_t)len, st.st_size);
117 /* Unmap, update size, remap */
120 tdb->map_size = st.st_size;
125 /* Endian conversion: we only ever deal with 8 byte quantities */
126 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
128 if (unlikely((tdb->flags & TDB_CONVERT)) && buf) {
129 uint64_t i, *p = (uint64_t *)buf;
130 for (i = 0; i < size / 8; i++)
131 p[i] = bswap_64(p[i]);
136 /* FIXME: Return the off? */
137 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb,
138 tdb_off_t base, uint64_t start, uint64_t end)
143 /* Zero vs non-zero is the same unconverted: minor optimization. */
144 val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t),
145 (end - start) * sizeof(tdb_off_t), false);
149 for (i = 0; i < (end - start); i++) {
153 tdb_access_release(tdb, val);
157 /* Return first zero offset in num offset array, or num. */
158 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
164 /* Zero vs non-zero is the same unconverted: minor optimization. */
165 val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false);
169 for (i = 0; i < num; i++) {
173 tdb_access_release(tdb, val);
177 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
179 char buf[8192] = { 0 };
180 void *p = tdb->methods->direct(tdb, off, len, true);
181 enum TDB_ERROR ecode;
183 assert(!tdb->read_only);
189 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
190 ecode = tdb->methods->twrite(tdb, off, buf, todo);
191 if (ecode != TDB_SUCCESS) {
201 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
205 if (likely(!(tdb->flags & TDB_CONVERT))) {
206 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
212 if (tdb_read_convert(tdb, off, &ret, sizeof(ret)) == -1)
217 /* write a lump of data at a specified offset */
218 static enum TDB_ERROR tdb_write(struct tdb_context *tdb, tdb_off_t off,
219 const void *buf, tdb_len_t len)
221 enum TDB_ERROR ecode;
223 if (tdb->read_only) {
224 return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
225 "Write to read-only database");
228 /* FIXME: Bogus optimization? */
233 ecode = tdb->methods->oob(tdb, off + len, 0);
234 if (ecode != TDB_SUCCESS) {
239 memcpy(off + (char *)tdb->map_ptr, buf, len);
242 ret = pwrite(tdb->fd, buf, len, off);
244 /* This shouldn't happen: we avoid sparse files. */
248 return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
249 "tdb_write: %zi at %zu len=%zu (%s)",
250 ret, (size_t)off, (size_t)len,
257 /* read a lump of data at a specified offset */
258 static enum TDB_ERROR tdb_read(struct tdb_context *tdb, tdb_off_t off,
259 void *buf, tdb_len_t len)
261 enum TDB_ERROR ecode;
263 ecode = tdb->methods->oob(tdb, off + len, 0);
264 if (ecode != TDB_SUCCESS) {
269 memcpy(buf, off + (char *)tdb->map_ptr, len);
271 ssize_t r = pread(tdb->fd, buf, len, off);
273 return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
274 "tdb_read failed with %zi at %zu "
275 "len=%zu (%s) map_size=%zu",
276 r, (size_t)off, (size_t)len,
278 (size_t)tdb->map_size);
284 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
285 const void *rec, size_t len)
287 enum TDB_ERROR ecode;
289 if (unlikely((tdb->flags & TDB_CONVERT))) {
290 void *conv = malloc(len);
292 tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
293 "tdb_write: no memory converting"
297 memcpy(conv, rec, len);
298 ecode = tdb->methods->twrite(tdb, off,
299 tdb_convert(tdb, conv, len), len);
302 ecode = tdb->methods->twrite(tdb, off, rec, len);
305 if (ecode != TDB_SUCCESS) {
312 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
313 void *rec, size_t len)
315 enum TDB_ERROR ecode = tdb->methods->tread(tdb, off, rec, len);
316 tdb_convert(tdb, rec, len);
317 if (ecode != TDB_SUCCESS) {
324 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
326 if (tdb->read_only) {
327 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
328 "Write to read-only database");
332 if (likely(!(tdb->flags & TDB_CONVERT))) {
333 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
340 return tdb_write_convert(tdb, off, &val, sizeof(val));
343 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
344 tdb_len_t len, unsigned int prefix)
347 enum TDB_ERROR ecode;
349 /* some systems don't like zero length malloc */
350 buf = malloc(prefix + len ? prefix + len : 1);
352 tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_USE_ERROR,
353 "tdb_alloc_read malloc failed len=%zu",
354 (size_t)(prefix + len));
356 ecode = tdb->methods->tread(tdb, offset, buf+prefix, len);
357 if (unlikely(ecode != TDB_SUCCESS)) {
366 /* read a lump of data, allocating the space for it */
367 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
369 return _tdb_alloc_read(tdb, offset, len, 0);
372 static int fill(struct tdb_context *tdb,
373 const void *buf, size_t size,
374 tdb_off_t off, tdb_len_t len)
377 size_t n = len > size ? size : len;
378 ssize_t ret = pwrite(tdb->fd, buf, n, off);
383 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
384 "fill failed: %zi at %zu len=%zu (%s)",
385 ret, (size_t)off, (size_t)len,
395 /* expand a file. we prefer to use ftruncate, as that is what posix
396 says to use for mmap expansion */
397 static enum TDB_ERROR tdb_expand_file(struct tdb_context *tdb,
402 if (tdb->read_only) {
403 return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
404 "Expand on read-only database");
407 if (tdb->flags & TDB_INTERNAL) {
408 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
410 return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
411 "No memory to expand database");
414 tdb->map_size += addition;
416 /* Unmap before trying to write; old TDB claimed OpenBSD had
417 * problem with this otherwise. */
420 /* If this fails, we try to fill anyway. */
421 if (ftruncate(tdb->fd, tdb->map_size + addition))
424 /* now fill the file with something. This ensures that the
425 file isn't sparse, which would be very bad if we ran out of
426 disk. This must be done with write, not via mmap */
427 memset(buf, 0x43, sizeof(buf));
428 if (fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
430 tdb->map_size += addition;
436 const void *tdb_access_read(struct tdb_context *tdb,
437 tdb_off_t off, tdb_len_t len, bool convert)
439 const void *ret = NULL;
441 if (likely(!(tdb->flags & TDB_CONVERT)))
442 ret = tdb->methods->direct(tdb, off, len, false);
445 struct tdb_access_hdr *hdr;
446 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
448 hdr->next = tdb->access;
452 tdb_convert(tdb, (void *)ret, len);
455 tdb->direct_access++;
460 void *tdb_access_write(struct tdb_context *tdb,
461 tdb_off_t off, tdb_len_t len, bool convert)
465 if (tdb->read_only) {
466 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
467 "Write to read-only database");
471 if (likely(!(tdb->flags & TDB_CONVERT)))
472 ret = tdb->methods->direct(tdb, off, len, true);
475 struct tdb_access_hdr *hdr;
476 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
478 hdr->next = tdb->access;
482 hdr->convert = convert;
485 tdb_convert(tdb, (void *)ret, len);
488 tdb->direct_access++;
493 static struct tdb_access_hdr **find_hdr(struct tdb_context *tdb, const void *p)
495 struct tdb_access_hdr **hp;
497 for (hp = &tdb->access; *hp; hp = &(*hp)->next) {
504 void tdb_access_release(struct tdb_context *tdb, const void *p)
506 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
513 tdb->direct_access--;
516 int tdb_access_commit(struct tdb_context *tdb, void *p)
518 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
524 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
526 ret = tdb_write(tdb, hdr->off, p, hdr->len);
530 tdb->direct_access--;
535 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len,
538 if (unlikely(!tdb->map_ptr))
541 if (unlikely(tdb_oob(tdb, off + len, true) != TDB_SUCCESS))
543 return (char *)tdb->map_ptr + off;
546 void add_stat_(struct tdb_context *tdb, uint64_t *s, size_t val)
548 if ((uintptr_t)s < (uintptr_t)tdb->stats + tdb->stats->size)
552 static const struct tdb_methods io_methods = {
561 initialise the default methods table
563 void tdb_io_init(struct tdb_context *tdb)
565 tdb->methods = &io_methods;