- /* we need to free up the old recovery area, then allocate a
- new one at the end of the file. Note that we cannot use
- normal allocation to allocate the new one as that might return
- us an area that is being currently used (as of the start of
- the transaction) */
- if (recovery_head != 0) {
- add_stat(tdb, frees, 1);
- ecode = add_free_record(tdb, recovery_head,
- sizeof(rec) + rec.max_len);
- if (ecode != TDB_SUCCESS) {
- return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
- "tdb_recovery_allocate:"
- " failed to free previous"
- " recovery area");
+ /* We temporarily revert to the old I/O methods, so we can use
+ * tdb_access_read */
+ tdb->tdb2.io = tdb->tdb2.transaction->io_methods;
+
+ /* build the recovery data into a single blob to allow us to do a single
+ large write, which should be more efficient */
+ p = (unsigned char *)(rec + 1);
+ for (i=0;i<tdb->tdb2.transaction->num_blocks;i++) {
+ tdb_off_t offset;
+ tdb_len_t length;
+ unsigned int off;
+ const unsigned char *buffer;
+
+ if (tdb->tdb2.transaction->blocks[i] == NULL) {
+ continue;
+ }
+
+ offset = i * PAGESIZE;
+ length = PAGESIZE;
+ if (i == tdb->tdb2.transaction->num_blocks-1) {
+ length = tdb->tdb2.transaction->last_block_size;
+ }
+
+ if (offset >= tdb->tdb2.transaction->old_map_size) {
+ continue;
+ }
+
+ if (offset + length > tdb->file->map_size) {
+ ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
+ "tdb_transaction_setup_recovery:"
+ " transaction data over new region"
+ " boundary");
+ goto fail;
+ }
+ if (offset + length > tdb->tdb2.transaction->old_map_size) {
+ /* Short read at EOF. */
+ length = tdb->tdb2.transaction->old_map_size - offset;
+ }
+ buffer = tdb_access_read(tdb, offset, length, false);
+ if (TDB_PTR_IS_ERR(buffer)) {
+ ecode = TDB_PTR_ERR(buffer);
+ goto fail;