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->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),
204 if (tdb_read_convert(tdb, off, &ret, sizeof(ret)) == -1)
209 /* Even on files, we can get partial writes due to signals. */
210 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
214 ret = pwrite(fd, buf, len, off);
221 buf = (char *)buf + ret;
228 /* Even on files, we can get partial reads due to signals. */
229 bool tdb_pread_all(int fd, void *buf, size_t len, tdb_off_t off)
233 ret = pread(fd, buf, len, off);
241 buf = (char *)buf + ret;
248 bool tdb_read_all(int fd, void *buf, size_t len)
252 ret = read(fd, buf, len);
260 buf = (char *)buf + ret;
266 /* write a lump of data at a specified offset */
267 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
268 const void *buf, tdb_len_t len)
270 if (tdb->read_only) {
271 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
272 "Write to read-only database");
276 /* FIXME: Bogus optimization? */
281 if (tdb->methods->oob(tdb, off + len, 0) != 0)
285 memcpy(off + (char *)tdb->map_ptr, buf, len);
287 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
288 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
289 "tdb_write failed at %zu len=%zu (%s)",
290 (size_t)off, (size_t)len, strerror(errno));
297 /* read a lump of data at a specified offset */
298 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
301 if (tdb->methods->oob(tdb, off + len, 0) != 0) {
306 memcpy(buf, off + (char *)tdb->map_ptr, len);
308 if (!tdb_pread_all(tdb->fd, buf, len, off)) {
309 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
310 "tdb_read failed at %zu "
311 "len=%zu (%s) map_size=%zu",
312 (size_t)off, (size_t)len,
314 (size_t)tdb->map_size);
321 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
322 const void *rec, size_t len)
325 if (unlikely((tdb->flags & TDB_CONVERT))) {
326 void *conv = malloc(len);
328 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
329 "tdb_write: no memory converting"
333 memcpy(conv, rec, len);
334 ret = tdb->methods->write(tdb, off,
335 tdb_convert(tdb, conv, len), len);
338 ret = tdb->methods->write(tdb, off, rec, len);
343 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
344 void *rec, size_t len)
346 int ret = tdb->methods->read(tdb, off, rec, len);
347 tdb_convert(tdb, rec, len);
351 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
353 if (tdb->read_only) {
354 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
355 "Write to read-only database");
359 if (likely(!(tdb->flags & TDB_CONVERT))) {
360 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
367 return tdb_write_convert(tdb, off, &val, sizeof(val));
370 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
371 tdb_len_t len, unsigned int prefix)
375 /* some systems don't like zero length malloc */
376 buf = malloc(prefix + len ? prefix + len : 1);
378 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_ERROR,
379 "tdb_alloc_read malloc failed len=%zu",
380 (size_t)(prefix + len));
381 } else if (unlikely(tdb->methods->read(tdb, offset, buf+prefix,
389 /* read a lump of data, allocating the space for it */
390 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
392 return _tdb_alloc_read(tdb, offset, len, 0);
395 static int fill(struct tdb_context *tdb,
396 const void *buf, size_t size,
397 tdb_off_t off, tdb_len_t len)
400 size_t n = len > size ? size : len;
402 if (!tdb_pwrite_all(tdb->fd, buf, n, off)) {
403 tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL,
404 "fill write failed: giving up!");
413 /* expand a file. we prefer to use ftruncate, as that is what posix
414 says to use for mmap expansion */
415 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
419 if (tdb->read_only) {
420 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
421 "Expand on read-only database");
425 if (tdb->flags & TDB_INTERNAL) {
426 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
428 tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL,
429 "No memory to expand database");
433 tdb->map_size += addition;
435 /* Unmap before trying to write; old TDB claimed OpenBSD had
436 * problem with this otherwise. */
439 /* If this fails, we try to fill anyway. */
440 if (ftruncate(tdb->fd, tdb->map_size + addition))
443 /* now fill the file with something. This ensures that the
444 file isn't sparse, which would be very bad if we ran out of
445 disk. This must be done with write, not via mmap */
446 memset(buf, 0x43, sizeof(buf));
447 if (0 || fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
449 tdb->map_size += addition;
455 const void *tdb_access_read(struct tdb_context *tdb,
456 tdb_off_t off, tdb_len_t len, bool convert)
458 const void *ret = NULL;
460 if (likely(!(tdb->flags & TDB_CONVERT)))
461 ret = tdb->methods->direct(tdb, off, len, false);
464 struct tdb_access_hdr *hdr;
465 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
467 hdr->next = tdb->access;
471 tdb_convert(tdb, (void *)ret, len);
474 tdb->direct_access++;
479 void *tdb_access_write(struct tdb_context *tdb,
480 tdb_off_t off, tdb_len_t len, bool convert)
484 if (tdb->read_only) {
485 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_DEBUG_WARNING,
486 "Write to read-only database");
490 if (likely(!(tdb->flags & TDB_CONVERT)))
491 ret = tdb->methods->direct(tdb, off, len, true);
494 struct tdb_access_hdr *hdr;
495 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
497 hdr->next = tdb->access;
501 hdr->convert = convert;
504 tdb_convert(tdb, (void *)ret, len);
507 tdb->direct_access++;
512 static struct tdb_access_hdr **find_hdr(struct tdb_context *tdb, const void *p)
514 struct tdb_access_hdr **hp;
516 for (hp = &tdb->access; *hp; hp = &(*hp)->next) {
523 void tdb_access_release(struct tdb_context *tdb, const void *p)
525 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
532 tdb->direct_access--;
535 int tdb_access_commit(struct tdb_context *tdb, void *p)
537 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
543 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
545 ret = tdb_write(tdb, hdr->off, p, hdr->len);
549 tdb->direct_access--;
554 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len,
557 if (unlikely(!tdb->map_ptr))
560 if (unlikely(tdb_oob(tdb, off + len, true) == -1))
562 return (char *)tdb->map_ptr + off;
565 void add_stat_(struct tdb_context *tdb, uint64_t *stat, size_t val)
567 if ((uintptr_t)stat < (uintptr_t)tdb->stats + tdb->stats->size)
571 static const struct tdb_methods io_methods = {
580 initialise the default methods table
582 void tdb_io_init(struct tdb_context *tdb)
584 tdb->methods = &io_methods;