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_DEBUG_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 int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe)
74 /* We can't hold pointers during this: we could unmap! */
75 assert(!tdb->direct_access
76 || (tdb->flags & TDB_NOLOCK)
77 || tdb_has_expansion_lock(tdb));
79 if (len <= tdb->map_size)
81 if (tdb->flags & TDB_INTERNAL) {
83 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
84 "tdb_oob len %lld beyond internal"
87 (long long)tdb->map_size);
92 if (tdb_lock_expand(tdb, F_RDLCK) != 0)
95 if (fstat(tdb->fd, &st) != 0) {
96 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
97 "Failed to fstat file: %s", strerror(errno));
98 tdb_unlock_expand(tdb, F_RDLCK);
102 tdb_unlock_expand(tdb, F_RDLCK);
104 if (st.st_size < (size_t)len) {
106 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
107 "tdb_oob len %zu beyond eof at %zu",
108 (size_t)len, st.st_size);
113 /* Unmap, update size, remap */
116 tdb->map_size = st.st_size;
121 /* Endian conversion: we only ever deal with 8 byte quantities */
122 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
124 if (unlikely((tdb->flags & TDB_CONVERT)) && buf) {
125 uint64_t i, *p = (uint64_t *)buf;
126 for (i = 0; i < size / 8; i++)
127 p[i] = bswap_64(p[i]);
132 /* FIXME: Return the off? */
133 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb,
134 tdb_off_t base, uint64_t start, uint64_t end)
139 /* Zero vs non-zero is the same unconverted: minor optimization. */
140 val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t),
141 (end - start) * sizeof(tdb_off_t), false);
145 for (i = 0; i < (end - start); i++) {
149 tdb_access_release(tdb, val);
153 /* Return first zero offset in num offset array, or num. */
154 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
160 /* Zero vs non-zero is the same unconverted: minor optimization. */
161 val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false);
165 for (i = 0; i < num; i++) {
169 tdb_access_release(tdb, val);
173 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
175 char buf[8192] = { 0 };
176 void *p = tdb->methods->direct(tdb, off, len, true);
178 assert(!tdb->read_only);
184 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
185 if (tdb->methods->twrite(tdb, off, buf, todo) == -1)
193 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
197 if (likely(!(tdb->flags & TDB_CONVERT))) {
198 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
204 if (tdb_read_convert(tdb, off, &ret, sizeof(ret)) == -1)
209 /* write a lump of data at a specified offset */
210 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
211 const void *buf, tdb_len_t len)
213 if (tdb->read_only) {
214 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
215 "Write to read-only database");
219 /* FIXME: Bogus optimization? */
224 if (tdb->methods->oob(tdb, off + len, 0) != 0)
228 memcpy(off + (char *)tdb->map_ptr, buf, len);
231 ret = pwrite(tdb->fd, buf, len, off);
233 /* This shouldn't happen: we avoid sparse files. */
237 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
238 "tdb_write: %zi at %zu len=%zu (%s)",
239 ret, (size_t)off, (size_t)len,
247 /* read a lump of data at a specified offset */
248 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
251 if (tdb->methods->oob(tdb, off + len, 0) != 0) {
256 memcpy(buf, off + (char *)tdb->map_ptr, len);
258 ssize_t r = pread(tdb->fd, buf, len, off);
260 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
261 "tdb_read failed with %zi at %zu "
262 "len=%zu (%s) map_size=%zu",
263 r, (size_t)off, (size_t)len,
265 (size_t)tdb->map_size);
272 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
273 const void *rec, size_t len)
276 if (unlikely((tdb->flags & TDB_CONVERT))) {
277 void *conv = malloc(len);
279 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
280 "tdb_write: no memory converting"
284 memcpy(conv, rec, len);
285 ret = tdb->methods->twrite(tdb, off,
286 tdb_convert(tdb, conv, len), len);
289 ret = tdb->methods->twrite(tdb, off, rec, len);
294 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
295 void *rec, size_t len)
297 int ret = tdb->methods->tread(tdb, off, rec, len);
298 tdb_convert(tdb, rec, len);
302 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
304 if (tdb->read_only) {
305 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
306 "Write to read-only database");
310 if (likely(!(tdb->flags & TDB_CONVERT))) {
311 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
318 return tdb_write_convert(tdb, off, &val, sizeof(val));
321 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
322 tdb_len_t len, unsigned int prefix)
326 /* some systems don't like zero length malloc */
327 buf = malloc(prefix + len ? prefix + len : 1);
329 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_ERROR,
330 "tdb_alloc_read malloc failed len=%zu",
331 (size_t)(prefix + len));
332 } else if (unlikely(tdb->methods->tread(tdb, offset, buf+prefix, len)
340 /* read a lump of data, allocating the space for it */
341 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
343 return _tdb_alloc_read(tdb, offset, len, 0);
346 static int fill(struct tdb_context *tdb,
347 const void *buf, size_t size,
348 tdb_off_t off, tdb_len_t len)
351 size_t n = len > size ? size : len;
352 ssize_t ret = pwrite(tdb->fd, buf, n, off);
357 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
358 "fill failed: %zi at %zu len=%zu (%s)",
359 ret, (size_t)off, (size_t)len,
369 /* expand a file. we prefer to use ftruncate, as that is what posix
370 says to use for mmap expansion */
371 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
375 if (tdb->read_only) {
376 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
377 "Expand on read-only database");
381 if (tdb->flags & TDB_INTERNAL) {
382 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
384 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
385 "No memory to expand database");
389 tdb->map_size += addition;
391 /* Unmap before trying to write; old TDB claimed OpenBSD had
392 * problem with this otherwise. */
395 /* If this fails, we try to fill anyway. */
396 if (ftruncate(tdb->fd, tdb->map_size + addition))
399 /* now fill the file with something. This ensures that the
400 file isn't sparse, which would be very bad if we ran out of
401 disk. This must be done with write, not via mmap */
402 memset(buf, 0x43, sizeof(buf));
403 if (0 || fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
405 tdb->map_size += addition;
411 const void *tdb_access_read(struct tdb_context *tdb,
412 tdb_off_t off, tdb_len_t len, bool convert)
414 const void *ret = NULL;
416 if (likely(!(tdb->flags & TDB_CONVERT)))
417 ret = tdb->methods->direct(tdb, off, len, false);
420 struct tdb_access_hdr *hdr;
421 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
423 hdr->next = tdb->access;
427 tdb_convert(tdb, (void *)ret, len);
430 tdb->direct_access++;
435 void *tdb_access_write(struct tdb_context *tdb,
436 tdb_off_t off, tdb_len_t len, bool convert)
440 if (tdb->read_only) {
441 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
442 "Write to read-only database");
446 if (likely(!(tdb->flags & TDB_CONVERT)))
447 ret = tdb->methods->direct(tdb, off, len, true);
450 struct tdb_access_hdr *hdr;
451 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
453 hdr->next = tdb->access;
457 hdr->convert = convert;
460 tdb_convert(tdb, (void *)ret, len);
463 tdb->direct_access++;
468 static struct tdb_access_hdr **find_hdr(struct tdb_context *tdb, const void *p)
470 struct tdb_access_hdr **hp;
472 for (hp = &tdb->access; *hp; hp = &(*hp)->next) {
479 void tdb_access_release(struct tdb_context *tdb, const void *p)
481 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
488 tdb->direct_access--;
491 int tdb_access_commit(struct tdb_context *tdb, void *p)
493 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
499 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
501 ret = tdb_write(tdb, hdr->off, p, hdr->len);
505 tdb->direct_access--;
510 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len,
513 if (unlikely(!tdb->map_ptr))
516 if (unlikely(tdb_oob(tdb, off + len, true) == -1))
518 return (char *)tdb->map_ptr + off;
521 void add_stat_(struct tdb_context *tdb, uint64_t *s, size_t val)
523 if ((uintptr_t)s < (uintptr_t)tdb->stats + tdb->stats->size)
527 static const struct tdb_methods io_methods = {
536 initialise the default methods table
538 void tdb_io_init(struct tdb_context *tdb)
540 tdb->methods = &io_methods;