From f771461b4a048d2fb9acde9d38f833438bc51a29 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 11 Dec 2015 15:48:29 +0100 Subject: [PATCH] repo: Use per-transaction staging dir Concurrent pulls break since we're sharing the staging directory for all transactions in the repo. This makes us use a per-transaction directory. However, in order for resumes to work we first look for existing staging directories and try to aquire an exclusive lock for them. If we can't find any staging directory or they are all already locked, then we create a new one. https://bugzilla.gnome.org/show_bug.cgi?id=757611 --- src/libostree/ostree-repo-commit.c | 51 ++++++++++------------------- src/libostree/ostree-repo-private.h | 2 ++ src/libostree/ostree-repo.c | 3 ++ 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 6afc54a1..d861e625 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -1161,47 +1161,28 @@ ostree_repo_prepare_transaction (OstreeRepo *self, { gboolean ret = FALSE; gboolean ret_transaction_resume = FALSE; - struct stat stbuf; + g_autofree char *stagedir_boot_id_prefix = NULL; + g_autofree char *stagedir_name = NULL; + glnx_fd_close int stagedir_fd = -1; + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; g_return_val_if_fail (self->in_transaction == FALSE, FALSE); - /* We used to create a `transaction` symbolic link, but it's now - * obsoleted by the per-commit .commitpartial files. We no longer - * create it, but let's still read it if it exists, as well as - * unlink it when we're done. - */ - if (fstatat (self->repo_dir_fd, "transaction", &stbuf, AT_SYMLINK_NOFOLLOW) != 0) - { - if (errno == ENOENT) - ret_transaction_resume = FALSE; - else - { - glnx_set_error_from_errno (error); - goto out; - } - } - else - ret_transaction_resume = TRUE; - memset (&self->txn_stats, 0, sizeof (OstreeRepoTransactionStats)); self->in_transaction = TRUE; - self->commit_stagedir_name = g_strconcat ("tmpobjects-", self->boot_id, NULL); - if (mkdirat (self->tmp_dir_fd, self->commit_stagedir_name, 0777) == -1) - { - int errsv = errno; - if (G_UNLIKELY (errsv != EEXIST)) - { - gs_set_error_from_errno (error, errsv); - goto out; - } - } + stagedir_boot_id_prefix = g_strconcat ("staging-", self->boot_id, "-", NULL); - if (!gs_opendirat (self->tmp_dir_fd, self->commit_stagedir_name, FALSE, - &self->commit_stagedir_fd, error)) + if (!_ostree_repo_allocate_tmpdir (self->tmp_dir_fd, + stagedir_boot_id_prefix, + &self->commit_stagedir_name, + &self->commit_stagedir_fd, + &self->commit_stagedir_lock, + &ret_transaction_resume, + cancellable, error)) goto out; - + ret = TRUE; if (out_transaction_resume) *out_transaction_resume = ret_transaction_resume; @@ -1485,6 +1466,8 @@ ostree_repo_commit_transaction (OstreeRepo *self, { (void) close (self->commit_stagedir_fd); self->commit_stagedir_fd = -1; + + glnx_release_lock_file (&self->commit_stagedir_lock); } g_clear_pointer (&self->commit_stagedir_name, g_free); @@ -1519,11 +1502,13 @@ ostree_repo_abort_transaction (OstreeRepo *self, g_hash_table_remove_all (self->loose_object_devino_hash); g_clear_pointer (&self->txn_refs, g_hash_table_destroy); - + if (self->commit_stagedir_fd != -1) { (void) close (self->commit_stagedir_fd); self->commit_stagedir_fd = -1; + + glnx_release_lock_file (&self->commit_stagedir_lock); } g_clear_pointer (&self->commit_stagedir_name, g_free); diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 8f57f95b..1985b2ea 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -21,6 +21,7 @@ #pragma once #include "ostree-repo.h" +#include "libglnx.h" #ifdef HAVE_LIBSOUP #include "ostree-fetcher.h" @@ -43,6 +44,7 @@ struct OstreeRepo { char *boot_id; int commit_stagedir_fd; char *commit_stagedir_name; + GLnxLockFile commit_stagedir_lock; GFile *repodir; int repo_dir_fd; diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index bc03719a..57d81c64 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -514,6 +514,7 @@ ostree_repo_finalize (GObject *object) if (self->commit_stagedir_fd != -1) (void) close (self->commit_stagedir_fd); g_free (self->commit_stagedir_name); + glnx_release_lock_file (&self->commit_stagedir_lock); g_clear_object (&self->tmp_dir); if (self->tmp_dir_fd) (void) close (self->tmp_dir_fd); @@ -674,6 +675,7 @@ static void ostree_repo_init (OstreeRepo *self) { static gsize gpgme_initialized; + GLnxLockFile empty_lockfile = GLNX_LOCK_FILE_INIT; if (g_once_init_enter (&gpgme_initialized)) { @@ -694,6 +696,7 @@ ostree_repo_init (OstreeRepo *self) self->commit_stagedir_fd = -1; self->objects_dir_fd = -1; self->uncompressed_objects_dir_fd = -1; + self->commit_stagedir_lock = empty_lockfile; } /**