repo: Use per-transaction staging dir
authorAlexander Larsson <alexl@redhat.com>
Fri, 11 Dec 2015 14:48:29 +0000 (15:48 +0100)
committerAlexander Larsson <alexl@redhat.com>
Mon, 14 Dec 2015 07:38:51 +0000 (08:38 +0100)
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
src/libostree/ostree-repo-private.h
src/libostree/ostree-repo.c

index 6afc54a1f37559b1ffd9f84f784f78782d4bb39a..d861e6252a78325cc4ed14a1cc8cdbf8c5bbbdc0 100644 (file)
@@ -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);
 
index 8f57f95b2545b381fc306c08471a4030c53c33e3..1985b2ea1a6fd970f0b7e6a48946fa65bd4f4c32 100644 (file)
@@ -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;
index bc03719a6b5942b953dda2b2641b6c3bb19ff005..57d81c644e8fbcdcd7f07bf5a846c9f3a4449d09 100644 (file)
@@ -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;
 }
 
 /**