summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2014-10-01 23:19:31 -0400
committerColin Walters <walters@verbum.org>2014-10-01 23:20:35 -0400
commit7ab1fb23696cf26ddf985f4077fb271be2705a44 (patch)
treeccd9ad3e1f72a9d20eef34677659d594f113bd9a
parent1b8759a67e7705b2dba4b6c4d99e0c7b0b82da41 (diff)
downloadostree-7ab1fb23696cf26ddf985f4077fb271be2705a44.tar.gz
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.
-rw-r--r--src/libostree/ostree-repo.c175
-rw-r--r--src/libostree/ostree-repo.h7
-rw-r--r--src/ostree/ot-builtin-pull-local.c68
3 files changed, 186 insertions, 64 deletions
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 7069d01d..0d24a967 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 de38ce43..856b764a 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 50b05a6f..bd9773b7 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))