Am I going mad or does TDB allow multiple writers to update the
freelist?
Richard Sharpe
realrichardsharpe at gmail.com
Tue Jan 20 21:52:55 GMT 2009
Hi,
In tdb/common/freelist.c:tdb_allocate we see:
tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length,
struct list_struct *rec)
{
tdb_off_t rec_ptr, last_ptr, newrec_ptr;
struct {
tdb_off_t rec_ptr, last_ptr;
tdb_len_t rec_len;
} bestfit;
if (tdb_lock(tdb, -1, F_WRLCK) == -1)
return 0;
/* Extra bytes required for tailer */
length += sizeof(tdb_off_t);
We take a lock out on the freelist (that -1) in there. This should
allow us to mess with pointers in the free list without the
possibility of races and corruption.
However, in tdb/common/lock.c:tdb_lock we see this:
/* lock a list in the database. list -1 is the alloc list */
int tdb_lock(struct tdb_context *tdb, int list, int ltype)
{
struct tdb_lock_type *new_lck;
int i;
/* 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, TDB_DEBUG_ERROR,"tdb_lock: invalid list
%d for ltype=%d\n",
list, ltype));
return -1;
}
if (tdb->flags & TDB_NOLOCK)
return 0;
for (i=0; i<tdb->num_lockrecs; i++) {
if (tdb->lockrecs[i].list == list) {
if (tdb->lockrecs[i].count == 0) {
/*
* Can't happen, see tdb_unlock(). It should
* be an assert.
*/
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: "
"lck->count == 0 for list %d", list));
}
/*
* Just increment the in-memory struct, posix locks
* don't stack.
*/
tdb->lockrecs[i].count++;
return 0;
}
}
Am I reading this correctly? Are we allowing another process to take
the free list lock when it is already locked? Surely I have missed
something here.
--
Regards,
Richard Sharpe
More information about the samba-technical
mailing list