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 /* Either make a copy into pad and return that, or return ptr into mmap. */
127 /* Note: pad has to be a real object, so we can't get here if len
128 * overflows size_t */
129 void *tdb_get(struct tdb_context *tdb, tdb_off_t off, void *pad, size_t len)
131 if (likely(!(tdb->flags & TDB_CONVERT))) {
132 void *ret = tdb->methods->direct(tdb, off, len);
136 return tdb_read_convert(tdb, off, pad, len) == -1 ? NULL : pad;
139 /* Endian conversion: we only ever deal with 8 byte quantities */
140 void *tdb_convert(const struct tdb_context *tdb, void *buf, tdb_len_t size)
142 if (unlikely((tdb->flags & TDB_CONVERT)) && buf) {
143 uint64_t i, *p = (uint64_t *)buf;
144 for (i = 0; i < size / 8; i++)
145 p[i] = bswap_64(p[i]);
150 /* FIXME: Return the off? */
151 uint64_t tdb_find_nonzero_off(struct tdb_context *tdb,
152 tdb_off_t base, uint64_t start, uint64_t end)
157 /* Zero vs non-zero is the same unconverted: minor optimization. */
158 val = tdb_access_read(tdb, base + start * sizeof(tdb_off_t),
159 (end - start) * sizeof(tdb_off_t), false);
163 for (i = 0; i < (end - start); i++) {
167 tdb_access_release(tdb, val);
171 /* Return first zero offset in num offset array, or num. */
172 uint64_t tdb_find_zero_off(struct tdb_context *tdb, tdb_off_t off,
178 /* Zero vs non-zero is the same unconverted: minor optimization. */
179 val = tdb_access_read(tdb, off, num * sizeof(tdb_off_t), false);
183 for (i = 0; i < num; i++) {
187 tdb_access_release(tdb, val);
191 int zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
193 char buf[8192] = { 0 };
194 void *p = tdb->methods->direct(tdb, off, len);
200 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
201 if (tdb->methods->write(tdb, off, buf, todo) == -1)
209 tdb_off_t tdb_read_off(struct tdb_context *tdb, tdb_off_t off)
213 ret = tdb_get(tdb, off, &pad, sizeof(pad));
220 /* Even on files, we can get partial writes due to signals. */
221 bool tdb_pwrite_all(int fd, const void *buf, size_t len, tdb_off_t off)
225 ret = pwrite(fd, buf, len, off);
232 buf = (char *)buf + ret;
239 /* Even on files, we can get partial reads due to signals. */
240 bool tdb_pread_all(int fd, void *buf, size_t len, tdb_off_t off)
244 ret = pread(fd, buf, len, off);
252 buf = (char *)buf + ret;
259 bool tdb_read_all(int fd, void *buf, size_t len)
263 ret = read(fd, buf, len);
271 buf = (char *)buf + ret;
277 /* write a lump of data at a specified offset */
278 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
279 const void *buf, tdb_len_t len)
285 if (tdb->read_only) {
286 tdb->ecode = TDB_ERR_RDONLY;
290 if (tdb->methods->oob(tdb, off + len, 0) != 0)
294 memcpy(off + (char *)tdb->map_ptr, buf, len);
296 if (!tdb_pwrite_all(tdb->fd, buf, len, off)) {
297 tdb->ecode = TDB_ERR_IO;
298 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
299 "tdb_write failed at %llu len=%llu (%s)\n",
300 (long long)off, (long long)len,
308 /* read a lump of data at a specified offset */
309 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
312 if (tdb->methods->oob(tdb, off + len, 0) != 0) {
317 memcpy(buf, off + (char *)tdb->map_ptr, len);
319 if (!tdb_pread_all(tdb->fd, buf, len, off)) {
320 /* Ensure ecode is set for log fn. */
321 tdb->ecode = TDB_ERR_IO;
322 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
323 "tdb_read failed at %lld "
324 "len=%lld (%s) map_size=%lld\n",
325 (long long)off, (long long)len,
327 (long long)tdb->map_size);
334 int tdb_write_convert(struct tdb_context *tdb, tdb_off_t off,
335 const void *rec, size_t len)
338 if (unlikely((tdb->flags & TDB_CONVERT))) {
339 void *conv = malloc(len);
341 tdb->ecode = TDB_ERR_OOM;
342 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
343 "tdb_write: no memory converting %zu bytes\n",
347 memcpy(conv, rec, len);
348 ret = tdb->methods->write(tdb, off,
349 tdb_convert(tdb, conv, len), len);
352 ret = tdb->methods->write(tdb, off, rec, len);
357 int tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
358 void *rec, size_t len)
360 int ret = tdb->methods->read(tdb, off, rec, len);
361 tdb_convert(tdb, rec, len);
365 int tdb_write_off(struct tdb_context *tdb, tdb_off_t off, tdb_off_t val)
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);
377 if (unlikely(!buf)) {
378 tdb->ecode = TDB_ERR_OOM;
379 tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
380 "tdb_alloc_read malloc failed len=%lld\n",
381 (long long)prefix + len);
382 } else if (unlikely(tdb->methods->read(tdb, offset, buf+prefix, len))) {
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->ecode = TDB_ERR_IO;
404 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
405 "fill write failed: giving up!\n");
414 /* expand a file. we prefer to use ftruncate, as that is what posix
415 says to use for mmap expansion */
416 static int tdb_expand_file(struct tdb_context *tdb, tdb_len_t addition)
420 if (tdb->read_only) {
421 tdb->ecode = TDB_ERR_RDONLY;
425 if (tdb->flags & TDB_INTERNAL) {
426 char *new = realloc(tdb->map_ptr, tdb->map_size + addition);
428 tdb->ecode = TDB_ERR_OOM;
432 tdb->map_size += addition;
434 /* Unmap before trying to write; old TDB claimed OpenBSD had
435 * problem with this otherwise. */
438 /* If this fails, we try to fill anyway. */
439 if (ftruncate(tdb->fd, tdb->map_size + addition))
442 /* now fill the file with something. This ensures that the
443 file isn't sparse, which would be very bad if we ran out of
444 disk. This must be done with write, not via mmap */
445 memset(buf, 0x43, sizeof(buf));
446 if (fill(tdb, buf, sizeof(buf), tdb->map_size, addition) == -1)
448 tdb->map_size += addition;
454 /* This is only neded for tdb_access_commit, but used everywhere to simplify. */
455 struct tdb_access_hdr {
461 const void *tdb_access_read(struct tdb_context *tdb,
462 tdb_off_t off, tdb_len_t len, bool convert)
464 const void *ret = NULL;
466 if (likely(!(tdb->flags & TDB_CONVERT)))
467 ret = tdb->methods->direct(tdb, off, len);
470 struct tdb_access_hdr *hdr;
471 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
475 tdb_convert(tdb, (void *)ret, len);
478 tdb->direct_access++;
483 void *tdb_access_write(struct tdb_context *tdb,
484 tdb_off_t off, tdb_len_t len, bool convert)
488 if (likely(!(tdb->flags & TDB_CONVERT)))
489 ret = tdb->methods->direct(tdb, off, len);
492 struct tdb_access_hdr *hdr;
493 hdr = _tdb_alloc_read(tdb, off, len, sizeof(*hdr));
497 hdr->convert = convert;
500 tdb_convert(tdb, (void *)ret, len);
503 tdb->direct_access++;
508 void tdb_access_release(struct tdb_context *tdb, const void *p)
511 || (char *)p < (char *)tdb->map_ptr
512 || (char *)p >= (char *)tdb->map_ptr + tdb->map_size)
513 free((struct tdb_access_hdr *)p - 1);
515 tdb->direct_access--;
518 int tdb_access_commit(struct tdb_context *tdb, void *p)
523 || (char *)p < (char *)tdb->map_ptr
524 || (char *)p >= (char *)tdb->map_ptr + tdb->map_size) {
525 struct tdb_access_hdr *hdr;
527 hdr = (struct tdb_access_hdr *)p - 1;
529 ret = tdb_write_convert(tdb, hdr->off, p, hdr->len);
531 ret = tdb_write(tdb, hdr->off, p, hdr->len);
534 tdb->direct_access--;
539 static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len)
541 if (unlikely(!tdb->map_ptr))
544 if (unlikely(tdb_oob(tdb, off + len, true) == -1))
546 return (char *)tdb->map_ptr + off;
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;