- /* 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);
- if (add_free_record(tdb, recovery_head,
- sizeof(rec) + rec.max_len) != 0) {
- tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL,
- "tdb_recovery_allocate:"
- " failed to free previous recovery area");
- return -1;
+ /* 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;
+ }
+
+ /* Skip over anything the same at the start. */
+ off = same(tdb->tdb2.transaction->blocks[i], buffer, length);
+ offset += off;
+
+ while (off < length) {
+ tdb_len_t len;
+ unsigned int samelen;
+
+ len = different(tdb->tdb2.transaction->blocks[i] + off,
+ buffer + off, length - off,
+ sizeof(offset) + sizeof(len) + 1,
+ &samelen);
+
+ memcpy(p, &offset, sizeof(offset));
+ memcpy(p + sizeof(offset), &len, sizeof(len));
+ tdb_convert(tdb, p, sizeof(offset) + sizeof(len));
+ p += sizeof(offset) + sizeof(len);
+ memcpy(p, buffer + off, len);
+ p += len;
+ off += len + samelen;
+ offset += len + samelen;