s3:dbwrap_ctdb: fix a race in starting concurrent transactions on a single node
authorMichael Adam <obnox@samba.org>
Wed, 28 Oct 2009 00:54:04 +0000 (01:54 +0100)
committerMichael Adam <obnox@samba.org>
Tue, 3 Nov 2009 00:02:36 +0000 (01:02 +0100)
There are two races in concurrent transactions on a single node.
One in starting a transaction and one with replay during commit.

This commit closes the first race by storing the client pid in the
transaction-lock record and comparing the stored pid against its own
pid after releasing the lock and refetching the record inside the
transaction.

Michael

source3/lib/dbwrap_ctdb.c

index fce6126a5c0def760b8554e9af0df16315540324..49df62afd305d06d94154f0196a64ed8a77afa75 100644 (file)
@@ -316,12 +316,14 @@ static int db_ctdb_transaction_destructor(struct db_ctdb_transaction_handle *h)
 static int db_ctdb_transaction_fetch_start(struct db_ctdb_transaction_handle *h)
 {
        struct db_record *rh;
+       struct db_ctdb_rec *crec;
        TDB_DATA key;
        TALLOC_CTX *tmp_ctx;
        const char *keyname = CTDB_TRANSACTION_LOCK_KEY;
        int ret;
        struct db_ctdb_ctx *ctx = h->ctx;
        TDB_DATA data;
+       pid_t pid;
        NTSTATUS status;
        struct ctdb_ltdb_header header;
 
@@ -337,6 +339,23 @@ again:
                talloc_free(tmp_ctx);
                return -1;
        }
+       crec = talloc_get_type_abort(rh->private_data, struct db_ctdb_rec);
+
+       /*
+        * store the pid in the database:
+        * it is not enought that the node is dmaster...
+        */
+       pid = getpid();
+       data.dptr = (unsigned char *)&pid;
+       data.dsize = sizeof(pid_t);
+       status = db_ctdb_ltdb_store(ctx, key, &(crec->header), data);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, (__location__ " Failed to store pid in transaction "
+                         "record: %s\n", nt_errstr(status)));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
        talloc_free(rh);
 
        ret = tdb_transaction_start(ctx->wtdb->tdb);
@@ -353,6 +372,12 @@ again:
                goto again;
        }
 
+       if ((data.dsize != sizeof(pid_t)) || (*(pid_t *)(data.dptr) != pid)) {
+               tdb_transaction_cancel(ctx->wtdb->tdb);
+               talloc_free(tmp_ctx);
+               goto again;
+       }
+
        talloc_free(tmp_ctx);
 
        return 0;