+/*
+ sync to disk
+*/
+static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length)
+{
+ if (tdb->flags & TDB_NOSYNC) {
+ return 0;
+ }
+
+ if (fsync(tdb->fd) != 0) {
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
+ return -1;
+ }
+#ifdef MS_SYNC
+ if (tdb->map_ptr) {
+ tdb_off_t moffset = offset & ~(tdb->page_size-1);
+ if (msync(moffset + (char *)tdb->map_ptr,
+ length + (offset - moffset), MS_SYNC) != 0) {
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n",
+ strerror(errno)));
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+/* ltype is F_WRLCK after prepare. */
+int _tdb_transaction_cancel(struct tdb_context *tdb, int ltype)
+{
+ int i, ret = 0;
+
+ if (tdb->transaction == NULL) {
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n"));
+ return -1;
+ }
+
+ if (tdb->transaction->nesting != 0) {
+ tdb->transaction->transaction_error = 1;
+ tdb->transaction->nesting--;
+ return 0;
+ }
+
+ tdb->map_size = tdb->transaction->old_map_size;
+
+ /* free all the transaction blocks */
+ for (i=0;i<tdb->transaction->num_blocks;i++) {
+ if (tdb->transaction->blocks[i] != NULL) {
+ free(tdb->transaction->blocks[i]);
+ }
+ }
+ SAFE_FREE(tdb->transaction->blocks);
+
+ if (tdb->transaction->magic_offset) {
+ const struct tdb_methods *methods = tdb->transaction->io_methods;
+ uint32_t invalid = TDB_RECOVERY_INVALID_MAGIC;
+
+ /* remove the recovery marker */
+ if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &invalid, 4) == -1 ||
+ transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) {
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n"));
+ ret = -1;
+ }
+ }
+
+ if (tdb->transaction->open_lock_taken) {
+ tdb_brunlock(tdb, F_WRLCK, OPEN_LOCK, 1);
+ tdb->transaction->open_lock_taken = false;
+ }
+
+ /* remove any global lock created during the transaction */
+ if (tdb->allrecord_lock.count != 0) {
+ tdb_brunlock(tdb, tdb->allrecord_lock.ltype,
+ FREELIST_TOP, 4*tdb->header.hash_size);
+ tdb->allrecord_lock.count = 0;
+ }
+
+ /* remove any locks created during the transaction */
+ if (tdb->num_locks != 0) {
+ for (i=0;i<tdb->num_lockrecs;i++) {
+ tdb_brunlock(tdb, tdb->lockrecs[i].ltype,
+ tdb->lockrecs[i].off, 1);
+ }
+ tdb->num_locks = 0;
+ tdb->num_lockrecs = 0;
+ SAFE_FREE(tdb->lockrecs);
+ }
+
+ /* restore the normal io methods */
+ tdb->methods = tdb->transaction->io_methods;
+
+ tdb_brunlock(tdb, ltype, FREELIST_TOP, 0);
+ tdb_transaction_unlock(tdb, F_WRLCK);
+ SAFE_FREE(tdb->transaction->hash_heads);
+ SAFE_FREE(tdb->transaction);
+
+ return ret;
+}