diff options
Diffstat (limited to 'source4/lib/tdb/common/transaction.c')
-rw-r--r-- | source4/lib/tdb/common/transaction.c | 37 |
1 files changed, 36 insertions, 1 deletions
diff --git a/source4/lib/tdb/common/transaction.c b/source4/lib/tdb/common/transaction.c index becc6cd059..ace4aa51a6 100644 --- a/source4/lib/tdb/common/transaction.c +++ b/source4/lib/tdb/common/transaction.c @@ -195,7 +195,7 @@ fail: static int transaction_write(struct tdb_context *tdb, tdb_off_t off, const void *buf, tdb_len_t len) { - struct tdb_transaction_el *el; + struct tdb_transaction_el *el, *best_el=NULL; if (len == 0) { return 0; @@ -213,6 +213,10 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, for (el=tdb->transaction->elements_last;el;el=el->prev) { tdb_len_t partial; + if (best_el == NULL && off == el->offset+el->length) { + best_el = el; + } + if (off+len <= el->offset) { continue; } @@ -248,6 +252,28 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, return 0; } + /* see if we can append the new entry to an existing entry */ + if (best_el && best_el->offset + best_el->length == off && + (off+len < tdb->transaction->old_map_size || + off > tdb->transaction->old_map_size)) { + unsigned char *data = best_el->data; + el = best_el; + el->data = realloc(el->data, el->length + len); + if (el->data == NULL) { + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + el->data = data; + return -1; + } + if (buf) { + memcpy(el->data + el->length, buf, len); + } else { + memset(el->data + el->length, TDB_PAD_BYTE, len); + } + el->length += len; + return 0; + } + /* add a new entry at the end of the list */ el = malloc(sizeof(*el)); if (el == NULL) { @@ -433,6 +459,15 @@ int tdb_transaction_start(struct tdb_context *tdb) tdb->transaction->io_methods = tdb->methods; tdb->methods = &transaction_methods; + /* by calling this transaction write here, we ensure that we don't grow the + transaction linked list due to hash table updates */ + if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads, + TDB_HASHTABLE_SIZE(tdb)) != 0) { + TDB_LOG((tdb, 0, "tdb_transaction_start: failed to prime hash table\n")); + tdb->ecode = TDB_ERR_IO; + goto fail; + } + return 0; fail: |