diff options
-rw-r--r-- | source3/tdb/Makefile.in | 67 | ||||
-rw-r--r-- | source3/tdb/aclocal.m4 | 13 | ||||
-rwxr-xr-x | source3/tdb/autogen.sh | 10 | ||||
-rw-r--r-- | source3/tdb/common/freelist.c | 22 | ||||
-rw-r--r-- | source3/tdb/common/io.c | 44 | ||||
-rw-r--r-- | source3/tdb/common/lock.c | 152 | ||||
-rw-r--r-- | source3/tdb/common/open.c | 75 | ||||
-rw-r--r-- | source3/tdb/common/tdb.c | 19 | ||||
-rw-r--r-- | source3/tdb/common/tdb_private.h | 58 | ||||
-rw-r--r-- | source3/tdb/common/tdbutil.c | 29 | ||||
-rw-r--r-- | source3/tdb/common/transaction.c | 126 | ||||
-rw-r--r-- | source3/tdb/common/traverse.c | 28 | ||||
-rw-r--r-- | source3/tdb/config.mk | 3 | ||||
-rw-r--r-- | source3/tdb/include/tdb.h | 22 | ||||
-rw-r--r-- | source3/tdb/include/tdbutil.h | 2 | ||||
-rw-r--r-- | source3/tdb/tools/tdbtest.c | 53 | ||||
-rw-r--r-- | source3/tdb/tools/tdbtorture.c | 241 |
17 files changed, 558 insertions, 406 deletions
diff --git a/source3/tdb/Makefile.in b/source3/tdb/Makefile.in index 1c23aaeea7..e158752d43 100644 --- a/source3/tdb/Makefile.in +++ b/source3/tdb/Makefile.in @@ -1,54 +1,81 @@ +#!gmake # # Makefile for tdb directory # -CFLAGS = -Iinclude @CFLAGS@ CC = @CC@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ libdir = @libdir@ +VPATH = @srcdir@:@libreplacedir@ +srcdir = @srcdir@ +builddir = @builddir@ +CFLAGS = -I$(srcdir)/include -Iinclude -I@libreplacedir@ @CFLAGS@ + +.PHONY: test PROGS = bin/tdbtool bin/tdbtorture -TDB_OBJ = common/tdb.o common/dump.o common/io.o common/lock.o \ - common/open.o common/traverse.o common/freelist.o common/error.o \ - common/transaction.o +TDB_OBJ = @TDBOBJ@ @LIBREPLACEOBJ@ + +DIRS = bin common tools -all: $(PROGS) +all: showflags dirs $(PROGS) + +showflags: + @echo 'tdb will be compiled with flags:' + @echo ' CFLAGS = $(CFLAGS)' + @echo ' LIBS = $(LIBS)' .c.o: @echo Compiling $*.c + @mkdir -p `dirname $@` @$(CC) $(CFLAGS) -c $< -o $@ +dirs: + @mkdir -p $(DIRS) + install: all mkdir -p $(bindir) mkdir -p $(includedir) mkdir -p $(libdir) mkdir -p $(libdir)/pkgconfig cp $(PROGS) $(bindir) - cp include/tdb.h $(includedir) + cp $(srcdir)/include/tdb.h $(includedir) cp tdb.pc $(libdir)/pkgconfig -bin/tdbtest: tools/tdbtest.o $(TDB_OBJ) - $(CC) $(CFLAGS) -o bin/tdbtest tools/tdbtest.o $(TDB_OBJ) -lgdbm +libtdb.a: $(TDB_OBJ) + ar -rv libtdb.a $(TDB_OBJ) + +bin/tdbtest: tools/tdbtest.o libtdb.a + $(CC) $(CFLAGS) -o bin/tdbtest tools/tdbtest.o -L. -ltdb -lgdbm + +bin/tdbtool: tools/tdbtool.o libtdb.a + $(CC) $(CFLAGS) -o bin/tdbtool tools/tdbtool.o -L. -ltdb + +bin/tdbtorture: tools/tdbtorture.o libtdb.a + $(CC) $(CFLAGS) -o bin/tdbtorture tools/tdbtorture.o -L. -ltdb -bin/tdbtool: tools/tdbtool.o $(TDB_OBJ) - $(CC) $(CFLAGS) -o bin/tdbtool tools/tdbtool.o $(TDB_OBJ) +bin/tdbdump: tools/tdbdump.o libtdb.a + $(CC) $(CFLAGS) -o bin/tdbdump tools/tdbdump.o -L. -ltdb -bin/tdbtorture: tools/tdbtorture.o $(TDB_OBJ) - $(CC) $(CFLAGS) -o bin/tdbtorture tools/tdbtorture.o $(TDB_OBJ) +bin/tdbbackup: tools/tdbbackup.o libtdb.a + $(CC) $(CFLAGS) -o bin/tdbbackup tools/tdbbackup.o -L. -ltdb -bin/tdbdump: tools/tdbdump.o $(TDB_OBJ) - $(CC) $(CFLAGS) -o bin/tdbdump tools/tdbdump.o $(TDB_OBJ) +test: bin/tdbtorture + bin/tdbtorture -bin/tdbbackup: tools/tdbbackup.o $(TDB_OBJ) - $(CC) $(CFLAGS) -o bin/tdbbackup tools/tdbbackup.o $(TDB_OBJ) +installcheck: test install clean: - rm -f $(PROGS) common/*.o tools/*.o *~ *.bak */*~ */*.bak *% core test.db test.tdb test.gdbm + rm -f $(PROGS) *.o *.a common/*.o tools/*.o tdb.pc + rm -f test.db test.tdb torture.tdb test.gdbm -installcheck: install - $(bindir)/tdbtorture +distclean: clean + rm -f *~ */*~ + rm -f config.log config.status include/config.h config.cache + rm -f Makefile -test: installcheck +realdistclean: distclean + rm -f configure include/config.h.in diff --git a/source3/tdb/aclocal.m4 b/source3/tdb/aclocal.m4 index cdc2a2fa52..5605e476ba 100644 --- a/source3/tdb/aclocal.m4 +++ b/source3/tdb/aclocal.m4 @@ -1,12 +1 @@ -dnl see if a declaration exists for a function or variable -dnl defines HAVE_function_DECL if it exists -dnl AC_HAVE_DECL(var, includes) -AC_DEFUN(AC_HAVE_DECL, -[ - AC_CACHE_CHECK([for $1 declaration],ac_cv_have_$1_decl,[ - AC_TRY_COMPILE([$2],[int i = (int)$1], - ac_cv_have_$1_decl=yes,ac_cv_have_$1_decl=no)]) - if test x"$ac_cv_have_$1_decl" = x"yes"; then - AC_DEFINE([HAVE_]translit([$1], [a-z], [A-Z])[_DECL],1,[Whether $1() is available]) - fi -]) +m4_include(libreplace.m4) diff --git a/source3/tdb/autogen.sh b/source3/tdb/autogen.sh index 89053ad9f8..bf84eeee19 100755 --- a/source3/tdb/autogen.sh +++ b/source3/tdb/autogen.sh @@ -1,7 +1,13 @@ #!/bin/sh -autoheader || exit 1 -autoconf || exit 1 +rm -rf autom4te.cache +rm -f configure config.h.in + +IPATHS="-I libreplace -I lib/replace -I ../libreplace -I ../replace" +autoconf $IPATHS || exit 1 +autoheader $IPATHS || exit 1 + +rm -rf autom4te.cache echo "Now run ./configure and then make." exit 0 diff --git a/source3/tdb/common/freelist.c b/source3/tdb/common/freelist.c index 3483751164..9d1ae59801 100644 --- a/source3/tdb/common/freelist.c +++ b/source3/tdb/common/freelist.c @@ -37,7 +37,7 @@ static int rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_str if (rec->magic == TDB_MAGIC) { /* this happens when a app is showdown while deleting a record - we should not completely fail when this happens */ - TDB_LOG((tdb, 0,"rec_free_read non-free magic 0x%x at offset=%d - fixing\n", + TDB_LOG((tdb, TDB_DEBUG_WARNING, "rec_free_read non-free magic 0x%x at offset=%d - fixing\n", rec->magic, off)); rec->magic = TDB_FREE_MAGIC; if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1) @@ -47,7 +47,7 @@ static int rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_str if (rec->magic != TDB_FREE_MAGIC) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; - TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", + TDB_LOG((tdb, TDB_DEBUG_WARNING, "rec_free_read bad magic 0x%x at offset=%d\n", rec->magic, off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } @@ -73,7 +73,7 @@ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_ /* Follow chain (next offset is at start of record) */ last_ptr = i; } - TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off)); + TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } @@ -102,7 +102,7 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) /* set an initial tailer, so if we fail we don't leave a bogus record */ if (update_tailer(tdb, offset, rec) != 0) { - TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed!\n")); goto fail; } @@ -112,14 +112,14 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) struct list_struct r; if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { - TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right)); goto left; } /* If it's free, expand to include it. */ if (r.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, right, r.next) == -1) { - TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right free failed at %u\n", right)); goto left; } rec->rec_len += sizeof(r) + r.rec_len; @@ -135,7 +135,7 @@ left: /* Read in tailer and jump back to header */ if (tdb_ofs_read(tdb, left, &leftsize) == -1) { - TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left offset read failed at %u\n", left)); goto update; } @@ -148,14 +148,14 @@ left: /* Now read in record */ if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { - TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); goto update; } /* If it's free, expand to include it. */ if (l.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, left, l.next) == -1) { - TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left)); goto update; } else { offset = left; @@ -166,7 +166,7 @@ left: update: if (update_tailer(tdb, offset, rec) == -1) { - TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); goto fail; } @@ -176,7 +176,7 @@ update: if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || tdb_rec_write(tdb, offset, rec) == -1 || tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) { - TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset)); goto fail; } diff --git a/source3/tdb/common/io.c b/source3/tdb/common/io.c index 5d7edfefff..9a6084888d 100644 --- a/source3/tdb/common/io.c +++ b/source3/tdb/common/io.c @@ -29,28 +29,6 @@ #include "tdb_private.h" -#ifndef HAVE_PREAD - static ssize_t pread(int fd, void *buf, size_t count, off_t offset) -{ - if (lseek(fd, offset, SEEK_SET) != offset) { - errno = EIO; - return -1; - } - return read(fd, buf, count); -} -#endif - -#ifndef HAVE_PWRITE - static ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) -{ - if (lseek(fd, offset, SEEK_SET) != offset) { - errno = EIO; - return -1; - } - return write(fd, buf, count); -} -#endif - /* check for an out of bounds access - if it is out of bounds then see if the database has been expanded by someone else and expand if necessary @@ -65,7 +43,7 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n", + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n", (int)len, (int)tdb->map_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); @@ -79,7 +57,7 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n", + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n", (int)len, (int)st.st_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); @@ -114,7 +92,7 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off, } else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n", + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); } @@ -146,8 +124,8 @@ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, if (ret != (ssize_t)len) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d ret=%d (%s) map_size=%d\n", - off, len, (int)ret, strerror(errno), (int)tdb->map_size)); + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d len=%d ret=%d (%s) map_size=%d\n", + off, len, ret, strerror(errno), (int)tdb->map_size)); return TDB_ERRCODE(TDB_ERR_IO, -1); } } @@ -217,7 +195,7 @@ void tdb_mmap(struct tdb_context *tdb) if (tdb->map_ptr == MAP_FAILED) { tdb->map_ptr = NULL; - TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", tdb->map_size, strerror(errno))); } } else { @@ -242,7 +220,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad if (ftruncate(tdb->fd, size+addition) == -1) { char b = 0; if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { - TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", size+addition, strerror(errno))); return -1; } @@ -256,7 +234,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad int n = addition>sizeof(buf)?sizeof(buf):addition; int ret = pwrite(tdb->fd, buf, n, size); if (ret != n) { - TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of %d failed (%s)\n", n, strerror(errno))); return -1; } @@ -275,7 +253,7 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size) tdb_off_t offset; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { - TDB_LOG((tdb, 0, "lock failed in tdb_expand\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n")); return -1; } @@ -364,7 +342,7 @@ char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len) if (!(buf = (char *)malloc(len))) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_OOM; - TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n", + TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n", len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_OOM, buf); } @@ -383,7 +361,7 @@ int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct * if (TDB_BAD_MAGIC(rec)) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; - TDB_LOG((tdb, 0,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0); diff --git a/source3/tdb/common/lock.c b/source3/tdb/common/lock.c index a96d77a229..a5bff2d0b3 100644 --- a/source3/tdb/common/lock.c +++ b/source3/tdb/common/lock.c @@ -36,8 +36,8 @@ note that a len of zero means lock to end of file */ -int tdb_brlock_len(struct tdb_context *tdb, tdb_off_t offset, - int rw_type, int lck_type, int probe, size_t len) +int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, + int rw_type, int lck_type, int probe, size_t len) { struct flock fl; int ret; @@ -68,7 +68,7 @@ int tdb_brlock_len(struct tdb_context *tdb, tdb_off_t offset, if (!probe && lck_type != F_SETLK) { /* Ensure error code is set for log fun to examine. */ tdb->ecode = TDB_ERR_LOCK; - TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", + TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", tdb->fd, offset, rw_type, lck_type, (int)len)); } return TDB_ERRCODE(TDB_ERR_LOCK, -1); @@ -88,7 +88,7 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len) int count = 1000; while (count--) { struct timeval tv; - if (tdb_brlock_len(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) { + if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) { return 0; } if (errno != EDEADLK) { @@ -99,27 +99,26 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len) tv.tv_usec = 1; select(0, NULL, NULL, NULL, &tv); } - TDB_LOG((tdb, 5,"tdb_brlock_upgrade failed at offset %d\n", offset)); + TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset)); return -1; } -/* a byte range locking function - return 0 on success - this functions locks/unlocks 1 byte at the specified offset. - - On error, errno is also set so that errors are passed back properly - through tdb_open(). */ -int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, - int rw_type, int lck_type, int probe) -{ - return tdb_brlock_len(tdb, offset, rw_type, lck_type, probe, 1); -} - /* lock a list in the database. list -1 is the alloc list */ int tdb_lock(struct tdb_context *tdb, int list, int ltype) { + /* a global lock allows us to avoid per chain locks */ + if (tdb->global_lock.count && + (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { + return 0; + } + + if (tdb->global_lock.count) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + if (list < -1 || list >= (int)tdb->header.hash_size) { - TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", + TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n", list, ltype)); return -1; } @@ -129,8 +128,8 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype) /* Since fcntl locks don't nest, we do a lock for the first one, and simply bump the count for future ones */ if (tdb->locked[list+1].count == 0) { - if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) { - TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", + if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0, 1)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d ltype=%d (%s)\n", list, ltype, strerror(errno))); return -1; } @@ -148,23 +147,33 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype) { int ret = -1; + /* a global lock allows us to avoid per chain locks */ + if (tdb->global_lock.count && + (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { + return 0; + } + + if (tdb->global_lock.count) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + if (tdb->flags & TDB_NOLOCK) return 0; /* Sanity checks */ if (list < -1 || list >= (int)tdb->header.hash_size) { - TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); return ret; } if (tdb->locked[list+1].count==0) { - TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); return ret; } if (tdb->locked[list+1].count == 1) { /* Down to last nested lock: unlock underneath */ - ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0); + ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0, 1); tdb->num_locks--; } else { ret = 0; @@ -172,40 +181,97 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype) tdb->locked[list+1].count--; if (ret) - TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n")); return ret; } /* lock/unlock entire database */ -int tdb_lockall(struct tdb_context *tdb) +static int _tdb_lockall(struct tdb_context *tdb, int ltype) { - u32 i; - /* There are no locks on read-only dbs */ if (tdb->read_only || tdb->traverse_read) return TDB_ERRCODE(TDB_ERR_LOCK, -1); - for (i = 0; i < tdb->header.hash_size; i++) - if (tdb_lock(tdb, i, F_WRLCK)) - break; - /* If error, release locks we have... */ - if (i < tdb->header.hash_size) { - u32 j; + if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) { + tdb->global_lock.count++; + return 0; + } + + if (tdb->global_lock.count) { + /* a global lock of a different type exists */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->num_locks != 0) { + /* can't combine global and chain locks */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, F_SETLKW, + 0, 4*tdb->header.hash_size)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno))); + return -1; + } + + tdb->global_lock.count = 1; + tdb->global_lock.ltype = ltype; + + return 0; +} + +/* unlock entire db */ +static int _tdb_unlockall(struct tdb_context *tdb, int ltype) +{ + /* There are no locks on read-only dbs */ + if (tdb->read_only || tdb->traverse_read) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } - for ( j = 0; j < i; j++) - tdb_unlock(tdb, j, F_WRLCK); - return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); + if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->global_lock.count > 1) { + tdb->global_lock.count--; + return 0; } + if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, + 0, 4*tdb->header.hash_size)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno))); + return -1; + } + + tdb->global_lock.count = 0; + tdb->global_lock.ltype = 0; + return 0; } -void tdb_unlockall(struct tdb_context *tdb) + +/* lock entire database with write lock */ +int tdb_lockall(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK); +} + +/* unlock entire database with write lock */ +int tdb_unlockall(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_WRLCK); +} + +/* lock entire database with read lock */ +int tdb_lockall_read(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_RDLCK); +} + +/* unlock entire database with read lock */ +int tdb_unlockall_read(struct tdb_context *tdb) { - u32 i; - for (i=0; i < tdb->header.hash_size; i++) - tdb_unlock(tdb, i, F_WRLCK); + return _tdb_unlockall(tdb, F_RDLCK); } /* lock/unlock one hash chain. This is meant to be used to reduce @@ -235,7 +301,7 @@ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) /* record lock stops delete underneath */ int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off) { - return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0; + return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0; } /* @@ -249,7 +315,7 @@ int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off) for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) return -1; - return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1); + return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1); } /* @@ -258,7 +324,7 @@ int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off) */ int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off) { - return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0); + return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1); } /* fcntl locks don't stack: avoid unlocking someone else's */ @@ -272,5 +338,5 @@ int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off) for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) count++; - return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0); + return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0); } diff --git a/source3/tdb/common/open.c b/source3/tdb/common/open.c index b53b4c9f5c..e1f21aa856 100644 --- a/source3/tdb/common/open.c +++ b/source3/tdb/common/open.c @@ -123,15 +123,16 @@ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, } /* a default logging function */ -static void null_log_fn(struct tdb_context *tdb, int level, const char *fmt, ...) +static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); +static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) { } struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, - int open_flags, mode_t mode, - tdb_log_func log_fn, - tdb_hash_func hash_fn) + int open_flags, mode_t mode, + const struct tdb_logging_context *log_ctx, + tdb_hash_func hash_fn) { struct tdb_context *tdb; struct stat st; @@ -150,7 +151,12 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, tdb->map_ptr = NULL; tdb->flags = tdb_flags; tdb->open_flags = open_flags; - tdb->log_fn = log_fn?log_fn:null_log_fn; + if (log_ctx) { + tdb->log = *log_ctx; + } else { + tdb->log.log_fn = null_log_fn; + tdb->log.log_private = NULL; + } tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; /* cache the page size */ @@ -160,7 +166,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, } if ((open_flags & O_ACCMODE) == O_WRONLY) { - TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n", + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n", name)); errno = EINVAL; goto fail; @@ -180,31 +186,31 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); tdb->flags &= ~TDB_CLEAR_IF_FIRST; if (tdb_new_database(tdb, hash_size) != 0) { - TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!")); goto fail; } goto internal; } if ((tdb->fd = open(name, open_flags, mode)) == -1) { - TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n", + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_open_ex: could not open file %s: %s\n", name, strerror(errno))); goto fail; /* errno set by open(2) */ } /* ensure there is only one process initialising at once */ - if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n", + if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n", name, strerror(errno))); goto fail; /* errno set by tdb_brlock */ } /* we need to zero database if we are the only one with it open */ if ((tdb_flags & TDB_CLEAR_IF_FIRST) && - (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) { + (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0))) { open_flags |= O_CREAT; if (ftruncate(tdb->fd, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_open_ex: " + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " "failed to truncate %s: %s\n", name, strerror(errno))); goto fail; /* errno set by ftruncate */ @@ -236,13 +242,13 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, goto fail; if (tdb->header.rwlocks != 0) { - TDB_LOG((tdb, 5, "tdb_open_ex: spinlocks no longer supported\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n")); goto fail; } /* Is it already in the open list? If so, fail. */ if (tdb_already_open(st.st_dev, st.st_ino)) { - TDB_LOG((tdb, 2, "tdb_open_ex: " + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " "%s (%d,%d) is already open in this process\n", name, (int)st.st_dev, (int)st.st_ino)); errno = EBUSY; @@ -260,7 +266,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, tdb->locked = (struct tdb_lock_type *)calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0])); if (!tdb->locked) { - TDB_LOG((tdb, 2, "tdb_open_ex: " + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " "failed to allocate lock structure for %s\n", name)); errno = ENOMEM; @@ -268,8 +274,8 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, } tdb_mmap(tdb); if (locked) { - if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_open_ex: " + if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " "failed to take ACTIVE_LOCK on %s: %s\n", name, strerror(errno))); goto fail; @@ -283,7 +289,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, if (tdb_flags & TDB_CLEAR_IF_FIRST) { /* leave this lock in place to indicate it's in use */ - if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) + if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1) goto fail; } @@ -296,7 +302,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, /* Internal (memory-only) databases skip all the code above to * do with disk files, and resume here by releasing their * global lock and hooking into the active list. */ - if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1) + if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1) goto fail; tdb->next = tdbs; tdbs = tdb; @@ -317,7 +323,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, SAFE_FREE(tdb->name); if (tdb->fd != -1) if (close(tdb->fd) != 0) - TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n")); SAFE_FREE(tdb->locked); SAFE_FREE(tdb); errno = save_errno; @@ -365,11 +371,16 @@ int tdb_close(struct tdb_context *tdb) } /* register a loging function */ -void tdb_logging_function(struct tdb_context *tdb, void (*fn)(struct tdb_context *, int , const char *, ...)) +void tdb_set_logging_function(struct tdb_context *tdb, + const struct tdb_logging_context *log) { - tdb->log_fn = fn?fn:null_log_fn; + tdb->log = *log; } +void *tdb_get_logging_private(struct tdb_context *tdb) +{ + return tdb->log.log_private; +} /* reopen a tdb - this can be used after a fork to ensure that we have an independent seek pointer from our parent and to re-establish locks */ @@ -381,38 +392,38 @@ int tdb_reopen(struct tdb_context *tdb) return 0; /* Nothing to do. */ } - if (tdb->num_locks != 0) { - TDB_LOG((tdb, 0, "tdb_reopen: reopen not allowed with locks held\n")); + if (tdb->num_locks != 0 || tdb->global_lock.count) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n")); goto fail; } if (tdb->transaction != 0) { - TDB_LOG((tdb, 0, "tdb_reopen: reopen not allowed inside a transaction\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n")); goto fail; } if (tdb_munmap(tdb) != 0) { - TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); goto fail; } if (close(tdb->fd) != 0) - TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n")); tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0); if (tdb->fd == -1) { - TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno))); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno))); goto fail; } if ((tdb->flags & TDB_CLEAR_IF_FIRST) && - (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)) { - TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n")); + (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n")); goto fail; } if (fstat(tdb->fd, &st) != 0) { - TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); goto fail; } if (st.st_ino != tdb->inode || st.st_dev != tdb->device) { - TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n")); goto fail; } tdb_mmap(tdb); diff --git a/source3/tdb/common/tdb.c b/source3/tdb/common/tdb.c index 524d404a50..addb5b4378 100644 --- a/source3/tdb/common/tdb.c +++ b/source3/tdb/common/tdb.c @@ -42,7 +42,7 @@ static void tdb_increment_seqnum(struct tdb_context *tdb) return; } - if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1) != 0) { + if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) { return; } @@ -53,7 +53,7 @@ static void tdb_increment_seqnum(struct tdb_context *tdb) seqnum++; tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum); - tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1); + tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1); } @@ -236,7 +236,7 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) } if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) - TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n")); return ret; } @@ -403,7 +403,7 @@ int tdb_fd(struct tdb_context *tdb) */ tdb_log_func tdb_log_fn(struct tdb_context *tdb) { - return tdb->log_fn; + return tdb->log.log_fn; } @@ -429,3 +429,14 @@ int tdb_hash_size(struct tdb_context *tdb) { return tdb->header.hash_size; } + +size_t tdb_map_size(struct tdb_context *tdb) +{ + return tdb->map_size; +} + +int tdb_get_flags(struct tdb_context *tdb) +{ + return tdb->flags; +} + diff --git a/source3/tdb/common/tdb_private.h b/source3/tdb/common/tdb_private.h index d4f94048a4..cd57365434 100644 --- a/source3/tdb/common/tdb_private.h +++ b/source3/tdb/common/tdb_private.h @@ -24,42 +24,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _SAMBA_BUILD_ -#include <stdlib.h> -#include <stdio.h> -#ifdef HAVE_STDINT_H -#include <stdint.h> -#endif -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/mman.h> -#include <sys/stat.h> -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "system/shmem.h" +#include "system/select.h" #include "tdb.h" -#ifndef HAVE_PREAD_DECL -ssize_t pread(int fd, void *buf, size_t count, off_t offset); -#endif -#ifndef HAVE_PWRITE_DECL -ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); -#endif - -#else -#include "includes.h" -#undef malloc -#undef realloc -#undef calloc -#undef strdup -#endif - #ifndef u32 #define u32 unsigned #endif @@ -100,21 +71,13 @@ typedef u32 tdb_off_t; /* NB assumes there is a local variable called "tdb" that is the * current context, also takes doubly-parenthesized print-style * argument. */ -#define TDB_LOG(x) tdb->log_fn x +#define TDB_LOG(x) tdb->log.log_fn x /* lock offsets */ #define GLOBAL_LOCK 0 #define ACTIVE_LOCK 4 #define TRANSACTION_LOCK 8 -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif - -#ifndef MAP_FAILED -#define MAP_FAILED ((void *)-1) -#endif - /* free memory if the pointer is valid and zero the pointer */ #ifndef SAFE_FREE #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) @@ -178,7 +141,7 @@ struct tdb_methods { void (*next_hash_chain)(struct tdb_context *, u32 *); int (*tdb_oob)(struct tdb_context *, tdb_off_t , int ); int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t ); - int (*tdb_brlock)(struct tdb_context *, tdb_off_t , int, int, int); + int (*tdb_brlock)(struct tdb_context *, tdb_off_t , int, int, int, size_t); }; struct tdb_context { @@ -188,6 +151,7 @@ struct tdb_context { tdb_len_t map_size; /* how much space has been mapped */ int read_only; /* opened read-only */ int traverse_read; /* read-only traversal */ + struct tdb_lock_type global_lock; struct tdb_lock_type *locked; /* array of chain locks */ enum TDB_ERROR ecode; /* error code for last tdb error */ struct tdb_header header; /* a cached copy of the header */ @@ -196,7 +160,7 @@ struct tdb_context { struct tdb_context *next; /* all tdbs to avoid multiple opens */ dev_t device; /* uniquely identifies this tdb */ ino_t inode; /* uniquely identifies this tdb */ - void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...) PRINTF_ATTRIBUTE(3,4); /* logging function */ + struct tdb_logging_context log; unsigned int (*hash_fn)(TDB_DATA *key); int open_flags; /* flags used in the open - needed by reopen */ unsigned int num_locks; /* number of chain locks held */ @@ -213,10 +177,8 @@ int tdb_munmap(struct tdb_context *tdb); void tdb_mmap(struct tdb_context *tdb); int tdb_lock(struct tdb_context *tdb, int list, int ltype); int tdb_unlock(struct tdb_context *tdb, int list, int ltype); -int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe); +int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); -int tdb_brlock_len(struct tdb_context *tdb, tdb_off_t offset, - int rw_type, int lck_type, int probe, size_t len); int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); diff --git a/source3/tdb/common/tdbutil.c b/source3/tdb/common/tdbutil.c index 45e1891324..3e18c09fbf 100644 --- a/source3/tdb/common/tdbutil.c +++ b/source3/tdb/common/tdbutil.c @@ -19,8 +19,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "tdb_private.h" -#include <fnmatch.h> +#include "includes.h" +#undef malloc +#undef realloc +#undef calloc +#undef strdup /*************************************************************** Allow a caller to set a "alarm" flag that tdb can check to abort @@ -91,7 +94,7 @@ static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); if (gotalarm) { DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n", - timeout, key.dptr, tdb->name )); + timeout, key.dptr, tdb_name(tdb))); /* TODO: If we time out waiting for a lock, it might * be nice to use F_GETLK to get the pid of the * process currently holding the lock and print that @@ -657,7 +660,7 @@ int tdb_unpack(char *buf, int bufsize, const char *fmt, ...) Log tdb messages via DEBUG(). ****************************************************************************/ -static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) +static void tdb_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, const char *format, ...) { va_list ap; char *ptr = NULL; @@ -669,7 +672,7 @@ static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) if (!ptr || !*ptr) return; - DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr)); + DEBUG((int)level, ("tdb(%s): %s", tdb_name(tdb) ? tdb_name(tdb) : "unnamed", ptr)); SAFE_FREE(ptr); } @@ -682,12 +685,16 @@ TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode) { TDB_CONTEXT *tdb; + struct tdb_logging_context log_ctx; if (!lp_use_mmap()) tdb_flags |= TDB_NOMMAP; + log_ctx.log_fn = tdb_log; + log_ctx.log_private = NULL; + tdb = tdb_open_ex(name, hash_size, tdb_flags, - open_flags, mode, tdb_log, NULL); + open_flags, mode, &log_ctx, NULL); if (!tdb) return NULL; @@ -773,16 +780,6 @@ void tdb_search_list_free(TDB_LIST_NODE* node) }; } -size_t tdb_map_size(struct tdb_context *tdb) -{ - return tdb->map_size; -} - -int tdb_get_flags(struct tdb_context *tdb) -{ - return tdb->flags; -} - /**************************************************************************** tdb_store, wrapped in a transaction. This way we make sure that a process that dies within writing does not leave a corrupt tdb behind. diff --git a/source3/tdb/common/transaction.c b/source3/tdb/common/transaction.c index 8e21e3ad87..a7ce5e3f61 100644 --- a/source3/tdb/common/transaction.c +++ b/source3/tdb/common/transaction.c @@ -183,7 +183,7 @@ static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, return tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv); fail: - TDB_LOG((tdb, 0, "transaction_read: failed at off=%d len=%d\n", off, len)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len)); tdb->ecode = TDB_ERR_IO; tdb->transaction->transaction_error = 1; return -1; @@ -308,7 +308,7 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, return 0; fail: - TDB_LOG((tdb, 0, "transaction_write: failed at off=%d len=%d\n", off, len)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", off, len)); tdb->ecode = TDB_ERR_IO; tdb->transaction->transaction_error = 1; return -1; @@ -359,7 +359,7 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, brlock during a transaction - ignore them */ int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, - int rw_type, int lck_type, int probe) + int rw_type, int lck_type, int probe, size_t len) { return 0; } @@ -382,7 +382,7 @@ int tdb_transaction_start(struct tdb_context *tdb) { /* some sanity checks */ if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) { - TDB_LOG((tdb, 0, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n")); tdb->ecode = TDB_ERR_EINVAL; return -1; } @@ -390,16 +390,16 @@ int tdb_transaction_start(struct tdb_context *tdb) /* cope with nested tdb_transaction_start() calls */ if (tdb->transaction != NULL) { tdb->transaction->nesting++; - TDB_LOG((tdb, 0, "tdb_transaction_start: nesting %d\n", + TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", tdb->transaction->nesting)); return 0; } - if (tdb->num_locks != 0) { + if (tdb->num_locks != 0 || tdb->global_lock.count) { /* the caller must not have any locks when starting a transaction as otherwise we'll be screwed by lack of nested locks in posix */ - TDB_LOG((tdb, 0, "tdb_transaction_start: cannot start a transaction with locks held\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } @@ -408,7 +408,7 @@ int tdb_transaction_start(struct tdb_context *tdb) /* you cannot use transactions inside a traverse (although you can use traverse inside a transaction) as otherwise you can end up with deadlock */ - TDB_LOG((tdb, 0, "tdb_transaction_start: cannot start a transaction within a traverse\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } @@ -423,8 +423,8 @@ int tdb_transaction_start(struct tdb_context *tdb) /* get the transaction write lock. This is a blocking lock. As discussed with Volker, there are a number of ways we could make this async, which we will probably do in the future */ - if (tdb_brlock_len(tdb, TRANSACTION_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_start: failed to get transaction lock\n")); + if (tdb_brlock(tdb, TRANSACTION_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get transaction lock\n")); tdb->ecode = TDB_ERR_LOCK; SAFE_FREE(tdb->transaction); return -1; @@ -432,23 +432,23 @@ int tdb_transaction_start(struct tdb_context *tdb) /* get a read lock from the freelist to the end of file. This is upgraded to a write lock during the commit */ - if (tdb_brlock_len(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_start: failed to get hash locks\n")); + if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n")); tdb->ecode = TDB_ERR_LOCK; goto fail; } /* setup a copy of the hash table heads so the hash scan in traverse can be fast */ - tdb->transaction->hash_heads = (unsigned int *) - calloc(tdb->header.hash_size+1, sizeof(tdb_off_t)); + tdb->transaction->hash_heads = (u32 *) + calloc(tdb->header.hash_size+1, sizeof(u32)); if (tdb->transaction->hash_heads == NULL) { tdb->ecode = TDB_ERR_OOM; goto fail; } if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads, TDB_HASHTABLE_SIZE(tdb), 0) != 0) { - TDB_LOG((tdb, 0, "tdb_transaction_start: failed to read hash heads\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n")); tdb->ecode = TDB_ERR_IO; goto fail; } @@ -467,7 +467,7 @@ int tdb_transaction_start(struct tdb_context *tdb) transaction linked list due to hash table updates */ if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads, TDB_HASHTABLE_SIZE(tdb)) != 0) { - TDB_LOG((tdb, 0, "tdb_transaction_start: failed to prime hash table\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to prime hash table\n")); tdb->ecode = TDB_ERR_IO; goto fail; } @@ -475,8 +475,8 @@ int tdb_transaction_start(struct tdb_context *tdb) return 0; fail: - tdb_brlock_len(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); - tdb_brlock_len(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); + tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); SAFE_FREE(tdb->transaction->hash_heads); SAFE_FREE(tdb->transaction); return -1; @@ -489,7 +489,7 @@ fail: int tdb_transaction_cancel(struct tdb_context *tdb) { if (tdb->transaction == NULL) { - TDB_LOG((tdb, 0, "tdb_transaction_cancel: no transaction\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n")); return -1; } @@ -509,12 +509,18 @@ int tdb_transaction_cancel(struct tdb_context *tdb) free(el); } + /* remove any global lock created during the transaction */ + if (tdb->global_lock.count != 0) { + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size); + tdb->global_lock.count = 0; + } + /* remove any locks created during the transaction */ if (tdb->num_locks != 0) { int h; for (h=0;h<tdb->header.hash_size+1;h++) { if (tdb->locked[h].count != 0) { - tdb_brlock_len(tdb,FREELIST_TOP+4*h,F_UNLCK,F_SETLKW, 0, 1); + tdb_brlock(tdb,FREELIST_TOP+4*h,F_UNLCK,F_SETLKW, 0, 1); tdb->locked[h].count = 0; } } @@ -524,8 +530,8 @@ int tdb_transaction_cancel(struct tdb_context *tdb) /* restore the normal io methods */ tdb->methods = tdb->transaction->io_methods; - tdb_brlock_len(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); - tdb_brlock_len(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); + tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); SAFE_FREE(tdb->transaction->hash_heads); SAFE_FREE(tdb->transaction); @@ -539,7 +545,7 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t { if (fsync(tdb->fd) != 0) { tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0, "tdb_transaction: fsync failed\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); return -1; } #ifdef MS_SYNC @@ -548,7 +554,7 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t if (msync(moffset + (char *)tdb->map_ptr, length + (offset - moffset), MS_SYNC) != 0) { tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0, "tdb_transaction: msync failed - %s\n", + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n", strerror(errno))); return -1; } @@ -591,7 +597,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, tdb_off_t recovery_head; if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { - TDB_LOG((tdb, 0, "tdb_recovery_allocate: failed to read recovery head\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n")); return -1; } @@ -599,7 +605,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, if (recovery_head != 0 && methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { - TDB_LOG((tdb, 0, "tdb_recovery_allocate: failed to read recovery record\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n")); return -1; } @@ -619,7 +625,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, the transaction) */ if (recovery_head != 0) { if (tdb_free(tdb, recovery_head, &rec) == -1) { - TDB_LOG((tdb, 0, "tdb_recovery_allocate: failed to free previous recovery area\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n")); return -1; } } @@ -635,7 +641,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, (tdb->map_size - tdb->transaction->old_map_size) + sizeof(rec) + *recovery_max_size) == -1) { - TDB_LOG((tdb, 0, "tdb_recovery_allocate: failed to create recovery area\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n")); return -1; } @@ -651,7 +657,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, CONVERT(recovery_head); if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD, &recovery_head, sizeof(tdb_off_t)) == -1) { - TDB_LOG((tdb, 0, "tdb_recovery_allocate: failed to write recovery head\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n")); return -1; } @@ -705,7 +711,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, continue; } if (el->offset + el->length > tdb->transaction->old_map_size) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: transaction data over new region boundary\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n")); free(data); tdb->ecode = TDB_ERR_CORRUPT; return -1; @@ -733,7 +739,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, /* write the recovery data to the recovery area */ if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: failed to write recovery data\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n")); free(data); tdb->ecode = TDB_ERR_IO; return -1; @@ -755,7 +761,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, *magic_offset = recovery_offset + offsetof(struct list_struct, magic); if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: failed to write recovery magic\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -778,14 +784,14 @@ int tdb_transaction_commit(struct tdb_context *tdb) u32 zero = 0; if (tdb->transaction == NULL) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: no transaction\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); return -1; } if (tdb->transaction->transaction_error) { tdb->ecode = TDB_ERR_IO; tdb_transaction_cancel(tdb); - TDB_LOG((tdb, 0, "tdb_transaction_commit: transaction error pending\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n")); return -1; } @@ -804,16 +810,16 @@ int tdb_transaction_commit(struct tdb_context *tdb) /* if there are any locks pending then the caller has not nested their locks properly, so fail the transaction */ - if (tdb->num_locks) { + if (tdb->num_locks || tdb->global_lock.count) { tdb->ecode = TDB_ERR_LOCK; - TDB_LOG((tdb, 0, "tdb_transaction_commit: locks pending on commit\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n")); tdb_transaction_cancel(tdb); return -1; } /* upgrade the main transaction lock region to a write lock */ if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_start: failed to upgrade hash locks\n")); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n")); tdb->ecode = TDB_ERR_LOCK; tdb_transaction_cancel(tdb); return -1; @@ -821,8 +827,8 @@ int tdb_transaction_commit(struct tdb_context *tdb) /* get the global lock - this prevents new users attaching to the database during the commit */ - if (tdb_brlock_len(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: failed to get global lock\n")); + if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n")); tdb->ecode = TDB_ERR_LOCK; tdb_transaction_cancel(tdb); return -1; @@ -831,8 +837,8 @@ int tdb_transaction_commit(struct tdb_context *tdb) if (!(tdb->flags & TDB_NOSYNC)) { /* write the recovery data to the end of the file */ if (transaction_setup_recovery(tdb, &magic_offset) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: failed to setup recovery data\n")); - tdb_brlock_len(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n")); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); tdb_transaction_cancel(tdb); return -1; } @@ -844,8 +850,8 @@ int tdb_transaction_commit(struct tdb_context *tdb) tdb->map_size - tdb->transaction->old_map_size) == -1) { tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, 0, "tdb_transaction_commit: expansion failed\n")); - tdb_brlock_len(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n")); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); tdb_transaction_cancel(tdb); return -1; } @@ -858,7 +864,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) struct tdb_transaction_el *el = tdb->transaction->elements; if (methods->tdb_write(tdb, el->offset, el->data, el->length) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: write failed during commit\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n")); /* we've overwritten part of the data and possibly expanded the file, so we need to @@ -867,9 +873,9 @@ int tdb_transaction_commit(struct tdb_context *tdb) tdb_transaction_recover(tdb); tdb_transaction_cancel(tdb); - tdb_brlock_len(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); - TDB_LOG((tdb, 0, "tdb_transaction_commit: write failed\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n")); return -1; } tdb->transaction->elements = el->next; @@ -885,7 +891,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) /* remove the recovery marker */ if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_commit: failed to remove recovery magic\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n")); return -1; } @@ -895,7 +901,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) } } - tdb_brlock_len(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); /* TODO: maybe write to some dummy hdr field, or write to magic @@ -933,7 +939,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) /* find the recovery area */ if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to read recovery head\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -946,7 +952,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) /* read the recovery record */ if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to read recovery record\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -957,7 +963,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) } if (tdb->read_only) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: attempt to recover read only database\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n")); tdb->ecode = TDB_ERR_CORRUPT; return -1; } @@ -966,7 +972,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) data = (unsigned char *)malloc(rec.data_len); if (data == NULL) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to allocate recovery data\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n")); tdb->ecode = TDB_ERR_OOM; return -1; } @@ -974,7 +980,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) /* read the full recovery data */ if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data, rec.data_len, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to read recovery data\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -991,7 +997,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) { free(data); - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs)); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs)); tdb->ecode = TDB_ERR_IO; return -1; } @@ -1001,7 +1007,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) free(data); if (transaction_sync(tdb, 0, tdb->map_size) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to sync recovery\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -1009,7 +1015,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) /* if the recovery area is after the recovered eof then remove it */ if (recovery_eof <= recovery_head) { if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to remove recovery head\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -1018,7 +1024,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) /* remove the recovery magic */ if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic), &zero) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to remove recovery magic\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -1026,7 +1032,7 @@ int tdb_transaction_recover(struct tdb_context *tdb) /* reduce the file size to the old size */ tdb_munmap(tdb); if (ftruncate(tdb->fd, recovery_eof) != 0) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to reduce to recovery size\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n")); tdb->ecode = TDB_ERR_IO; return -1; } @@ -1034,12 +1040,12 @@ int tdb_transaction_recover(struct tdb_context *tdb) tdb_mmap(tdb); if (transaction_sync(tdb, 0, recovery_eof) == -1) { - TDB_LOG((tdb, 0, "tdb_transaction_recover: failed to sync2 recovery\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n")); tdb->ecode = TDB_ERR_IO; return -1; } - TDB_LOG((tdb, 0, "tdb_transaction_recover: recovered %d byte database\n", + TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n", recovery_eof)); /* all done */ diff --git a/source3/tdb/common/traverse.c b/source3/tdb/common/traverse.c index 712504a764..6d3b111479 100644 --- a/source3/tdb/common/traverse.c +++ b/source3/tdb/common/traverse.c @@ -100,7 +100,7 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc /* Detect infinite loops. From "Shlomi Yaakobovich" <Shlomi@exanet.com>. */ if (tlock->off == rec->next) { - TDB_LOG((tdb, 0, "tdb_next_lock: loop detected.\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n")); goto fail; } @@ -127,7 +127,7 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc fail: tlock->off = 0; if (tdb_unlock(tdb, tlock->hash, tlock->lock_rw) != 0) - TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n")); return -1; } @@ -163,7 +163,7 @@ static int tdb_traverse_internal(struct tdb_context *tdb, if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) goto out; if (tdb_unlock_record(tdb, tl->off) != 0) - TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); goto out; } key.dsize = rec.key_len; @@ -180,7 +180,7 @@ static int tdb_traverse_internal(struct tdb_context *tdb, /* They want us to terminate traversal */ ret = count; if (tdb_unlock_record(tdb, tl->off) != 0) { - TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; ret = -1; } SAFE_FREE(key.dptr); @@ -208,8 +208,8 @@ int tdb_traverse_read(struct tdb_context *tdb, /* we need to get a read lock on the transaction lock here to cope with the lock ordering semantics of solaris10 */ - if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_RDLCK, F_SETLKW, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_traverse_read: failed to get transaction lock\n")); + if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_traverse_read: failed to get transaction lock\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } @@ -218,7 +218,7 @@ int tdb_traverse_read(struct tdb_context *tdb, ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_read--; - tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0); + tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); return ret; } @@ -237,15 +237,15 @@ int tdb_traverse(struct tdb_context *tdb, return tdb_traverse_read(tdb, fn, private_data); } - if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_WRLCK, F_SETLKW, 0) == -1) { - TDB_LOG((tdb, 0, "tdb_traverse: failed to get transaction lock\n")); + if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_traverse: failed to get transaction lock\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } ret = tdb_traverse_internal(tdb, fn, private_data, &tl); - tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0); + tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); return ret; } @@ -269,7 +269,7 @@ TDB_DATA tdb_firstkey(struct tdb_context *tdb) key.dsize = rec.key_len; key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0) - TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n")); return key; } @@ -311,7 +311,7 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) return tdb_null; tdb->travlocks.hash = BUCKET(rec.full_hash); if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) { - TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); return tdb_null; } } @@ -325,11 +325,11 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) key.dsize); /* Unlock the chain of this new record */ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) - TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); } /* Unlock the chain of old record */ if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0) - TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); return key; } diff --git a/source3/tdb/config.mk b/source3/tdb/config.mk index b1e6e82d74..c5d1a33990 100644 --- a/source3/tdb/config.mk +++ b/source3/tdb/config.mk @@ -8,8 +8,7 @@ OBJ_FILES = \ common/tdb.o common/dump.o common/io.o common/lock.o \ common/open.o common/traverse.o common/freelist.o \ common/error.o common/transaction.o common/tdbutil.o -PUBLIC_DEPENDENCIES = \ - LIBREPLACE +CFLAGS = -Ilib/tdb/include PUBLIC_HEADERS = include/tdb.h # # End SUBSYSTEM ldb diff --git a/source3/tdb/include/tdb.h b/source3/tdb/include/tdb.h index 82f8828c89..691c26af81 100644 --- a/source3/tdb/include/tdb.h +++ b/source3/tdb/include/tdb.h @@ -55,6 +55,10 @@ enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY}; +/* debugging uses one of the following levels */ +enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, + TDB_DEBUG_WARNING, TDB_DEBUG_TRACE}; + typedef struct TDB_DATA { char *dptr; size_t dsize; @@ -76,19 +80,24 @@ typedef struct TDB_DATA { typedef struct tdb_context TDB_CONTEXT; typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *); -typedef void (*tdb_log_func)(struct tdb_context *, int , const char *, ...); +typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_ATTRIBUTE(3, 4); typedef unsigned int (*tdb_hash_func)(TDB_DATA *key); +struct tdb_logging_context { + tdb_log_func log_fn; + void *log_private; +}; + struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode); struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, - tdb_log_func log_fn, + const struct tdb_logging_context *log_ctx, tdb_hash_func hash_fn); int tdb_reopen(struct tdb_context *tdb); int tdb_reopen_all(int parent_longlived); -void tdb_logging_function(struct tdb_context *tdb, tdb_log_func); +void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log); enum TDB_ERROR tdb_error(struct tdb_context *tdb); const char *tdb_errorstr(struct tdb_context *tdb); TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key); @@ -102,16 +111,21 @@ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *); int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *); int tdb_exists(struct tdb_context *tdb, TDB_DATA key); int tdb_lockall(struct tdb_context *tdb); -void tdb_unlockall(struct tdb_context *tdb); +int tdb_unlockall(struct tdb_context *tdb); +int tdb_lockall_read(struct tdb_context *tdb); +int tdb_unlockall_read(struct tdb_context *tdb); const char *tdb_name(struct tdb_context *tdb); int tdb_fd(struct tdb_context *tdb); tdb_log_func tdb_log_fn(struct tdb_context *tdb); +void *tdb_get_logging_private(struct tdb_context *tdb); int tdb_transaction_start(struct tdb_context *tdb); int tdb_transaction_commit(struct tdb_context *tdb); int tdb_transaction_cancel(struct tdb_context *tdb); int tdb_transaction_recover(struct tdb_context *tdb); int tdb_get_seqnum(struct tdb_context *tdb); int tdb_hash_size(struct tdb_context *tdb); +size_t tdb_map_size(struct tdb_context *tdb); +int tdb_get_flags(struct tdb_context *tdb); /* Low level locking functions: use with care */ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key); diff --git a/source3/tdb/include/tdbutil.h b/source3/tdb/include/tdbutil.h index 72d1505f4e..cb5d98fc52 100644 --- a/source3/tdb/include/tdbutil.h +++ b/source3/tdb/include/tdbutil.h @@ -67,7 +67,5 @@ int tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val); int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout); -int tdb_get_flags(struct tdb_context *tdb); -size_t tdb_map_size(struct tdb_context *tdb); #endif /* __TDBUTIL_H__ */ diff --git a/source3/tdb/tools/tdbtest.c b/source3/tdb/tools/tdbtest.c index 89295a3291..c7a09789fe 100644 --- a/source3/tdb/tools/tdbtest.c +++ b/source3/tdb/tools/tdbtest.c @@ -1,48 +1,43 @@ -#include <stdlib.h> -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <stdarg.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <signal.h> -#include "tdb.h" -#include <gdbm.h> - /* a test program for tdb - the trivial database */ +#include "replace.h" +#include "tdb.h" +#include "system/filesys.h" +#include "system/time.h" + +#include <gdbm.h> #define DELETE_PROB 7 #define STORE_PROB 5 -static TDB_CONTEXT *db; +static struct tdb_context *db; static GDBM_FILE gdbm; struct timeval tp1,tp2; -static void start_timer(void) +static void _start_timer(void) { gettimeofday(&tp1,NULL); } -static double end_timer(void) +static double _end_timer(void) { gettimeofday(&tp2,NULL); return((tp2.tv_sec - tp1.tv_sec) + (tp2.tv_usec - tp1.tv_usec)*1.0e-6); } -static void fatal(char *why) +static void fatal(const char *why) { perror(why); exit(1); } -static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) +#ifdef PRINTF_ATTRIBUTE +static void tdb_log(struct tdb_context *tdb, int level, const char *format, ...) PRINTF_ATTRIBUTE(3,4); +#endif +static void tdb_log(struct tdb_context *tdb, int level, const char *format, ...) { va_list ap; @@ -179,7 +174,7 @@ static void addrec_gdbm(void) free(d); } -static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { #if 0 printf("[%s] [%s]\n", key.dptr, dbuf.dptr); @@ -192,14 +187,15 @@ static void merge_test(void) { int i; char keys[5][2]; + char tdata[] = "test"; TDB_DATA key, data; for (i = 0; i < 5; i++) { - sprintf(keys[i], "%d", i); + snprintf(keys[i],2, "%d", i); key.dptr = keys[i]; key.dsize = 2; - data.dptr = "test"; + data.dptr = tdata; data.dsize = 4; if (tdb_store(db, key, data, TDB_REPLACE) != 0) { @@ -219,16 +215,17 @@ static void merge_test(void) tdb_delete(db, key); } -int main(int argc, char *argv[]) + int main(int argc, const char *argv[]) { int i, seed=0; int loops = 10000; + char test_gdbm[] = "test.gdbm"; unlink("test.gdbm"); db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT | O_TRUNC, 0600); - gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, + gdbm = gdbm_open(test_gdbm, 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, 0600, NULL); if (!db || !gdbm) { @@ -239,17 +236,17 @@ int main(int argc, char *argv[]) #if 1 srand(seed); - start_timer(); + _start_timer(); for (i=0;i<loops;i++) addrec_gdbm(); - printf("gdbm got %.2f ops/sec\n", i/end_timer()); + printf("gdbm got %.2f ops/sec\n", i/_end_timer()); #endif merge_test(); srand(seed); - start_timer(); + _start_timer(); for (i=0;i<loops;i++) addrec_db(); - printf("tdb got %.2f ops/sec\n", i/end_timer()); + printf("tdb got %.2f ops/sec\n", i/_end_timer()); compare_db(); diff --git a/source3/tdb/tools/tdbtorture.c b/source3/tdb/tools/tdbtorture.c index 2d367b91e1..14a2b48cdc 100644 --- a/source3/tdb/tools/tdbtorture.c +++ b/source3/tdb/tools/tdbtorture.c @@ -1,41 +1,43 @@ -#include <stdlib.h> -#include <time.h> -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <signal.h> -#include <stdarg.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> -#include "tdb.h" - /* this tests tdb by doing lots of ops from several simultaneous - writers - that stresses the locking code. Build with TDB_DEBUG=1 - for best effect */ + writers - that stresses the locking code. +*/ +#include "replace.h" +#include "tdb.h" +#include "system/time.h" +#include "system/wait.h" +#include "system/filesys.h" + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif #define REOPEN_PROB 30 #define DELETE_PROB 8 #define STORE_PROB 4 #define APPEND_PROB 6 -#define LOCKSTORE_PROB 0 +#define TRANSACTION_PROB 10 +#define LOCKSTORE_PROB 5 #define TRAVERSE_PROB 20 +#define TRAVERSE_READ_PROB 20 #define CULL_PROB 100 #define KEYLEN 3 #define DATALEN 100 -#define LOCKLEN 20 -static TDB_CONTEXT *db; +static struct tdb_context *db; +static int in_transaction; +static int error_count; -static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) +#ifdef PRINTF_ATTRIBUTE +static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4); +#endif +static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) { va_list ap; + error_count++; + va_start(ap, format); vfprintf(stdout, format, ap); va_end(ap); @@ -50,10 +52,10 @@ static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) #endif } -static void fatal(char *why) +static void fatal(const char *why) { perror(why); - exit(1); + error_count++; } static char *randbuf(int len) @@ -69,41 +71,62 @@ static char *randbuf(int len) return buf; } -static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, +static int cull_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { +#if CULL_PROB if (random() % CULL_PROB == 0) { tdb_delete(tdb, key); } +#endif return 0; } static void addrec_db(void) { - int klen, dlen, slen; - char *k, *d, *s; - TDB_DATA key, data, lockkey; + int klen, dlen; + char *k, *d; + TDB_DATA key, data; klen = 1 + (rand() % KEYLEN); dlen = 1 + (rand() % DATALEN); - slen = 1 + (rand() % LOCKLEN); k = randbuf(klen); d = randbuf(dlen); - s = randbuf(slen); - key.dptr = k; + key.dptr = (unsigned char *)k; key.dsize = klen+1; - data.dptr = d; + data.dptr = (unsigned char *)d; data.dsize = dlen+1; - lockkey.dptr = s; - lockkey.dsize = slen+1; +#if TRANSACTION_PROB + if (in_transaction == 0 && random() % TRANSACTION_PROB == 0) { + if (tdb_transaction_start(db) != 0) { + fatal("tdb_transaction_start failed"); + } + in_transaction++; + goto next; + } + if (in_transaction && random() % TRANSACTION_PROB == 0) { + if (tdb_transaction_commit(db) != 0) { + fatal("tdb_transaction_commit failed"); + } + in_transaction--; + goto next; + } + if (in_transaction && random() % TRANSACTION_PROB == 0) { + if (tdb_transaction_cancel(db) != 0) { + fatal("tdb_transaction_cancel failed"); + } + in_transaction--; + goto next; + } +#endif #if REOPEN_PROB - if (random() % REOPEN_PROB == 0) { - tdb_reopen_all(1); + if (in_transaction == 0 && random() % REOPEN_PROB == 0) { + tdb_reopen_all(0); goto next; } #endif @@ -135,13 +158,13 @@ static void addrec_db(void) #if LOCKSTORE_PROB if (random() % LOCKSTORE_PROB == 0) { - tdb_chainlock(db, lockkey); + tdb_chainlock(db, key); data = tdb_fetch(db, key); if (tdb_store(db, key, data, TDB_REPLACE) != 0) { fatal("tdb_store failed"); } if (data.dptr) free(data.dptr); - tdb_chainunlock(db, lockkey); + tdb_chainunlock(db, key); goto next; } #endif @@ -153,75 +176,143 @@ static void addrec_db(void) } #endif +#if TRAVERSE_READ_PROB + if (random() % TRAVERSE_READ_PROB == 0) { + tdb_traverse_read(db, NULL, NULL); + goto next; + } +#endif + data = tdb_fetch(db, key); if (data.dptr) free(data.dptr); next: free(k); free(d); - free(s); } -static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, +static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { tdb_delete(tdb, key); return 0; } -#ifndef NPROC -#define NPROC 6 -#endif - -#ifndef NLOOPS -#define NLOOPS 200000 -#endif +static void usage(void) +{ + printf("Usage: tdbtorture [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n"); + exit(0); +} -int main(int argc, char *argv[]) + int main(int argc, char * const *argv) { - int i, seed=0; - int loops = NLOOPS; - pid_t pids[NPROC]; + int i, seed = -1; + int num_procs = 3; + int num_loops = 5000; + int hash_size = 2; + int c; + extern char *optarg; + pid_t *pids; + + struct tdb_logging_context log_ctx; + log_ctx.log_fn = tdb_log; + + while ((c = getopt(argc, argv, "n:l:s:H:h")) != -1) { + switch (c) { + case 'n': + num_procs = strtol(optarg, NULL, 0); + break; + case 'l': + num_loops = strtol(optarg, NULL, 0); + break; + case 'H': + hash_size = strtol(optarg, NULL, 0); + break; + case 's': + seed = strtol(optarg, NULL, 0); + break; + default: + usage(); + } + } + + unlink("torture.tdb"); + + pids = calloc(sizeof(pid_t), num_procs); + pids[0] = getpid(); - db = tdb_open("torture.tdb", 0, TDB_CLEAR_IF_FIRST, - O_RDWR | O_CREAT, 0600); + for (i=0;i<num_procs-1;i++) { + if ((pids[i+1]=fork()) == 0) break; + } + + db = tdb_open_ex("torture.tdb", hash_size, TDB_CLEAR_IF_FIRST, + O_RDWR | O_CREAT, 0600, &log_ctx, NULL); if (!db) { fatal("db open failed"); } - for (i=0;i<NPROC;i++) { - pids[i] = fork(); - if (pids[i] == 0) { - tdb_reopen_all(1); + if (seed == -1) { + seed = (getpid() + time(NULL)) & 0x7FFFFFFF; + } - tdb_logging_function(db, tdb_log); + if (i == 0) { + printf("testing with %d processes, %d loops, %d hash_size, seed=%d\n", + num_procs, num_loops, hash_size, seed); + } - srand(seed + getpid()); - srandom(seed + getpid() + time(NULL)); - for (i=0;i<loops;i++) addrec_db(); + srand(seed + i); + srandom(seed + i); - tdb_traverse(db, NULL, NULL); - tdb_traverse(db, traverse_fn, NULL); - tdb_traverse(db, traverse_fn, NULL); + for (i=0;i<num_loops && error_count == 0;i++) { + addrec_db(); + } - tdb_close(db); - exit(0); - } + if (error_count == 0) { + tdb_traverse_read(db, NULL, NULL); + tdb_traverse(db, traverse_fn, NULL); + tdb_traverse(db, traverse_fn, NULL); } - for (i=0;i<NPROC;i++) { - int status; - if (waitpid(pids[i], &status, 0) != pids[i]) { - printf("failed to wait for %d\n", - (int)pids[i]); + tdb_close(db); + + if (getpid() != pids[0]) { + return error_count; + } + + for (i=1;i<num_procs;i++) { + int status, j; + pid_t pid; + if (error_count != 0) { + /* try and stop the test on any failure */ + for (j=1;j<num_procs;j++) { + if (pids[j] != 0) { + kill(pids[j], SIGTERM); + } + } + } + pid = waitpid(-1, &status, 0); + if (pid == -1) { + perror("failed to wait for child\n"); + exit(1); + } + for (j=1;j<num_procs;j++) { + if (pids[j] == pid) break; + } + if (j == num_procs) { + printf("unknown child %d exited!?\n", (int)pid); exit(1); } if (WEXITSTATUS(status) != 0) { printf("child %d exited with status %d\n", - (int)pids[i], WEXITSTATUS(status)); - exit(1); + (int)pid, WEXITSTATUS(status)); + error_count++; } + pids[j] = 0; } - printf("OK\n"); - return 0; + + if (error_count == 0) { + printf("OK\n"); + } + + return error_count; } |