+ tdb_access_release(tdb, buffer);
+ }
+
+ *len = p - (unsigned char *)(rec + 1);
+ tdb->methods = old_methods;
+ return rec;
+
+fail:
+ free(rec);
+ tdb->methods = old_methods;
+ return TDB_ERR_PTR(ecode);
+}
+
+static tdb_off_t create_recovery_area(struct tdb_context *tdb,
+ tdb_len_t rec_length,
+ struct tdb_recovery_record *rec)
+{
+ tdb_off_t off, recovery_off;
+ tdb_len_t addition;
+ enum TDB_ERROR ecode;
+ const struct tdb_methods *methods = tdb->transaction->io_methods;
+
+ /* round up to a multiple of page size. Overallocate, since each
+ * such allocation forces us to expand the file. */
+ rec->max_len
+ = (((sizeof(*rec) + rec_length + rec_length / 2)
+ + PAGESIZE-1) & ~(PAGESIZE-1))
+ - sizeof(*rec);
+ off = tdb->file->map_size;
+
+ /* Restore ->map_size before calling underlying expand_file.
+ Also so that we don't try to expand the file again in the
+ transaction commit, which would destroy the recovery
+ area */
+ addition = (tdb->file->map_size - tdb->transaction->old_map_size) +
+ sizeof(*rec) + rec->max_len;
+ tdb->file->map_size = tdb->transaction->old_map_size;
+ tdb->stats.transaction_expand_file++;
+ ecode = methods->expand_file(tdb, addition);
+ if (ecode != TDB_SUCCESS) {
+ return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
+ "tdb_recovery_allocate:"
+ " failed to create recovery area");
+ }
+
+ /* we have to reset the old map size so that we don't try to
+ expand the file again in the transaction commit, which
+ would destroy the recovery area */
+ tdb->transaction->old_map_size = tdb->file->map_size;
+
+ /* write the recovery header offset and sync - we can sync without a race here
+ as the magic ptr in the recovery record has not been set */
+ recovery_off = off;
+ tdb_convert(tdb, &recovery_off, sizeof(recovery_off));
+ ecode = methods->twrite(tdb, offsetof(struct tdb_header, recovery),
+ &recovery_off, sizeof(tdb_off_t));
+ if (ecode != TDB_SUCCESS) {
+ return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
+ "tdb_recovery_allocate:"
+ " failed to write recovery head");