repo/private: allow committing/aborting through a transaction guard

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.
This commit is contained in:
Luca BRUNO 2021-10-01 16:04:02 +00:00
parent 5bf4b1dabc
commit c987534595
No known key found for this signature in database
GPG Key ID: A9834A2252078E4E
4 changed files with 156 additions and 35 deletions

View 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;

View 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

View 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);

View 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;