+ /* 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");
+ }
+ transaction_write_existing(tdb, offsetof(struct tdb_header, recovery),
+ &recovery_off,
+ sizeof(tdb_off_t));
+ return off;
+}
+
+/*
+ setup the recovery data that will be used on a crash during commit
+*/
+static enum TDB_ERROR transaction_setup_recovery(struct tdb_context *tdb)
+{
+ tdb_len_t recovery_size = 0;
+ tdb_off_t recovery_off = 0;
+ tdb_off_t old_map_size = tdb->transaction->old_map_size;
+ struct tdb_recovery_record *recovery;
+ const struct tdb_methods *methods = tdb->transaction->io_methods;
+ uint64_t magic;
+ enum TDB_ERROR ecode;
+
+ recovery = alloc_recovery(tdb, &recovery_size);
+ if (TDB_PTR_IS_ERR(recovery))
+ return TDB_PTR_ERR(recovery);
+
+ ecode = tdb_recovery_area(tdb, methods, &recovery_off, recovery);
+ if (ecode) {
+ free(recovery);
+ return ecode;
+ }
+
+ if (recovery->max_len < recovery_size) {
+ /* Not large enough. Free up old recovery area. */
+ if (recovery_off) {
+ tdb->stats.frees++;
+ ecode = add_free_record(tdb, recovery_off,
+ sizeof(*recovery)
+ + recovery->max_len,
+ TDB_LOCK_WAIT, true);
+ free(recovery);
+ if (ecode != TDB_SUCCESS) {
+ return tdb_logerr(tdb, ecode, TDB_LOG_ERROR,
+ "tdb_recovery_allocate:"
+ " failed to free previous"
+ " recovery area");
+ }
+
+ /* Refresh recovery after add_free_record above. */
+ recovery = alloc_recovery(tdb, &recovery_size);
+ if (TDB_PTR_IS_ERR(recovery))
+ return TDB_PTR_ERR(recovery);
+ }
+
+ recovery_off = create_recovery_area(tdb, recovery_size,
+ recovery);
+ if (TDB_OFF_IS_ERR(recovery_off)) {
+ free(recovery);
+ return recovery_off;
+ }
+ }
+
+ /* Now we know size, convert rec header. */
+ recovery->magic = TDB_RECOVERY_INVALID_MAGIC;
+ recovery->len = recovery_size;
+ recovery->eof = old_map_size;
+ tdb_convert(tdb, recovery, sizeof(*recovery));