diff --git a/Makefile-ostadmin.am b/Makefile-ostadmin.am index 636e2194..45cb2d0b 100644 --- a/Makefile-ostadmin.am +++ b/Makefile-ostadmin.am @@ -22,8 +22,11 @@ endif ostadmin_SOURCES = src/ostadmin/main.c \ src/ostadmin/ot-admin-builtins.h \ src/ostadmin/ot-admin-builtin-init.c \ + src/ostadmin/ot-admin-builtin-diff.c \ src/ostadmin/ot-admin-builtin-deploy.c \ src/ostadmin/ot-admin-builtin-update-kernel.c \ + src/ostadmin/ot-admin-functions.h \ + src/ostadmin/ot-admin-functions.c \ src/ostadmin/ot-admin-main.h \ src/ostadmin/ot-admin-main.c \ $(NULL) diff --git a/src/libostree/ostree-diff.c b/src/libostree/ostree-diff.c index e5e40c3d..d7649cf3 100644 --- a/src/libostree/ostree-diff.c +++ b/src/libostree/ostree-diff.c @@ -97,14 +97,14 @@ diff_item_new (GFile *a, return ret; } - static gboolean - diff_files (GFile *a, - GFileInfo *a_info, - GFile *b, - GFileInfo *b_info, - OstreeDiffItem **out_item, - GCancellable *cancellable, - GError **error) +static gboolean +diff_files (GFile *a, + GFileInfo *a_info, + GFile *b, + GFileInfo *b_info, + OstreeDiffItem **out_item, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; ot_lfree char *checksum_a = NULL; diff --git a/src/ostadmin/main.c b/src/ostadmin/main.c index 8a2ded39..ca061f78 100644 --- a/src/ostadmin/main.c +++ b/src/ostadmin/main.c @@ -31,6 +31,7 @@ static OtAdminBuiltin builtins[] = { { "deploy", ot_admin_builtin_deploy, 0 }, + { "diff", ot_admin_builtin_diff, 0 }, { "init", ot_admin_builtin_init, 0 }, { "update-kernel", ot_admin_builtin_update_kernel, 0 }, { NULL } diff --git a/src/ostadmin/ot-admin-builtin-deploy.c b/src/ostadmin/ot-admin-builtin-deploy.c index 94eadfd3..6322f2c4 100644 --- a/src/ostadmin/ot-admin-builtin-deploy.c +++ b/src/ostadmin/ot-admin-builtin-deploy.c @@ -23,96 +23,481 @@ #include "config.h" #include "ot-admin-builtins.h" +#include "ot-admin-functions.h" #include "ostree.h" #include typedef struct { OstreeRepo *repo; + GFile *ostree_dir; } OtAdminDeploy; static gboolean opt_no_kernel; +static gboolean opt_force; static char *opt_ostree_dir = "/ostree"; static GOptionEntry options[] = { { "ostree-dir", 0, 0, G_OPTION_ARG_STRING, &opt_ostree_dir, "Path to OSTree root directory (default: /ostree)", NULL }, { "no-kernel", 0, 0, G_OPTION_ARG_NONE, &opt_no_kernel, "Don't update kernel related config (initramfs, bootloader)", NULL }, + { "force", 0, 0, G_OPTION_ARG_NONE, &opt_force, "Overwrite any existing deployment", NULL }, { NULL } }; +/** + * update_current: + * + * Atomically swap the /ostree/current symbolic link to point to a new + * path. If successful, the old current will be saved as + * /ostree/previous. + * + * Unless the new-current equals current, in which case, do nothing. + */ static gboolean -update_current (const char *deploy_target, +update_current (OtAdminDeploy *self, + GFile *current_deployment, + GFile *deploy_target, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; - ot_lfree char *tmp_symlink = NULL; - ot_lfree char *current_name = NULL; + ot_lobj GFile *current_path = NULL; + ot_lobj GFile *previous_path = NULL; + ot_lobj GFile *tmp_current_path = NULL; + ot_lobj GFile *tmp_previous_path = NULL; + ot_lobj GFileInfo *previous_info = NULL; + ot_lfree char *relative_current = NULL; + ot_lfree char *relative_previous = NULL; - tmp_symlink = g_build_filename (opt_ostree_dir, "tmp-current", NULL); - (void) unlink (tmp_symlink); + current_path = g_file_get_child (self->ostree_dir, "current"); + previous_path = g_file_get_child (self->ostree_dir, "previous"); - if (symlink (deploy_target, tmp_symlink) < 0) + relative_current = g_file_get_relative_path (self->ostree_dir, deploy_target); + g_assert (relative_current); + + if (current_deployment) + { + ot_lfree char *relative_previous = NULL; + + if (g_file_equal (current_deployment, deploy_target)) + { + g_print ("ostadmin: %s already points to %s\n", ot_gfile_get_path_cached (current_path), + relative_current); + return TRUE; + } + + tmp_previous_path = g_file_get_child (self->ostree_dir, "tmp-previous"); + (void) ot_gfile_unlink (tmp_previous_path, NULL, NULL); + + relative_previous = g_file_get_relative_path (self->ostree_dir, current_deployment); + g_assert (relative_previous); + if (symlink (relative_previous, ot_gfile_get_path_cached (tmp_previous_path)) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + } + + tmp_current_path = g_file_get_child (self->ostree_dir, "tmp-current"); + (void) ot_gfile_unlink (tmp_current_path, NULL, NULL); + + if (symlink (relative_current, ot_gfile_get_path_cached (tmp_current_path)) < 0) { ot_util_set_error_from_errno (error, errno); goto out; } - current_name = g_build_filename (opt_ostree_dir, "current", NULL); - if (rename (tmp_symlink, current_name) < 0) + if (!ot_gfile_rename (tmp_current_path, current_path, + cancellable, error)) + goto out; + + if (tmp_previous_path) { - ot_util_set_error_from_errno (error, errno); - goto out; + if (!ot_gfile_rename (tmp_previous_path, previous_path, + cancellable, error)) + goto out; } - g_print ("%s set to %s\n", current_name, deploy_target); + g_print ("ostadmin: %s set to %s\n", ot_gfile_get_path_cached (current_path), + relative_current); ret = TRUE; out: return ret; } +typedef struct { + GError **error; + gboolean caught_error; + + GMainLoop *loop; +} ProcessOneCheckoutData; + +static void +on_checkout_complete (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ProcessOneCheckoutData *data = user_data; + GError *local_error = NULL; + + if (!ostree_repo_checkout_tree_finish ((OstreeRepo*)object, result, + &local_error)) + goto out; + + out: + if (local_error) + { + data->caught_error = TRUE; + g_propagate_error (data->error, local_error); + } + g_main_loop_quit (data->loop); +} + + +/** + * ensure_unlinked: + * + * Like ot_gfile_unlink(), but return successfully if the file doesn't + * exist. + */ static gboolean -do_checkout (OtAdminDeploy *self, +ensure_unlinked (GFile *path, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GError *temp_error = NULL; + + if (!ot_gfile_unlink (path, cancellable, &temp_error)) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * copy_one_config_file: + * + * Copy @file from @modified_etc to @new_etc, overwriting any existing + * file there. + */ +static gboolean +copy_one_config_file (OtAdminDeploy *self, + GFile *modified_etc, + GFile *new_etc, + GFile *file, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lobj GFile *target_file = NULL; + ot_lobj GFile *target_parent = NULL; + ot_lfree char *path = NULL; + + path = g_file_get_relative_path (modified_etc, file); + g_assert (path); + target_file = g_file_resolve_relative_path (new_etc, path); + target_parent = g_file_get_parent (target_file); + + /* FIXME actually we need to copy permissions and xattrs */ + if (!ot_gfile_ensure_directory (target_parent, TRUE, error)) + goto out; + + if (!g_file_copy (file, target_file, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA, + cancellable, NULL, NULL, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +/** + * merge_etc_changes: + * + * Compute the difference between @orig_etc and @modified_etc, + * and apply that to @new_etc. + * + * The algorithm for computing the difference is pretty simple; it's + * approximately equivalent to "diff -unR orig_etc modified_etc", + * except that rather than attempting a 3-way merge if a file is also + * changed in @new_etc, the modified version always wins. + */ +static gboolean +merge_etc_changes (OtAdminDeploy *self, + GFile *orig_etc, + GFile *modified_etc, + GFile *new_etc, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lobj GFile *ostree_etc = NULL; + ot_lobj GFile *tmp_etc = NULL; + ot_lptrarray GPtrArray *modified = NULL; + ot_lptrarray GPtrArray *removed = NULL; + ot_lptrarray GPtrArray *added = NULL; + guint i; + + modified = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + + if (!ostree_diff_dirs (orig_etc, modified_etc, modified, removed, added, + cancellable, error)) + { + g_prefix_error (error, "While computing configuration diff: "); + goto out; + } + + if (modified->len > 0 || removed->len > 0 || added->len > 0) + g_print ("ostadmin: Processing config: %u modified, %u removed, %u added\n", + modified->len, + removed->len, + added->len); + else + g_print ("ostadmin: No modified configuration\n"); + + for (i = 0; i < removed->len; i++) + { + GFile *file = removed->pdata[i]; + ot_lobj GFile *target_file = NULL; + ot_lfree char *path = NULL; + + path = g_file_get_relative_path (orig_etc, file); + g_assert (path); + target_file = g_file_resolve_relative_path (new_etc, path); + + if (!ensure_unlinked (target_file, cancellable, error)) + goto out; + } + + for (i = 0; i < modified->len; i++) + { + OstreeDiffItem *diff = modified->pdata[i]; + if (!copy_one_config_file (self, modified_etc, new_etc, diff->src, + cancellable, error)) + goto out; + } + for (i = 0; i < added->len; i++) + { + GFile *file = added->pdata[i]; + if (!copy_one_config_file (self, modified_etc, new_etc, file, + cancellable, error)) + goto out; + } + + ret = TRUE; + out: + return ret; +} + +/** + * deploy_tree: + * + * Look up @revision in the repository, and check it out in + * OSTREE_DIR/deploy/DEPLOY_TARGET. + * + * Merge configuration changes from the old deployment, if any. + * + * Update the OSTREE_DIR/current and OSTREE_DIR/previous symbolic + * links. + */ +static gboolean +deploy_tree (OtAdminDeploy *self, const char *deploy_target, const char *revision, + GFile **out_deploy_dir, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; - ot_lobj GFile *deploy_path = NULL; + ot_lobj GFile *deploy_dir = NULL; + ot_lfree char *deploy_target_fullname = NULL; + ot_lfree char *deploy_target_fullname_tmp = NULL; + ot_lobj GFile *deploy_target_path = NULL; + ot_lobj GFile *deploy_target_path_tmp = NULL; + ot_lfree char *deploy_target_etc_name = NULL; + ot_lobj GFile *deploy_target_etc_path = NULL; + ot_lobj GFile *deploy_target_default_etc_path = NULL; ot_lobj GFile *deploy_parent = NULL; - ot_lfree char *tree_ref = NULL; - ot_lfree char *repo_path = NULL; - ot_lfree char *repo_arg = NULL; - ot_lptrarray GPtrArray *checkout_args = NULL; + ot_lobj GFile *previous_deployment = NULL; + ot_lobj GFile *previous_deployment_etc = NULL; + ot_lobj GFile *previous_deployment_etc_default = NULL; + ot_lobj OstreeRepoFile *root = NULL; + ot_lobj GFileInfo *file_info = NULL; + ot_lobj GFileInfo *existing_checkout_info = NULL; + ot_lfree char *checkout_target_name = NULL; + ot_lfree char *checkout_target_tmp_name = NULL; + ot_lfree char *resolved_commit = NULL; + GError *temp_error = NULL; + gboolean skip_checkout; - repo_path = g_build_filename (opt_ostree_dir, "repo", NULL); - repo_arg = g_strconcat ("--repo=", repo_path, NULL); + if (!revision) + revision = deploy_target; - deploy_path = ot_gfile_from_build_path (opt_ostree_dir, deploy_target, NULL); - deploy_parent = g_file_get_parent (deploy_path); + deploy_dir = g_file_get_child (self->ostree_dir, "deploy"); + + if (!ostree_repo_resolve_rev (self->repo, revision, FALSE, &resolved_commit, error)) + goto out; + + root = (OstreeRepoFile*)ostree_repo_file_new_root (self->repo, resolved_commit); + if (!ostree_repo_file_ensure_resolved (root, error)) + goto out; + + file_info = g_file_query_info ((GFile*)root, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (!file_info) + goto out; + + deploy_target_fullname = g_strconcat (deploy_target, "-", resolved_commit, NULL); + deploy_target_path = g_file_resolve_relative_path (deploy_dir, deploy_target_fullname); + + deploy_target_fullname_tmp = g_strconcat (deploy_target_fullname, ".tmp", NULL); + deploy_target_path_tmp = g_file_resolve_relative_path (deploy_dir, deploy_target_fullname_tmp); + + deploy_parent = g_file_get_parent (deploy_target_path); if (!ot_gfile_ensure_directory (deploy_parent, TRUE, error)) goto out; - checkout_args = g_ptr_array_new (); - ot_ptrarray_add_many (checkout_args, "ostree", repo_arg, - "checkout", "--atomic-retarget", revision ? revision : deploy_target, - ot_gfile_get_path_cached (deploy_path), NULL); - g_ptr_array_add (checkout_args, NULL); + deploy_target_etc_name = g_strconcat (deploy_target, "-", resolved_commit, "-etc", NULL); + deploy_target_etc_path = g_file_resolve_relative_path (deploy_dir, deploy_target_etc_name); - if (!ot_spawn_sync_checked (opt_ostree_dir, (char**)checkout_args->pdata, NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, NULL, error)) + /* Delete any previous temporary data */ + if (!ot_gio_shutil_rm_rf (deploy_target_path_tmp, cancellable, error)) + goto out; + + existing_checkout_info = g_file_query_info (deploy_target_path, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, &temp_error); + if (existing_checkout_info) + { + if (opt_force) + { + if (!ot_gio_shutil_rm_rf (deploy_target_path, cancellable, error)) + goto out; + if (!ot_gio_shutil_rm_rf (deploy_target_etc_path, cancellable, error)) + goto out; + + skip_checkout = FALSE; + } + else + skip_checkout = TRUE; + } + else if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + skip_checkout = FALSE; + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + + if (!ot_admin_get_current_deployment (self->ostree_dir, &previous_deployment, + cancellable, error)) + goto out; + if (previous_deployment) + { + ot_lfree char *etc_name; + ot_lobj GFile *parent; + + etc_name = g_strconcat (ot_gfile_get_basename_cached (previous_deployment), "-etc", NULL); + parent = g_file_get_parent (previous_deployment); + + previous_deployment_etc = g_file_get_child (parent, etc_name); + + if (!g_file_query_exists (previous_deployment_etc, cancellable) + || g_file_equal (previous_deployment, deploy_target_path)) + g_clear_object (&previous_deployment_etc); + else + previous_deployment_etc_default = g_file_get_child (previous_deployment, "etc"); + } + + + if (!skip_checkout) + { + ProcessOneCheckoutData checkout_data; + + g_print ("ostadmin: Creating deployment %s\n", + ot_gfile_get_path_cached (deploy_target_path)); + + memset (&checkout_data, 0, sizeof (checkout_data)); + checkout_data.loop = g_main_loop_new (NULL, TRUE); + checkout_data.error = error; + + ostree_repo_checkout_tree_async (self->repo, 0, 0, deploy_target_path_tmp, root, + file_info, cancellable, + on_checkout_complete, &checkout_data); + + g_main_loop_run (checkout_data.loop); + + g_main_loop_unref (checkout_data.loop); + + if (checkout_data.caught_error) + goto out; + + if (!ostree_run_triggers_in_root (deploy_target_path_tmp, cancellable, error)) + goto out; + + deploy_target_default_etc_path = ot_gfile_get_child_strconcat (deploy_target_path_tmp, "etc", NULL); + + if (!ot_gio_shutil_rm_rf (deploy_target_etc_path, cancellable, error)) + goto out; + + if (!ot_gio_shutil_cp_a (deploy_target_default_etc_path, deploy_target_etc_path, + cancellable, error)) + goto out; + + g_print ("ostadmin: Created %s\n", ot_gfile_get_path_cached (deploy_target_etc_path)); + + if (previous_deployment_etc) + { + if (!merge_etc_changes (self, previous_deployment_etc_default, + previous_deployment_etc, deploy_target_etc_path, + cancellable, error)) + goto out; + } + else + g_print ("ostadmin: No previous deployment; therefore, no configuration changes to merge\n"); + + if (!ot_gfile_rename (deploy_target_path_tmp, deploy_target_path, + cancellable, error)) + goto out; + } + + if (!update_current (self, previous_deployment, deploy_target_path, + cancellable, error)) goto out; ret = TRUE; + ot_transfer_out_value (out_deploy_dir, &deploy_target_path); out: return ret; } +/** + * do_update_kernel: + * + * Ensure we have a GRUB entry, initramfs set up, etc. + */ static gboolean do_update_kernel (OtAdminDeploy *self, - const char *deploy_target, + GFile *deploy_path, GCancellable *cancellable, GError **error) { @@ -121,11 +506,12 @@ do_update_kernel (OtAdminDeploy *self, args = g_ptr_array_new (); ot_ptrarray_add_many (args, "ostadmin", "update-kernel", - "--ostree-dir", opt_ostree_dir, - deploy_target, NULL); + "--ostree-dir", ot_gfile_get_path_cached (self->ostree_dir), + ot_gfile_get_path_cached (deploy_path), NULL); g_ptr_array_add (args, NULL); - if (!ot_spawn_sync_checked (opt_ostree_dir, (char**)args->pdata, NULL, G_SPAWN_SEARCH_PATH, + if (!ot_spawn_sync_checked (ot_gfile_get_path_cached (self->ostree_dir), + (char**)args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, error)) goto out; @@ -142,6 +528,8 @@ ot_admin_builtin_deploy (int argc, char **argv, GError **error) OtAdminDeploy self_data; OtAdminDeploy *self = &self_data; gboolean ret = FALSE; + ot_lobj GFile *repo_path = NULL; + ot_lobj GFile *deploy_path = NULL; const char *deploy_target = NULL; const char *revision = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; @@ -149,6 +537,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GError **error) memset (self, 0, sizeof (*self)); context = g_option_context_new ("NAME [REVISION] - Check out revision NAME (or REVISION as NAME)"); + g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) @@ -159,25 +548,35 @@ ot_admin_builtin_deploy (int argc, char **argv, GError **error) ot_util_usage_error (context, "NAME must be specified", error); goto out; } - + + self->ostree_dir = g_file_new_for_path (opt_ostree_dir); + + if (!ot_admin_ensure_initialized (self->ostree_dir, cancellable, error)) + goto out; + + repo_path = g_file_get_child (self->ostree_dir, "repo"); + self->repo = ostree_repo_new (repo_path); + if (!ostree_repo_check (self->repo, error)) + goto out; + deploy_target = argv[1]; if (argc > 2) revision = argv[2]; - if (!do_checkout (self, deploy_target, revision, cancellable, error)) + if (!deploy_tree (self, deploy_target, revision, &deploy_path, + cancellable, error)) goto out; if (!opt_no_kernel) { - if (!do_update_kernel (self, deploy_target, cancellable, error)) + if (!do_update_kernel (self, deploy_path, cancellable, error)) goto out; } - - if (!update_current (deploy_target, cancellable, error)) - goto out; ret = TRUE; out: + g_clear_object (&self->repo); + g_clear_object (&self->ostree_dir); if (context) g_option_context_free (context); return ret; diff --git a/src/ostadmin/ot-admin-builtin-diff.c b/src/ostadmin/ot-admin-builtin-diff.c new file mode 100644 index 00000000..c828f8e2 --- /dev/null +++ b/src/ostadmin/ot-admin-builtin-diff.c @@ -0,0 +1,102 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ostree.h" + +#include + +static char *opt_ostree_dir = "/ostree"; + +static GOptionEntry options[] = { + { "ostree-dir", 0, 0, G_OPTION_ARG_STRING, &opt_ostree_dir, "Path to OSTree root directory (default: /ostree)", NULL }, + { NULL } +}; + +gboolean +ot_admin_builtin_diff (int argc, char **argv, GError **error) +{ + GOptionContext *context; + gboolean ret = FALSE; + ot_lobj GFile *ostree_dir = NULL; + ot_lobj GFile *repo_path = NULL; + ot_lobj GFile *deployment = NULL; + ot_lobj GFile *deploy_parent = NULL; + ot_lptrarray GPtrArray *modified = NULL; + ot_lptrarray GPtrArray *removed = NULL; + ot_lptrarray GPtrArray *added = NULL; + ot_lobj GFile *orig_etc_path = NULL; + ot_lobj GFile *new_etc_path = NULL; + __attribute__((unused)) GCancellable *cancellable = NULL; + + context = g_option_context_new ("[NAME] - Diff configuration for revision NAME"); + + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + + ostree_dir = g_file_new_for_path (opt_ostree_dir); + repo_path = g_file_get_child (ostree_dir, "repo"); + + if (argc > 1) + { + deployment = ot_gfile_get_child_build_path (ostree_dir, "deploy", argv[1], NULL); + if (!g_file_query_exists (deployment, NULL)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Deployment %s doesn't exist", ot_gfile_get_path_cached (deployment)); + goto out; + } + } + else + { + if (!ot_admin_get_current_deployment (ostree_dir, &deployment, + cancellable, error)) + goto out; + } + + orig_etc_path = g_file_resolve_relative_path (deployment, "etc"); + deploy_parent = g_file_get_parent (deployment); + new_etc_path = ot_gfile_get_child_strconcat (deploy_parent, + ot_gfile_get_basename_cached (deployment), + "-etc", NULL); + + modified = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + if (!ostree_diff_dirs (orig_etc_path, new_etc_path, modified, removed, added, + cancellable, error)) + goto out; + + ostree_diff_print (new_etc_path, modified, removed, added); + + ret = TRUE; + out: + g_clear_object (&ostree_dir); + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostadmin/ot-admin-builtin-init.c b/src/ostadmin/ot-admin-builtin-init.c index a96c02a4..703e14fc 100644 --- a/src/ostadmin/ot-admin-builtin-init.c +++ b/src/ostadmin/ot-admin-builtin-init.c @@ -23,6 +23,7 @@ #include "config.h" #include "ot-admin-builtins.h" +#include "ot-admin-functions.h" #include "otutil.h" #include @@ -49,48 +50,11 @@ ot_admin_builtin_init (int argc, char **argv, GError **error) if (!g_option_context_parse (context, &argc, &argv, error)) goto out; - g_clear_object (&dir); - dir = ot_gfile_from_build_path (opt_ostree_dir, "repo", NULL); - if (!ot_gfile_ensure_directory (dir, TRUE, error)) - goto out; + dir = g_file_new_for_path (opt_ostree_dir); - /* We presently copy over host kernel modules */ - g_clear_object (&dir); - dir = ot_gfile_from_build_path (opt_ostree_dir, "modules", NULL); - if (!ot_gfile_ensure_directory (dir, TRUE, error)) + if (!ot_admin_ensure_initialized (dir, cancellable, error)) goto out; - g_clear_object (&dir); - dir = ot_gfile_from_build_path (opt_ostree_dir, "repo", "objects", NULL); - if (!g_file_query_exists (dir, NULL)) - { - ot_lfree char *opt_repo_path = g_strdup_printf ("--repo=%s/repo", opt_ostree_dir); - const char *child_argv[] = { "ostree", opt_repo_path, "init", NULL }; - - if (!ot_spawn_sync_checked (NULL, (char**)child_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, - NULL, NULL, error)) - { - g_prefix_error (error, "Failed to initialize repository: "); - goto out; - } - } - - /* Ensure a few subdirectories of /var exist, since we need them for - dracut generation */ - g_clear_object (&dir); - dir = ot_gfile_from_build_path (opt_ostree_dir, "var", "log", NULL); - if (!ot_gfile_ensure_directory (dir, TRUE, error)) - goto out; - g_clear_object (&dir); - dir = ot_gfile_from_build_path (opt_ostree_dir, "var", "tmp", NULL); - if (!ot_gfile_ensure_directory (dir, TRUE, error)) - goto out; - if (chmod (ot_gfile_get_path_cached (dir), 01777) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - g_print ("%s initialized as OSTree root\n", opt_ostree_dir); ret = TRUE; diff --git a/src/ostadmin/ot-admin-builtin-update-kernel.c b/src/ostadmin/ot-admin-builtin-update-kernel.c index dd538ca4..d8edad5c 100644 --- a/src/ostadmin/ot-admin-builtin-update-kernel.c +++ b/src/ostadmin/ot-admin-builtin-update-kernel.c @@ -32,7 +32,7 @@ typedef struct { OstreeRepo *repo; } OtAdminUpdateKernel; -static char *opt_ostree_dir; +static char *opt_ostree_dir = "/ostree"; static GOptionEntry options[] = { { "ostree-dir", 0, 0, G_OPTION_ARG_STRING, &opt_ostree_dir, "Path to OSTree root directory", NULL }, @@ -70,14 +70,13 @@ copy_modules (const char *release, static gboolean update_initramfs (const char *release, - const char *deploy_target, + const char *deploy_path, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; ot_lfree char *initramfs_name = NULL; ot_lobj GFile *initramfs_file = NULL; - ot_lfree char *last_deploy_path = NULL; initramfs_name = g_strconcat ("initramfs-ostree-", release, ".img", NULL); initramfs_file = ot_gfile_from_build_path ("/boot", initramfs_name, NULL); @@ -98,8 +97,6 @@ update_initramfs (const char *release, ostree_vardir = g_build_filename (opt_ostree_dir, "var", NULL); ostree_moduledir = g_build_filename (opt_ostree_dir, "modules", NULL); - last_deploy_path = g_build_filename (opt_ostree_dir, deploy_target, NULL); - mkinitramfs_args = g_ptr_array_new (); /* Note: the hardcoded /tmp path below is not actually a * security flaw, because we've bind-mounted dracut's view @@ -113,12 +110,12 @@ update_initramfs (const char *release, "--mount-bind", ostree_vardir, "/var", "--mount-bind", ot_gfile_get_path_cached (tmpdir), "/tmp", "--mount-bind", ostree_moduledir, "/lib/modules", - last_deploy_path, + deploy_path, "dracut", "-f", "/tmp/initramfs-ostree.img", release, NULL); g_ptr_array_add (mkinitramfs_args, NULL); - g_print ("Generating initramfs using %s...\n", last_deploy_path); + g_print ("Generating initramfs using %s...\n", deploy_path); if (!ot_spawn_sync_checked (NULL, (char**)mkinitramfs_args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, error)) @@ -277,14 +274,11 @@ ot_admin_builtin_update_kernel (int argc, char **argv, GError **error) OtAdminUpdateKernel self_data; OtAdminUpdateKernel *self = &self_data; gboolean ret = FALSE; - const char *deploy_target = NULL; + const char *deploy_path = NULL; struct utsname utsname; const char *release; __attribute__((unused)) GCancellable *cancellable = NULL; - if (!opt_ostree_dir) - opt_ostree_dir = "/ostree"; - memset (self, 0, sizeof (*self)); context = g_option_context_new ("[OSTREE_REVISION [KERNEL_RELEASE]] - Update kernel and regenerate initial ramfs"); @@ -294,9 +288,9 @@ ot_admin_builtin_update_kernel (int argc, char **argv, GError **error) goto out; if (argc > 1) - deploy_target = argv[1]; + deploy_path = argv[1]; else - deploy_target = "current"; + deploy_path = "current"; (void) uname (&utsname); @@ -314,7 +308,7 @@ ot_admin_builtin_update_kernel (int argc, char **argv, GError **error) if (!copy_modules (release, cancellable, error)) goto out; - if (!update_initramfs (release, deploy_target, cancellable, error)) + if (!update_initramfs (release, deploy_path, cancellable, error)) goto out; if (!update_grub (release, cancellable, error)) diff --git a/src/ostadmin/ot-admin-builtins.h b/src/ostadmin/ot-admin-builtins.h index 59d220bf..4ceca2a5 100644 --- a/src/ostadmin/ot-admin-builtins.h +++ b/src/ostadmin/ot-admin-builtins.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS gboolean ot_admin_builtin_init (int argc, char **argv, GError **error); gboolean ot_admin_builtin_deploy (int argc, char **argv, GError **error); +gboolean ot_admin_builtin_diff (int argc, char **argv, GError **error); gboolean ot_admin_builtin_update_kernel (int argc, char **argv, GError **error); G_END_DECLS diff --git a/src/ostadmin/ot-admin-functions.c b/src/ostadmin/ot-admin-functions.c new file mode 100644 index 00000000..52096850 --- /dev/null +++ b/src/ostadmin/ot-admin-functions.c @@ -0,0 +1,144 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "ot-admin-functions.h" +#include "otutil.h" +#include "ostree-core.h" + +gboolean +ot_admin_ensure_initialized (GFile *ostree_dir, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lobj GFile *dir = NULL; + + g_clear_object (&dir); + dir = g_file_get_child (ostree_dir, "repo"); + if (!ot_gfile_ensure_directory (dir, TRUE, error)) + goto out; + + g_clear_object (&dir); + dir = g_file_get_child (ostree_dir, "deploy"); + if (!ot_gfile_ensure_directory (dir, TRUE, error)) + goto out; + + g_clear_object (&dir); + dir = g_file_get_child (ostree_dir, "modules"); + if (!ot_gfile_ensure_directory (dir, TRUE, error)) + goto out; + + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (ostree_dir, "repo", "objects", NULL); + if (!g_file_query_exists (dir, NULL)) + { + ot_lfree char *opt_repo_arg = g_strdup_printf ("--repo=%s/repo", + ot_gfile_get_path_cached (ostree_dir)); + const char *child_argv[] = { "ostree", opt_repo_arg, "init", NULL }; + + if (!ot_spawn_sync_checked (NULL, (char**)child_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, + NULL, NULL, error)) + { + g_prefix_error (error, "Failed to initialize repository: "); + goto out; + } + } + + /* Ensure a few subdirectories of /var exist, since we need them for + dracut generation */ + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (ostree_dir, "var", "log", NULL); + if (!ot_gfile_ensure_directory (dir, TRUE, error)) + goto out; + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (ostree_dir, "var", "tmp", NULL); + if (!ot_gfile_ensure_directory (dir, TRUE, error)) + goto out; + if (chmod (ot_gfile_get_path_cached (dir), 01777) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +/** + * ot_admin_get_current_deployment: + * + * Returns in @out_deployment the full file path of the current + * deployment that the /ostree/current symbolic link points to, or + * %NULL if none. + */ +gboolean +ot_admin_get_current_deployment (GFile *ostree_dir, + GFile **out_deployment, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lobj GFile *current_path = NULL; + ot_lobj GFileInfo *file_info = NULL; + ot_lobj GFile *ret_deployment = NULL; + GError *temp_error = NULL; + + current_path = g_file_get_child (ostree_dir, "current"); + + file_info = g_file_query_info (current_path, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, &temp_error); + if (!file_info) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + else + { + const char *target; + if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_SYMBOLIC_LINK) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not a symbolic link"); + goto out; + } + target = g_file_info_get_symlink_target (file_info); + g_assert (target); + ret_deployment = g_file_resolve_relative_path (ostree_dir, target); + } + + ret = TRUE; + ot_transfer_out_value (out_deployment, &ret_deployment); + out: + return ret; +} + diff --git a/src/ostadmin/ot-admin-functions.h b/src/ostadmin/ot-admin-functions.h new file mode 100644 index 00000000..f4e1b74e --- /dev/null +++ b/src/ostadmin/ot-admin-functions.h @@ -0,0 +1,41 @@ +/* -*- 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 + */ + +#ifndef __OT_ADMIN_FUNCTIONS__ +#define __OT_ADMIN_FUNCTIONS__ + +#include + +G_BEGIN_DECLS + +gboolean ot_admin_ensure_initialized (GFile *ostree_dir, + GCancellable *cancellable, + GError **error); + +gboolean ot_admin_get_current_deployment (GFile *ostree_dir, + GFile **out_deployment, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c index 12a8f392..2b63851d 100644 --- a/src/ostree/ot-builtin-checkout.c +++ b/src/ostree/ot-builtin-checkout.c @@ -30,7 +30,6 @@ #include static gboolean opt_user_mode; -static gboolean opt_atomic_retarget; static gboolean opt_no_triggers; static char *opt_subpath; static gboolean opt_union; @@ -41,89 +40,12 @@ static GOptionEntry options[] = { { "user-mode", 'U', 0, G_OPTION_ARG_NONE, &opt_user_mode, "Do not change file ownership or initialize extended attributes", NULL }, { "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" }, { "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL }, - { "atomic-retarget", 0, 0, G_OPTION_ARG_NONE, &opt_atomic_retarget, "Make a symbolic link for destination, suffix with checksum", NULL }, { "no-triggers", 0, 0, G_OPTION_ARG_NONE, &opt_no_triggers, "Don't run triggers", NULL }, { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &opt_from_stdin, "Process many checkouts from standard input", NULL }, { "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", NULL }, { NULL } }; -static gboolean -atomic_symlink_swap (GFile *dest, - const char *target, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - ot_lobj GFile *parent = NULL; - ot_lfree char *tmp_name = NULL; - ot_lobj GFile *tmp_link = NULL; - - parent = g_file_get_parent (dest); - /* HACK - should use randomly generated temporary target name */ - tmp_name = g_strconcat (ot_gfile_get_basename_cached (dest), - "-tmplink", NULL); - tmp_link = g_file_get_child (parent, tmp_name); - if (symlink (target, ot_gfile_get_path_cached (tmp_link)) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - if (!ot_gfile_rename (tmp_link, dest, cancellable, error)) - goto out; - - ret = TRUE; - out: - return ret; -} - -static gboolean -parse_commit_from_symlink (GFile *symlink, - char **out_commit, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - const char *target; - const char *last_dash; - const char *checksum; - ot_lobj GFileInfo *file_info = NULL; - ot_lfree char *ret_commit = NULL; - - file_info = g_file_query_info (symlink, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!file_info) - goto out; - - target = g_file_info_get_symlink_target (file_info); - if (target == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not a symbolic link"); - goto out; - } - - last_dash = strrchr (target, '-'); - if (last_dash == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid existing symlink target; no trailing dash"); - goto out; - } - checksum = last_dash + 1; - - if (!ostree_validate_structureof_checksum_string (checksum, error)) - goto out; - - ret_commit = g_strdup (checksum); - - ret = TRUE; - ot_transfer_out_value (out_commit, &ret_commit); - out: - return ret; -} - typedef struct { gboolean caught_error; GError **error; @@ -263,7 +185,10 @@ process_many_checkouts (OstreeRepo *repo, if (!process_one_checkout (repo, resolved_commit, subpath, target, cancellable, error)) - goto out; + { + g_prefix_error (error, "Processing tree %s: ", resolved_commit); + goto out; + } g_free (revision); } @@ -286,11 +211,9 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error gboolean ret = FALSE; const char *commit; const char *destination; - gboolean skip_checkout; ot_lobj OstreeRepo *repo = NULL; ot_lfree char *existing_commit = NULL; ot_lfree char *resolved_commit = NULL; - ot_lfree char *suffixed_destination = NULL; ot_lfree char *tmp_destination = NULL; ot_lobj GFileInfo *symlink_file_info = NULL; ot_lobj GFile *checkout_target = NULL; @@ -319,13 +242,6 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error if (opt_from_stdin || opt_from_file) { - if (opt_atomic_retarget) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "--atomic-retarget may not be used with --from-stdin or --from-file"); - goto out; - } - destination = argv[1]; checkout_target = g_file_new_for_path (destination); @@ -349,69 +265,18 @@ ostree_builtin_checkout (int argc, char **argv, GFile *repo_path, GError **error if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) goto out; - if (opt_atomic_retarget) - { - GError *temp_error = NULL; + checkout_target = g_file_new_for_path (destination); - suffixed_destination = g_strconcat (destination, "-", resolved_commit, NULL); - checkout_target = g_file_new_for_path (suffixed_destination); - tmp_destination = g_strconcat (suffixed_destination, ".tmp", NULL); - checkout_target_tmp = g_file_new_for_path (tmp_destination); - symlink_target = g_file_new_for_path (destination); + if (!process_one_checkout (repo, resolved_commit, opt_subpath, + checkout_target_tmp ? checkout_target_tmp : checkout_target, + cancellable, error)) + goto out; - if (!parse_commit_from_symlink (symlink_target, &existing_commit, - cancellable, &temp_error)) - { - if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - skip_checkout = FALSE; - g_clear_error (&temp_error); - } - else - { - g_propagate_error (error, temp_error); - goto out; - } - } - else - { - skip_checkout = strcmp (existing_commit, resolved_commit) == 0; - } - } - else + if (!opt_no_triggers) { - checkout_target = g_file_new_for_path (destination); - skip_checkout = FALSE; - } - - if (skip_checkout) - { - g_print ("ostree-checkout: Rev %s is already checked out as %s\n", commit, resolved_commit); - } - else - { - if (!process_one_checkout (repo, resolved_commit, opt_subpath, - checkout_target_tmp ? checkout_target_tmp : checkout_target, - cancellable, error)) + if (!ostree_run_triggers_in_root (checkout_target_tmp ? checkout_target_tmp : checkout_target, + cancellable, error)) goto out; - - if (!opt_no_triggers) - { - if (!ostree_run_triggers_in_root (checkout_target_tmp ? checkout_target_tmp : checkout_target, - cancellable, error)) - goto out; - } - - if (opt_atomic_retarget) - { - if (!ot_gfile_rename (checkout_target_tmp, checkout_target, cancellable, error)) - goto out; - if (!atomic_symlink_swap (symlink_target, - ot_gfile_get_basename_cached (checkout_target), - cancellable, error)) - goto out; - g_print ("ostree-checkout: Rev %s checked out as %s\n", commit, resolved_commit); - } } } diff --git a/src/ostree/ot-builtin-diff.c b/src/ostree/ot-builtin-diff.c index 7a559bef..3762968b 100644 --- a/src/ostree/ot-builtin-diff.c +++ b/src/ostree/ot-builtin-diff.c @@ -65,7 +65,6 @@ ostree_builtin_diff (int argc, char **argv, GFile *repo_path, GError **error) gboolean ret = FALSE; GOptionContext *context; GCancellable *cancellable = NULL; - int i; const char *src; const char *target; ot_lobj OstreeRepo *repo = NULL; diff --git a/src/switchroot/ostree-switch-root.c b/src/switchroot/ostree-switch-root.c index ff16b165..6330223a 100644 --- a/src/switchroot/ostree-switch-root.c +++ b/src/switchroot/ostree-switch-root.c @@ -154,11 +154,12 @@ main(int argc, char *argv[]) const char *toproot_bind_mounts[] = { "/home", "/root", "/tmp", NULL }; const char *ostree_bind_mounts[] = { "/var", NULL }; /* ostree_readonly_bind_mounts /lib/modules -> modules */ - const char *readonly_bind_mounts[] = { "/bin", "/etc", "/lib", "/sbin", "/usr", + const char *readonly_bind_mounts[] = { "/bin", "/lib", "/sbin", "/usr", NULL }; const char *root_mountpoint = NULL; const char *ostree_target = NULL; const char *ostree_subinit = NULL; + char ostree_target_path[PATH_MAX]; char srcpath[PATH_MAX]; char destpath[PATH_MAX]; struct stat stbuf; @@ -199,7 +200,7 @@ main(int argc, char *argv[]) perrorv ("Invalid ostree root '%s'", destpath); exit (1); } - + for (i = 0; initramfs_move_mounts[i] != NULL; i++) { const char *path = initramfs_move_mounts[i]; @@ -245,16 +246,46 @@ main(int argc, char *argv[]) /* From this point on we're chrooted into the real root filesystem, * so we no longer refer to root_mountpoint. */ + + snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_target); + fprintf (stderr, "Examining %s\n", destpath); + if (lstat (destpath, &stbuf) < 0) + { + perrorv ("Second stat of ostree root '%s' failed: ", destpath); + exit (1); + } + if (S_ISLNK (stbuf.st_mode)) + { + if (readlink (destpath, ostree_target_path, PATH_MAX) < 0) + { + perrorv ("readlink(%s) failed: ", destpath); + exit (1); + } + fprintf (stderr, "Resolved OSTree target to: %s\n", ostree_target_path); + } + else + { + strncpy (ostree_target_path, ostree_target, PATH_MAX); + fprintf (stderr, "OSTree target is: %s\n", ostree_target_path); + } - snprintf (destpath, sizeof(destpath), "/ostree/%s/sysroot", ostree_target); + snprintf (destpath, sizeof(destpath), "/ostree/%s/sysroot", ostree_target_path); if (mount ("/", destpath, NULL, MS_BIND, NULL) < 0) { perrorv ("Failed to bind mount / to '%s'", destpath); exit (1); } + snprintf (srcpath, sizeof(srcpath), "/ostree/%s-etc", ostree_target_path); + snprintf (destpath, sizeof(destpath), "/ostree/%s/etc", ostree_target_path); + if (mount (srcpath, destpath, NULL, MS_BIND, NULL) < 0) + { + perrorv ("Failed to bind mount '%s' to '%s'", srcpath, destpath); + exit (1); + } + snprintf (srcpath, sizeof(srcpath), "%s", "/ostree/var"); - snprintf (destpath, sizeof(destpath), "/ostree/%s/var", ostree_target); + snprintf (destpath, sizeof(destpath), "/ostree/%s/var", ostree_target_path); if (mount (srcpath, destpath, NULL, MS_BIND, NULL) < 0) { perrorv ("Failed to bind mount '%s' to '%s'", srcpath, destpath); @@ -274,7 +305,7 @@ main(int argc, char *argv[]) for (i = 0; ostree_bind_mounts[i] != NULL; i++) { snprintf (srcpath, sizeof(srcpath), "/ostree/%s", ostree_bind_mounts[i]); - snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, ostree_bind_mounts[i]); + snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target_path, ostree_bind_mounts[i]); if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0) { perrorv ("failed to bind mount (class:bind) %s to %s", srcpath, destpath); @@ -284,7 +315,7 @@ main(int argc, char *argv[]) for (i = 0; readonly_bind_mounts[i] != NULL; i++) { - snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, readonly_bind_mounts[i]); + snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target_path, readonly_bind_mounts[i]); if (mount (destpath, destpath, NULL, MS_BIND, NULL) < 0) { perrorv ("failed to bind mount (class:readonly) %s", destpath); @@ -299,14 +330,14 @@ main(int argc, char *argv[]) /* This should come after we've bind mounted /lib */ snprintf (srcpath, sizeof(srcpath), "/ostree/modules"); - snprintf (destpath, sizeof(destpath), "/ostree/%s/lib/modules", ostree_target); + snprintf (destpath, sizeof(destpath), "/ostree/%s/lib/modules", ostree_target_path); if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0) { perrorv ("failed to bind mount %s to %s", srcpath, destpath); exit (1); } - snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_target); + snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_target_path); if (chroot (destpath) < 0) { perrorv ("failed to change root to '%s'", destpath);