lib/repo-commit: Abort a transaction if preparing it fails
authorPhilip Withnall <withnall@endlessm.com>
Tue, 26 Jun 2018 13:39:16 +0000 (14:39 +0100)
committerAtomic Bot <atomic-devel@projectatomic.io>
Fri, 29 Jun 2018 19:32:44 +0000 (19:32 +0000)
If ostree_repo_prepare_transaction() fails, we should reset the
repository’s state so that the failed call was essentially idempotent.
Do that by calling ostree_repo_abort_transaction() on the failure path.

Typically, the way for preparing a transaction to fail is for its
GCancellable to be triggered, rather than because any of the operations
involved in preparing a transaction are particularly failure prone.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Closes: #1647
Approved by: cgwalters

src/libostree/ostree-repo-commit.c

index a3a6df44222c2a2d7ea723eddd5a535610aed36e..54bd8b6658d76375f1ff91e77e3b9f770374176f 100644 (file)
@@ -1614,9 +1614,15 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
                                  GCancellable   *cancellable,
                                  GError        **error)
 {
+  g_autoptr(_OstreeRepoAutoTransaction) txn = NULL;
 
   g_return_val_if_fail (self->in_transaction == FALSE, FALSE);
 
+  g_debug ("Preparing transaction in repository %p", self);
+
+  /* Set up to abort the transaction if we return early from this function. */
+  txn = self;
+
   memset (&self->txn.stats, 0, sizeof (OstreeRepoTransactionStats));
 
   self->txn_locked = _ostree_repo_lock_push (self, OSTREE_REPO_LOCK_SHARED,
@@ -1631,6 +1637,7 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
       struct statvfs stvfsbuf;
       if (TEMP_FAILURE_RETRY (fstatvfs (self->repo_dir_fd, &stvfsbuf)) < 0)
         return glnx_throw_errno_prefix (error, "fstatvfs");
+
       g_mutex_lock (&self->txn_lock);
       self->txn.blocksize = stvfsbuf.f_bsize;
       guint64 reserved_blocks = min_free_space_calculate_reserved_blocks (self, &stvfsbuf);
@@ -1663,6 +1670,9 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
                                      cancellable, error))
     return FALSE;
 
+  /* Success: do not abort the transaction when returning. */
+  txn = NULL;
+
   if (out_transaction_resume)
     *out_transaction_resume = ret_transaction_resume;
   return TRUE;
@@ -2150,6 +2160,8 @@ ostree_repo_commit_transaction (OstreeRepo                  *self,
 {
   g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
 
+  g_debug ("Committing transaction in repository %p", self);
+
   if ((self->test_error_flags & OSTREE_REPO_TEST_ERROR_PRE_COMMIT) > 0)
     return glnx_throw (error, "OSTREE_REPO_TEST_ERROR_PRE_COMMIT specified");
 
@@ -2234,6 +2246,8 @@ ostree_repo_abort_transaction (OstreeRepo     *self,
   if (!self->in_transaction)
     return TRUE;
 
+  g_debug ("Aborting transaction in repository %p", self);
+
   if (self->loose_object_devino_hash)
     g_hash_table_remove_all (self->loose_object_devino_hash);