[PATCH] lib/tdb: TDB_ALLOW_NESTING flag (port from ctdb)

Rusty Russell rusty at rustcorp.com.au
Mon Aug 10 01:48:45 MDT 2009


Ported from ctdb change 3e49e41c21eb8c53084aa8cc7fd3557bdd8eb7b6
("New attempt at TDB transaction nesting allow/disallow").

Unfortunately, this is an API change if you were using nested transactions.
I've added the flag where needed to make quicktest (source4) and test
(source3) pass again.

As a general rule, nested transactions are dangerous: if you don't know you're
nesting, you can do a tdb_transaction_commit() which "succeeds", then gets
wiped by a crash, close, or outer tdb_transaction_cancel().

Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>

diff --git a/lib/tdb/common/transaction.c b/lib/tdb/common/transaction.c
index e97fe67..d6d34fb 100644
--- a/lib/tdb/common/transaction.c
+++ b/lib/tdb/common/transaction.c
@@ -85,6 +85,13 @@
     still available, but no transaction recovery area is used and no
     fsync/msync calls are made.
 
+  - if TDB_ALLOW_NESTING is passed to flags in tdb open, or added using
+    tdb_add_flags() transaction is enabled.
+    The default is that transaction nesting is not allowed and an attempt
+    to create a nested transaction will fail with TDB_ERR_NESTING.
+
+    Beware. when transactions are nested a transaction successfully
+    completed with tdb_transaction_commit() can be silently unrolled later.
 */
 
 
@@ -434,6 +441,10 @@ int tdb_transaction_start(struct tdb_context *tdb)
 
 	/* cope with nested tdb_transaction_start() calls */
 	if (tdb->transaction != NULL) {
+		if (!(tdb->flags & TDB_ALLOW_NESTING)) {
+			tdb->ecode = TDB_ERR_NESTING;
+			return -1;
+		}
 		tdb->transaction->nesting++;
 		TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", 
 			 tdb->transaction->nesting));
diff --git a/lib/tdb/include/tdb.h b/lib/tdb/include/tdb.h
index 22496f5..c55ac6d 100644
--- a/lib/tdb/include/tdb.h
+++ b/lib/tdb/include/tdb.h
@@ -48,13 +48,15 @@ extern "C" {
 #define TDB_NOSYNC   64 /* don't use synchronous transactions */
 #define TDB_SEQNUM   128 /* maintain a sequence number */
 #define TDB_VOLATILE   256 /* Activate the per-hashchain freelist, default 5 */
+#define TDB_ALLOW_NESTING 512 /* Allow transactions to nest */
 
 #define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
 
 /* error codes */
 enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
 		TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
-		TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY};
+		TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY,
+		TDB_ERR_NESTING};
 
 /* debugging uses one of the following levels */
 enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, 
diff --git a/source3/include/reg_db.h b/source3/include/reg_db.h
index 5cafa0a..a1a83f2 100644
--- a/source3/include/reg_db.h
+++ b/source3/include/reg_db.h
@@ -20,7 +20,7 @@
 #ifndef _REG_DB_H
 #define _REG_DB_H
 
-#define REG_TDB_FLAGS   TDB_SEQNUM
+#define REG_TDB_FLAGS   (TDB_SEQNUM|TDB_ALLOW_NESTING)
 
 #define REGVER_V1       1       /* first db version with write support */
 
diff --git a/source3/lib/account_pol.c b/source3/lib/account_pol.c
index f4101e9..bb2ac69 100644
--- a/source3/lib/account_pol.c
+++ b/source3/lib/account_pol.c
@@ -212,8 +212,8 @@ bool init_account_policy(void)
 		return True;
 	}
 
-	db = db_open(NULL, state_path("account_policy.tdb"), 0, TDB_DEFAULT,
-		     O_RDWR, 0600);
+	db = db_open(NULL, state_path("account_policy.tdb"), 0,
+		     TDB_ALLOW_NESTING, O_RDWR, 0600);
 
 	if (db == NULL) { /* the account policies files does not exist or open
 			   * failed, try to create a new one */
diff --git a/source3/lib/ldb/ldb_tdb/ldb_tdb.c b/source3/lib/ldb/ldb_tdb/ldb_tdb.c
index 27cc0c6..7811791 100644
--- a/source3/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/source3/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -64,6 +64,7 @@ static int ltdb_err_map(enum TDB_ERROR tdb_code)
 		return LDB_ERR_PROTOCOL_ERROR;
 	case TDB_ERR_LOCK:
 	case TDB_ERR_NOLOCK:
+	case TDB_ERR_NESTING:
 		return LDB_ERR_BUSY;
 	case TDB_ERR_LOCK_TIMEOUT:
 		return LDB_ERR_TIME_LIMIT_EXCEEDED;
diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c
index b063f07..5ea9441 100644
--- a/source3/passdb/secrets.c
+++ b/source3/passdb/secrets.c
@@ -65,7 +65,7 @@ bool secrets_init(void)
 	}
 
 	db_ctx = db_open(NULL, fname, 0,
-			 TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+			 TDB_ALLOW_NESTING, O_RDWR|O_CREAT, 0600);
 
 	if (db_ctx == NULL) {
 		DEBUG(0,("Failed to open %s\n", fname));
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
index d4f7b45..c179c07 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -1246,7 +1246,7 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url,
 		path = url;
 	}
 
-	tdb_flags = TDB_DEFAULT | TDB_SEQNUM;
+	tdb_flags = TDB_DEFAULT | TDB_SEQNUM | TDB_ALLOW_NESTING;
 
 	/* check for the 'nosync' option */
 	if (flags & LDB_FLG_NOSYNC) {


More information about the samba-technical mailing list