This is definitely a bad idea in general, but SAMBA uses nested transactions
in many and varied ways (some of them probably reflect real bugs) and it's
far easier to support them inside tdb2 with a flag.
We already have part of the TDB1 infrastructure in place, so this patch
just completes it and fixes one place where I'd messed it up.
}
if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT
}
if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT
- | TDB_NOSYNC | TDB_SEQNUM)) {
+ | TDB_NOSYNC | TDB_SEQNUM | TDB_ALLOW_NESTING)) {
ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
"tdb_open: unknown flags %u", tdb_flags);
goto fail;
ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
"tdb_open: unknown flags %u", tdb_flags);
goto fail;
case TDB_SEQNUM:
tdb->flags |= TDB_SEQNUM;
break;
case TDB_SEQNUM:
tdb->flags |= TDB_SEQNUM;
break;
+ case TDB_ALLOW_NESTING:
+ tdb->flags |= TDB_ALLOW_NESTING;
+ break;
default:
tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR,
default:
tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR,
case TDB_SEQNUM:
tdb->flags &= ~TDB_SEQNUM;
break;
case TDB_SEQNUM:
tdb->flags &= ~TDB_SEQNUM;
break;
+ case TDB_ALLOW_NESTING:
+ tdb->flags &= ~TDB_ALLOW_NESTING;
+ break;
default:
tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR,
default:
tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR,
#define TDB_CONVERT 16 /* convert endian */
#define TDB_NOSYNC 64 /* don't use synchronous transactions */
#define TDB_SEQNUM 128 /* maintain a sequence number */
#define TDB_CONVERT 16 /* convert endian */
#define TDB_NOSYNC 64 /* don't use synchronous transactions */
#define TDB_SEQNUM 128 /* maintain a sequence number */
+#define TDB_ALLOW_NESTING 256 /* fake nested transactions */
/**
* tdb_close - close and free a tdb.
/**
* tdb_close - close and free a tdb.
* to read the tdb, but not alter it (they will block), nor will they see
* any changes until tdb_transaction_commit() is called.
*
* to read the tdb, but not alter it (they will block), nor will they see
* any changes until tdb_transaction_commit() is called.
*
+ * Note that if the TDB_ALLOW_NESTING flag is set, a tdb_transaction_start()
+ * within a transaction will succeed, but it's not a real transaction:
+ * (1) An inner transaction which is committed is not actually committed until
+ * the outer transaction is; if the outer transaction is cancelled, the
+ * inner ones are discarded.
+ * (2) tdb_transaction_cancel() marks the outer transaction as having an error,
+ * so the final tdb_transaction_commit() will fail.
+ * (3) the outer transaction will see the results of the inner transaction.
+ *
* See Also:
* tdb_transaction_cancel, tdb_transaction_commit.
*/
* See Also:
* tdb_transaction_cancel, tdb_transaction_commit.
*/
/**
* tdb_add_flag - set a flag for a tdb
* @tdb: the tdb context returned from tdb_open()
/**
* tdb_add_flag - set a flag for a tdb
* @tdb: the tdb context returned from tdb_open()
- * @flag: one of TDB_NOLOCK, TDB_NOMMAP or TDB_NOSYNC.
+ * @flag: one of TDB_NOLOCK, TDB_NOMMAP, TDB_NOSYNC or TDB_ALLOW_NESTING.
*
* You can use this to set a flag on the TDB. You cannot set these flags
* on a TDB_INTERNAL tdb.
*
* You can use this to set a flag on the TDB. You cannot set these flags
* on a TDB_INTERNAL tdb.
/**
* tdb_remove_flag - unset a flag for a tdb
* @tdb: the tdb context returned from tdb_open()
/**
* tdb_remove_flag - unset a flag for a tdb
* @tdb: the tdb context returned from tdb_open()
- * @flag: one of TDB_NOLOCK, TDB_NOMMAP or TDB_NOSYNC.
+ * @flag: one of TDB_NOLOCK, TDB_NOMMAP, TDB_NOSYNC or TDB_ALLOW_NESTING.
*
* You can use this to clear a flag on the TDB. You cannot clear flags
* on a TDB_INTERNAL tdb.
*
* You can use this to clear a flag on the TDB. You cannot clear flags
* on a TDB_INTERNAL tdb.
/* when inside a transaction we need to keep track of any
nested tdb_transaction_start() calls, as these are allowed,
but don't create a new transaction */
/* when inside a transaction we need to keep track of any
nested tdb_transaction_start() calls, as these are allowed,
but don't create a new transaction */
/* set when a prepare has already occurred */
bool prepared;
/* set when a prepare has already occurred */
bool prepared;
/* cope with nested tdb_transaction_start() calls */
if (tdb->transaction != NULL) {
/* cope with nested tdb_transaction_start() calls */
if (tdb->transaction != NULL) {
- return tdb->last_error = tdb_logerr(tdb, TDB_ERR_IO,
- TDB_LOG_USE_ERROR,
- "tdb_transaction_start:"
- " already inside"
- " transaction");
+ if (!(tdb->flags & TDB_ALLOW_NESTING)) {
+ return tdb->last_error
+ = tdb_logerr(tdb, TDB_ERR_IO,
+ TDB_LOG_USE_ERROR,
+ "tdb_transaction_start:"
+ " already inside transaction");
+ }
+ tdb->transaction->nesting++;
+ return 0;
}
if (tdb_has_hash_locks(tdb)) {
}
if (tdb_has_hash_locks(tdb)) {
if (tdb->transaction->nesting != 0) {
if (tdb->transaction->nesting != 0) {
- tdb->transaction->nesting--;