diff options
Diffstat (limited to 'source3/tdb/tdb.c')
-rw-r--r-- | source3/tdb/tdb.c | 53 |
1 files changed, 40 insertions, 13 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); |