[ostree] Add API to directly link() objects between repositories



commit 7ab1fb23696cf26ddf985f4077fb271be2705a44
Author: Colin Walters <walters verbum org>
Date:   Wed Oct 1 23:19:31 2014 -0400

    Add API to directly link() objects between repositories
    
    And use it in pull-local.  As one might expect, this is blazingly fast
    if they're on the same filesystem.
    
    I'll be using this to "promote" builds between different repositories.

 src/libostree/ostree-repo.c        |  175 ++++++++++++++++++++++++++++++++++++
 src/libostree/ostree-repo.h        |    7 ++
 src/ostree/ot-builtin-pull-local.c |   68 +-------------
 3 files changed, 186 insertions(+), 64 deletions(-)
---
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 7069d01..0d24a96 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -1632,6 +1632,181 @@ ostree_repo_delete_object (OstreeRepo           *self,
   return ret;
 }
 
+static gboolean
+copy_detached_metadata (OstreeRepo    *self,
+                        OstreeRepo    *source,
+                        const char   *checksum,
+                        GCancellable  *cancellable,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_variant GVariant *detached_meta = NULL;
+          
+  if (!ostree_repo_read_commit_detached_metadata (source,
+                                                  checksum, &detached_meta,
+                                                  cancellable, error))
+    goto out;
+
+  if (detached_meta)
+    {
+      if (!ostree_repo_write_commit_detached_metadata (self,
+                                                       checksum, detached_meta,
+                                                       cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+import_one_object_copy (OstreeRepo    *self,
+                        OstreeRepo    *source,
+                        const char   *checksum,
+                        OstreeObjectType objtype,
+                        GCancellable  *cancellable,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  guint64 length;
+  gs_unref_object GInputStream *object = NULL;
+
+  if (!ostree_repo_load_object_stream (source, objtype, checksum,
+                                       &object, &length,
+                                       cancellable, error))
+    goto out;
+
+  if (objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      if (!ostree_repo_write_content_trusted (self, checksum,
+                                              object, length,
+                                              cancellable, error))
+        goto out;
+    }
+  else
+    {
+      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
+        {
+          if (!copy_detached_metadata (self, source, checksum, cancellable, error))
+            goto out;
+        }
+      if (!ostree_repo_write_metadata_stream_trusted (self, objtype,
+                                                      checksum, object, length,
+                                                      cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+import_one_object_link (OstreeRepo    *self,
+                        OstreeRepo    *source,
+                        const char   *checksum,
+                        OstreeObjectType objtype,
+                        gboolean       *out_was_supported,
+                        GCancellable  *cancellable,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
+
+  _ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
+
+  if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_path_buf, cancellable, error))
+    goto out;
+
+  *out_was_supported = TRUE;
+  if (linkat (source->objects_dir_fd, loose_path_buf, self->objects_dir_fd, loose_path_buf, 0) != 0)
+    {
+      if (errno == EEXIST)
+        {
+          ret = TRUE;
+        }
+      else if (errno == EMLINK || errno == EXDEV || errno == EPERM)
+        {
+          /* EMLINK, EXDEV and EPERM shouldn't be fatal; we just can't do the
+           * optimization of hardlinking instead of copying.
+           */
+          *out_was_supported = FALSE;
+          ret = TRUE;
+        }
+      else
+        ot_util_set_error_from_errno (error, errno);
+      
+      goto out;
+    }
+
+  if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
+    {
+      if (!copy_detached_metadata (self, source, checksum, cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * ostree_repo_import_object_from:
+ * @self: Destination repo
+ * @source: Source repo
+ * @objtype: Object type
+ * @checksum: checksum
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Copy object named by @objtype and @checksum into @self from the
+ * source repository @source.  If both repositories are of the same
+ * type and on the same filesystem, this will simply be a fast Unix
+ * hard link operation.
+ *
+ * Otherwise, a copy will be performed.
+ */
+gboolean
+ostree_repo_import_object_from (OstreeRepo           *self,
+                                OstreeRepo           *source,
+                                OstreeObjectType      objtype,
+                                const char           *checksum, 
+                                GCancellable         *cancellable,
+                                GError              **error)
+{
+  gboolean ret = FALSE;
+  gboolean hardlink_was_supported = FALSE;
+      
+  if (self->mode == source->mode)
+    {
+      if (!import_one_object_link (self, source, checksum, objtype,
+                                   &hardlink_was_supported,
+                                   cancellable, error))
+        goto out;
+    }
+
+  if (!hardlink_was_supported)
+    {
+      gboolean has_object;
+
+      if (!ostree_repo_has_object (self, objtype, checksum, &has_object,
+                                   cancellable, error))
+        goto out;
+  
+      if (!has_object)
+        {
+          if (!import_one_object_copy (self, source, checksum, objtype,
+                                       cancellable, error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
 /**
  * ostree_repo_query_object_storage_size:
  * @self: Repo
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index de38ce4..856b764 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -265,6 +265,13 @@ gboolean      ostree_repo_query_object_storage_size (OstreeRepo           *self,
                                                      GCancellable         *cancellable,
                                                      GError              **error);
 
+gboolean      ostree_repo_import_object_from (OstreeRepo           *self,
+                                              OstreeRepo           *source,
+                                              OstreeObjectType      objtype,
+                                              const char           *sha256, 
+                                              GCancellable         *cancellable,
+                                              GError              **error);
+
 gboolean      ostree_repo_delete_object (OstreeRepo           *self,
                                          OstreeObjectType      objtype,
                                          const char           *sha256, 
diff --git a/src/ostree/ot-builtin-pull-local.c b/src/ostree/ot-builtin-pull-local.c
index 50b05a6..bd9773b 100644
--- a/src/ostree/ot-builtin-pull-local.c
+++ b/src/ostree/ot-builtin-pull-local.c
@@ -55,61 +55,6 @@ termination_condition (OtLocalCloneData  *self)
   return g_atomic_int_get (&self->n_objects_checked) == self->n_objects_to_check;
 }
 
-static gboolean
-import_one_object (OtLocalCloneData *data,
-                   const char   *checksum,
-                   OstreeObjectType objtype,
-                   GCancellable  *cancellable,
-                   GError        **error)
-{
-  gboolean ret = FALSE;
-  guint64 length;
-  gs_unref_object GInputStream *object = NULL;
-  
-  if (!ostree_repo_load_object_stream (data->src_repo, objtype, checksum,
-                                       &object, &length,
-                                       cancellable, error))
-    goto out;
-
-  if (objtype == OSTREE_OBJECT_TYPE_FILE)
-    {
-      if (!ostree_repo_write_content_trusted (data->dest_repo, checksum,
-                                              object, length,
-                                              cancellable, error))
-        goto out;
-    }
-  else
-    {
-      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
-        {
-          gs_unref_variant GVariant *detached_meta = NULL;
-          
-          if (!ostree_repo_read_commit_detached_metadata (data->src_repo,
-                                                          checksum, &detached_meta,
-                                                          cancellable, error))
-            goto out;
-
-          if (detached_meta)
-            {
-              if (!ostree_repo_write_commit_detached_metadata (data->dest_repo,
-                                                               checksum, detached_meta,
-                                                               cancellable, error))
-                goto out;
-            }
-        }
-      if (!ostree_repo_write_metadata_stream_trusted (data->dest_repo, objtype,
-                                                      checksum, object, length,
-                                                      cancellable, error))
-        goto out;
-    }
-
-  g_atomic_int_inc (&data->n_objects_copied);
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
 static void
 import_one_object_thread (gpointer   object,
                           gpointer   user_data)
@@ -120,21 +65,16 @@ import_one_object_thread (gpointer   object,
   GError **error = &local_error;
   const char *checksum;
   OstreeObjectType objtype;
-  gboolean has_object;
   GCancellable *cancellable = NULL;
 
   ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
 
-  if (!ostree_repo_has_object (data->dest_repo, objtype, checksum, &has_object,
-                               cancellable, error))
+  if (!ostree_repo_import_object_from (data->dest_repo, data->src_repo,
+                                       objtype, checksum, cancellable, error))
     goto out;
+ 
+  g_atomic_int_inc (&data->n_objects_copied);
 
-  if (!has_object)
-    {
-      if (!import_one_object (data, checksum, objtype, cancellable, error))
-        goto out;
-    }
-  
  out:
   g_atomic_int_add (&data->n_objects_checked, 1);
   if (termination_condition (data))


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