diff options
-rw-r--r-- | Makefile-libostree-defines.am | 1 | ||||
-rw-r--r-- | Makefile-libostree.am | 1 | ||||
-rw-r--r-- | src/libostree/ostree-sysroot-upgrader.c | 496 | ||||
-rw-r--r-- | src/libostree/ostree-sysroot-upgrader.h | 71 | ||||
-rw-r--r-- | src/libostree/ostree-types.h | 1 | ||||
-rw-r--r-- | src/libostree/ostree.h | 1 | ||||
-rw-r--r-- | src/ostree/ot-admin-builtin-switch.c | 141 | ||||
-rw-r--r-- | src/ostree/ot-admin-builtin-upgrade.c | 119 | ||||
-rw-r--r-- | src/ostree/ot-admin-functions.c | 60 | ||||
-rw-r--r-- | src/ostree/ot-admin-functions.h | 11 |
10 files changed, 645 insertions, 257 deletions
diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am index 02037ed2..7f776e0a 100644 --- a/Makefile-libostree-defines.am +++ b/Makefile-libostree-defines.am @@ -29,6 +29,7 @@ libostree_public_headers = \ src/libostree/ostree-diff.h \ src/libostree/ostree-sepolicy.h \ src/libostree/ostree-sysroot.h \ + src/libostree/ostree-sysroot-upgrader.h \ src/libostree/ostree-deployment.h \ src/libostree/ostree-bootconfig-parser.h \ $(NULL) diff --git a/Makefile-libostree.am b/Makefile-libostree.am index 04d03be6..988f1cbf 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -61,6 +61,7 @@ libostree_1_la_SOURCES = \ src/libostree/ostree-sysroot.c \ src/libostree/ostree-sysroot-cleanup.c \ src/libostree/ostree-sysroot-deploy.c \ + src/libostree/ostree-sysroot-upgrader.c \ src/libostree/ostree-bootconfig-parser.c \ src/libostree/ostree-deployment.c \ src/libostree/ostree-bootloader.h \ diff --git a/src/libostree/ostree-sysroot-upgrader.c b/src/libostree/ostree-sysroot-upgrader.c new file mode 100644 index 00000000..c565434f --- /dev/null +++ b/src/libostree/ostree-sysroot-upgrader.c @@ -0,0 +1,496 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014 Colin Walters <walters@verbum.org> + * + * 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. + */ + +#include "config.h" + +#include "otutil.h" +#include "libgsystem.h" + +#include "ostree-sysroot-upgrader.h" + +/** + * SECTION:libostree-sysroot-upgrader + * @title: Simple upgrade class + * @short_description: Upgrade OSTree systems + * + * The #OstreeSysrootUpgrader class allows performing simple upgrade + * operations. + */ +typedef struct { + GObjectClass parent_class; +} OstreeSysrootUpgraderClass; + +struct OstreeSysrootUpgrader { + GObject parent; + + OstreeSysroot *sysroot; + char *osname; + + OstreeDeployment *merge_deployment; + GKeyFile *origin; + char *origin_remote; + char *origin_ref; + + char *new_revision; +}; + +enum { + PROP_0, + + PROP_SYSROOT, + PROP_OSNAME +}; + +static void ostree_sysroot_upgrader_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (OstreeSysrootUpgrader, ostree_sysroot_upgrader, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, ostree_sysroot_upgrader_initable_iface_init)) + +static gboolean +parse_refspec (OstreeSysrootUpgrader *self, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_free char *origin_refspec = NULL; + + origin_refspec = g_key_file_get_string (self->origin, "origin", "refspec", NULL); + if (!origin_refspec) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No origin/refspec in current deployment origin; cannot upgrade via ostree"); + goto out; + } + g_clear_pointer (&self->origin_remote, g_free); + g_clear_pointer (&self->origin_ref, g_free); + if (!ostree_parse_refspec (origin_refspec, + &self->origin_remote, + &self->origin_ref, + error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +static gboolean +ostree_sysroot_upgrader_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeSysrootUpgrader *self = (OstreeSysrootUpgrader*)initable; + OstreeDeployment *booted_deployment = + ostree_sysroot_get_booted_deployment (self->sysroot); + gs_unref_object GFile *deployment_path = NULL; + gs_unref_object GFile *deployment_origin_path = NULL; + + if (booted_deployment == NULL && self->osname == NULL) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not currently booted into an OSTree system and no OS specified"); + goto out; + } + + if (self->osname == NULL) + { + g_assert (booted_deployment); + self->osname = g_strdup (ostree_deployment_get_osname (booted_deployment)); + } + + self->merge_deployment = ostree_sysroot_get_merge_deployment (self->sysroot, self->osname); + if (self->merge_deployment == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No previous deployment for OS '%s'", self->osname); + goto out; + } + + deployment_path = ostree_sysroot_get_deployment_directory (self->sysroot, self->merge_deployment); + deployment_origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path); + + self->origin = ostree_deployment_get_origin (self->merge_deployment); + if (!self->origin) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No origin known for deployment %s.%d", + ostree_deployment_get_csum (self->merge_deployment), + ostree_deployment_get_deployserial (self->merge_deployment)); + goto out; + } + + if (!parse_refspec (self, cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +static void +ostree_sysroot_upgrader_initable_iface_init (GInitableIface *iface) +{ + iface->init = ostree_sysroot_upgrader_initable_init; +} + +static void +ostree_sysroot_upgrader_finalize (GObject *object) +{ + OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object); + + g_clear_object (&self->sysroot); + g_free (self->osname); + + g_clear_object (&self->merge_deployment); + if (self->origin) + g_key_file_unref (self->origin); + g_free (self->origin_remote); + g_free (self->origin_ref); + + G_OBJECT_CLASS (ostree_sysroot_upgrader_parent_class)->finalize (object); +} + +static void +ostree_sysroot_upgrader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object); + + switch (prop_id) + { + case PROP_SYSROOT: + self->sysroot = g_value_dup_object (value); + break; + case PROP_OSNAME: + self->osname = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ostree_sysroot_upgrader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object); + + switch (prop_id) + { + case PROP_SYSROOT: + g_value_set_object (value, self->sysroot); + break; + case PROP_OSNAME: + g_value_set_string (value, self->osname); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ostree_sysroot_upgrader_constructed (GObject *object) +{ + OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object); + + g_assert (self->sysroot != NULL); + + G_OBJECT_CLASS (ostree_sysroot_upgrader_parent_class)->constructed (object); +} + +static void +ostree_sysroot_upgrader_class_init (OstreeSysrootUpgraderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = ostree_sysroot_upgrader_constructed; + object_class->get_property = ostree_sysroot_upgrader_get_property; + object_class->set_property = ostree_sysroot_upgrader_set_property; + object_class->finalize = ostree_sysroot_upgrader_finalize; + + g_object_class_install_property (object_class, + PROP_SYSROOT, + g_param_spec_object ("sysroot", "", "", + OSTREE_TYPE_SYSROOT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_OSNAME, + g_param_spec_string ("osname", "", "", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +ostree_sysroot_upgrader_init (OstreeSysrootUpgrader *self) +{ +} + +/** + * ostree_sysroot_upgrader_new: + * @sysroot: An #OstreeSysroot + * + * Returns: (transfer full): An upgrader + */ +OstreeSysrootUpgrader* +ostree_sysroot_upgrader_new (OstreeSysroot *sysroot, + GCancellable *cancellable, + GError **error) +{ + return g_initable_new (OSTREE_TYPE_SYSROOT_UPGRADER, cancellable, error, + "sysroot", sysroot, NULL); +} + +/** + * ostree_sysroot_upgrader_new_for_os: + * @sysroot: An #OstreeSysroot + * @osname: (allow-none): Operating system name + * + * Returns: (transfer full): An upgrader + */ +OstreeSysrootUpgrader* +ostree_sysroot_upgrader_new_for_os (OstreeSysroot *sysroot, + const char *osname, + GCancellable *cancellable, + GError **error) +{ + return g_initable_new (OSTREE_TYPE_SYSROOT_UPGRADER, cancellable, error, + "sysroot", sysroot, "osname", osname, NULL); +} + +/** + * ostree_sysroot_upgrader_get_origin: + * @self: Sysroot + * + * Returns: (transfer none): The origin file, or %NULL if unknown + */ +GKeyFile * +ostree_sysroot_upgrader_get_origin (OstreeSysrootUpgrader *self) +{ + return self->origin; +} + +/** + * ostree_sysroot_upgrader_set_origin: + * @self: Sysroot + * @origin: (allow-none): The new origin + * @cancellable: Cancellable + * @error: Error + * + * Replace the origin with @origin. + */ +gboolean +ostree_sysroot_upgrader_set_origin (OstreeSysrootUpgrader *self, + GKeyFile *origin, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + + g_clear_pointer (&self->origin, g_key_file_unref); + if (origin) + { + self->origin = g_key_file_ref (origin); + if (!parse_refspec (self, cancellable, error)) + goto out; + } + + ret = TRUE; + out: + return ret; +} + +/** + * ostree_sysroot_upgrader_check_timestamps: + * @repo: Repo + * @from_rev: From revision + * @to_rev: To revision + * @error: Error + * + * Check that the timestamp on @to_rev is equal to or newer than + * @from_rev. This protects systems against man-in-the-middle + * attackers which provide a client with an older commit. + */ +gboolean +ostree_sysroot_upgrader_check_timestamps (OstreeRepo *repo, + const char *from_rev, + const char *to_rev, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_variant GVariant *old_commit = NULL; + gs_unref_variant GVariant *new_commit = NULL; + + if (!ostree_repo_load_variant (repo, + OSTREE_OBJECT_TYPE_COMMIT, + from_rev, + &old_commit, + error)) + goto out; + + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, + to_rev, &new_commit, + error)) + goto out; + + if (ostree_commit_get_timestamp (old_commit) > ostree_commit_get_timestamp (new_commit)) + { + GDateTime *old_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (old_commit)); + GDateTime *new_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (new_commit)); + gs_free char *old_ts_str = NULL; + gs_free char *new_ts_str = NULL; + + g_assert (old_ts); + g_assert (new_ts); + old_ts_str = g_date_time_format (old_ts, "%c"); + new_ts_str = g_date_time_format (new_ts, "%c"); + g_date_time_unref (old_ts); + g_date_time_unref (new_ts); + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current revision '%s' with timestamp '%s'; use --allow-downgrade to permit", + to_rev, new_ts_str, from_rev, old_ts_str); + goto out; + } + + ret = TRUE; + out: + return ret; +} + + +/** + * ostree_sysroot_upgrader_pull: + * @self: Upgrader + * @flags: Flags controlling pull behavior + * @upgrader_flags: Flags controlling upgrader behavior + * @progress: (allow-none): Progress + * @out_changed: (out): Whether or not the origin changed + * @cancellable: Cancellable + * @error: Error + * + * Perform a pull from the origin. First check if the ref has + * changed, if so download the linked objects, and store the updated + * ref locally. Then @out_changed will be %TRUE. + * + * If the origin remote is unchanged, @out_changed will be set to + * %FALSE. + */ +gboolean +ostree_sysroot_upgrader_pull (OstreeSysrootUpgrader *self, + OstreeRepoPullFlags flags, + OstreeSysrootUpgraderPullFlags upgrader_flags, + OstreeAsyncProgress *progress, + gboolean *out_changed, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object OstreeRepo *repo = NULL; + char *refs_to_fetch[] = { self->origin_ref, NULL }; + gs_free char *from_revision = NULL; + gs_free char *new_revision = NULL; + gs_free char *origin_refspec = NULL; + + if (!ostree_sysroot_get_repo (self->sysroot, &repo, cancellable, error)) + goto out; + + if (self->origin_remote) + origin_refspec = g_strconcat (self->origin_remote, ":", self->origin_ref, NULL); + else + origin_refspec = g_strdup (self->origin_ref); + + if (!ostree_repo_resolve_rev (repo, origin_refspec, TRUE, &from_revision, + error)) + goto out; + + if (!ostree_repo_pull (repo, self->origin_remote, refs_to_fetch, + flags, progress, + cancellable, error)) + goto out; + + if (!ostree_repo_resolve_rev (repo, origin_refspec, FALSE, &self->new_revision, + error)) + goto out; + + if (g_strcmp0 (from_revision, self->new_revision) == 0) + { + *out_changed = FALSE; + } + else + { + *out_changed = TRUE; + if (from_revision) + { + if (!ostree_sysroot_upgrader_check_timestamps (repo, from_revision, + self->new_revision, + error)) + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * ostree_sysroot_upgrader_deploy: + * @self: Self + * @cancellable: Cancellable + * @error: Error + * + * Write the new deployment to disk, perform a configuration merge + * with /etc, and update the bootloader configuration. + */ +gboolean +ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object OstreeDeployment *new_deployment = NULL; + + if (!ostree_sysroot_deploy_tree (self->sysroot, self->osname, + self->new_revision, + self->origin, + self->merge_deployment, + NULL, + &new_deployment, + cancellable, error)) + goto out; + + if (!ostree_sysroot_simple_write_deployment (self->sysroot, self->osname, + new_deployment, + self->merge_deployment, + 0, + cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} diff --git a/src/libostree/ostree-sysroot-upgrader.h b/src/libostree/ostree-sysroot-upgrader.h new file mode 100644 index 00000000..79f36355 --- /dev/null +++ b/src/libostree/ostree-sysroot-upgrader.h @@ -0,0 +1,71 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014 Colin Walters <walters@verbum.org> + * + * 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. + */ + +#pragma once + +#include "ostree-sysroot.h" + +G_BEGIN_DECLS + +#define OSTREE_TYPE_SYSROOT_UPGRADER ostree_sysroot_upgrader_get_type() +#define OSTREE_SYSROOT_UPGRADER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_SYSROOT_UPGRADER, OstreeSysrootUpgrader)) +#define OSTREE_IS_SYSROOT_UPGRADER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SYSROOT_UPGRADER)) + +GType ostree_sysroot_upgrader_get_type (void); + +OstreeSysrootUpgrader *ostree_sysroot_upgrader_new (OstreeSysroot *sysroot, + GCancellable *cancellable, + GError **error); + +OstreeSysrootUpgrader *ostree_sysroot_upgrader_new_for_os (OstreeSysroot *sysroot, + const char *osname, + GCancellable *cancellable, + GError **error); + +GKeyFile *ostree_sysroot_upgrader_get_origin (OstreeSysrootUpgrader *self); +gboolean ostree_sysroot_upgrader_set_origin (OstreeSysrootUpgrader *self, GKeyFile *origin, + GCancellable *cancellable, GError **error); + +gboolean ostree_sysroot_upgrader_check_timestamps (OstreeRepo *repo, + const char *from_rev, + const char *to_rev, + GError **error); + +typedef enum { + OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_NONE = 0, + OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER = (1 << 0) +} OstreeSysrootUpgraderPullFlags; + +gboolean ostree_sysroot_upgrader_pull (OstreeSysrootUpgrader *self, + OstreeRepoPullFlags flags, + OstreeSysrootUpgraderPullFlags upgrader_flags, + OstreeAsyncProgress *progress, + gboolean *out_changed, + GCancellable *cancellable, + GError **error); + +gboolean ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + diff --git a/src/libostree/ostree-types.h b/src/libostree/ostree-types.h index 8be12584..533d3b10 100644 --- a/src/libostree/ostree-types.h +++ b/src/libostree/ostree-types.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS typedef struct OstreeRepo OstreeRepo; typedef struct OstreeSePolicy OstreeSePolicy; typedef struct OstreeSysroot OstreeSysroot; +typedef struct OstreeSysrootUpgrader OstreeSysrootUpgrader; typedef struct OstreeMutableTree OstreeMutableTree; typedef struct OstreeRepoFile OstreeRepoFile; diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h index f1e74507..57b2ffd2 100644 --- a/src/libostree/ostree.h +++ b/src/libostree/ostree.h @@ -26,6 +26,7 @@ #include <ostree-mutable-tree.h> #include <ostree-repo-file.h> #include <ostree-sysroot.h> +#include <ostree-sysroot-upgrader.h> #include <ostree-deployment.h> #include <ostree-bootconfig-parser.h> #include <ostree-diff.h> diff --git a/src/ostree/ot-admin-builtin-switch.c b/src/ostree/ot-admin-builtin-switch.c index 1edd7f5d..d9d6ee03 100644 --- a/src/ostree/ot-admin-builtin-switch.c +++ b/src/ostree/ot-admin-builtin-switch.c @@ -46,14 +46,21 @@ ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancell GOptionContext *context; const char *new_ref = NULL; gs_unref_object OstreeRepo *repo = NULL; + gs_free char *origin_refspec = NULL; gs_free char *origin_remote = NULL; gs_free char *origin_ref = NULL; + gs_free char *new_refspec = NULL; gs_free char *new_revision = NULL; gs_unref_object GFile *deployment_path = NULL; gs_unref_object GFile *deployment_origin_path = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL; - GKeyFile *origin; + gs_unref_object OstreeSysrootUpgrader *upgrader = NULL; + gs_unref_object OstreeAsyncProgress *progress = NULL; + gboolean changed; + GSConsole *console; + GKeyFile *old_origin; + GKeyFile *new_origin = NULL; context = g_option_context_new ("REF - Construct new tree from current origin and deploy it, if it changed"); g_option_context_add_main_entries (context, options, NULL); @@ -72,108 +79,74 @@ ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancell if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; - if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) + upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname, + cancellable, error); + if (!upgrader) goto out; - if (!ot_admin_deploy_prepare (sysroot, opt_osname, &merge_deployment, - &origin_remote, &origin_ref, - &origin, - cancellable, error)) + old_origin = ostree_sysroot_upgrader_get_origin (upgrader); + origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL); + + if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error)) goto out; + + if (origin_remote) + new_refspec = g_strconcat (origin_remote, ":", new_ref, NULL); + else + new_refspec = g_strdup (new_ref); - if (strcmp (origin_ref, new_ref) == 0) + if (strcmp (origin_refspec, new_refspec) == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Old and new refs are equal: %s", new_ref); + "Old and new refs are equal: %s", new_refspec); goto out; } - { - gs_free char *new_refspec = NULL; - if (origin_remote) - new_refspec = g_strconcat (origin_remote, ":", new_ref, NULL); - else - new_refspec = g_strdup (new_ref); - g_key_file_unref (origin); - origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec); - } + new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec); + if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error)) + goto out; - if (origin_remote) + console = gs_console_get (); + if (console) { - OstreeRepoPullFlags pullflags = 0; - char *refs_to_fetch[] = { (char*)new_ref, NULL }; - GSConsole *console; - gs_unref_object OstreeAsyncProgress *progress = NULL; - - g_print ("Fetching remote %s ref %s\n", origin_remote, new_ref); - - console = gs_console_get (); - if (console) - { - gs_console_begin_status_line (console, "", NULL, NULL); - progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console); - } - - if (!ostree_repo_pull (repo, origin_remote, refs_to_fetch, pullflags, progress, - cancellable, error)) - goto out; + gs_console_begin_status_line (console, "", NULL, NULL); + progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console); } - if (!ostree_repo_resolve_rev (repo, new_ref, FALSE, &new_revision, - error)) + if (!ostree_sysroot_upgrader_pull (upgrader, 0, 0, progress, &changed, + cancellable, error)) goto out; - if (TRUE) - { - gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/"); + if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) + goto out; + + if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) + goto out; + + if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) + goto out; + + g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref); + ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL); + + if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) + goto out; + + { + gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/"); - /* Here we perform cleanup of any leftover data from previous - * partial failures. This avoids having to call gs_shutil_rm_rf() - * at random points throughout the process. - * - * TODO: Add /ostree/transaction file, and only do this cleanup if - * we find it. - */ - if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) - { - g_prefix_error (error, "Performing initial cleanup: "); - goto out; - } - - if (!ostree_sysroot_deploy_tree (sysroot, - opt_osname, new_revision, origin, - merge_deployment, - NULL, - &new_deployment, - cancellable, error)) - goto out; - - if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, - new_deployment, - merge_deployment, - 0, - cancellable, error)) - goto out; - - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) - goto out; - - g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref); - ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL); - - if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) - goto out; - - if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) - { - gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, - cancellable, error, - "systemctl", "reboot", NULL); - } - } + if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) + { + gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, + cancellable, error, + "systemctl", "reboot", NULL); + } + } ret = TRUE; out: + if (new_origin) + g_key_file_unref (new_origin); if (context) g_option_context_free (context); return ret; diff --git a/src/ostree/ot-admin-builtin-upgrade.c b/src/ostree/ot-admin-builtin-upgrade.c index badaf2e2..f8527117 100644 --- a/src/ostree/ot-admin-builtin-upgrade.c +++ b/src/ostree/ot-admin-builtin-upgrade.c @@ -49,7 +49,7 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel { gboolean ret = FALSE; GOptionContext *context; - gs_unref_object OstreeRepo *repo = NULL; + gs_unref_object OstreeSysrootUpgrader *upgrader = NULL; gs_free char *origin_remote = NULL; gs_free char *origin_ref = NULL; gs_free char *origin_refspec = NULL; @@ -58,7 +58,9 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel gs_unref_object GFile *deployment_origin_path = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL; - GKeyFile *origin; + GSConsole *console; + gs_unref_object OstreeAsyncProgress *progress = NULL; + gboolean changed; context = g_option_context_new ("Construct new tree from current origin and deploy it, if it changed"); g_option_context_add_main_entries (context, options, NULL); @@ -69,118 +71,31 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; - - if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) - goto out; - - if (!ot_admin_deploy_prepare (sysroot, opt_osname, &merge_deployment, - &origin_remote, &origin_ref, - &origin, - cancellable, error)) + upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname, + cancellable, error); + if (!upgrader) goto out; - if (origin_remote) + console = gs_console_get (); + if (console) { - OstreeRepoPullFlags pullflags = 0; - char *refs_to_fetch[] = { origin_ref, NULL }; - GSConsole *console; - gs_unref_object OstreeAsyncProgress *progress = NULL; - - g_print ("Fetching remote %s ref %s\n", origin_remote, origin_ref); - - console = gs_console_get (); - if (console) - { - gs_console_begin_status_line (console, "", NULL, NULL); - progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console); - } - - if (!ostree_repo_pull (repo, origin_remote, refs_to_fetch, pullflags, progress, - cancellable, error)) - goto out; - - origin_refspec = g_strconcat (origin_remote, ":", origin_ref, NULL); + gs_console_begin_status_line (console, "", NULL, NULL); + progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console); } - else - origin_refspec = g_strdup (origin_ref); - - if (!ostree_repo_resolve_rev (repo, origin_refspec, FALSE, &new_revision, - error)) + if (!ostree_sysroot_upgrader_pull (upgrader, 0, 0, progress, &changed, + cancellable, error)) goto out; - - if (strcmp (ostree_deployment_get_csum (merge_deployment), new_revision) == 0) + + if (!changed) { - g_print ("Refspec %s is unchanged\n", origin_refspec); + g_print ("No update available.\n"); } else { gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/"); - if (!opt_allow_downgrade) - { - const char *old_revision = ostree_deployment_get_csum (merge_deployment); - gs_unref_variant GVariant *old_commit = NULL; - gs_unref_variant GVariant *new_commit = NULL; - - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, - old_revision, - &old_commit, - error)) - goto out; - - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, - new_revision, &new_commit, - error)) - goto out; - - if (ostree_commit_get_timestamp (old_commit) > ostree_commit_get_timestamp (new_commit)) - { - GDateTime *old_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (old_commit)); - GDateTime *new_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (new_commit)); - gs_free char *old_ts_str = NULL; - gs_free char *new_ts_str = NULL; - - g_assert (old_ts); - g_assert (new_ts); - old_ts_str = g_date_time_format (old_ts, "%c"); - new_ts_str = g_date_time_format (new_ts, "%c"); - g_date_time_unref (old_ts); - g_date_time_unref (new_ts); - - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current revision '%s' with timestamp '%s'; use --allow-downgrade to permit", - new_revision, new_ts_str, old_revision, old_ts_str); - goto out; - } - } - - /* Here we perform cleanup of any leftover data from previous - * partial failures. This avoids having to call gs_shutil_rm_rf() - * at random points throughout the process. - * - * TODO: Add /ostree/transaction file, and only do this cleanup if - * we find it. - */ - if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) - { - g_prefix_error (error, "Performing initial cleanup: "); - goto out; - } - - if (!ostree_sysroot_deploy_tree (sysroot, - opt_osname, new_revision, origin, - merge_deployment, - NULL, - &new_deployment, - cancellable, error)) - goto out; - - if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, - new_deployment, - merge_deployment, - 0, - cancellable, error)) + if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) goto out; if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index 198f3e48..34e5c6b2 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -48,63 +48,3 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot *sysroot, out: return ret; } - -gboolean -ot_admin_deploy_prepare (OstreeSysroot *sysroot, - const char *osname, - OstreeDeployment **out_merge_deployment, - char **out_origin_remote, - char **out_origin_ref, - GKeyFile **out_origin, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - gs_free char *origin_refspec = NULL; - gs_free char *origin_remote = NULL; - gs_free char *origin_ref = NULL; - gs_unref_object GFile *deployment_path = NULL; - gs_unref_object GFile *deployment_origin_path = NULL; - gs_unref_object OstreeDeployment *merge_deployment = NULL; - GKeyFile *origin; - - if (!ot_admin_require_booted_deployment_or_osname (sysroot, osname, - cancellable, error)) - goto out; - merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, osname); - if (merge_deployment == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No previous deployment for OS '%s'", osname); - goto out; - } - - deployment_path = ostree_sysroot_get_deployment_directory (sysroot, merge_deployment); - deployment_origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path); - - origin = ostree_deployment_get_origin (merge_deployment); - if (!origin) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No origin known for current deployment"); - goto out; - } - origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL); - if (!origin_refspec) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No origin/refspec in current deployment origin; cannot upgrade via ostree"); - goto out; - } - if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error)) - goto out; - - ret = TRUE; - gs_transfer_out_value (out_merge_deployment, &merge_deployment); - gs_transfer_out_value (out_origin_remote, &origin_remote); - gs_transfer_out_value (out_origin_ref, &origin_ref); - gs_transfer_out_value (out_origin, &origin); - out: - g_clear_pointer (&origin, g_key_file_unref); - return ret; -} diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index 8d903713..ab830369 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -32,16 +32,5 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot *sysroot, const char *osname, GCancellable *cancellable, GError **error); - -gboolean -ot_admin_deploy_prepare (OstreeSysroot *sysroot, - const char *osname, - OstreeDeployment **merge_deployment, - char **origin_remote, - char **origin_ref, - GKeyFile **out_origin, - GCancellable *cancellable, - GError **error); - G_END_DECLS |