git » libjio » commit c6095ee

When a transaction fails to commit, try to rollback it before returning. The huge comment in the code says it all, read it.

author Alberto Bertogli
2004-07-15 02:22:46 UTC
committer Alberto Bertogli
2007-07-15 13:16:22 UTC
parent 691fd2200492eed1af50078045ad40ddc80ef4c0

When a transaction fails to commit, try to rollback it before returning. The huge comment in the code says it all, read it.

When a transaction fails to commit, try to rollback it before returning. The
huge comment in the code says it all, read it.

While at it, fix a very small but theoretically possible race: we need to
unlink the transaction before freeing it. Otherwise, we could:

task 1			task 2

free_tid(5)
			get_tid() (returns 5)
			write the transaction file
unlink(5)

And we have just unlinked a valid transaction!

trans.c +25 -1

diff --git a/trans.c b/trans.c
index 6bc3904..870001d 100644
--- a/trans.c
+++ b/trans.c
@@ -364,8 +364,8 @@ int jtrans_commit(struct jtrans *ts)
 	} else {
 		/* the transaction has been applied, so we cleanup and remove
 		 * it from the disk */
-		free_tid(ts->fs, ts->id);
 		unlink(name);
+		free_tid(ts->fs, ts->id);
 	}
 
 	/* mark the transaction as commited, _after_ it was removed */
@@ -373,6 +373,30 @@ int jtrans_commit(struct jtrans *ts)
 
 
 exit:
+	/* If the transaction failed we try to recover by rollbacking it
+	 * NOTE: on extreme conditions (ENOSPC/disk failure) this can fail
+	 * too! There's nothing much we can do in that case, the caller should
+	 * take care of it by itself.
+	 * The transaction file might be OK at this point, so the data could
+	 * be recovered by a posterior jfsck(); however, that's not what the
+	 * user expects (after all, if we return failure, new data should
+	 * never appear), so we remove the transaction file.
+	 * Transactions that were successfuly recovered by rollbacking them
+	 * will have J_ROLLBACKED in their flags, so the caller can verify if
+	 * the failure was recovered or not. */
+	if (!(ts->flags & J_COMMITED)) {
+		unlink(name);
+		free_tid(ts->fs, ts->id);
+
+		rv = ts->flags;
+		ts->flags = ts->flags | J_NOLOCK;
+		if (jtrans_rollback(ts) >= 0) {
+			ts->flags = rv | J_ROLLBACKED;
+		} else {
+			ts->flags = rv;
+		}
+	}
+
 	close(fd);
 	for (op = ts->op; op != NULL; op = op->next) {
 		if (op->locked)