diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c index b3df9a0c..e31e47b3 100644 --- a/src/libotutil/ot-gio-utils.c +++ b/src/libotutil/ot-gio-utils.c @@ -392,5 +392,96 @@ ot_gio_checksum_stream_finish (GInputStream *in, g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == ot_gio_checksum_stream_async); return g_memdup (g_simple_async_result_get_op_res_gpointer (simple), 32); - } + +/** + * ot_gio_shutil_cp_al_or_fallback: + * @src: Source path + * @dest: Destination path + * @cancellable: + * @error: + * + * Recursively copy path @src (which must be a directory) to the + * target @dest. If possible, hardlinks are used; if a hardlink is + * not possible, a regular copy is created. Any existing files are + * overwritten. + * + * Returns: %TRUE on success + */ +gboolean +ot_gio_shutil_cp_al_or_fallback (GFile *src, + GFile *dest, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lobj GFileEnumerator *enumerator = NULL; + ot_lobj GFileInfo *file_info = NULL; + GError *temp_error = NULL; + + enumerator = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (!enumerator) + goto out; + + if (!ot_gfile_ensure_directory (dest, FALSE, error)) + goto out; + + while ((file_info = g_file_enumerator_next_file (enumerator, cancellable, &temp_error)) != NULL) + { + const char *name = g_file_info_get_name (file_info); + ot_lobj GFile *src_child = g_file_get_child (src, name); + ot_lobj GFile *dest_child = g_file_get_child (dest, name); + + if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) + { + if (!ot_gfile_ensure_directory (dest_child, FALSE, error)) + goto out; + + /* Can't do this even though we'd like to; it fails with an error about + * setting standard::type not being supported =/ + * + if (!g_file_set_attributes_from_info (dest_child, file_info, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error)) + goto out; + */ + if (chmod (ot_gfile_get_path_cached (dest_child), + g_file_info_get_attribute_uint32 (file_info, "unix::mode")) == -1) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + + if (!ot_gio_shutil_cp_al_or_fallback (src_child, dest_child, cancellable, error)) + goto out; + } + else + { + (void) unlink (ot_gfile_get_path_cached (dest_child)); + if (link (ot_gfile_get_path_cached (src_child), ot_gfile_get_path_cached (dest_child)) == -1) + { + if (!(errno == EMLINK || errno == EXDEV)) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + if (!g_file_copy (src_child, dest_child, + G_FILE_COPY_OVERWRITE | G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS, + cancellable, NULL, NULL, error)) + goto out; + } + } + g_clear_object (&file_info); + } + if (temp_error) + { + g_propagate_error (error, temp_error); + goto out; + } + + ret = TRUE; + out: + return ret; +} + diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h index 6440375b..5855a974 100644 --- a/src/libotutil/ot-gio-utils.h +++ b/src/libotutil/ot-gio-utils.h @@ -96,6 +96,12 @@ guchar * ot_gio_checksum_stream_finish (GInputStream *in, GAsyncResult *result, GError **error); +gboolean ot_gio_shutil_cp_al_or_fallback (GFile *src, + GFile *dest, + GCancellable *cancellable, + GError **error); + + G_END_DECLS #endif diff --git a/src/ostadmin/ot-admin-builtin-deploy.c b/src/ostadmin/ot-admin-builtin-deploy.c index 6d85f338..e35eeec2 100644 --- a/src/ostadmin/ot-admin-builtin-deploy.c +++ b/src/ostadmin/ot-admin-builtin-deploy.c @@ -32,15 +32,46 @@ typedef struct { OstreeRepo *repo; } OtAdminDeploy; -static gboolean opt_checkout_only; +static gboolean opt_no_initramfs; +static gboolean opt_no_bootloader; static char *opt_ostree_dir; static GOptionEntry options[] = { { "ostree-dir", 0, 0, G_OPTION_ARG_STRING, &opt_ostree_dir, "Path to OSTree root directory", NULL }, - { "checkout-only", 0, 0, G_OPTION_ARG_NONE, &opt_checkout_only, "Don't generate initramfs or update bootloader", NULL }, + { "no-initramfs", 0, 0, G_OPTION_ARG_NONE, &opt_no_initramfs, "Don't generate initramfs", NULL }, + { "no-bootloader", 0, 0, G_OPTION_ARG_NONE, &opt_no_bootloader, "Don't update bootloader", NULL }, { NULL } }; +static gboolean +copy_modules (const char *release, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ot_lobj GFile *src_modules_file = NULL; + ot_lobj GFile *dest_modules_parent = NULL; + ot_lobj GFile *dest_modules_file = NULL; + + src_modules_file = ot_gfile_from_build_path ("/lib/modules", release, NULL); + dest_modules_file = ot_gfile_from_build_path (opt_ostree_dir, "modules", release, NULL); + dest_modules_parent = g_file_get_parent (dest_modules_file); + if (!ot_gfile_ensure_directory (dest_modules_parent, FALSE, error)) + goto out; + + if (!g_file_query_exists (dest_modules_file, cancellable)) + { + if (!ot_gio_shutil_cp_al_or_fallback (src_modules_file, dest_modules_file, cancellable, error)) + goto out; + } + + ret = TRUE; + out: + if (error) + g_prefix_error (error, "Error copying kernel modules: "); + return ret; +} + static gboolean update_initramfs (const char *release, const char *deploy_target, @@ -48,33 +79,10 @@ update_initramfs (const char *release, GError **error) { gboolean ret = FALSE; - ot_lobj GFile *dest_modules_parent = NULL; - ot_lobj GFile *dest_modules_file = NULL; ot_lfree char *initramfs_name = NULL; ot_lobj GFile *initramfs_file = NULL; ot_lfree char *last_deploy_path = NULL; - dest_modules_file = ot_gfile_from_build_path (opt_ostree_dir, "modules", release, NULL); - dest_modules_parent = g_file_get_parent (dest_modules_file); - if (!ot_gfile_ensure_directory (dest_modules_parent, FALSE, error)) - goto out; - if (!g_file_query_exists (dest_modules_file, NULL)) - { - ot_lptrarray GPtrArray *cp_args = NULL; - ot_lobj GFile *src_modules_file = ot_gfile_from_build_path ("/lib/modules", release, NULL); - - cp_args = g_ptr_array_new (); - ot_ptrarray_add_many (cp_args, "cp", "-al", ot_gfile_get_path_cached (src_modules_file), - ot_gfile_get_path_cached (dest_modules_file), NULL); - g_ptr_array_add (cp_args, NULL); - - g_print ("Copying kernel modules from %s\n", ot_gfile_get_path_cached (src_modules_file)); - if (!ot_spawn_sync_checked (NULL, (char**)cp_args->pdata, NULL, - G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, NULL, error)) - goto out; - } - initramfs_name = g_strconcat ("initramfs-ostree-", release, ".img", NULL); initramfs_file = ot_gfile_from_build_path ("/boot", initramfs_name, NULL); if (!g_file_query_exists (initramfs_file, NULL)) @@ -345,6 +353,8 @@ ot_admin_builtin_deploy (int argc, char **argv, GError **error) gboolean ret = FALSE; const char *deploy_target = NULL; const char *revision = NULL; + struct utsname utsname; + const char *release; __attribute__((unused)) GCancellable *cancellable = NULL; if (!opt_ostree_dir) @@ -371,26 +381,28 @@ ot_admin_builtin_deploy (int argc, char **argv, GError **error) if (!do_checkout (self, deploy_target, revision, cancellable, error)) goto out; - if (!opt_checkout_only) + (void) uname (&utsname); + + if (strcmp (utsname.sysname, "Linux") != 0) { - - struct utsname utsname; - const char *release; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unsupported machine %s", utsname.sysname); + goto out; + } - (void) uname (&utsname); + release = utsname.release; - if (strcmp (utsname.sysname, "Linux") != 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unsupported machine %s", utsname.sysname); - goto out; - } - - release = utsname.release; - + if (!copy_modules (release, cancellable, error)) + goto out; + + if (!opt_no_initramfs) + { if (!update_initramfs (release, deploy_target, cancellable, error)) goto out; - + } + + if (!opt_no_bootloader) + { if (!update_grub (release, cancellable, error)) goto out; }