/* 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 */
- int nesting;
+ unsigned int nesting;
/* set when a prepare has already occurred */
bool prepared;
/* 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)) {
tdb_len_t recovery_size = 0;
int i;
- recovery_size = sizeof(tdb_len_t);
+ recovery_size = 0;
for (i=0;i<tdb->transaction->num_blocks;i++) {
if (i * PAGESIZE >= tdb->transaction->old_map_size) {
break;
us an area that is being currently used (as of the start of
the transaction) */
if (recovery_head != 0) {
- add_stat(tdb, frees, 1);
+ tdb->stats.frees++;
ecode = add_free_record(tdb, recovery_head,
- sizeof(rec) + rec.max_len);
+ sizeof(rec) + rec.max_len,
+ TDB_LOCK_WAIT, true);
if (ecode != TDB_SUCCESS) {
return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
"tdb_recovery_allocate:"
/* the tdb_free() call might have increased the recovery size */
*recovery_size = tdb_recovery_size(tdb);
- /* round up to a multiple of page size */
+ /* round up to a multiple of page size. Overallocate, since each
+ * such allocation forces us to expand the file. */
*recovery_max_size
- = (((sizeof(rec) + *recovery_size) + PAGESIZE-1)
- & ~(PAGESIZE-1))
+ = (((sizeof(rec) + *recovery_size + *recovery_size / 2)
+ + PAGESIZE-1) & ~(PAGESIZE-1))
- sizeof(rec);
*recovery_offset = tdb->file->map_size;
recovery_head = *recovery_offset;
const struct tdb_methods *methods = tdb->transaction->io_methods;
struct tdb_recovery_record *rec;
tdb_off_t old_map_size = tdb->transaction->old_map_size;
- uint64_t magic, tailer;
+ uint64_t magic;
int i;
enum TDB_ERROR ecode;
if (offset >= old_map_size) {
continue;
}
+
if (offset + length > tdb->file->map_size) {
free(data);
return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
/* the recovery area contains the old data, not the
new data, so we have to call the original tdb_read
method to get it */
- ecode = methods->tread(tdb, offset,
- p + sizeof(offset) + sizeof(length),
- length);
+ if (offset + length > old_map_size) {
+ /* Short read at EOF, and zero fill. */
+ unsigned int len = old_map_size - offset;
+ ecode = methods->tread(tdb, offset,
+ p + sizeof(offset) + sizeof(length),
+ len);
+ memset(p + sizeof(offset) + sizeof(length) + len, 0,
+ length - len);
+ } else {
+ ecode = methods->tread(tdb, offset,
+ p + sizeof(offset) + sizeof(length),
+ length);
+ }
if (ecode != TDB_SUCCESS) {
free(data);
return ecode;
p += sizeof(offset) + sizeof(length) + length;
}
- /* and the tailer */
- tailer = sizeof(*rec) + recovery_max_size;
- memcpy(p, &tailer, sizeof(tailer));
- tdb_convert(tdb, p, sizeof(tailer));
-
/* write the recovery data to the recovery area */
ecode = methods->twrite(tdb, recovery_offset, data,
sizeof(*rec) + recovery_size);
if (tdb->transaction->nesting != 0) {
- tdb->transaction->nesting--;
return TDB_SUCCESS;
}