[ostree] repo: Add a new iterator traversal API for commits
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree] repo: Add a new iterator traversal API for commits
- Date: Mon, 16 Feb 2015 15:15:25 +0000 (UTC)
commit 0f74ed62b72a659191bf070911133d731ecd59da
Author: Colin Walters <walters verbum org>
Date: Wed Feb 11 03:25:17 2015 -0500
repo: Add a new iterator traversal API for commits
This is a more optimized version of the GFile * APIs, and is now used
internally by the previous ostree_repo_traverse().
src/libostree/ostree-repo-private.h | 9 +
src/libostree/ostree-repo-traverse.c | 441 ++++++++++++++++++++++++++--------
src/libostree/ostree-repo.h | 53 ++++
3 files changed, 408 insertions(+), 95 deletions(-)
---
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index d7ea7ac..c4db502 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -154,6 +154,15 @@ _ostree_repo_file_new_root (OstreeRepo *repo,
const char *contents_checksum,
const char *metadata_checksum);
+gboolean
+_ostree_repo_traverse_dirtree_internal (OstreeRepo *repo,
+ const char *dirtree_checksum,
+ int recursion_depth,
+ GHashTable *inout_reachable,
+ GHashTable *inout_content_names,
+ GCancellable *cancellable,
+ GError **error);
+
OstreeRepoCommitFilterResult
_ostree_repo_commit_modifier_apply (OstreeRepo *self,
OstreeRepoCommitModifier *modifier,
diff --git a/src/libostree/ostree-repo-traverse.c b/src/libostree/ostree-repo-traverse.c
index 122fcce..6638b6b 100644
--- a/src/libostree/ostree-repo-traverse.c
+++ b/src/libostree/ostree-repo-traverse.c
@@ -26,6 +26,261 @@
#include "otutil.h"
#include "libgsystem.h"
+struct _OstreeRepoRealCommitTraverseIter {
+ gboolean initialized;
+ OstreeRepo *repo;
+ GVariant *commit;
+ GVariant *current_dir;
+ const char *name;
+ OstreeRepoCommitIterResult state;
+ guint idx;
+ char checksum_content[65];
+ char checksum_meta[65];
+};
+
+/**
+ * ostree_repo_commit_traverse_iter_init_commit:
+ * @iter: An iter
+ * @repo: A repo
+ * @commit: Variant of type %OSTREE_OBJECT_TYPE_COMMIT
+ * @flags: Flags
+ * @error: Error
+ *
+ * Initialize (in place) an iterator over the root of a commit object.
+ */
+gboolean
+ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *iter,
+ OstreeRepo *repo,
+ GVariant *commit,
+ OstreeRepoCommitTraverseFlags flags,
+ GError **error)
+{
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+ gboolean ret = FALSE;
+ const guchar *csum;
+ gs_unref_variant GVariant *meta_csum_bytes = NULL;
+ gs_unref_variant GVariant *content_csum_bytes = NULL;
+
+ memset (real, 0, sizeof (*real));
+ real->initialized = TRUE;
+ real->repo = g_object_ref (repo);
+ real->commit = g_variant_ref (commit);
+ real->current_dir = NULL;
+ real->idx = 0;
+
+ g_variant_get_child (commit, 6, "@ay", &content_csum_bytes);
+ csum = ostree_checksum_bytes_peek_validate (content_csum_bytes, error);
+ if (!csum)
+ goto out;
+ ostree_checksum_inplace_from_bytes (csum, real->checksum_content);
+
+ g_variant_get_child (commit, 7, "@ay", &meta_csum_bytes);
+ csum = ostree_checksum_bytes_peek_validate (meta_csum_bytes, error);
+ if (!csum)
+ goto out;
+ ostree_checksum_inplace_from_bytes (csum, real->checksum_meta);
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+/**
+ * ostree_repo_commit_traverse_iter_init_dirtree:
+ * @iter: An iter
+ * @repo: A repo
+ * @commit: Variant of type %OSTREE_OBJECT_TYPE_DIR_TREE
+ * @flags: Flags
+ * @error: Error
+ *
+ * Initialize (in place) an iterator over a directory tree.
+ */
+gboolean
+ostree_repo_commit_traverse_iter_init_dirtree (OstreeRepoCommitTraverseIter *iter,
+ OstreeRepo *repo,
+ GVariant *dirtree,
+ OstreeRepoCommitTraverseFlags flags,
+ GError **error)
+{
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+
+ memset (real, 0, sizeof (*real));
+ real->initialized = TRUE;
+ real->repo = g_object_ref (repo);
+ real->current_dir = g_variant_ref (dirtree);
+ real->idx = 0;
+
+ return TRUE;
+}
+
+/**
+ * ostree_repo_commit_traverse_iter_next:
+ * @iter: An iter
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Step the interator to the next item. Files will be returned first,
+ * then subdirectories. Call this in a loop; upon encountering
+ * %OSTREE_REPO_COMMIT_ITER_RESULT_END, there will be no more files or
+ * directories. If %OSTREE_REPO_COMMIT_ITER_RESULT_DIR is returned,
+ * then call ostree_repo_commit_traverse_iter_get_dir() to retrieve
+ * data for that directory. Similarly, if
+ * %OSTREE_REPO_COMMIT_ITER_RESULT_FILE is returned, call
+ * ostree_repo_commit_traverse_iter_get_file().
+ *
+ * If %OSTREE_REPO_COMMIT_ITER_RESULT_ERROR is returned, it is a
+ * program error to call any further API on @iter except for
+ * ostree_repo_commit_traverse_iter_clear().
+ */
+OstreeRepoCommitIterResult
+ostree_repo_commit_traverse_iter_next (OstreeRepoCommitTraverseIter *iter,
+ GCancellable *cancellable,
+ GError **error)
+{
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+ OstreeRepoCommitIterResult res = OSTREE_REPO_COMMIT_ITER_RESULT_ERROR;
+
+ if (!real->current_dir)
+ {
+ if (!ostree_repo_load_variant (real->repo, OSTREE_OBJECT_TYPE_DIR_TREE,
+ real->checksum_content,
+ &real->current_dir,
+ error))
+ goto out;
+ res = OSTREE_REPO_COMMIT_ITER_RESULT_DIR;
+ }
+ else
+ {
+ guint nfiles;
+ guint ndirs;
+ guint idx;
+ const guchar *csum;
+ gs_unref_variant GVariant *content_csum_v = NULL;
+ gs_unref_variant GVariant *meta_csum_v = NULL;
+ gs_unref_variant GVariant *files_variant = NULL;
+ gs_unref_variant GVariant *dirs_variant = NULL;
+
+ files_variant = g_variant_get_child_value (real->current_dir, 0);
+ dirs_variant = g_variant_get_child_value (real->current_dir, 1);
+
+ nfiles = g_variant_n_children (files_variant);
+ ndirs = g_variant_n_children (dirs_variant);
+ if (real->idx < nfiles)
+ {
+ idx = real->idx;
+ g_variant_get_child (files_variant, idx, "(&s ay)",
+ &real->name,
+ &content_csum_v);
+
+ csum = ostree_checksum_bytes_peek_validate (content_csum_v, error);
+ if (!csum)
+ goto out;
+ ostree_checksum_inplace_from_bytes (csum, real->checksum_content);
+
+ res = OSTREE_REPO_COMMIT_ITER_RESULT_FILE;
+
+ real->idx++;
+ }
+ else if (real->idx < nfiles + ndirs)
+ {
+ idx = real->idx - nfiles;
+
+ g_variant_get_child (dirs_variant, idx, "(&s ay@ay)",
+ &real->name, &content_csum_v, &meta_csum_v);
+
+ csum = ostree_checksum_bytes_peek_validate (content_csum_v, error);
+ if (!csum)
+ goto out;
+ ostree_checksum_inplace_from_bytes (csum, real->checksum_content);
+
+ csum = ostree_checksum_bytes_peek_validate (meta_csum_v, error);
+ if (!csum)
+ goto out;
+ ostree_checksum_inplace_from_bytes (csum, real->checksum_meta);
+
+ res = OSTREE_REPO_COMMIT_ITER_RESULT_DIR;
+
+ real->idx++;
+ }
+ else
+ res = OSTREE_REPO_COMMIT_ITER_RESULT_END;
+ }
+
+ real->state = res;
+ out:
+ return res;
+}
+
+/**
+ * ostree_repo_commit_traverse_iter_get_file:
+ * @iter: An iter
+ * @out_name: (out) (transfer none): Name of current file
+ * @out_checksum: (out) (transfer none): Checksum of current file
+ *
+ * Return information on the current file. This function may only be
+ * called if %OSTREE_REPO_COMMIT_ITER_RESULT_FILE was returned from
+ * ostree_repo_commit_traverse_iter_next().
+ */
+void
+ostree_repo_commit_traverse_iter_get_file (OstreeRepoCommitTraverseIter *iter,
+ char **out_name,
+ char **out_checksum)
+{
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+ *out_name = (char*)real->name;
+ *out_checksum = (char*)real->checksum_content;
+}
+
+/**
+ * ostree_repo_commit_traverse_iter_get_dir:
+ * @iter: An iter
+ * @out_name: (out) (transfer none): Name of current dir
+ * @out_content_checksum: (out) (transfer none): Checksum of current content
+ * @out_meta_checksum: (out) (transfer none): Checksum of current metadata
+ *
+ * Return information on the current directory. This function may
+ * only be called if %OSTREE_REPO_COMMIT_ITER_RESULT_DIR was returned
+ * from ostree_repo_commit_traverse_iter_next().
+ */
+void
+ostree_repo_commit_traverse_iter_get_dir (OstreeRepoCommitTraverseIter *iter,
+ char **out_name,
+ char **out_content_checksum,
+ char **out_meta_checksum)
+{
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+ *out_name = (char*)real->name;
+ *out_content_checksum = (char*)real->checksum_content;
+ *out_meta_checksum = (char*)real->checksum_meta;
+}
+
+void
+ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter)
+{
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+ g_clear_pointer (&real->commit, g_variant_unref);
+ g_clear_pointer (&real->current_dir, g_variant_unref);
+}
+
+void
+ostree_repo_commit_traverse_iter_cleanup (void *p)
+{
+ OstreeRepoCommitTraverseIter *iter = p;
+ struct _OstreeRepoRealCommitTraverseIter *real =
+ (struct _OstreeRepoRealCommitTraverseIter*)iter;
+ if (real->initialized)
+ {
+ ostree_repo_commit_traverse_iter_clear (iter);
+ real->initialized = FALSE;
+ }
+}
+
/**
* ostree_repo_traverse_new_reachable:
*
@@ -42,82 +297,68 @@ ostree_repo_traverse_new_reachable (void)
}
static gboolean
-traverse_dirtree_internal (OstreeRepo *repo,
- const char *dirtree_checksum,
- int recursion_depth,
- GHashTable *inout_reachable,
- GCancellable *cancellable,
- GError **error)
+traverse_dirtree (OstreeRepo *repo,
+ const char *checksum,
+ GHashTable *inout_reachable,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean
+traverse_iter (OstreeRepo *repo,
+ OstreeRepoCommitTraverseIter *iter,
+ GHashTable *inout_reachable,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
- int n, i;
- gs_unref_variant GVariant *key = NULL;
- gs_unref_variant GVariant *tree = NULL;
- gs_unref_variant GVariant *files_variant = NULL;
- gs_unref_variant GVariant *dirs_variant = NULL;
- gs_unref_variant GVariant *csum_v = NULL;
- gs_unref_variant GVariant *content_csum_v = NULL;
- gs_unref_variant GVariant *metadata_csum_v = NULL;
- gs_free char *tmp_checksum = NULL;
- if (recursion_depth > OSTREE_MAX_RECURSION)
+ while (TRUE)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Maximum recursion limit reached during traversal");
- goto out;
- }
-
- if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_DIR_TREE, dirtree_checksum, &tree,
error))
- goto out;
-
- if (!tree)
- return TRUE;
+ gs_unref_variant GVariant *key = NULL;
+ OstreeRepoCommitIterResult iterres =
+ ostree_repo_commit_traverse_iter_next (iter, cancellable, error);
+
+ if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_ERROR)
+ goto out;
+ else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_END)
+ break;
+ else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_FILE)
+ {
+ char *name;
+ char *checksum;
- key = ostree_object_name_serialize (dirtree_checksum, OSTREE_OBJECT_TYPE_DIR_TREE);
- if (!g_hash_table_lookup (inout_reachable, key))
- {
- g_hash_table_insert (inout_reachable, key, key);
- key = NULL;
+ ostree_repo_commit_traverse_iter_get_file (iter, &name, &checksum);
- /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
- files_variant = g_variant_get_child_value (tree, 0);
- n = g_variant_n_children (files_variant);
- for (i = 0; i < n; i++)
- {
- const char *filename;
-
- g_clear_pointer (&csum_v, (GDestroyNotify) g_variant_unref);
- g_variant_get_child (files_variant, i, "(&s ay)", &filename, &csum_v);
- g_free (tmp_checksum);
- tmp_checksum = ostree_checksum_from_bytes_v (csum_v);
- key = ostree_object_name_serialize (tmp_checksum, OSTREE_OBJECT_TYPE_FILE);
+ key = ostree_object_name_serialize (checksum, OSTREE_OBJECT_TYPE_FILE);
g_hash_table_replace (inout_reachable, key, key);
key = NULL;
}
-
- dirs_variant = g_variant_get_child_value (tree, 1);
- n = g_variant_n_children (dirs_variant);
- for (i = 0; i < n; i++)
+ else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_DIR)
{
- const char *dirname;
-
- g_clear_pointer (&content_csum_v, (GDestroyNotify) g_variant_unref);
- g_clear_pointer (&metadata_csum_v, (GDestroyNotify) g_variant_unref);
- g_variant_get_child (dirs_variant, i, "(&s ay@ay)",
- &dirname, &content_csum_v, &metadata_csum_v);
-
- g_free (tmp_checksum);
- tmp_checksum = ostree_checksum_from_bytes_v (content_csum_v);
- if (!traverse_dirtree_internal (repo, tmp_checksum, recursion_depth + 1,
- inout_reachable, cancellable, error))
- goto out;
+ char *name;
+ char *content_checksum;
+ char *meta_checksum;
- g_free (tmp_checksum);
- tmp_checksum = ostree_checksum_from_bytes_v (metadata_csum_v);
- key = ostree_object_name_serialize (tmp_checksum, OSTREE_OBJECT_TYPE_DIR_META);
+ ostree_repo_commit_traverse_iter_get_dir (iter, &name, &content_checksum,
+ &meta_checksum);
+
+ key = ostree_object_name_serialize (meta_checksum, OSTREE_OBJECT_TYPE_DIR_META);
g_hash_table_replace (inout_reachable, key, key);
key = NULL;
+
+ key = ostree_object_name_serialize (content_checksum, OSTREE_OBJECT_TYPE_DIR_TREE);
+ if (!g_hash_table_lookup (inout_reachable, key))
+ {
+ g_hash_table_replace (inout_reachable, key, key);
+ key = NULL;
+
+ if (!traverse_dirtree (repo, content_checksum, inout_reachable,
+ cancellable, error))
+ goto out;
+ }
}
+ else
+ g_assert_not_reached ();
}
ret = TRUE;
@@ -125,6 +366,36 @@ traverse_dirtree_internal (OstreeRepo *repo,
return ret;
}
+static gboolean
+traverse_dirtree (OstreeRepo *repo,
+ const char *checksum,
+ GHashTable *inout_reachable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gs_unref_variant GVariant *dirtree = NULL;
+ ostree_cleanup_repo_commit_traverse_iter
+ OstreeRepoCommitTraverseIter iter = { 0, };
+
+ if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_DIR_TREE,
+ checksum, &dirtree,
+ error))
+ goto out;
+
+ if (!ostree_repo_commit_traverse_iter_init_dirtree (&iter, repo, dirtree,
+ OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
+ error))
+ goto out;
+
+ if (!traverse_iter (repo, &iter, inout_reachable, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
/**
* ostree_repo_traverse_commit_union: (skip)
* @repo: Repo
@@ -151,58 +422,38 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo,
while (TRUE)
{
gboolean recurse = FALSE;
- gs_unref_variant GVariant *meta_csum_bytes = NULL;
- gs_unref_variant GVariant *content_csum_bytes = NULL;
gs_unref_variant GVariant *key = NULL;
gs_unref_variant GVariant *commit = NULL;
+ ostree_cleanup_repo_commit_traverse_iter
+ OstreeRepoCommitTraverseIter iter = { 0, };
key = ostree_object_name_serialize (commit_checksum, OSTREE_OBJECT_TYPE_COMMIT);
if (g_hash_table_contains (inout_reachable, key))
break;
- /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
- if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum, &commit,
error))
+ if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_COMMIT,
+ commit_checksum, &commit,
+ error))
goto out;
-
+
/* Just return if the parent isn't found; we do expect most
* people to have partial repositories.
*/
if (!commit)
break;
-
- g_hash_table_add (inout_reachable, key);
- key = NULL;
- g_variant_get_child (commit, 7, "@ay", &meta_csum_bytes);
- g_free (tmp_checksum);
- if (G_UNLIKELY (g_variant_n_children (meta_csum_bytes) == 0))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted commit '%s'; invalid tree metadata",
- commit_checksum);
- goto out;
- }
-
- tmp_checksum = ostree_checksum_from_bytes_v (meta_csum_bytes);
- key = ostree_object_name_serialize (tmp_checksum, OSTREE_OBJECT_TYPE_DIR_META);
- g_hash_table_replace (inout_reachable, key, key);
+ g_hash_table_add (inout_reachable, key);
key = NULL;
- g_variant_get_child (commit, 6, "@ay", &content_csum_bytes);
- g_free (tmp_checksum);
- if (G_UNLIKELY (g_variant_n_children (content_csum_bytes) == 0))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted commit '%s'; invalid tree content",
- commit_checksum);
- goto out;
- }
-
- tmp_checksum = ostree_checksum_from_bytes_v (content_csum_bytes);
- if (!traverse_dirtree_internal (repo, tmp_checksum, 0, inout_reachable, cancellable, error))
+ if (!ostree_repo_commit_traverse_iter_init_commit (&iter, repo, commit,
+ OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
+ error))
goto out;
+ if (!traverse_iter (repo, &iter, inout_reachable, cancellable, error))
+ goto out;
+
if (maxdepth == -1 || maxdepth > 0)
{
g_free (tmp_checksum);
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 0ecf9bd..c59364a 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -536,6 +536,59 @@ gboolean ostree_repo_traverse_commit_union (OstreeRepo *repo,
GCancellable *cancellable,
GError **error);
+struct _OstreeRepoCommitTraverseIter {
+ gboolean initialized;
+ gpointer dummy[10];
+ char dummy_checksum_data[65*2];
+};
+
+typedef struct _OstreeRepoCommitTraverseIter OstreeRepoCommitTraverseIter;
+
+typedef enum {
+ OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE = (1 << 0)
+} OstreeRepoCommitTraverseFlags;
+
+gboolean
+ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *iter,
+ OstreeRepo *repo,
+ GVariant *commit,
+ OstreeRepoCommitTraverseFlags flags,
+ GError **error);
+
+gboolean
+ostree_repo_commit_traverse_iter_init_dirtree (OstreeRepoCommitTraverseIter *iter,
+ OstreeRepo *repo,
+ GVariant *dirtree,
+ OstreeRepoCommitTraverseFlags flags,
+ GError **error);
+
+typedef enum {
+ OSTREE_REPO_COMMIT_ITER_RESULT_ERROR,
+ OSTREE_REPO_COMMIT_ITER_RESULT_END,
+ OSTREE_REPO_COMMIT_ITER_RESULT_FILE,
+ OSTREE_REPO_COMMIT_ITER_RESULT_DIR
+} OstreeRepoCommitIterResult;
+
+OstreeRepoCommitIterResult ostree_repo_commit_traverse_iter_next (OstreeRepoCommitTraverseIter *iter,
+ GCancellable *cancellable,
+ GError **error);
+
+void ostree_repo_commit_traverse_iter_get_file (OstreeRepoCommitTraverseIter *iter,
+ char **out_name,
+ char **out_checksum);
+
+void ostree_repo_commit_traverse_iter_get_dir (OstreeRepoCommitTraverseIter *iter,
+ char **out_name,
+ char **out_content_checksum,
+ char **out_meta_checksum);
+
+void
+ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter);
+
+void ostree_repo_commit_traverse_iter_cleanup (void *p);
+
+#define ostree_cleanup_repo_commit_traverse_iter __attribute__
((cleanup(ostree_repo_commit_traverse_iter_cleanup)))
+
/**
* OstreeRepoPruneFlags:
* @OSTREE_REPO_PRUNE_FLAGS_NONE: No special options for pruning
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]