summaryrefslogtreecommitdiff
path: root/source3/tdb
diff options
context:
space:
mode:
Diffstat (limited to 'source3/tdb')
-rw-r--r--source3/tdb/tdb.c53
-rw-r--r--source3/tdb/tdb.h7
-rw-r--r--source3/tdb/tdbtest.c5
-rw-r--r--source3/tdb/tdbtool.c5
-rw-r--r--source3/tdb/tdbtorture.c5
5 files changed, 54 insertions, 21 deletions
diff --git a/source3/tdb/tdb.c b/source3/tdb/tdb.c
index 7b8f8db4c0..731d1fdbc8 100644
--- a/source3/tdb/tdb.c
+++ b/source3/tdb/tdb.c
@@ -44,6 +44,14 @@
#define TDB_LEN_MULTIPLIER 10
#define FREELIST_TOP (sizeof(struct tdb_header))
+#define LOCK_SET 1
+#define LOCK_CLEAR 0
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+#define LIST_LOCK_BASE 1024
+
#define BUCKET(hash) ((hash) % tdb->header.hash_size)
/* the body of the database is made of one list_struct for the free space
@@ -85,7 +93,8 @@ static char *memdup(char *d, int size)
/* a byte range locking function - return 0 on success
this functions locks/unlocks 1 byte at the specified offset */
-static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, int set)
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset,
+ int set, int rw_type, int lck_type)
{
#if NOLOCK
return 0;
@@ -94,13 +103,13 @@ static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, int set)
if (tdb->read_only) return -1;
- fl.l_type = set?F_WRLCK:F_UNLCK;
+ fl.l_type = set==LOCK_SET?rw_type:F_UNLCK;
fl.l_whence = SEEK_SET;
fl.l_start = offset;
fl.l_len = 1;
fl.l_pid = 0;
- if (fcntl(tdb->fd, F_SETLKW, &fl) != 0) {
+ if (fcntl(tdb->fd, lck_type, &fl) != 0 && lck_type == F_SETLKW) {
#if TDB_DEBUG
printf("lock %d failed at %d (%s)\n",
set, offset, strerror(errno));
@@ -121,7 +130,8 @@ static int tdb_lock(TDB_CONTEXT *tdb, int list)
return -1;
}
if (tdb->locked[list+1] == 0) {
- if (tdb_brlock(tdb, 4*(list+1), 1) != 0) {
+ if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET,
+ F_WRLCK, F_SETLKW) != 0) {
return -1;
}
}
@@ -146,7 +156,8 @@ static int tdb_unlock(TDB_CONTEXT *tdb, int list)
return -1;
}
if (tdb->locked[list+1] == 1) {
- if (tdb_brlock(tdb, 4*(list+1), 0) != 0) {
+ if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR,
+ F_WRLCK, F_SETLKW) != 0) {
return -1;
}
}
@@ -264,6 +275,8 @@ static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
buf = (char *)malloc(len);
+ if (!buf) return NULL;
+
if (tdb_read(tdb, offset, buf, len) == -1) {
free(buf);
return NULL;
@@ -1091,7 +1104,8 @@ int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
return is NULL on error
*/
-TDB_CONTEXT *tdb_open(char *name, int hash_size, int flags, mode_t mode)
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
{
TDB_CONTEXT tdb, *ret;
struct stat st;
@@ -1100,21 +1114,36 @@ TDB_CONTEXT *tdb_open(char *name, int hash_size, int flags, mode_t mode)
tdb.name = NULL;
tdb.map_ptr = NULL;
- if ((flags & O_ACCMODE) == O_WRONLY) goto fail;
+ if ((open_flags & O_ACCMODE) == O_WRONLY) goto fail;
if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
memset(&tdb, 0, sizeof(tdb));
- tdb.fd = open(name, flags, mode);
+ tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY);
+
+ tdb.fd = open(name, open_flags, mode);
if (tdb.fd == -1) goto fail;
- tdb_brlock(&tdb, 0, 1);
+ /* ensure there is only one process initialising at once */
+ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW);
+
+ if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+ /* we need to zero the database if we are the only
+ one with it open */
+ if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) {
+ ftruncate(tdb.fd, 0);
+ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK);
+ }
+ }
+
+ /* leave this lock in place */
+ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW);
if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) ||
tdb.header.version != TDB_VERSION) {
/* its not a valid database - possibly initialise it */
- if (!(flags & O_CREAT)) {
+ if (!(open_flags & O_CREAT)) {
goto fail;
}
if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
@@ -1131,7 +1160,6 @@ TDB_CONTEXT *tdb_open(char *name, int hash_size, int flags, mode_t mode)
sizeof(tdb.locked[0]));
if (!tdb.locked) goto fail;
tdb.map_size = st.st_size;
- tdb.read_only = ((flags & O_ACCMODE) == O_RDONLY);
#if HAVE_MMAP
tdb.map_ptr = (void *)mmap(NULL, st.st_size,
tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
@@ -1148,11 +1176,10 @@ TDB_CONTEXT *tdb_open(char *name, int hash_size, int flags, mode_t mode)
hash_size, tdb.map_size);
#endif
- tdb_brlock(&tdb, 0, 0);
+ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW);
return ret;
fail:
- tdb_brlock(&tdb, 0, 0);
if (tdb.name) free(tdb.name);
if (tdb.fd != -1) close(tdb.fd);
if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
diff --git a/source3/tdb/tdb.h b/source3/tdb/tdb.h
index 527e65740a..9cb8f71a90 100644
--- a/source3/tdb/tdb.h
+++ b/source3/tdb/tdb.h
@@ -44,16 +44,21 @@ typedef struct {
struct tdb_header header; /* a cached copy of the header */
} TDB_CONTEXT;
+/* flags to tdb_store() */
#define TDB_REPLACE 1
#define TDB_INSERT 2
+/* flags for tdb_open() */
+#define TDB_CLEAR_IF_FIRST 1
+
#if STANDALONE
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode);
int tdb_writelock(TDB_CONTEXT *tdb);
int tdb_writeunlock(TDB_CONTEXT *tdb);
TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
-TDB_CONTEXT *tdb_open(char *name, int hash_size, int flags, mode_t mode);
int tdb_close(TDB_CONTEXT *tdb);
TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
diff --git a/source3/tdb/tdbtest.c b/source3/tdb/tdbtest.c
index 8089e7be7d..581d8192d9 100644
--- a/source3/tdb/tdbtest.c
+++ b/source3/tdb/tdbtest.c
@@ -180,11 +180,12 @@ static int traverse_fn(TDB_CONTEXT *db, TDB_DATA key, TDB_DATA dbuf)
int main(int argc, char *argv[])
{
int i, seed=0;
- int loops = 50000;
+ int loops = 10000;
unlink("test.gdbm");
- db = tdb_open("test.db", 0, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ db = tdb_open("test.db", 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
0600, NULL);
diff --git a/source3/tdb/tdbtool.c b/source3/tdb/tdbtool.c
index 5eb410d418..428bae84c2 100644
--- a/source3/tdb/tdbtool.c
+++ b/source3/tdb/tdbtool.c
@@ -41,7 +41,8 @@ static void create_tdb(void)
return;
}
if (tdb) tdb_close(tdb);
- tdb = tdb_open(tok, 0, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
}
static void open_tdb(void)
@@ -52,7 +53,7 @@ static void open_tdb(void)
return;
}
if (tdb) tdb_close(tdb);
- tdb = tdb_open(tok, 0, O_RDWR, 0600);
+ tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
}
static void insert_tdb(void)
diff --git a/source3/tdb/tdbtorture.c b/source3/tdb/tdbtorture.c
index 1aac6d4a01..3eb462d79e 100644
--- a/source3/tdb/tdbtorture.c
+++ b/source3/tdb/tdbtorture.c
@@ -93,13 +93,12 @@ int main(int argc, char *argv[])
int i, seed=0;
int loops = NLOOPS;
- unlink("test.tdb");
-
for (i=0;i<NPROC-1;i++) {
if (fork() == 0) break;
}
- db = tdb_open("test.tdb", 0, O_RDWR | O_CREAT, 0600);
+ db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT, 0600);
if (!db) {
fatal("db open failed");
}