diff --git a/Makefile-ostree.am b/Makefile-ostree.am index cfbd4569..13f967e9 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -62,6 +62,8 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-run-triggers.c \ src/ostree/ot-admin-builtin-upgrade.c \ src/ostree/ot-admin-builtins.h \ + src/ostree/ot-admin-cleanup.c \ + src/ostree/ot-admin-util.c \ src/ostree/ot-admin-functions.h \ src/ostree/ot-admin-functions.c \ src/ostree/ot-admin-deploy.h \ diff --git a/src/ostree/ot-admin-cleanup.c b/src/ostree/ot-admin-cleanup.c new file mode 100644 index 00000000..d647fa5e --- /dev/null +++ b/src/ostree/ot-admin-cleanup.c @@ -0,0 +1,400 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 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 "ot-admin-functions.h" +#include "ot-deployment.h" +#include "ot-config-parser.h" +#include "otutil.h" +#include "ostree-core.h" +#include "ostree-prune.h" +#include "libgsystem.h" + +static gboolean +list_deployment_dirs_for_os (GFile *osdir, + GPtrArray *inout_deployments, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + const char *osname = gs_file_get_basename_cached (osdir); + gs_unref_object GFileEnumerator *dir_enum = NULL; + gs_unref_object GFile *osdeploy_dir = NULL; + GError *temp_error = NULL; + + osdeploy_dir = g_file_get_child (osdir, "deploy"); + + dir_enum = g_file_enumerate_children (osdeploy_dir, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, &temp_error); + if (!dir_enum) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + goto done; + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + + while (TRUE) + { + const char *name; + GFileInfo *file_info = NULL; + GFile *child = NULL; + gs_unref_object OtDeployment *deployment = NULL; + gs_free char *csum = NULL; + gint deployserial; + + if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child, + cancellable, error)) + goto out; + if (file_info == NULL) + break; + + name = g_file_info_get_name (file_info); + + if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) + continue; + + if (!ot_admin_parse_deploy_path_name (name, &csum, &deployserial, error)) + goto out; + + deployment = ot_deployment_new (-1, osname, csum, deployserial, NULL, -1); + g_ptr_array_add (inout_deployments, g_object_ref (deployment)); + } + + done: + ret = TRUE; + out: + return ret; +} +static gboolean +list_all_deployment_directories (GFile *sysroot, + GPtrArray **out_deployments, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object GFileEnumerator *dir_enum = NULL; + gs_unref_object GFile *deploydir = NULL; + gs_unref_object GFile *osdir = NULL; + gs_unref_ptrarray GPtrArray *ret_deployments = NULL; + GError *temp_error = NULL; + + deploydir = g_file_resolve_relative_path (sysroot, "ostree/deploy"); + + ret_deployments = g_ptr_array_new_with_free_func (g_object_unref); + + dir_enum = g_file_enumerate_children (deploydir, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, &temp_error); + if (!dir_enum) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + goto done; + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + + while (TRUE) + { + GFileInfo *file_info = NULL; + GFile *child = NULL; + + if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child, + NULL, error)) + goto out; + if (file_info == NULL) + break; + + if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) + continue; + + if (!list_deployment_dirs_for_os (child, ret_deployments, cancellable, error)) + goto out; + } + + done: + ret = TRUE; + ot_transfer_out_value (out_deployments, &ret_deployments); + out: + return ret; +} + +static gboolean +cleanup_other_bootversions (GFile *sysroot, + int bootversion, + int subbootversion, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int cleanup_bootversion; + int cleanup_subbootversion; + gs_free char *cleanup_boot_name = NULL; + gs_unref_object GFile *cleanup_boot_dir = NULL; + + cleanup_bootversion = bootversion == 0 ? 1 : 0; + cleanup_subbootversion = subbootversion == 0 ? 1 : 0; + + cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "boot/loader.%d", cleanup_bootversion); + if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + goto out; + g_clear_object (&cleanup_boot_dir); + + cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d", cleanup_bootversion); + if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + goto out; + g_clear_object (&cleanup_boot_dir); + + cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d.0", cleanup_bootversion); + if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + goto out; + g_clear_object (&cleanup_boot_dir); + + cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d.1", cleanup_bootversion); + if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + goto out; + g_clear_object (&cleanup_boot_dir); + + cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d.%d", bootversion, + cleanup_subbootversion); + if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + goto out; + g_clear_object (&cleanup_boot_dir); + + ret = TRUE; + out: + return ret; +} + +static gboolean +cleanup_old_deployments (GFile *sysroot, + GPtrArray *deployments, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + guint32 root_device; + guint64 root_inode; + guint i; + gs_unref_object GFile *active_root = g_file_new_for_path ("/"); + gs_unref_hashtable GHashTable *active_deployment_dirs = NULL; + gs_unref_ptrarray GPtrArray *all_deployment_dirs = NULL; + + if (!ot_admin_util_get_devino (active_root, &root_device, &root_inode, + cancellable, error)) + goto out; + + active_deployment_dirs = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, NULL, g_object_unref); + + for (i = 0; i < deployments->len; i++) + { + OtDeployment *deployment = deployments->pdata[i]; + GFile *deployment_path = ot_admin_get_deployment_directory (sysroot, deployment); + /* Transfer ownership */ + g_hash_table_insert (active_deployment_dirs, deployment_path, deployment_path); + } + + if (!list_all_deployment_directories (sysroot, &all_deployment_dirs, + cancellable, error)) + goto out; + + for (i = 0; i < all_deployment_dirs->len; i++) + { + OtDeployment *deployment = all_deployment_dirs->pdata[i]; + gs_unref_object GFile *deployment_path = ot_admin_get_deployment_directory (sysroot, deployment); + gs_unref_object GFile *origin_path = ot_admin_get_deployment_origin_path (deployment_path); + if (!g_hash_table_lookup (active_deployment_dirs, deployment_path)) + { + guint32 device; + guint64 inode; + + if (!ot_admin_util_get_devino (deployment_path, &device, &inode, + cancellable, error)) + goto out; + + /* This shouldn't happen, because higher levels should + * disallow having the booted deployment not in the active + * deployment list, but let's be extra safe. */ + if (device == root_device && inode == root_inode) + continue; + + g_print ("ostadmin: Deleting deployment %s\n", gs_file_get_path_cached (deployment_path)); + if (!gs_shutil_rm_rf (deployment_path, cancellable, error)) + goto out; + if (!gs_shutil_rm_rf (origin_path, cancellable, error)) + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +cleanup_ref_prefix (OstreeRepo *repo, + int bootversion, + int subbootversion, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_free char *prefix = NULL; + gs_unref_hashtable GHashTable *refs = NULL; + GHashTableIter hashiter; + gpointer hashkey, hashvalue; + + prefix = g_strdup_printf ("ostree/%d/%d", bootversion, subbootversion); + + if (!ostree_repo_list_refs (repo, prefix, &refs, cancellable, error)) + goto out; + + g_hash_table_iter_init (&hashiter, refs); + while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) + { + const char *suffix = hashkey; + gs_free char *ref = g_strconcat (prefix, "/", suffix, NULL); + if (!ostree_repo_write_refspec (repo, ref, NULL, error)) + goto out; + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +generate_deployment_refs_and_prune (GFile *sysroot, + OstreeRepo *repo, + int bootversion, + int subbootversion, + GPtrArray *deployments, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int cleanup_bootversion; + int cleanup_subbootversion; + guint i; + gint n_objects_total, n_objects_pruned; + guint64 freed_space; + gs_free char *cleanup_boot_name = NULL; + gs_unref_object GFile *cleanup_boot_dir = NULL; + + cleanup_bootversion = (bootversion == 0) ? 1 : 0; + cleanup_subbootversion = (subbootversion == 0) ? 1 : 0; + + if (!cleanup_ref_prefix (repo, cleanup_bootversion, 0, + cancellable, error)) + goto out; + + if (!cleanup_ref_prefix (repo, cleanup_bootversion, 1, + cancellable, error)) + goto out; + + if (!cleanup_ref_prefix (repo, bootversion, cleanup_subbootversion, + cancellable, error)) + goto out; + + for (i = 0; i < deployments->len; i++) + { + OtDeployment *deployment = deployments->pdata[i]; + gs_free char *refname = g_strdup_printf ("ostree/%d/%d/%u", + bootversion, subbootversion, + i); + if (!ostree_repo_write_refspec (repo, refname, ot_deployment_get_csum (deployment), + error)) + goto out; + } + + if (!ostree_prune (repo, OSTREE_PRUNE_FLAGS_REFS_ONLY, 0, + &n_objects_total, &n_objects_pruned, &freed_space, + cancellable, error)) + goto out; + if (freed_space > 0) + { + char *freed_space_str = g_format_size_full (freed_space, 0); + g_print ("Freed objects: %s\n", freed_space_str); + } + + ret = TRUE; + out: + return ret; +} + +gboolean +ot_admin_cleanup (GFile *sysroot, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_ptrarray GPtrArray *deployments = NULL; + gs_unref_object OstreeRepo *repo = NULL; + int bootversion; + int subbootversion; + + if (!ot_admin_list_deployments (sysroot, &bootversion, &deployments, + cancellable, error)) + goto out; + + if (!ot_admin_read_current_subbootversion (sysroot, bootversion, &subbootversion, + cancellable, error)) + goto out; + + if (!cleanup_other_bootversions (sysroot, bootversion, subbootversion, + cancellable, error)) + goto out; + + if (!cleanup_old_deployments (sysroot, deployments, + cancellable, error)) + goto out; + + if (deployments->len > 0) + { + if (!ot_admin_get_repo (sysroot, &repo, cancellable, error)) + goto out; + + if (!generate_deployment_refs_and_prune (sysroot, repo, bootversion, + subbootversion, deployments, + cancellable, error)) + goto out; + } + + ret = TRUE; + out: + return ret; +} diff --git a/src/ostree/ot-admin-deploy.c b/src/ostree/ot-admin-deploy.c index a3fa0c33..707c8a1c 100644 --- a/src/ostree/ot-admin-deploy.c +++ b/src/ostree/ot-admin-deploy.c @@ -1148,7 +1148,7 @@ ot_admin_deploy (GFile *sysroot, for (strviter = add_kernel_argv; *strviter; strviter++) { char *karg = g_strdup (*strviter); - const char *val = ot_admin_split_keyeq (karg); + const char *val = ot_admin_util_split_keyeq (karg); ot_ordered_hash_replace_key_take (ohash, karg, val); } diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index 89466f19..5ebeb9c3 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -32,33 +32,6 @@ #include "ostree-prune.h" #include "libgsystem.h" -/* - * Modify @arg which should be of the form key=value to make @arg just - * contain key. Return a pointer to the start of value. - */ -char * -ot_admin_split_keyeq (char *arg) -{ - char *eq; - - eq = strchr (arg, '='); - if (eq) - { - /* Note key/val are in one malloc block, - * so we don't free val... - */ - *eq = '\0'; - return eq+1; - } - else - { - /* ...and this allows us to insert a constant - * string. - */ - return ""; - } -} - OtOrderedHash * ot_admin_parse_kernel_args (const char *options) { @@ -77,7 +50,7 @@ ot_admin_parse_kernel_args (const char *options) char *arg = *iter; char *val; - val = ot_admin_split_keyeq (arg); + val = ot_admin_util_split_keyeq (arg); g_ptr_array_add (ret->order, arg); g_hash_table_insert (ret->table, arg, val); @@ -228,11 +201,11 @@ parse_bootlink (const char *bootlink, return ret; } -static gboolean -parse_deploy_path_name (const char *name, - char **out_csum, - int *out_serial, - GError **error) +gboolean +ot_admin_parse_deploy_path_name (const char *name, + char **out_csum, + int *out_serial, + GError **error) { gboolean ret = FALSE; __attribute__((cleanup(match_info_cleanup))) GMatchInfo *match = NULL; @@ -346,8 +319,8 @@ parse_deployment (GFile *sysroot, &treebootserial_target, cancellable, error)) goto out; - if (!parse_deploy_path_name (gs_file_get_basename_cached (treebootserial_target), - &treecsum, &deployserial, error)) + if (!ot_admin_parse_deploy_path_name (gs_file_get_basename_cached (treebootserial_target), + &treecsum, &deployserial, error)) goto out; if (!parse_origin (sysroot, treebootserial_target, &origin, @@ -387,28 +360,6 @@ parse_kernel_commandline (OtOrderedHash **out_args, return ret; } -static gboolean -get_devino (GFile *path, - guint32 *out_device, - guint64 *out_inode, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - gs_unref_object GFileInfo *finfo = g_file_query_info (path, "unix::device,unix::inode", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - - if (!finfo) - goto out; - - ret = TRUE; - *out_device = g_file_info_get_attribute_uint32 (finfo, "unix::device"); - *out_inode = g_file_info_get_attribute_uint64 (finfo, "unix::inode"); - out: - return ret; -} - /** * ot_admin_find_booted_deployment: * @@ -435,8 +386,8 @@ ot_admin_find_booted_deployment (GFile *target_sysroot, guint32 root_device; guint64 root_inode; - if (!get_devino (active_root, &root_device, &root_inode, - cancellable, error)) + if (!ot_admin_util_get_devino (active_root, &root_device, &root_inode, + cancellable, error)) goto out; if (!parse_kernel_commandline (&kernel_args, cancellable, error)) @@ -452,8 +403,8 @@ ot_admin_find_booted_deployment (GFile *target_sysroot, guint32 device; guint64 inode; - if (!get_devino (deployment_path, &device, &inode, - cancellable, error)) + if (!ot_admin_util_get_devino (deployment_path, &device, &inode, + cancellable, error)) goto out; if (device == root_device && inode == root_inode) @@ -708,129 +659,6 @@ ot_admin_read_boot_loader_configs (GFile *sysroot, return ret; } -static gboolean -list_deployment_dirs_for_os (GFile *osdir, - GPtrArray *inout_deployments, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - const char *osname = gs_file_get_basename_cached (osdir); - gs_unref_object GFileEnumerator *dir_enum = NULL; - gs_unref_object GFile *osdeploy_dir = NULL; - GError *temp_error = NULL; - - osdeploy_dir = g_file_get_child (osdir, "deploy"); - - dir_enum = g_file_enumerate_children (osdeploy_dir, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, &temp_error); - if (!dir_enum) - { - if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - g_clear_error (&temp_error); - goto done; - } - else - { - g_propagate_error (error, temp_error); - goto out; - } - } - - while (TRUE) - { - const char *name; - GFileInfo *file_info = NULL; - GFile *child = NULL; - gs_unref_object OtDeployment *deployment = NULL; - gs_free char *csum = NULL; - gint deployserial; - - if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child, - cancellable, error)) - goto out; - if (file_info == NULL) - break; - - name = g_file_info_get_name (file_info); - - if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) - continue; - - if (!parse_deploy_path_name (name, &csum, &deployserial, error)) - goto out; - - deployment = ot_deployment_new (-1, osname, csum, deployserial, NULL, -1); - g_ptr_array_add (inout_deployments, g_object_ref (deployment)); - } - - done: - ret = TRUE; - out: - return ret; -} - -static gboolean -list_all_deployment_directories (GFile *sysroot, - GPtrArray **out_deployments, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - gs_unref_object GFileEnumerator *dir_enum = NULL; - gs_unref_object GFile *deploydir = NULL; - gs_unref_object GFile *osdir = NULL; - gs_unref_ptrarray GPtrArray *ret_deployments = NULL; - GError *temp_error = NULL; - - deploydir = g_file_resolve_relative_path (sysroot, "ostree/deploy"); - - ret_deployments = g_ptr_array_new_with_free_func (g_object_unref); - - dir_enum = g_file_enumerate_children (deploydir, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, &temp_error); - if (!dir_enum) - { - if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - g_clear_error (&temp_error); - goto done; - } - else - { - g_propagate_error (error, temp_error); - goto out; - } - } - - while (TRUE) - { - GFileInfo *file_info = NULL; - GFile *child = NULL; - - if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child, - NULL, error)) - goto out; - if (file_info == NULL) - break; - - if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) - continue; - - if (!list_deployment_dirs_for_os (child, ret_deployments, cancellable, error)) - goto out; - } - - done: - ret = TRUE; - ot_transfer_out_value (out_deployments, &ret_deployments); - out: - return ret; -} - static char * get_ostree_kernel_arg_from_config (OtConfigParser *config) { @@ -982,252 +810,6 @@ ot_admin_get_deployment_directory (GFile *sysroot, return g_file_resolve_relative_path (sysroot, path); } -static gboolean -cleanup_other_bootversions (GFile *sysroot, - int bootversion, - int subbootversion, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - int cleanup_bootversion; - int cleanup_subbootversion; - gs_free char *cleanup_boot_name = NULL; - gs_unref_object GFile *cleanup_boot_dir = NULL; - - cleanup_bootversion = bootversion == 0 ? 1 : 0; - cleanup_subbootversion = subbootversion == 0 ? 1 : 0; - - cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "boot/loader.%d", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) - goto out; - g_clear_object (&cleanup_boot_dir); - - cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) - goto out; - g_clear_object (&cleanup_boot_dir); - - cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d.0", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) - goto out; - g_clear_object (&cleanup_boot_dir); - - cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d.1", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) - goto out; - g_clear_object (&cleanup_boot_dir); - - cleanup_boot_dir = ot_gfile_resolve_path_printf (sysroot, "ostree/boot.%d.%d", bootversion, - cleanup_subbootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) - goto out; - g_clear_object (&cleanup_boot_dir); - - ret = TRUE; - out: - return ret; -} - -static gboolean -cleanup_old_deployments (GFile *sysroot, - GPtrArray *deployments, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - guint32 root_device; - guint64 root_inode; - guint i; - gs_unref_object GFile *active_root = g_file_new_for_path ("/"); - gs_unref_hashtable GHashTable *active_deployment_dirs = NULL; - gs_unref_ptrarray GPtrArray *all_deployment_dirs = NULL; - - if (!get_devino (active_root, &root_device, &root_inode, - cancellable, error)) - goto out; - - active_deployment_dirs = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, NULL, g_object_unref); - - for (i = 0; i < deployments->len; i++) - { - OtDeployment *deployment = deployments->pdata[i]; - GFile *deployment_path = ot_admin_get_deployment_directory (sysroot, deployment); - /* Transfer ownership */ - g_hash_table_insert (active_deployment_dirs, deployment_path, deployment_path); - } - - if (!list_all_deployment_directories (sysroot, &all_deployment_dirs, - cancellable, error)) - goto out; - - for (i = 0; i < all_deployment_dirs->len; i++) - { - OtDeployment *deployment = all_deployment_dirs->pdata[i]; - gs_unref_object GFile *deployment_path = ot_admin_get_deployment_directory (sysroot, deployment); - gs_unref_object GFile *origin_path = ot_admin_get_deployment_origin_path (deployment_path); - if (!g_hash_table_lookup (active_deployment_dirs, deployment_path)) - { - guint32 device; - guint64 inode; - - if (!get_devino (deployment_path, &device, &inode, - cancellable, error)) - goto out; - - /* This shouldn't happen, because higher levels should - * disallow having the booted deployment not in the active - * deployment list, but let's be extra safe. */ - if (device == root_device && inode == root_inode) - continue; - - g_print ("ostadmin: Deleting deployment %s\n", gs_file_get_path_cached (deployment_path)); - if (!gs_shutil_rm_rf (deployment_path, cancellable, error)) - goto out; - if (!gs_shutil_rm_rf (origin_path, cancellable, error)) - goto out; - } - } - - ret = TRUE; - out: - return ret; -} - -static gboolean -cleanup_ref_prefix (OstreeRepo *repo, - int bootversion, - int subbootversion, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - gs_free char *prefix = NULL; - gs_unref_hashtable GHashTable *refs = NULL; - GHashTableIter hashiter; - gpointer hashkey, hashvalue; - - prefix = g_strdup_printf ("ostree/%d/%d", bootversion, subbootversion); - - if (!ostree_repo_list_refs (repo, prefix, &refs, cancellable, error)) - goto out; - - g_hash_table_iter_init (&hashiter, refs); - while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) - { - const char *suffix = hashkey; - gs_free char *ref = g_strconcat (prefix, "/", suffix, NULL); - if (!ostree_repo_write_refspec (repo, ref, NULL, error)) - goto out; - } - - ret = TRUE; - out: - return ret; -} - -static gboolean -generate_deployment_refs_and_prune (GFile *sysroot, - OstreeRepo *repo, - int bootversion, - int subbootversion, - GPtrArray *deployments, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - int cleanup_bootversion; - int cleanup_subbootversion; - guint i; - gint n_objects_total, n_objects_pruned; - guint64 freed_space; - gs_free char *cleanup_boot_name = NULL; - gs_unref_object GFile *cleanup_boot_dir = NULL; - - cleanup_bootversion = (bootversion == 0) ? 1 : 0; - cleanup_subbootversion = (subbootversion == 0) ? 1 : 0; - - if (!cleanup_ref_prefix (repo, cleanup_bootversion, 0, - cancellable, error)) - goto out; - - if (!cleanup_ref_prefix (repo, cleanup_bootversion, 1, - cancellable, error)) - goto out; - - if (!cleanup_ref_prefix (repo, bootversion, cleanup_subbootversion, - cancellable, error)) - goto out; - - for (i = 0; i < deployments->len; i++) - { - OtDeployment *deployment = deployments->pdata[i]; - gs_free char *refname = g_strdup_printf ("ostree/%d/%d/%u", - bootversion, subbootversion, - i); - if (!ostree_repo_write_refspec (repo, refname, ot_deployment_get_csum (deployment), - error)) - goto out; - } - - if (!ostree_prune (repo, OSTREE_PRUNE_FLAGS_REFS_ONLY, 0, - &n_objects_total, &n_objects_pruned, &freed_space, - cancellable, error)) - goto out; - if (freed_space > 0) - { - char *freed_space_str = g_format_size_full (freed_space, 0); - g_print ("Freed objects: %s\n", freed_space_str); - } - - ret = TRUE; - out: - return ret; -} - -gboolean -ot_admin_cleanup (GFile *sysroot, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - gs_unref_ptrarray GPtrArray *deployments = NULL; - gs_unref_object OstreeRepo *repo = NULL; - int bootversion; - int subbootversion; - - if (!ot_admin_list_deployments (sysroot, &bootversion, &deployments, - cancellable, error)) - goto out; - - if (!ot_admin_read_current_subbootversion (sysroot, bootversion, &subbootversion, - cancellable, error)) - goto out; - - if (!cleanup_other_bootversions (sysroot, bootversion, subbootversion, - cancellable, error)) - goto out; - - if (!cleanup_old_deployments (sysroot, deployments, - cancellable, error)) - goto out; - - if (deployments->len > 0) - { - if (!ot_admin_get_repo (sysroot, &repo, cancellable, error)) - goto out; - - if (!generate_deployment_refs_and_prune (sysroot, repo, bootversion, - subbootversion, deployments, - cancellable, error)) - goto out; - } - - ret = TRUE; - out: - return ret; -} - OtBootloader * ot_admin_query_bootloader (GFile *sysroot) { diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index 5a3c0763..950d11e7 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -31,6 +31,19 @@ G_BEGIN_DECLS +char *ot_admin_util_split_keyeq (char *str); + +gboolean ot_admin_util_get_devino (GFile *path, + guint32 *out_device, + guint64 *out_inode, + GCancellable *cancellable, + GError **error); + +gboolean ot_admin_parse_deploy_path_name (const char *name, + char **out_csum, + int *out_serial, + GError **error); + gboolean ot_admin_ensure_initialized (GFile *ostree_dir, GCancellable *cancellable, GError **error); @@ -40,7 +53,6 @@ gboolean ot_admin_check_os (GFile *sysroot, GCancellable *cancellable, GError **error); -char *ot_admin_split_keyeq (char *str); OtOrderedHash *ot_admin_parse_kernel_args (const char *options); char * ot_admin_kernel_arg_string_serialize (OtOrderedHash *ohash); diff --git a/src/ostree/ot-admin-util.c b/src/ostree/ot-admin-util.c new file mode 100644 index 00000000..871871fd --- /dev/null +++ b/src/ostree/ot-admin-util.c @@ -0,0 +1,78 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 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 "ot-admin-functions.h" +#include "otutil.h" +#include "ostree-core.h" +#include "libgsystem.h" + +/* + * Modify @arg which should be of the form key=value to make @arg just + * contain key. Return a pointer to the start of value. + */ +char * +ot_admin_util_split_keyeq (char *arg) +{ + char *eq; + + eq = strchr (arg, '='); + if (eq) + { + /* Note key/val are in one malloc block, + * so we don't free val... + */ + *eq = '\0'; + return eq+1; + } + else + { + /* ...and this allows us to insert a constant + * string. + */ + return ""; + } +} + +gboolean +ot_admin_util_get_devino (GFile *path, + guint32 *out_device, + guint64 *out_inode, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object GFileInfo *finfo = g_file_query_info (path, "unix::device,unix::inode", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + + if (!finfo) + goto out; + + ret = TRUE; + *out_device = g_file_info_get_attribute_uint32 (finfo, "unix::device"); + *out_inode = g_file_info_get_attribute_uint64 (finfo, "unix::inode"); + out: + return ret; +}