[ostree] repo: Add _ostree_repo_allocate_tmpdir helper



commit be19c888619f5113cc4b12c01df2e51ccf4375fd
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Dec 11 18:43:05 2015 +0100

    repo: Add _ostree_repo_allocate_tmpdir helper
    
    This creates a subdirectory of the tmp dir with a selected prefix,
    and takes a lockfile to ensure that nobody else is using the same directory.
    However, if a directory with the same prefix already exists and is
    not locked that is used instead.
    
    The later is useful if you want to support some kind of resumed operation
    on the tmpdir.
    
    touch reused dirs
    
    https://bugzilla.gnome.org/show_bug.cgi?id=757611

 src/libostree/ostree-repo-private.h |   10 +++
 src/libostree/ostree-repo.c         |  131 +++++++++++++++++++++++++++++++++++
 2 files changed, 141 insertions(+), 0 deletions(-)
---
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index ccb648f..8f57f95 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -91,6 +91,16 @@ struct OstreeRepo {
 };
 
 gboolean
+_ostree_repo_allocate_tmpdir (int           tmpdir_dfd,
+                              const char   *tmpdir_prefix,
+                              char        **tmpdir_name_out,
+                              int          *tmpdir_fd_out,
+                              GLnxLockFile *file_lock_out,
+                              gboolean *    reusing_dir_out,
+                              GCancellable *cancellable,
+                              GError      **error);
+
+gboolean
 _ostree_repo_ensure_loose_objdir_at (int             dfd,
                                      const char     *loose_path,
                                      GCancellable   *cancellable,
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 1f1cba2..bc03719 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -4617,3 +4617,134 @@ ostree_repo_regenerate_summary (OstreeRepo     *self,
     g_list_free (ordered_keys);
   return ret;
 }
+
+
+/* This allocates and locks a subdir of the repo tmp dir, using an existing
+ * one with the same prefix if it is not in use already. */
+gboolean
+_ostree_repo_allocate_tmpdir (int tmpdir_dfd,
+                              const char *tmpdir_prefix,
+                              char **tmpdir_name_out,
+                              int *tmpdir_fd_out,
+                              GLnxLockFile *file_lock_out,
+                              gboolean *reusing_dir_out,
+                              GCancellable *cancellable,
+                              GError **error)
+{
+  gboolean reusing_dir = FALSE;
+  g_autofree char *tmpdir_name = NULL;
+  glnx_fd_close int tmpdir_fd = -1;
+  g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+
+  /* Look for existing tmpdir (with same prefix) to reuse */
+  if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error))
+    return FALSE;
+
+  while (TRUE)
+    {
+      gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, };
+      struct dirent *dent;
+      glnx_fd_close int existing_tmpdir_fd = -1;
+      g_autoptr(GError) local_error = NULL;
+      g_autofree char *lock_name = NULL;
+
+      if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
+        return FALSE;
+
+      if (dent == NULL)
+        break;
+
+      if (!g_str_has_prefix (dent->d_name, tmpdir_prefix))
+        continue;
+
+      /* Quickly skip non-dirs, if unknown we ignore ENOTDIR when opening instead */
+      if (dent->d_type != DT_UNKNOWN &&
+          dent->d_type != DT_DIR)
+        continue;
+
+      if (!glnx_opendirat (dfd_iter.fd, dent->d_name, FALSE,
+                           &existing_tmpdir_fd, &local_error))
+        {
+          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+            continue;
+          else
+            {
+              g_propagate_error (error, g_steal_pointer (&local_error));
+              return FALSE;
+            }
+        }
+
+      lock_name = g_strconcat (dent->d_name, "-lock", NULL);
+
+      /* We put the lock outside the dir, so we can hold the lock
+       * until the directory is fully removed */
+      if (!glnx_make_lock_file (dfd_iter.fd, lock_name, LOCK_EX | LOCK_NB,
+                                file_lock_out, &local_error))
+        {
+          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+            continue;
+          else
+            {
+              g_propagate_error (error, g_steal_pointer (&local_error));
+              return FALSE;
+            }
+        }
+
+      /* Touch the reused directory so that we don't accidentally
+       *   remove it due to being old when cleaning up the tmpdir
+       */
+      (void)futimens (existing_tmpdir_fd, NULL);
+
+      /* We found an existing tmpdir which we managed to lock */
+      tmpdir_name = g_strdup (dent->d_name);
+      tmpdir_fd = glnx_steal_fd (&existing_tmpdir_fd);
+      reusing_dir = TRUE;
+    }
+
+  while (tmpdir_name == NULL)
+    {
+      g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
+      glnx_fd_close int new_tmpdir_fd = -1;
+      g_autoptr(GError) local_error = NULL;
+      g_autofree char *lock_name = NULL;
+
+      /* No existing tmpdir found, create a new */
+
+      if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error))
+        return FALSE;
+
+      if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE,
+                           &new_tmpdir_fd, error))
+        return FALSE;
+
+      lock_name = g_strconcat (tmpdir_name_template, "-lock", NULL);
+
+      /* Note, at this point we can race with another process that picks up this
+       * new directory. If that happens we need to retry, making a new directory. */
+      if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
+                                file_lock_out, &local_error))
+        {
+          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+            continue;
+          else
+            {
+              g_propagate_error (error, g_steal_pointer (&local_error));
+              return FALSE;
+            }
+        }
+
+      tmpdir_name = g_steal_pointer (&tmpdir_name_template);
+      tmpdir_fd = glnx_steal_fd (&new_tmpdir_fd);
+    }
+
+  if (tmpdir_name_out)
+    *tmpdir_name_out = g_steal_pointer (&tmpdir_name);
+
+  if (tmpdir_fd_out)
+    *tmpdir_fd_out = glnx_steal_fd (&tmpdir_fd);
+
+  if (reusing_dir_out)
+    *reusing_dir_out = reusing_dir;
+
+  return TRUE;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]