diff --git a/Makefile-libostree.am b/Makefile-libostree.am index f2b688fa..99e85c29 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -31,6 +31,8 @@ libostree_la_SOURCES = src/libostree/ostree.h \ src/libostree/ostree-repo-file-enumerator.c \ src/libostree/ostree-repo-file-enumerator.h \ src/libostree/ostree-types.h \ + src/libostree/ostree-traverse.c \ + src/libostree/ostree-traverse.h \ $(NULL) if USE_LIBARCHIVE libostree_la_SOURCES += src/libostree/ostree-libarchive-input-stream.h \ diff --git a/src/libostree/ostree-traverse.c b/src/libostree/ostree-traverse.c new file mode 100644 index 00000000..a207fadc --- /dev/null +++ b/src/libostree/ostree-traverse.c @@ -0,0 +1,164 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2011 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Colin Walters + */ + +#define _GNU_SOURCE + +#include "config.h" + +#include "ostree.h" +#include "otutil.h" + +GHashTable * +ostree_traverse_new_reachable (void) +{ + return g_hash_table_new_full (ostree_hash_object_name, g_variant_equal, + (GDestroyNotify)g_variant_unref, NULL); +} + +gboolean +ostree_traverse_dirtree (OstreeRepo *repo, + const char *dirtree_checksum, + GHashTable *inout_reachable, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GVariant *tree = NULL; + GVariant *files_variant = NULL; + GVariant *dirs_variant = NULL; + int n, i; + GVariant *key; + + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_DIR_TREE, dirtree_checksum, &tree, error)) + goto out; + + 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; + + /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */ + files_variant = g_variant_get_child_value (tree, 2); + n = g_variant_n_children (files_variant); + for (i = 0; i < n; i++) + { + const char *filename; + const char *checksum; + + g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum); + if (ostree_repo_get_mode (repo) == OSTREE_REPO_MODE_BARE) + { + key = ostree_object_name_serialize (checksum, OSTREE_OBJECT_TYPE_RAW_FILE); + g_hash_table_replace (inout_reachable, key, key); + key = NULL; + } + else + { + key = ostree_object_name_serialize (checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META); + g_hash_table_replace (inout_reachable, key, key); + key = ostree_object_name_serialize (checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT); + g_hash_table_replace (inout_reachable, key, key); + key = NULL; + } + } + + dirs_variant = g_variant_get_child_value (tree, 3); + n = g_variant_n_children (dirs_variant); + for (i = 0; i < n; i++) + { + const char *dirname; + const char *tree_checksum; + const char *meta_checksum; + + g_variant_get_child (dirs_variant, i, "(&s&s&s)", + &dirname, &tree_checksum, &meta_checksum); + + if (!ostree_traverse_dirtree (repo, tree_checksum, inout_reachable, + cancellable, error)) + goto out; + + key = ostree_object_name_serialize (meta_checksum, OSTREE_OBJECT_TYPE_DIR_META); + g_hash_table_replace (inout_reachable, key, key); + key = NULL; + } + } + + ret = TRUE; + out: + ot_clear_gvariant (&key); + ot_clear_gvariant (&tree); + ot_clear_gvariant (&files_variant); + ot_clear_gvariant (&dirs_variant); + return ret; +} + +gboolean +ostree_traverse_commit (OstreeRepo *repo, + const char *commit_checksum, + int maxdepth, + GHashTable *inout_reachable, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GVariant *commit = NULL; + const char *contents_checksum; + const char *meta_checksum; + GVariant *key; + + /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */ + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum, &commit, error)) + goto out; + + key = ostree_object_name_serialize (commit_checksum, OSTREE_OBJECT_TYPE_COMMIT); + g_hash_table_replace (inout_reachable, key, key); + + g_variant_get_child (commit, 7, "&s", &meta_checksum); + key = ostree_object_name_serialize (meta_checksum, OSTREE_OBJECT_TYPE_DIR_META); + g_hash_table_replace (inout_reachable, key, key); + + g_variant_get_child (commit, 6, "&s", &contents_checksum); + if (!ostree_traverse_dirtree (repo, contents_checksum, inout_reachable, cancellable, error)) + goto out; + + if (maxdepth == -1 || maxdepth > 0) + { + const char *parent_checksum; + + g_variant_get_child (commit, 2, "&s", &parent_checksum); + + if (parent_checksum[0]) + { + if (!ostree_traverse_commit (repo, parent_checksum, + maxdepth > 0 ? maxdepth - 1 : -1, + inout_reachable, cancellable, error)) + goto out; + } + } + + ret = TRUE; + out: + ot_clear_gvariant (&commit); + return ret; +} + diff --git a/src/libostree/ostree-traverse.h b/src/libostree/ostree-traverse.h new file mode 100644 index 00000000..9d5cb7f0 --- /dev/null +++ b/src/libostree/ostree-traverse.h @@ -0,0 +1,48 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2011 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Colin Walters + */ + +#ifndef _OSTREE_TRAVERSE +#define _OSTREE_TRAVERSE + +#include "ostree-core.h" +#include "ostree-types.h" + +G_BEGIN_DECLS + +GHashTable *ostree_traverse_new_reachable (void); + +gboolean ostree_traverse_dirtree (OstreeRepo *repo, + const char *commit_checksum, + GHashTable *inout_reachable, + GCancellable *cancellable, + GError **error); + +gboolean ostree_traverse_commit (OstreeRepo *repo, + const char *commit_checksum, + int maxdepth, + GHashTable *inout_reachable, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* _OSTREE_REPO */ diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h index bf4153ae..ecd84779 100644 --- a/src/libostree/ostree.h +++ b/src/libostree/ostree.h @@ -26,5 +26,6 @@ #include #include #include +#include #endif diff --git a/src/ostree/ot-builtin-prune.c b/src/ostree/ot-builtin-prune.c index 2c50f515..65a5f3ef 100644 --- a/src/ostree/ot-builtin-prune.c +++ b/src/ostree/ot-builtin-prune.c @@ -30,7 +30,7 @@ static gboolean verbose; static gboolean delete; -static int depth = 0; +static int depth = -1; static GOptionEntry options[] = { { "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, "Display progress", NULL }, @@ -63,129 +63,10 @@ log_verbose (const char *fmt, typedef struct { OstreeRepo *repo; GHashTable *reachable; - gboolean had_error; - GError **error; guint n_reachable; guint n_unreachable; } OtPruneData; -static gboolean -compute_reachable_objects_from_dir_contents (OstreeRepo *repo, - const char *sha256, - GHashTable *inout_reachable, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - GVariant *tree = NULL; - GVariant *files_variant = NULL; - GVariant *dirs_variant = NULL; - int n, i; - char *key; - - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_DIR_TREE, sha256, &tree, error)) - goto out; - - key = ostree_object_to_string (sha256, OSTREE_OBJECT_TYPE_DIR_TREE); - g_hash_table_replace (inout_reachable, key, key); - - /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */ - files_variant = g_variant_get_child_value (tree, 2); - n = g_variant_n_children (files_variant); - for (i = 0; i < n; i++) - { - const char *filename; - const char *checksum; - - g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum); - if (ostree_repo_get_mode (repo) == OSTREE_REPO_MODE_BARE) - { - key = ostree_object_to_string (checksum, OSTREE_OBJECT_TYPE_RAW_FILE); - g_hash_table_replace (inout_reachable, key, key); - } - else - { - key = ostree_object_to_string (checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META); - g_hash_table_replace (inout_reachable, key, key); - key = ostree_object_to_string (checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT); - g_hash_table_replace (inout_reachable, key, key); - } - } - - dirs_variant = g_variant_get_child_value (tree, 3); - n = g_variant_n_children (dirs_variant); - for (i = 0; i < n; i++) - { - const char *dirname; - const char *tree_checksum; - const char *meta_checksum; - - g_variant_get_child (dirs_variant, i, "(&s&s&s)", - &dirname, &tree_checksum, &meta_checksum); - - if (!compute_reachable_objects_from_dir_contents (repo, tree_checksum, inout_reachable, - cancellable, error)) - goto out; - - key = ostree_object_to_string (meta_checksum, OSTREE_OBJECT_TYPE_DIR_META); - g_hash_table_replace (inout_reachable, key, key); - } - - ret = TRUE; - out: - ot_clear_gvariant (&tree); - ot_clear_gvariant (&files_variant); - ot_clear_gvariant (&dirs_variant); - return ret; -} - -static gboolean -compute_reachable_objects_from_commit (OstreeRepo *repo, - const char *sha256, - int traverse_depth, - GHashTable *inout_reachable, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - GVariant *commit = NULL; - const char *parent_checksum; - const char *contents_checksum; - const char *meta_checksum; - char *key; - - if (depth == 0 || traverse_depth < depth) - { - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, sha256, &commit, error)) - goto out; - - key = ostree_object_to_string (sha256, OSTREE_OBJECT_TYPE_COMMIT); - g_hash_table_replace (inout_reachable, key, key); - - /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */ - g_variant_get_child (commit, 2, "&s", &parent_checksum); - - if (strlen (parent_checksum) > 0) - { - if (!compute_reachable_objects_from_commit (repo, parent_checksum, traverse_depth + 1, inout_reachable, cancellable, error)) - goto out; - } - - g_variant_get_child (commit, 6, "&s", &contents_checksum); - - if (!compute_reachable_objects_from_dir_contents (repo, contents_checksum, inout_reachable, cancellable, error)) - goto out; - - g_variant_get_child (commit, 7, "&s", &meta_checksum); - key = ostree_object_to_string (meta_checksum, OSTREE_OBJECT_TYPE_DIR_META); - g_hash_table_replace (inout_reachable, key, key); - } - - ret = TRUE; - out: - ot_clear_gvariant (&commit); - return ret; -} static gboolean prune_loose_object (OtPruneData *data, @@ -195,10 +76,10 @@ prune_loose_object (OtPruneData *data, GError **error) { gboolean ret = FALSE; - char *key; + GVariant *key = NULL; GFile *objf = NULL; - key = ostree_object_to_string (checksum, objtype); + key = ostree_object_name_serialize (checksum, objtype); objf = ostree_repo_get_object_path (data->repo, checksum, objtype); @@ -206,13 +87,13 @@ prune_loose_object (OtPruneData *data, { if (delete) { - if (!g_file_delete (objf, cancellable, error)) + if (!ot_gfile_unlink (objf, cancellable, error)) goto out; - g_print ("Deleted: %s\n", key); + g_print ("Deleted: %s.%s\n", checksum, ostree_object_type_to_string (objtype)); } else { - g_print ("Unreachable: %s\n", key); + g_print ("Unreachable: %s.%s\n", checksum, ostree_object_type_to_string (objtype)); } data->n_unreachable++; } @@ -222,11 +103,10 @@ prune_loose_object (OtPruneData *data, ret = TRUE; out: g_clear_object (&objf); - g_free (key); + ot_clear_gvariant (&key); return ret; } - gboolean ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) { @@ -253,9 +133,7 @@ ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) goto out; data.repo = repo; - data.reachable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - data.had_error = FALSE; - data.error = error; + data.reachable = ostree_traverse_new_reachable (); data.n_reachable = 0; data.n_unreachable = 0; @@ -267,10 +145,10 @@ ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *name = key; - const char *sha256 = value; + const char *checksum = value; - log_verbose ("Computing reachable, currently %u total, from %s: %s", g_hash_table_size (data.reachable), name, sha256); - if (!compute_reachable_objects_from_commit (repo, sha256, 0, data.reachable, cancellable, error)) + log_verbose ("Computing reachable, currently %u total, from %s: %s", g_hash_table_size (data.reachable), name, checksum); + if (!ostree_traverse_commit (repo, checksum, depth, data.reachable, cancellable, error)) goto out; } @@ -279,13 +157,6 @@ ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) g_hash_table_iter_init (&hash_iter, objects); - - if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, - &objects, cancellable, error)) - goto out; - - g_hash_table_iter_init (&hash_iter, objects); - while (g_hash_table_iter_next (&hash_iter, &key, &value)) { GVariant *serialized_key = key; @@ -305,9 +176,6 @@ ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) } } - if (data.had_error) - goto out; - g_print ("Total reachable: %u\n", data.n_reachable); g_print ("Total unreachable: %u\n", data.n_unreachable);