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);
178 assert(!tdb->read_only);
184 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
185 if (tdb->methods->write(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));
203 if (tdb_read_convert(tdb, off, &ret, sizeof(ret)) == -1)
208 /* Even on files, we can get partial writes due to signals. */
209 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
213 ret = pwrite(fd, buf, len, off);
220 buf = (char *)buf + ret;
227 /* Even on files, we can get partial reads due to signals. */
228 bool tdb_pread_all(int fd, void *buf, size_t len, tdb_off_t off)
232 ret = pread(fd, buf, len, off);
240 buf = (char *)buf + ret;
247 bool tdb_read_all(int fd, void *buf, size_t len)
251 ret = read(fd, buf, len);
259 buf = (char *)buf + ret;
265 /* write a lump of data at a specified offset */
266 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
267 const void *buf, tdb_len_t len)
269 if (tdb->read_only) {
270 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
271 "Write to read-only database");
275 /* FIXME: Bogus optimization? */
280 if (tdb->methods->oob(tdb, off + len, 0) != 0)
284 memcpy(off + (char *)tdb->map_ptr, buf, len);
286 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
287 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
288 "tdb_write failed at %zu len=%zu (%s)",
289 (size_t)off, (size_t)len, strerror(errno));
296 /* read a lump of data at a specified offset */
297 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
300 if (tdb->methods->oob(tdb, off + len, 0) != 0) {
305 memcpy(buf, off + (char *)tdb->map_ptr, len);
307 if (!tdb_pread_all(tdb->fd, buf, len, off)) {
308 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
309 "tdb_read failed at %zu "
310 "len=%zu (%s) map_size=%zu",
311 (size_t)off, (size_t)len,
313 (size_t)tdb->map_size);
320 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
321 const void *rec, size_t len)
324 if (unlikely((tdb->flags & TDB_CONVERT))) {
325 void *conv = malloc(len);
327 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
328 "tdb_write: no memory converting"
332 memcpy(conv, rec, len);
333 ret = tdb->methods->write(tdb, off,
334 tdb_convert(tdb, conv, len), len);
337 ret = tdb->methods->write(tdb, off, rec, len);
342 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
343 void *rec, size_t len)
345 int ret = tdb->methods->read(tdb, off, rec, len);
346 tdb_convert(tdb, rec, len);
350 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
352 if (tdb->read_only) {
353 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
354 "Write to read-only database");
358 if (likely(!(tdb->flags & TDB_CONVERT))) {
359 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p));
365 return tdb_write_convert(tdb, off, &val, sizeof(val));
368 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
369 tdb_len_t len, unsigned int prefix)
373 /* some systems don't like zero length malloc */
374 buf = malloc(prefix + len ? prefix + len : 1);
376 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_ERROR,
377 "tdb_alloc_read malloc failed len=%zu",
378 (size_t)(prefix + len));
379 } else if (unlikely(tdb->methods->read(tdb, offset, buf+prefix,
387 /* read a lump of data, allocating the space for it */
388 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
390 return _tdb_alloc_read(tdb, offset, len, 0);
393 static int fill(struct tdb_context *tdb,
394 const void *buf, size_t size,
395 tdb_off_t off, tdb_len_t len)
398 size_t n = len > size ? size : len;
400 if (!tdb_pwrite_all(tdb->fd, buf, n, off)) {
401 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
402 "fill write failed: giving up!");
411 /* expand a file. we prefer to use ftruncate, as that is what posix
412 says to use for mmap expansion */
413 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
417 if (tdb->read_only) {
418 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
419 "Expand on read-only database");
423 if (tdb->flags & TDB_INTERNAL) {
424 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
426 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
427 "No memory to expand database");
431 tdb->map_size += addition;
433 /* Unmap before trying to write; old TDB claimed OpenBSD had
434 * problem with this otherwise. */
437 /* If this fails, we try to fill anyway. */
438 if (ftruncate(tdb->fd, tdb->map_size + addition))
441 /* now fill the file with something. This ensures that the
442 file isn't sparse, which would be very bad if we ran out of
443 disk. This must be done with write, not via mmap */
444 memset(buf, 0x43, sizeof(buf));
445 if (0 || fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
447 tdb->map_size += addition;
453 /* This is only neded for tdb_access_commit, but used everywhere to simplify. */
454 struct tdb_access_hdr {
460 const void *tdb_access_read(struct tdb_context *tdb,
461 tdb_off_t off, tdb_len_t len, bool convert)
463 const void *ret = NULL;
465 if (likely(!(tdb->flags & TDB_CONVERT)))
466 ret = tdb->methods->direct(tdb, off, len);
469 struct tdb_access_hdr *hdr;
470 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
474 tdb_convert(tdb, (void *)ret, len);
477 tdb->direct_access++;
482 void *tdb_access_write(struct tdb_context *tdb,
483 tdb_off_t off, tdb_len_t len, bool convert)
487 if (tdb->read_only) {
488 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
489 "Write to read-only database");
493 if (likely(!(tdb->flags & TDB_CONVERT)))
494 ret = tdb->methods->direct(tdb, off, len);
497 struct tdb_access_hdr *hdr;
498 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
502 hdr->convert = convert;
505 tdb_convert(tdb, (void *)ret, len);
508 tdb->direct_access++;
513 bool is_direct(const struct tdb_context *tdb, const void *p)
516 && (char *)p >= (char *)tdb->map_ptr
517 && (char *)p < (char *)tdb->map_ptr + tdb->map_size);
520 void tdb_access_release(struct tdb_context *tdb, const void *p)
522 if (is_direct(tdb, p))
523 tdb->direct_access--;
525 free((struct tdb_access_hdr *)p - 1);
528 int tdb_access_commit(struct tdb_context *tdb, void *p)
533 || (char *)p < (char *)tdb->map_ptr
534 || (char *)p >= (char *)tdb->map_ptr + tdb->map_size) {
535 struct tdb_access_hdr *hdr;
537 hdr = (struct tdb_access_hdr *)p - 1;
539 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
541 ret = tdb_write(tdb, hdr->off, p, hdr->len);
544 tdb->direct_access--;
549 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len)
551 if (unlikely(!tdb->map_ptr))
554 if (unlikely(tdb_oob(tdb, off + len, true) == -1))
556 return (char *)tdb->map_ptr + off;
559 void add_stat_(struct tdb_context *tdb, uint64_t *stat, size_t val)
561 if ((uintptr_t)stat < (uintptr_t)tdb->stats + tdb->stats->size)
565 static const struct tdb_methods io_methods = {
574 initialise the default methods table
576 void tdb_io_init(struct tdb_context *tdb)
578 tdb->methods = &io_methods;