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