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->log(tdb, TDB_DEBUG_WARNING, tdb->log_priv,
60 "tdb_mmap failed for size %lld (%s)\n",
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 /* Ensure ecode is set for log fn. */
85 tdb->ecode = TDB_ERR_IO;
86 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
87 "tdb_oob len %lld beyond internal"
88 " malloc size %lld\n",
90 (long long)tdb->map_size);
95 if (tdb_lock_expand(tdb, F_RDLCK) != 0)
98 ret = fstat(tdb->fd, &st);
100 tdb_unlock_expand(tdb, F_RDLCK);
103 tdb->ecode = TDB_ERR_IO;
107 if (st.st_size < (size_t)len) {
109 /* Ensure ecode is set for log fn. */
110 tdb->ecode = TDB_ERR_IO;
111 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
112 "tdb_oob len %lld beyond eof at %lld\n",
113 (long long)len, (long long)st.st_size);
118 /* Unmap, update size, remap */
121 tdb->map_size = st.st_size;
126 /* Endian conversion: we only ever deal with 8 byte quantities */
127 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
129 if (unlikely((tdb->flags & TDB_CONVERT)) && buf) {
130 uint64_t i, *p = (uint64_t *)buf;
131 for (i = 0; i < size / 8; i++)
132 p[i] = bswap_64(p[i]);
137 /* FIXME: Return the off? */
138 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb,
139 tdb_off_t base, uint64_t start, uint64_t end)
144 /* Zero vs non-zero is the same unconverted: minor optimization. */
145 val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t),
146 (end - start) * sizeof(tdb_off_t), false);
150 for (i = 0; i < (end - start); i++) {
154 tdb_access_release(tdb, val);
158 /* Return first zero offset in num offset array, or num. */
159 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
165 /* Zero vs non-zero is the same unconverted: minor optimization. */
166 val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false);
170 for (i = 0; i < num; i++) {
174 tdb_access_release(tdb, val);
178 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
180 char buf[8192] = { 0 };
181 void *p = tdb->methods->direct(tdb, off, len);
183 if (tdb->read_only) {
184 tdb->ecode = TDB_ERR_RDONLY;
193 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
194 if (tdb->methods->write(tdb, off, buf, todo) == -1)
202 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
206 if (likely(!(tdb->flags & TDB_CONVERT))) {
207 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p));
212 if (tdb_read_convert(tdb, off, &ret, sizeof(ret)) == -1)
217 /* Even on files, we can get partial writes due to signals. */
218 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
222 ret = pwrite(fd, buf, len, off);
229 buf = (char *)buf + ret;
236 /* Even on files, we can get partial reads due to signals. */
237 bool tdb_pread_all(int fd, void *buf, size_t len, tdb_off_t off)
241 ret = pread(fd, buf, len, off);
249 buf = (char *)buf + ret;
256 bool tdb_read_all(int fd, void *buf, size_t len)
260 ret = read(fd, buf, len);
268 buf = (char *)buf + ret;
274 /* write a lump of data at a specified offset */
275 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
276 const void *buf, tdb_len_t len)
282 if (tdb->read_only) {
283 tdb->ecode = TDB_ERR_RDONLY;
287 if (tdb->methods->oob(tdb, off + len, 0) != 0)
291 memcpy(off + (char *)tdb->map_ptr, buf, len);
293 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
294 tdb->ecode = TDB_ERR_IO;
295 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
296 "tdb_write failed at %llu len=%llu (%s)\n",
297 (long long)off, (long long)len,
305 /* read a lump of data at a specified offset */
306 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
309 if (tdb->methods->oob(tdb, off + len, 0) != 0) {
314 memcpy(buf, off + (char *)tdb->map_ptr, len);
316 if (!tdb_pread_all(tdb->fd, buf, len, off)) {
317 /* Ensure ecode is set for log fn. */
318 tdb->ecode = TDB_ERR_IO;
319 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
320 "tdb_read failed at %lld "
321 "len=%lld (%s) map_size=%lld\n",
322 (long long)off, (long long)len,
324 (long long)tdb->map_size);
331 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
332 const void *rec, size_t len)
335 if (unlikely((tdb->flags & TDB_CONVERT))) {
336 void *conv = malloc(len);
338 tdb->ecode = TDB_ERR_OOM;
339 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
340 "tdb_write: no memory converting %zu bytes\n",
344 memcpy(conv, rec, len);
345 ret = tdb->methods->write(tdb, off,
346 tdb_convert(tdb, conv, len), len);
349 ret = tdb->methods->write(tdb, off, rec, len);
354 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
355 void *rec, size_t len)
357 int ret = tdb->methods->read(tdb, off, rec, len);
358 tdb_convert(tdb, rec, len);
362 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
364 if (tdb->read_only) {
365 tdb->ecode = TDB_ERR_RDONLY;
369 if (likely(!(tdb->flags & TDB_CONVERT))) {
370 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p));
376 return tdb_write_convert(tdb, off, &val, sizeof(val));
379 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
380 tdb_len_t len, unsigned int prefix)
384 /* some systems don't like zero length malloc */
385 buf = malloc(prefix + len ? prefix + len : 1);
386 if (unlikely(!buf)) {
387 tdb->ecode = TDB_ERR_OOM;
388 tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
389 "tdb_alloc_read malloc failed len=%lld\n",
390 (long long)prefix + len);
391 } else if (unlikely(tdb->methods->read(tdb, offset, buf+prefix, len))) {
398 /* read a lump of data, allocating the space for it */
399 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
401 return _tdb_alloc_read(tdb, offset, len, 0);
404 static int fill(struct tdb_context *tdb,
405 const void *buf, size_t size,
406 tdb_off_t off, tdb_len_t len)
409 size_t n = len > size ? size : len;
411 if (!tdb_pwrite_all(tdb->fd, buf, n, off)) {
412 tdb->ecode = TDB_ERR_IO;
413 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
414 "fill write failed: giving up!\n");
423 /* expand a file. we prefer to use ftruncate, as that is what posix
424 says to use for mmap expansion */
425 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
429 if (tdb->read_only) {
430 tdb->ecode = TDB_ERR_RDONLY;
434 if (tdb->flags & TDB_INTERNAL) {
435 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
437 tdb->ecode = TDB_ERR_OOM;
441 tdb->map_size += addition;
443 /* Unmap before trying to write; old TDB claimed OpenBSD had
444 * problem with this otherwise. */
447 /* If this fails, we try to fill anyway. */
448 if (ftruncate(tdb->fd, tdb->map_size + addition))
451 /* now fill the file with something. This ensures that the
452 file isn't sparse, which would be very bad if we ran out of
453 disk. This must be done with write, not via mmap */
454 memset(buf, 0x43, sizeof(buf));
455 if (0 || fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
457 tdb->map_size += addition;
463 /* This is only neded for tdb_access_commit, but used everywhere to simplify. */
464 struct tdb_access_hdr {
470 const void *tdb_access_read(struct tdb_context *tdb,
471 tdb_off_t off, tdb_len_t len, bool convert)
473 const void *ret = NULL;
475 if (likely(!(tdb->flags & TDB_CONVERT)))
476 ret = tdb->methods->direct(tdb, off, len);
479 struct tdb_access_hdr *hdr;
480 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
484 tdb_convert(tdb, (void *)ret, len);
487 tdb->direct_access++;
492 void *tdb_access_write(struct tdb_context *tdb,
493 tdb_off_t off, tdb_len_t len, bool convert)
497 if (tdb->read_only) {
498 tdb->ecode = TDB_ERR_RDONLY;
502 if (likely(!(tdb->flags & TDB_CONVERT)))
503 ret = tdb->methods->direct(tdb, off, len);
506 struct tdb_access_hdr *hdr;
507 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
511 hdr->convert = convert;
514 tdb_convert(tdb, (void *)ret, len);
517 tdb->direct_access++;
522 bool is_direct(const struct tdb_context *tdb, const void *p)
525 && (char *)p >= (char *)tdb->map_ptr
526 && (char *)p < (char *)tdb->map_ptr + tdb->map_size);
529 void tdb_access_release(struct tdb_context *tdb, const void *p)
531 if (is_direct(tdb, p))
532 tdb->direct_access--;
534 free((struct tdb_access_hdr *)p - 1);
537 int tdb_access_commit(struct tdb_context *tdb, void *p)
542 || (char *)p < (char *)tdb->map_ptr
543 || (char *)p >= (char *)tdb->map_ptr + tdb->map_size) {
544 struct tdb_access_hdr *hdr;
546 hdr = (struct tdb_access_hdr *)p - 1;
548 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
550 ret = tdb_write(tdb, hdr->off, p, hdr->len);
553 tdb->direct_access--;
558 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len)
560 if (unlikely(!tdb->map_ptr))
563 if (unlikely(tdb_oob(tdb, off + len, true) == -1))
565 return (char *)tdb->map_ptr + off;
568 void add_stat_(struct tdb_context *tdb, uint64_t *stat, size_t val)
570 if ((uintptr_t)stat < (uintptr_t)tdb->stats + tdb->stats->size)
574 static const struct tdb_methods io_methods = {
583 initialise the default methods table
585 void tdb_io_init(struct tdb_context *tdb)
587 tdb->methods = &io_methods;