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 enum TDB_ERROR tdb_oob(struct tdb_context *tdb, tdb_off_t len,
76 /* We can't hold pointers during this: we could unmap! */
77 assert(!tdb->direct_access
78 || (tdb->flags & TDB_NOLOCK)
79 || tdb_has_expansion_lock(tdb));
81 if (len <= tdb->map_size)
83 if (tdb->flags & TDB_INTERNAL) {
85 tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
86 "tdb_oob len %lld beyond internal"
89 (long long)tdb->map_size);
94 ecode = tdb_lock_expand(tdb, F_RDLCK);
95 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 enum TDB_ERROR 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);
181 enum TDB_ERROR ecode = TDB_SUCCESS;
183 assert(!tdb->read_only);
189 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
190 ecode = tdb->methods->twrite(tdb, off, buf, todo);
191 if (ecode != TDB_SUCCESS) {
200 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
203 enum TDB_ERROR ecode;
205 if (likely(!(tdb->flags & TDB_CONVERT))) {
206 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
212 ecode = tdb_read_convert(tdb, off, &ret, sizeof(ret));
213 if (ecode != TDB_SUCCESS) {
220 /* write a lump of data at a specified offset */
221 static enum TDB_ERROR tdb_write(struct tdb_context *tdb, tdb_off_t off,
222 const void *buf, tdb_len_t len)
224 enum TDB_ERROR ecode;
226 if (tdb->read_only) {
227 return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
228 "Write to read-only database");
231 /* FIXME: Bogus optimization? */
236 ecode = tdb->methods->oob(tdb, off + len, 0);
237 if (ecode != TDB_SUCCESS) {
242 memcpy(off + (char *)tdb->map_ptr, buf, len);
245 ret = pwrite(tdb->fd, buf, len, off);
247 /* This shouldn't happen: we avoid sparse files. */
251 return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
252 "tdb_write: %zi at %zu len=%zu (%s)",
253 ret, (size_t)off, (size_t)len,
260 /* read a lump of data at a specified offset */
261 static enum TDB_ERROR tdb_read(struct tdb_context *tdb, tdb_off_t off,
262 void *buf, tdb_len_t len)
264 enum TDB_ERROR ecode;
266 ecode = tdb->methods->oob(tdb, off + len, 0);
267 if (ecode != TDB_SUCCESS) {
272 memcpy(buf, off + (char *)tdb->map_ptr, len);
274 ssize_t r = pread(tdb->fd, buf, len, off);
276 return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
277 "tdb_read failed with %zi at %zu "
278 "len=%zu (%s) map_size=%zu",
279 r, (size_t)off, (size_t)len,
281 (size_t)tdb->map_size);
287 enum TDB_ERROR tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
288 const void *rec, size_t len)
290 enum TDB_ERROR ecode;
292 if (unlikely((tdb->flags & TDB_CONVERT))) {
293 void *conv = malloc(len);
295 return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
296 "tdb_write: no memory converting"
299 memcpy(conv, rec, len);
300 ecode = tdb->methods->twrite(tdb, off,
301 tdb_convert(tdb, conv, len), len);
304 ecode = tdb->methods->twrite(tdb, off, rec, len);
309 enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
310 void *rec, size_t len)
312 enum TDB_ERROR ecode = tdb->methods->tread(tdb, off, rec, len);
313 tdb_convert(tdb, rec, len);
317 enum TDB_ERROR tdb_write_off(struct tdb_context *tdb,
318 tdb_off_t off, tdb_off_t val)
320 if (tdb->read_only) {
321 return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
322 "Write to read-only database");
325 if (likely(!(tdb->flags & TDB_CONVERT))) {
326 tdb_off_t *p = tdb->methods->direct(tdb, off, sizeof(*p),
333 return tdb_write_convert(tdb, off, &val, sizeof(val));
336 static void *_tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset,
337 tdb_len_t len, unsigned int prefix)
340 enum TDB_ERROR ecode;
342 /* some systems don't like zero length malloc */
343 buf = malloc(prefix + len ? prefix + len : 1);
345 tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_USE_ERROR,
346 "tdb_alloc_read malloc failed len=%zu",
347 (size_t)(prefix + len));
349 ecode = tdb->methods->tread(tdb, offset, buf+prefix, len);
350 if (unlikely(ecode != TDB_SUCCESS)) {
359 /* read a lump of data, allocating the space for it */
360 void *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
362 return _tdb_alloc_read(tdb, offset, len, 0);
365 static enum TDB_ERROR fill(struct tdb_context *tdb,
366 const void *buf, size_t size,
367 tdb_off_t off, tdb_len_t len)
370 size_t n = len > size ? size : len;
371 ssize_t ret = pwrite(tdb->fd, buf, n, off);
376 return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR,
378 " %zi at %zu len=%zu (%s)",
379 ret, (size_t)off, (size_t)len,
388 /* expand a file. we prefer to use ftruncate, as that is what posix
389 says to use for mmap expansion */
390 static enum TDB_ERROR tdb_expand_file(struct tdb_context *tdb,
394 enum TDB_ERROR ecode;
396 if (tdb->read_only) {
397 return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
398 "Expand on read-only database");
401 if (tdb->flags & TDB_INTERNAL) {
402 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
404 return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
405 "No memory to expand database");
408 tdb->map_size += addition;
410 /* Unmap before trying to write; old TDB claimed OpenBSD had
411 * problem with this otherwise. */
414 /* If this fails, we try to fill anyway. */
415 if (ftruncate(tdb->fd, tdb->map_size + addition))
418 /* now fill the file with something. This ensures that the
419 file isn't sparse, which would be very bad if we ran out of
420 disk. This must be done with write, not via mmap */
421 memset(buf, 0x43, sizeof(buf));
422 ecode = fill(tdb, buf, sizeof(buf), tdb->map_size, addition);
423 if (ecode != TDB_SUCCESS)
425 tdb->map_size += addition;
431 const void *tdb_access_read(struct tdb_context *tdb,
432 tdb_off_t off, tdb_len_t len, bool convert)
434 const void *ret = NULL;
436 if (likely(!(tdb->flags & TDB_CONVERT)))
437 ret = tdb->methods->direct(tdb, off, len, false);
440 struct tdb_access_hdr *hdr;
441 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
443 hdr->next = tdb->access;
447 tdb_convert(tdb, (void *)ret, len);
450 tdb->direct_access++;
455 void *tdb_access_write(struct tdb_context *tdb,
456 tdb_off_t off, tdb_len_t len, bool convert)
460 if (tdb->read_only) {
461 tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
462 "Write to read-only database");
466 if (likely(!(tdb->flags & TDB_CONVERT)))
467 ret = tdb->methods->direct(tdb, off, len, true);
470 struct tdb_access_hdr *hdr;
471 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
473 hdr->next = tdb->access;
477 hdr->convert = convert;
480 tdb_convert(tdb, (void *)ret, len);
483 tdb->direct_access++;
488 static struct tdb_access_hdr **find_hdr(struct tdb_context *tdb, const void *p)
490 struct tdb_access_hdr **hp;
492 for (hp = &tdb->access; *hp; hp = &(*hp)->next) {
499 void tdb_access_release(struct tdb_context *tdb, const void *p)
501 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
508 tdb->direct_access--;
511 enum TDB_ERROR tdb_access_commit(struct tdb_context *tdb, void *p)
513 struct tdb_access_hdr *hdr, **hp = find_hdr(tdb, p);
514 enum TDB_ERROR ecode;
519 ecode = tdb_write_convert(tdb, hdr->off, p, hdr->len);
521 ecode = tdb_write(tdb, hdr->off, p, hdr->len);
525 tdb->direct_access--;
532 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len,
535 if (unlikely(!tdb->map_ptr))
538 if (unlikely(tdb_oob(tdb, off + len, true) != TDB_SUCCESS))
540 return (char *)tdb->map_ptr + off;
543 void add_stat_(struct tdb_context *tdb, uint64_t *s, size_t val)
545 if ((uintptr_t)s < (uintptr_t)tdb->stats + tdb->stats->size)
549 static const struct tdb_methods io_methods = {
558 initialise the default methods table
560 void tdb_io_init(struct tdb_context *tdb)
562 tdb->methods = &io_methods;