From 613f57007cd8492f8c0c1bfebd525921c60e0a50 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 29 Jun 2013 13:48:26 -0400 Subject: [PATCH] Extract prune logic into an internal API This will be used by ostree admin deploy. --- Makefile-ostree.am | 2 + src/ostree/ostree-prune.c | 180 ++++++++++++++++++++++++++++++++++ src/ostree/ostree-prune.h | 45 +++++++++ src/ostree/ot-builtin-prune.c | 156 ++++------------------------- 4 files changed, 248 insertions(+), 135 deletions(-) create mode 100644 src/ostree/ostree-prune.c create mode 100644 src/ostree/ostree-prune.h diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 7825db9b..4e33c4eb 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -24,6 +24,8 @@ endif ostree_SOURCES = src/ostree/main.c \ src/ostree/ostree-curl-fetcher.h \ src/ostree/ostree-curl-fetcher.c \ + src/ostree/ostree-prune.h \ + src/ostree/ostree-prune.c \ src/ostree/ot-builtin-admin.c \ src/ostree/ot-builtins.h \ src/ostree/ot-builtin-cat.c \ diff --git a/src/ostree/ostree-prune.c b/src/ostree/ostree-prune.c new file mode 100644 index 00000000..e4bcb8fd --- /dev/null +++ b/src/ostree/ostree-prune.c @@ -0,0 +1,180 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "ostree-prune.h" + +typedef struct { + OstreeRepo *repo; + GHashTable *reachable; + guint n_reachable_meta; + guint n_reachable_content; + guint n_unreachable_meta; + guint n_unreachable_content; + guint64 freed_bytes; +} OtPruneData; + +static gboolean +maybe_prune_loose_object (OtPruneData *data, + OstreePruneFlags flags, + const char *checksum, + OstreeObjectType objtype, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lvariant GVariant *key = NULL; + ot_lobj GFile *objf = NULL; + + key = ostree_object_name_serialize (checksum, objtype); + + objf = ostree_repo_get_object_path (data->repo, checksum, objtype); + + if (!g_hash_table_lookup_extended (data->reachable, key, NULL, NULL)) + { + if (!(flags & OSTREE_PRUNE_FLAGS_NO_PRUNE)) + { + ot_lobj GFileInfo *info = NULL; + + if ((info = g_file_query_info (objf, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error)) == NULL) + goto out; + + if (!gs_file_unlink (objf, cancellable, error)) + goto out; + + data->freed_bytes += g_file_info_get_size (info); + } + if (OSTREE_OBJECT_TYPE_IS_META (objtype)) + data->n_unreachable_meta++; + else + data->n_unreachable_content++; + } + else + { + if (OSTREE_OBJECT_TYPE_IS_META (objtype)) + data->n_reachable_meta++; + else + data->n_reachable_content++; + } + + ret = TRUE; + out: + return ret; +} + +gboolean +ostree_prune (OstreeRepo *repo, + OstreePruneFlags flags, + gint depth, + gint *out_objects_total, + gint *out_objects_pruned, + guint64 *out_pruned_object_size_total, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GHashTableIter hash_iter; + gpointer key, value; + gs_unref_hashtable GHashTable *objects = NULL; + gs_unref_hashtable GHashTable *all_refs = NULL; + gs_free char *formatted_freed_size = NULL; + OtPruneData data; + gboolean refs_only = flags & OSTREE_PRUNE_FLAGS_REFS_ONLY; + + memset (&data, 0, sizeof (data)); + + data.repo = repo; + data.reachable = ostree_traverse_new_reachable (); + + if (refs_only) + { + if (!ostree_repo_list_all_refs (repo, &all_refs, cancellable, error)) + goto out; + + g_hash_table_iter_init (&hash_iter, all_refs); + + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + const char *checksum = value; + + if (!ostree_traverse_commit (repo, checksum, depth, data.reachable, + cancellable, error)) + goto out; + } + } + + if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects, + cancellable, error)) + goto out; + + if (!refs_only) + { + g_hash_table_iter_init (&hash_iter, objects); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + GVariant *serialized_key = key; + const char *checksum; + OstreeObjectType objtype; + + ostree_object_name_deserialize (serialized_key, &checksum, &objtype); + + if (objtype != OSTREE_OBJECT_TYPE_COMMIT) + continue; + + if (!ostree_traverse_commit (repo, checksum, depth, data.reachable, + 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; + GVariant *objdata = value; + const char *checksum; + OstreeObjectType objtype; + gboolean is_loose; + + ostree_object_name_deserialize (serialized_key, &checksum, &objtype); + g_variant_get_child (objdata, 0, "b", &is_loose); + + if (!is_loose) + continue; + + if (!maybe_prune_loose_object (&data, flags, checksum, objtype, + cancellable, error)) + goto out; + } + ret = TRUE; + *out_objects_total = (data.n_reachable_meta + data.n_unreachable_meta + + data.n_reachable_content + data.n_unreachable_content); + *out_objects_pruned = (data.n_unreachable_meta + data.n_unreachable_content); + *out_pruned_object_size_total = data.freed_bytes; + out: + if (data.reachable) + g_hash_table_unref (data.reachable); + return ret; +} diff --git a/src/ostree/ostree-prune.h b/src/ostree/ostree-prune.h new file mode 100644 index 00000000..a5ce9350 --- /dev/null +++ b/src/ostree/ostree-prune.h @@ -0,0 +1,45 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013 Colin Walters + * + * This program 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 licence 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. + */ + +#ifndef __OT_PRUNE_H__ +#define __OT_PRUNE_H__ + +#include "ostree.h" + +G_BEGIN_DECLS + +typedef enum { + OSTREE_PRUNE_FLAGS_NONE, + OSTREE_PRUNE_FLAGS_NO_PRUNE, + OSTREE_PRUNE_FLAGS_REFS_ONLY +} OstreePruneFlags; + +gboolean ostree_prune (OstreeRepo *repo, + OstreePruneFlags flags, + gint depth, + gint *out_objects_total, + gint *out_objects_pruned, + guint64 *out_pruned_object_size_total, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* __OT_PRUNE_H__ */ diff --git a/src/ostree/ot-builtin-prune.c b/src/ostree/ot-builtin-prune.c index 29237682..f52153cb 100644 --- a/src/ostree/ot-builtin-prune.c +++ b/src/ostree/ot-builtin-prune.c @@ -23,7 +23,7 @@ #include "config.h" #include "ot-builtins.h" -#include "ostree.h" +#include "ostree-prune.h" #include #include @@ -39,80 +39,18 @@ static GOptionEntry options[] = { { NULL } }; -typedef struct { - OstreeRepo *repo; - GHashTable *reachable; - guint n_reachable_meta; - guint n_reachable_content; - guint n_unreachable_meta; - guint n_unreachable_content; - guint64 freed_bytes; -} OtPruneData; - -static gboolean -maybe_prune_loose_object (OtPruneData *data, - const char *checksum, - OstreeObjectType objtype, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - ot_lvariant GVariant *key = NULL; - ot_lobj GFile *objf = NULL; - - key = ostree_object_name_serialize (checksum, objtype); - - objf = ostree_repo_get_object_path (data->repo, checksum, objtype); - - if (!g_hash_table_lookup_extended (data->reachable, key, NULL, NULL)) - { - if (!opt_no_prune) - { - ot_lobj GFileInfo *info = NULL; - - if ((info = g_file_query_info (objf, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error)) == NULL) - goto out; - - if (!gs_file_unlink (objf, cancellable, error)) - goto out; - - data->freed_bytes += g_file_info_get_size (info); - } - if (OSTREE_OBJECT_TYPE_IS_META (objtype)) - data->n_unreachable_meta++; - else - data->n_unreachable_content++; - } - else - { - if (OSTREE_OBJECT_TYPE_IS_META (objtype)) - data->n_reachable_meta++; - else - data->n_reachable_content++; - } - - ret = TRUE; - out: - return ret; -} - gboolean ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) { gboolean ret = FALSE; GOptionContext *context; - GHashTableIter hash_iter; - gpointer key, value; GCancellable *cancellable = NULL; - ot_lhash GHashTable *objects = NULL; - ot_lobj OstreeRepo *repo = NULL; - ot_lhash GHashTable *all_refs = NULL; + gs_unref_object OstreeRepo *repo = NULL; gs_free char *formatted_freed_size = NULL; - OtPruneData data; - - memset (&data, 0, sizeof (data)); + OstreePruneFlags pruneflags = 0; + gint n_objects_total; + gint n_objects_pruned; + guint64 objsize_total; context = g_option_context_new ("- Search for unreachable objects"); g_option_context_add_main_entries (context, options, NULL); @@ -124,82 +62,30 @@ ostree_builtin_prune (int argc, char **argv, GFile *repo_path, GError **error) if (!ostree_repo_check (repo, error)) goto out; - data.repo = repo; - data.reachable = ostree_traverse_new_reachable (); - if (opt_refs_only) - { - if (!ostree_repo_list_all_refs (repo, &all_refs, cancellable, error)) - goto out; - - g_hash_table_iter_init (&hash_iter, all_refs); - - while (g_hash_table_iter_next (&hash_iter, &key, &value)) - { - const char *checksum = value; - - // g_print ("Computing reachable, currently %u total, from %s: %s\n", g_hash_table_size (data.reachable), name, checksum); - if (!ostree_traverse_commit (repo, checksum, opt_depth, data.reachable, cancellable, error)) - goto out; - } - } + pruneflags |= OSTREE_PRUNE_FLAGS_REFS_ONLY; + if (opt_no_prune) + pruneflags |= OSTREE_PRUNE_FLAGS_NO_PRUNE; - if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects, cancellable, error)) + if (!ostree_prune (repo, pruneflags, opt_depth, + &n_objects_total, &n_objects_pruned, &objsize_total, + cancellable, error)) goto out; - if (!opt_refs_only) - { - g_hash_table_iter_init (&hash_iter, objects); - while (g_hash_table_iter_next (&hash_iter, &key, &value)) - { - GVariant *serialized_key = key; - const char *checksum; - OstreeObjectType objtype; + formatted_freed_size = g_format_size_full (objsize_total, 0); - ostree_object_name_deserialize (serialized_key, &checksum, &objtype); - - if (objtype != OSTREE_OBJECT_TYPE_COMMIT) - continue; - - if (!ostree_traverse_commit (repo, checksum, opt_depth, data.reachable, 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; - GVariant *objdata = value; - const char *checksum; - OstreeObjectType objtype; - gboolean is_loose; - - ostree_object_name_deserialize (serialized_key, &checksum, &objtype); - g_variant_get_child (objdata, 0, "b", &is_loose); - - if (!is_loose) - continue; - - if (!maybe_prune_loose_object (&data, checksum, objtype, cancellable, error)) - goto out; - } - - formatted_freed_size = g_format_size_full (data.freed_bytes, 0); - - g_print ("Total reachable: %u meta, %u content\n", - data.n_reachable_meta, data.n_reachable_content); - if (opt_no_prune) - g_print ("Total unreachable: %u meta, %u content\n", - data.n_unreachable_meta, data.n_unreachable_content); + g_print ("Total objects: %u\n", n_objects_total); + if (n_objects_pruned == 0) + g_print ("No unreachable objects\n"); + else if (pruneflags & OSTREE_PRUNE_FLAGS_NO_PRUNE) + g_print ("Would delete: %u objects, freeing %s bytes\n", + n_objects_pruned, formatted_freed_size); else - g_print ("Freed %s from %u meta, %u content objects\n", - formatted_freed_size, data.n_unreachable_meta, data.n_unreachable_content); + g_print ("Deleted %u objects, %s bytes freed\n", + n_objects_pruned, formatted_freed_size); ret = TRUE; out: - if (data.reachable) - g_hash_table_unref (data.reachable); if (context) g_option_context_free (context); return ret;