diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index c87e8de8..8ac963e7 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -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; diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 67f755bd..a2666dec 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -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 diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 42d2b0e0..772eae26 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -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); diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index 91381cb0..c22a6851 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -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;