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 int tdb_oob(struct tdb_context *tdb, tdb_off_t len, bool probe)
75 /* We can't hold pointers during this: we could unmap! */
76 assert(!tdb->direct_access
77 || (tdb->flags & TDB_NOLOCK)
78 || tdb_has_expansion_lock(tdb));
80 if (len <= tdb->map_size)
82 if (tdb->flags & TDB_INTERNAL) {
84 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
85 "tdb_oob len %lld beyond internal"
88 (long long)tdb->map_size);
93 ecode = tdb_lock_expand(tdb, F_RDLCK);
94 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);
182 assert(!tdb->read_only);
188 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
189 if (tdb->methods->twrite(tdb, off, buf, todo) == -1)
197 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
201 if (likely(!(tdb->flags & TDB_CONVERT))) {
202 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
208 if (tdb_read_convert(tdb, off, &ret, sizeof(ret)) == -1)
213 /* write a lump of data at a specified offset */
214 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
215 const void *buf, tdb_len_t len)
217 if (tdb->read_only) {
218 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
219 "Write to read-only database");
223 /* FIXME: Bogus optimization? */
228 if (tdb->methods->oob(tdb, off + len, 0) != 0)
232 memcpy(off + (char *)tdb->map_ptr, buf, len);
235 ret = pwrite(tdb->fd, buf, len, off);
237 /* This shouldn't happen: we avoid sparse files. */
241 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
242 "tdb_write: %zi at %zu len=%zu (%s)",
243 ret, (size_t)off, (size_t)len,
251 /* read a lump of data at a specified offset */
252 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
255 if (tdb->methods->oob(tdb, off + len, 0) != 0) {
260 memcpy(buf, off + (char *)tdb->map_ptr, len);
262 ssize_t r = pread(tdb->fd, buf, len, off);
264 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
265 "tdb_read failed with %zi at %zu "
266 "len=%zu (%s) map_size=%zu",
267 r, (size_t)off, (size_t)len,
269 (size_t)tdb->map_size);
276 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
277 const void *rec, size_t len)
280 if (unlikely((tdb->flags & TDB_CONVERT))) {
281 void *conv = malloc(len);
283 tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
284 "tdb_write: no memory converting"
288 memcpy(conv, rec, len);
289 ret = tdb->methods->twrite(tdb, off,
290 tdb_convert(tdb, conv, len), len);
293 ret = tdb->methods->twrite(tdb, off, rec, len);
298 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
299 void *rec, size_t len)
301 int ret = tdb->methods->tread(tdb, off, rec, len);
302 tdb_convert(tdb, rec, len);
306 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
308 if (tdb->read_only) {
309 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
310 "Write to read-only database");
314 if (likely(!(tdb->flags & TDB_CONVERT))) {
315 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
322 return tdb_write_convert(tdb, off, &val, sizeof(val));
325 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
326 tdb_len_t len, unsigned int prefix)
330 /* some systems don't like zero length malloc */
331 buf = malloc(prefix + len ? prefix + len : 1);
333 tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_USE_ERROR,
334 "tdb_alloc_read malloc failed len=%zu",
335 (size_t)(prefix + len));
336 } else if (unlikely(tdb->methods->tread(tdb, offset, buf+prefix, len)
344 /* read a lump of data, allocating the space for it */
345 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
347 return _tdb_alloc_read(tdb, offset, len, 0);
350 static int fill(struct tdb_context *tdb,
351 const void *buf, size_t size,
352 tdb_off_t off, tdb_len_t len)
355 size_t n = len > size ? size : len;
356 ssize_t ret = pwrite(tdb->fd, buf, n, off);
361 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
362 "fill failed: %zi at %zu len=%zu (%s)",
363 ret, (size_t)off, (size_t)len,
373 /* expand a file. we prefer to use ftruncate, as that is what posix
374 says to use for mmap expansion */
375 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
379 if (tdb->read_only) {
380 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
381 "Expand on read-only database");
385 if (tdb->flags & TDB_INTERNAL) {
386 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
388 tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
389 "No memory to expand database");
393 tdb->map_size += addition;
395 /* Unmap before trying to write; old TDB claimed OpenBSD had
396 * problem with this otherwise. */
399 /* If this fails, we try to fill anyway. */
400 if (ftruncate(tdb->fd, tdb->map_size + addition))
403 /* now fill the file with something. This ensures that the
404 file isn't sparse, which would be very bad if we ran out of
405 disk. This must be done with write, not via mmap */
406 memset(buf, 0x43, sizeof(buf));
407 if (0 || fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
409 tdb->map_size += addition;
415 const void *tdb_access_read(struct tdb_context *tdb,
416 tdb_off_t off, tdb_len_t len, bool convert)
418 const void *ret = NULL;
420 if (likely(!(tdb->flags & TDB_CONVERT)))
421 ret = tdb->methods->direct(tdb, off, len, false);
424 struct tdb_access_hdr *hdr;
425 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
427 hdr->next = tdb->access;
431 tdb_convert(tdb, (void *)ret, len);
434 tdb->direct_access++;
439 void *tdb_access_write(struct tdb_context *tdb,
440 tdb_off_t off, tdb_len_t len, bool convert)
444 if (tdb->read_only) {
445 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
446 "Write to read-only database");
450 if (likely(!(tdb->flags & TDB_CONVERT)))
451 ret = tdb->methods->direct(tdb, off, len, true);
454 struct tdb_access_hdr *hdr;
455 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
457 hdr->next = tdb->access;
461 hdr->convert = convert;
464 tdb_convert(tdb, (void *)ret, len);
467 tdb->direct_access++;
472 static struct tdb_access_hdr **find_hdr(struct tdb_context *tdb, const void *p)
474 struct tdb_access_hdr **hp;
476 for (hp = &tdb->access; *hp; hp = &(*hp)->next) {
483 void tdb_access_release(struct tdb_context *tdb, const void *p)
485 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
492 tdb->direct_access--;
495 int tdb_access_commit(struct tdb_context *tdb, void *p)
497 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
503 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
505 ret = tdb_write(tdb, hdr->off, p, hdr->len);
509 tdb->direct_access--;
514 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len,
517 if (unlikely(!tdb->map_ptr))
520 if (unlikely(tdb_oob(tdb, off + len, true) == -1))
522 return (char *)tdb->map_ptr + off;
525 void add_stat_(struct tdb_context *tdb, uint64_t *s, size_t val)
527 if ((uintptr_t)s < (uintptr_t)tdb->stats + tdb->stats->size)
531 static const struct tdb_methods io_methods = {
540 initialise the default methods table
542 void tdb_io_init(struct tdb_context *tdb)
544 tdb->methods = &io_methods;