repo/private: allow committing/aborting through a transaction guard
authorLuca BRUNO <luca.bruno@coreos.com>
Fri, 1 Oct 2021 16:04:02 +0000 (16:04 +0000)
committerLuca BRUNO <luca.bruno@coreos.com>
Tue, 5 Oct 2021 12:23:22 +0000 (12:23 +0000)
This enhances the auto-transaction logic, augmenting the scope of a
transaction guard.
It allows committing or aborting a transaction through its guard.
It also supports tracking the completion status of a transaction
guard, avoiding double commits/aborts, while retaining the auto-cleanup
logic.

src/libostree/ostree-repo-commit.c
src/libostree/ostree-repo-private.h
src/libostree/ostree-repo.c
src/libostree/ostree-sysroot-cleanup.c

index c87e8de8921d5a9bf45e638743e0e592458a9657..8ac963e746b44d77e51efebb9334b3737272563a 100644 (file)
@@ -1672,14 +1672,18 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
                                  GCancellable   *cancellable,
                                  GError        **error)
 {
+  g_assert (self != NULL);
+
   guint64 reserved_bytes = 0;
 
   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. */
-  g_autoptr(_OstreeRepoAutoTransaction) txn = self;
+  /* Set up to abort the transaction if we return early from this function.
+   * This needs to be manually built here due to a circular dependency. */
+  g_autoptr(OstreeRepoAutoTransaction) txn = g_malloc(sizeof(OstreeRepoAutoTransaction));
+  txn->repo = self;
   (void) txn; /* Add use to silence static analysis */
 
   memset (&self->txn.stats, 0, sizeof (OstreeRepoTransactionStats));
@@ -1736,7 +1740,7 @@ ostree_repo_prepare_transaction (OstreeRepo     *self,
     return FALSE;
 
   /* Success: do not abort the transaction when returning. */
-  txn = NULL; (void) txn;
+  txn->repo = NULL; (void) txn;
 
   if (out_transaction_resume)
     *out_transaction_resume = ret_transaction_resume;
index 67f755bde0b851f553a43bb249f1be95fde138c0..a2666dec6a8092bc45711a9af542b26e13f6d28a 100644 (file)
@@ -229,36 +229,6 @@ struct OstreeRepo {
   OstreeRepo *parent_repo;
 };
 
-/* Taken from flatpak; may be made into public API later */
-typedef OstreeRepo _OstreeRepoAutoTransaction;
-static inline void
-_ostree_repo_auto_transaction_cleanup (void *p)
-{
-  if (p == NULL)
-    return;
-  g_return_if_fail (OSTREE_IS_REPO (p));
-
-  OstreeRepo *repo = p;
-  g_autoptr(GError) error = NULL;
-
-  if (!ostree_repo_abort_transaction (repo, NULL, &error))
-    g_warning("Failed to auto-cleanup OSTree transaction: %s", error->message);
-
-  g_object_unref (repo);
-}
-
-static inline _OstreeRepoAutoTransaction *
-_ostree_repo_auto_transaction_start (OstreeRepo     *repo,
-                                     GCancellable   *cancellable,
-                                     GError        **error)
-{
-  if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
-    return NULL;
-
-  return (_OstreeRepoAutoTransaction *) g_object_ref (repo);
-}
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (_OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_cleanup)
-
 typedef struct {
   dev_t dev;
   ino_t ino;
@@ -544,4 +514,39 @@ _ostree_repo_verify_bindings (const char  *collection_id,
                               GVariant    *commit,
                               GError     **error);
 
+/**
+ * OstreeRepoAutoTransaction:
+ *
+ * A transaction guard for a specific #OstreeRepo. It can be explicitly
+ * completed through abort/commit. If the guard has not been completed
+ * beforehand, on cleanup it is automatically aborted.
+ *
+ * Taken from flatpak; may be made into public API later
+ */
+typedef struct
+{
+  OstreeRepo *repo;
+} OstreeRepoAutoTransaction;
+
+OstreeRepoAutoTransaction *
+_ostree_repo_auto_transaction_start (OstreeRepo     *repo,
+                                     GCancellable   *cancellable,
+                                     GError        **error);
+
+gboolean
+_ostree_repo_auto_transaction_abort (OstreeRepoAutoTransaction  *txn,
+                                     GCancellable               *cancellable,
+                                     GError                    **error);
+
+gboolean
+_ostree_repo_auto_transaction_commit (OstreeRepoAutoTransaction  *txn,
+                                      OstreeRepoTransactionStats *out_stats,
+                                      GCancellable               *cancellable,
+                                      GError                    **error);
+
+void
+_ostree_repo_auto_transaction_cleanup (void *p);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_cleanup);
+
 G_END_DECLS
index 42d2b0e08c395fc683ef6dc883a53d62b6d2316b..772eae26e55fe7cee0405d33303744d9ce142507 100644 (file)
@@ -711,6 +711,118 @@ ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *auto_lock)
     }
 }
 
+
+/**
+ * _ostree_repo_auto_transaction_start:
+ * @repo: an #OsreeRepo object
+ * @cancellable: Cancellable
+ * @error: a #GError
+ *
+ * Start a transaction and return a guard for it.
+ *
+ * Returns: (transfer full): an #OsreeRepoAutoTransaction guard on success,
+ * %NULL otherwise.
+ */
+OstreeRepoAutoTransaction *
+_ostree_repo_auto_transaction_start (OstreeRepo     *repo,
+                                     GCancellable   *cancellable,
+                                     GError        **error)
+{
+  g_assert (repo != NULL);
+
+  if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
+    return NULL;
+
+  OstreeRepoAutoTransaction *txn = g_malloc(sizeof(OstreeRepoAutoTransaction));
+  txn->repo = g_object_ref (repo);
+
+  return g_steal_pointer (&txn);
+}
+
+/**
+ * _ostree_repo_auto_transaction_abort:
+ * @txn: an #OsreeRepoAutoTransaction guard
+ * @cancellable: Cancellable
+ * @error: a #GError
+ *
+ * Abort a transaction, marking the related guard as completed.
+ *
+ * Returns: %TRUE on successful commit, %FALSE otherwise.
+ */
+gboolean
+_ostree_repo_auto_transaction_abort (OstreeRepoAutoTransaction  *txn,
+                                     GCancellable               *cancellable,
+                                     GError                    **error)
+{
+  g_assert (txn != NULL);
+
+  if (txn->repo == NULL) {
+    return glnx_throw (error, "transaction already completed");
+  }
+
+  if (!ostree_repo_abort_transaction (txn->repo, cancellable, error))
+    return FALSE;
+
+  g_clear_object (&txn->repo);
+
+  return TRUE;
+}
+
+/**
+ * _ostree_repo_auto_transaction_commit:
+ * @txn: an #OsreeRepoAutoTransaction guard
+ * @cancellable: Cancellable
+ * @error: a #GError
+ *
+ * Commit a transaction, marking the related guard as completed.
+ *
+ * Returns: %TRUE on successful aborting, %FALSE otherwise.
+ */
+gboolean
+_ostree_repo_auto_transaction_commit (OstreeRepoAutoTransaction  *txn,
+                                      OstreeRepoTransactionStats *out_stats,
+                                      GCancellable               *cancellable,
+                                      GError                    **error)
+{
+  g_assert (txn != NULL);
+
+  if (txn->repo == NULL) {
+    return glnx_throw (error, "transaction already completed");
+  }
+
+  if (!ostree_repo_commit_transaction (txn->repo, out_stats, cancellable, error))
+    return FALSE;
+
+  g_clear_object (&txn->repo);
+
+  return TRUE;
+}
+
+/**
+ * _ostree_repo_auto_transaction_cleanup:
+ * @p: pointer to an #OsreeRepoAutoTransaction guard
+ *
+ * Destroy a transaction guard. If the transaction has not yet been completed,
+ * it gets aborted.
+ */
+void
+_ostree_repo_auto_transaction_cleanup (void *p)
+{
+  if (p == NULL)
+    return;
+
+  OstreeRepoAutoTransaction *txn = p;
+  // Auto-abort only if transaction has not already been aborted/committed.
+  if (txn->repo != NULL)
+    {
+      g_autoptr(GError) error = NULL;
+      if (!_ostree_repo_auto_transaction_abort (txn, NULL, &error)) {
+        g_warning("Failed to auto-cleanup OSTree transaction: %s", error->message);
+        g_clear_object (&txn->repo);
+      }
+    }
+}
+
 static GFile *
 get_remotes_d_dir (OstreeRepo          *self,
                    GFile               *sysroot);
index 91381cb0c6ae6f7a8f590b46ba66e3e017722952..c22a6851edf8f56a1960d758878fcd411afec346 100644 (file)
@@ -445,7 +445,7 @@ generate_deployment_refs (OstreeSysroot       *self,
                            cancellable, error))
     return FALSE;
 
-  g_autoptr(_OstreeRepoAutoTransaction) txn =
+  g_autoptr(OstreeRepoAutoTransaction) txn =
     _ostree_repo_auto_transaction_start (repo, cancellable, error);
   if (!txn)
     return FALSE;
@@ -458,7 +458,7 @@ generate_deployment_refs (OstreeSysroot       *self,
 
       ostree_repo_transaction_set_refspec (repo, refname, ostree_deployment_get_csum (deployment));
     }
-  if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
+  if (!_ostree_repo_auto_transaction_commit (txn, NULL, cancellable, error))
     return FALSE;
 
   return TRUE;