/** * gs_file_create: * @file: Path to non-existent file * @mode: Unix access permissions * @out_stream: (out) (transfer full) (allow-none): Newly created output, or %NULL * @cancellable: a #GCancellable * @error: a #GError * * Like g_file_create(), except this function allows specifying the * access mode. This allows atomically creating private files. */ gboolean gs_file_create (GFile *file, int mode, GOutputStream **out_stream, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int fd; GOutputStream *ret_stream = NULL; fd = open_nointr (gs_file_get_path_cached (file), O_WRONLY | O_CREAT | O_EXCL, mode); if (fd < 0) { gs_set_prefix_error_from_errno (error, errno, "open"); goto out; } if (fchmod (fd, mode) < 0) { close (fd); gs_set_prefix_error_from_errno (error, errno, "fchmod"); goto out; } ret_stream = g_unix_output_stream_new (fd, TRUE); ret = TRUE; gs_transfer_out_value (out_stream, &ret_stream); out: g_clear_object (&ret_stream); return ret; }
gboolean _rpmostree_file_load_contents_utf8_allow_noent (GFile *path, char **out_contents, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GError *temp_error = NULL; gs_free char *ret_contents = NULL; ret_contents = gs_file_load_contents_utf8 (path, cancellable, &temp_error); if (!ret_contents) { 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; gs_transfer_out_value (out_contents, &ret_contents); out: return ret; }
gboolean _rpmostree_util_enumerate_directory_allow_noent (GFile *dirpath, const char *queryargs, GFileQueryInfoFlags queryflags, GFileEnumerator **out_direnum, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GError *temp_error = NULL; gs_unref_object GFileEnumerator *ret_direnum = NULL; ret_direnum = g_file_enumerate_children (dirpath, queryargs, queryflags, cancellable, &temp_error); if (!ret_direnum) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); ret = TRUE; } else g_propagate_error (error, temp_error); goto out; } ret = TRUE; gs_transfer_out_value (out_direnum, &ret_direnum); out: return ret; }
gboolean rpmostree_get_pkglist_for_root (GFile *root, HySack *out_sack, HyPackageList *out_pkglist, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int rc; _cleanup_hysack_ HySack sack = NULL; _cleanup_hyquery_ HyQuery query = NULL; _cleanup_hypackagelist_ HyPackageList pkglist = NULL; #if BUILDOPT_HAWKEY_SACK_CREATE2 sack = hy_sack_create (NULL, NULL, gs_file_get_path_cached (root), NULL, HY_MAKE_CACHE_DIR); #else sack = hy_sack_create (NULL, NULL, gs_file_get_path_cached (root), HY_MAKE_CACHE_DIR); #endif if (sack == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create sack cache"); goto out; } rc = hy_sack_load_system_repo (sack, NULL, 0); if (!hif_error_set_from_hawkey (rc, error)) { g_prefix_error (error, "Failed to load system repo: "); goto out; } query = hy_query_create (sack); hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); pkglist = hy_query_run (query); ret = TRUE; gs_transfer_out_value (out_sack, &sack); gs_transfer_out_value (out_pkglist, &pkglist); out: return ret; }
gboolean rpmostree_db_option_context_parse (GOptionContext *context, const GOptionEntry *main_entries, int *argc, char ***argv, OstreeRepo **out_repo, GCancellable *cancellable, GError **error) { gs_unref_object OstreeRepo *repo = NULL; gboolean success = FALSE; /* Entries are listed in --help output in the order added. We add the * main entries ourselves so that we can add the --repo entry first. */ g_option_context_add_main_entries (context, global_entries, NULL); if (!rpmostree_option_context_parse (context, main_entries, argc, argv, RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, cancellable, NULL, error)) goto out; if (opt_repo == NULL) { gs_unref_object OstreeSysroot *sysroot = NULL; sysroot = ostree_sysroot_new_default (); if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; } else { gs_unref_object GFile *repo_file = g_file_new_for_path (opt_repo); repo = ostree_repo_new (repo_file); if (!ostree_repo_open (repo, cancellable, error)) goto out; } if (rpmReadConfigFiles (NULL, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "rpm failed to init: %s", rpmlogMessage ()); goto out; } gs_transfer_out_value (out_repo, &repo); success = TRUE; out: return success; }
/** * gs_file_open_in_tmpdir_at: * @tmpdir_fd: Directory to place temporary file * @mode: Default mode (will be affected by umask) * @out_name: (out) (transfer full): Newly created file name * @out_stream: (out) (transfer full) (allow-none): Newly created output stream * @cancellable: * @error: * * Like g_file_open_tmp(), except the file will be created in the * provided @tmpdir, and allows specification of the Unix @mode, which * means private files may be created. Return values will be stored * in @out_name, and optionally @out_stream. */ gboolean gs_file_open_in_tmpdir_at (int tmpdir_fd, int mode, char **out_name, GOutputStream **out_stream, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const int max_attempts = 128; int i; char *tmp_name = NULL; int fd; /* 128 attempts seems reasonable... */ for (i = 0; i < max_attempts; i++) { g_free (tmp_name); tmp_name = gs_fileutil_gen_tmp_name (NULL, NULL); do fd = openat (tmpdir_fd, tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode); while (fd == -1 && errno == EINTR); if (fd < 0 && errno != EEXIST) { gs_set_prefix_error_from_errno (error, errno, "openat"); goto out; } else if (fd != -1) break; } if (i == max_attempts) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Exhausted attempts to open temporary file"); goto out; } ret = TRUE; gs_transfer_out_value (out_name, &tmp_name); if (out_stream) *out_stream = g_unix_output_stream_new (fd, TRUE); else (void) close (fd); out: g_free (tmp_name); return ret; }
static gboolean parse_origin (OstreeSysroot *self, int deployment_dfd, const char *deployment_name, GKeyFile **out_origin, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GKeyFile) ret_origin = NULL; g_autofree char *origin_path = g_strconcat ("../", deployment_name, ".origin", NULL); struct stat stbuf; g_autofree char *origin_contents = NULL; ret_origin = g_key_file_new (); if (fstatat (deployment_dfd, origin_path, &stbuf, 0) != 0) { if (errno == ENOENT) ; else { glnx_set_error_from_errno (error); goto out; } } else { origin_contents = glnx_file_get_contents_utf8_at (deployment_dfd, origin_path, NULL, cancellable, error); if (!origin_contents) goto out; if (!g_key_file_load_from_data (ret_origin, origin_contents, -1, 0, error)) goto out; } ret = TRUE; gs_transfer_out_value (out_origin, &ret_origin); out: if (error) g_prefix_error (error, "Parsing %s: ", origin_path); if (ret_origin) g_key_file_unref (ret_origin); return ret; }
/* Given a directory @d, find the first child that is a directory, * returning it in @out_subdir. If there are multiple directories, * return an error. */ static gboolean find_ensure_one_subdirectory (GFile *d, GFile **out_subdir, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFileEnumerator *direnum = NULL; gs_unref_object GFile *ret_subdir = NULL; direnum = g_file_enumerate_children (d, "standard::name,standard::type", 0, cancellable, error); if (!direnum) goto out; while (TRUE) { GFileInfo *file_info; GFile *child; if (!gs_file_enumerator_iterate (direnum, &file_info, &child, cancellable, error)) goto out; if (!file_info) break; if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) { if (ret_subdir) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Multiple subdirectories found in: %s", gs_file_get_path_cached (d)); goto out; } ret_subdir = g_object_ref (child); } } ret = TRUE; gs_transfer_out_value (out_subdir, &ret_subdir); out: return ret; }
/* * Generate a sorted array of [(checksum: str, size: uint64, names: array[string]), ...] * for regular file content. */ static gboolean build_content_sizenames_filtered (OstreeRepo *repo, GVariant *commit, GHashTable *include_only_objects, GPtrArray **out_sizenames, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GPtrArray) ret_sizenames = g_ptr_array_new_with_free_func (_ostree_delta_content_sizenames_free); g_autoptr(GHashTable) sizenames_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, _ostree_delta_content_sizenames_free); ostree_cleanup_repo_commit_traverse_iter OstreeRepoCommitTraverseIter iter = { 0, }; if (!ostree_repo_commit_traverse_iter_init_commit (&iter, repo, commit, OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE, error)) goto out; if (!build_content_sizenames_recurse (repo, &iter, sizenames_map, include_only_objects, cancellable, error)) goto out; { GHashTableIter hashiter; gpointer hkey, hvalue; g_hash_table_iter_init (&hashiter, sizenames_map); while (g_hash_table_iter_next (&hashiter, &hkey, &hvalue)) { g_hash_table_iter_steal (&hashiter); g_ptr_array_add (ret_sizenames, hvalue); } } g_ptr_array_sort (ret_sizenames, compare_sizenames); ret = TRUE; gs_transfer_out_value (out_sizenames, &ret_sizenames); out: return ret; }
static gboolean terminal_app_dbus_register (GApplication *application, GDBusConnection *connection, const gchar *object_path, GError **error) { TerminalApp *app = TERMINAL_APP (application); gs_unref_object TerminalObjectSkeleton *object = NULL; gs_unref_object TerminalFactory *factory = NULL; if (!G_APPLICATION_CLASS (terminal_app_parent_class)->dbus_register (application, connection, object_path, error)) return FALSE; #ifdef ENABLE_SEARCH_PROVIDER if (g_settings_get_boolean (app->global_settings, TERMINAL_SETTING_SHELL_INTEGRATION_KEY)) { gs_unref_object TerminalSearchProvider *search_provider; search_provider = terminal_search_provider_new (); if (!terminal_search_provider_dbus_register (search_provider, connection, TERMINAL_SEARCH_PROVIDER_PATH, error)) return FALSE; gs_transfer_out_value (&app->search_provider, &search_provider); } #endif /* ENABLE_SEARCH_PROVIDER */ object = terminal_object_skeleton_new (TERMINAL_FACTORY_OBJECT_PATH); factory = terminal_factory_impl_new (); terminal_object_skeleton_set_factory (object, factory); app->object_manager = g_dbus_object_manager_server_new (TERMINAL_OBJECT_PATH_PREFIX); g_dbus_object_manager_server_export (app->object_manager, G_DBUS_OBJECT_SKELETON (object)); /* And export the object */ g_dbus_object_manager_server_set_connection (app->object_manager, connection); return TRUE; }
/** * ostree_repo_traverse_commit: * @repo: Repo * @commit_checksum: ASCII SHA256 checksum * @maxdepth: Traverse this many parent commits, -1 for unlimited * @out_reachable: (out) (transfer container) (element-type GVariant GVariant): Set of reachable objects * @cancellable: Cancellable * @error: Error * * Create a new set @out_reachable containing all objects reachable * from @commit_checksum, traversing @maxdepth parent commits. */ gboolean ostree_repo_traverse_commit (OstreeRepo *repo, const char *commit_checksum, int maxdepth, GHashTable **out_reachable, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GHashTable) ret_reachable = ostree_repo_traverse_new_reachable (); if (!ostree_repo_traverse_commit_union (repo, commit_checksum, maxdepth, ret_reachable, cancellable, error)) goto out; ret = TRUE; gs_transfer_out_value (out_reachable, &ret_reachable); out: return ret; }
/** * ostree_sysroot_query_bootloader: * @sysroot: Sysroot * @out_bootloader: (out) (transfer full) (allow-none): Return location for bootloader, may be %NULL * @cancellable: Cancellable * @error: Error */ gboolean _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot, OstreeBootloader **out_bootloader, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gboolean is_active; glnx_unref_object OstreeBootloader *ret_loader = NULL; ret_loader = (OstreeBootloader*)_ostree_bootloader_syslinux_new (sysroot); if (!_ostree_bootloader_query (ret_loader, &is_active, cancellable, error)) goto out; if (!is_active) { g_object_unref (ret_loader); ret_loader = (OstreeBootloader*)_ostree_bootloader_grub2_new (sysroot); if (!_ostree_bootloader_query (ret_loader, &is_active, cancellable, error)) goto out; } if (!is_active) { g_object_unref (ret_loader); ret_loader = (OstreeBootloader*)_ostree_bootloader_uboot_new (sysroot); if (!_ostree_bootloader_query (ret_loader, &is_active, cancellable, error)) goto out; } if (!is_active) g_clear_object (&ret_loader); ret = TRUE; gs_transfer_out_value (out_bootloader, &ret_loader); out: return ret; }
/** * gs_file_open_in_tmpdir: * @tmpdir: Directory to place temporary file * @mode: Default mode (will be affected by umask) * @out_file: (out) (transfer full): Newly created file path * @out_stream: (out) (transfer full) (allow-none): Newly created output stream * @cancellable: * @error: * * Like g_file_open_tmp(), except the file will be created in the * provided @tmpdir, and allows specification of the Unix @mode, which * means private files may be created. Return values will be stored * in @out_file, and optionally @out_stream. */ gboolean gs_file_open_in_tmpdir (GFile *tmpdir, int mode, GFile **out_file, GOutputStream **out_stream, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; DIR *d = NULL; int dfd = -1; char *tmp_name = NULL; GOutputStream *ret_stream = NULL; d = opendir (gs_file_get_path_cached (tmpdir)); if (!d) { gs_set_prefix_error_from_errno (error, errno, "opendir"); goto out; } dfd = dirfd (d); if (!gs_file_open_in_tmpdir_at (dfd, mode, &tmp_name, out_stream ? &ret_stream : NULL, cancellable, error)) goto out; ret = TRUE; *out_file = g_file_get_child (tmpdir, tmp_name); gs_transfer_out_value (out_stream, &ret_stream); out: if (d) (void) closedir (d); g_clear_object (&ret_stream); g_free (tmp_name); return ret; }
gboolean _ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self, int bootversion, GPtrArray **out_loader_configs, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int fd; /* Temporary owned by iterator */ g_autofree char *entries_path = g_strdup_printf ("boot/loader.%d/entries", bootversion); g_autoptr(GPtrArray) ret_loader_configs = NULL; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!ensure_sysroot_fd (self, error)) goto out; ret_loader_configs = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); fd = glnx_opendirat_with_errno (self->sysroot_fd, entries_path, TRUE); if (fd == -1) { if (errno == ENOENT) goto done; else { glnx_set_error_from_errno (error); goto out; } } if (!glnx_dirfd_iterator_init_take_fd (fd, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent; struct stat stbuf; if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, 0) != 0) { glnx_set_error_from_errno (error); goto out; } if (g_str_has_prefix (dent->d_name, "ostree-") && g_str_has_suffix (dent->d_name, ".conf") && S_ISREG (stbuf.st_mode)) { glnx_unref_object OstreeBootconfigParser *config = ostree_bootconfig_parser_new (); if (!ostree_bootconfig_parser_parse_at (config, dfd_iter.fd, dent->d_name, cancellable, error)) { g_prefix_error (error, "Parsing %s: ", dent->d_name); goto out; } g_ptr_array_add (ret_loader_configs, g_object_ref (config)); } } done: gs_transfer_out_value (out_loader_configs, &ret_loader_configs); ret = TRUE; out: return ret; }
static gboolean find_kernel_and_initramfs_in_bootdir (GFile *bootdir, GFile **out_kernel, GFile **out_initramfs, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFileEnumerator *direnum = NULL; gs_unref_object GFile *ret_kernel = NULL; gs_unref_object GFile *ret_initramfs = NULL; direnum = g_file_enumerate_children (bootdir, "standard::name", 0, cancellable, error); if (!direnum) goto out; while (TRUE) { const char *name; GFileInfo *file_info; GFile *child; if (!gs_file_enumerator_iterate (direnum, &file_info, &child, cancellable, error)) goto out; if (!file_info) break; name = g_file_info_get_name (file_info); /* Current Fedora 23 kernel.spec installs as just vmlinuz */ if (strcmp (name, "vmlinuz") == 0 || g_str_has_prefix (name, "vmlinuz-")) { if (ret_kernel) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Multiple vmlinuz- in %s", gs_file_get_path_cached (bootdir)); goto out; } ret_kernel = g_object_ref (child); } else if (g_str_has_prefix (name, "initramfs-")) { if (ret_initramfs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Multiple initramfs- in %s", gs_file_get_path_cached (bootdir)); goto out; } ret_initramfs = g_object_ref (child); } } ret = TRUE; gs_transfer_out_value (out_kernel, &ret_kernel); gs_transfer_out_value (out_initramfs, &ret_initramfs); out: return ret; }
gboolean rpmostree_db_option_context_parse (GOptionContext *context, const GOptionEntry *main_entries, int *argc, char ***argv, OstreeRepo **out_repo, GFile **out_rpmdbdir, gboolean *out_rpmdbdir_is_tmp, GCancellable *cancellable, GError **error) { gs_unref_object OstreeRepo *repo = NULL; gs_unref_object GFile *rpmdbdir = NULL; gboolean rpmdbdir_is_tmp = FALSE; gboolean success = FALSE; /* Entries are listed in --help output in the order added. We add the * main entries ourselves so that we can add the --repo entry first. */ g_option_context_add_main_entries (context, global_entries, NULL); if (!rpmostree_option_context_parse (context, main_entries, argc, argv, error)) goto out; if (opt_repo == NULL) { gs_unref_object OstreeSysroot *sysroot = NULL; sysroot = ostree_sysroot_new_default (); if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; } else { gs_unref_object GFile *repo_file = g_file_new_for_path (opt_repo); repo = ostree_repo_new (repo_file); if (!ostree_repo_open (repo, cancellable, error)) goto out; } if (rpmReadConfigFiles (NULL, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "rpm failed to init: %s", rpmlogMessage ()); goto out; } if (opt_rpmdbdir != NULL) { rpmdbdir = g_file_new_for_path (opt_rpmdbdir); } else { /* tmp on tmpfs is much faster than /var/tmp, * and the rpmdb itself shouldn't be too big. */ gs_free char *tmpd = g_mkdtemp (g_strdup ("/tmp/rpm-ostree.XXXXXX")); rpmdbdir = g_file_new_for_path (tmpd); rpmdbdir_is_tmp = TRUE; ostree_repo_set_disable_fsync (repo, TRUE); } gs_transfer_out_value (out_repo, &repo); gs_transfer_out_value (out_rpmdbdir, &rpmdbdir); if (out_rpmdbdir_is_tmp != NULL) *out_rpmdbdir_is_tmp = rpmdbdir_is_tmp; success = TRUE; out: return success; }
/** * ostree_repo_list_static_delta_names: * @self: Repo * @out_deltas: (out) (element-type utf8): String name of deltas (checksum-checksum.delta) * @cancellable: Cancellable * @error: Error * * This function synchronously enumerates all static deltas in the * repository, returning its result in @out_deltas. */ gboolean ostree_repo_list_static_delta_names (OstreeRepo *self, GPtrArray **out_deltas, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_ptrarray GPtrArray *ret_deltas = NULL; gs_unref_object GFileEnumerator *dir_enum = NULL; ret_deltas = g_ptr_array_new_with_free_func (g_free); if (g_file_query_exists (self->deltas_dir, NULL)) { dir_enum = g_file_enumerate_children (self->deltas_dir, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error); if (!dir_enum) goto out; while (TRUE) { gs_unref_object GFileEnumerator *dir_enum2 = NULL; GFileInfo *file_info; GFile *child; 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; dir_enum2 = g_file_enumerate_children (child, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error); if (!dir_enum2) goto out; while (TRUE) { GFileInfo *file_info2; GFile *child2; const char *name1; const char *name2; if (!gs_file_enumerator_iterate (dir_enum2, &file_info2, &child2, NULL, error)) goto out; if (file_info2 == NULL) break; if (g_file_info_get_file_type (file_info2) != G_FILE_TYPE_DIRECTORY) continue; name1 = gs_file_get_basename_cached (child); name2 = gs_file_get_basename_cached (child2); { gs_unref_object GFile *meta_path = g_file_get_child (child2, "superblock"); if (g_file_query_exists (meta_path, NULL)) { gs_free char *buf = g_strconcat (name1, name2, NULL); GString *out = g_string_new (""); char checksum[65]; guchar csum[32]; const char *dash = strchr (buf, '-'); ostree_checksum_b64_inplace_to_bytes (buf, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); if (dash) { g_string_append_c (out, '-'); ostree_checksum_b64_inplace_to_bytes (dash+1, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); } g_ptr_array_add (ret_deltas, g_string_free (out, FALSE)); } } } } } ret = TRUE; gs_transfer_out_value (out_deltas, &ret_deltas); out: return ret; }
static gboolean compute_checksum_from_treefile_and_goal (RpmOstreeTreeComposeContext *self, HyGoal goal, GFile *contextdir, JsonArray *add_files, char **out_checksum, GError **error) { gboolean ret = FALSE; g_autofree char *ret_checksum = NULL; GChecksum *checksum = g_checksum_new (G_CHECKSUM_SHA256); /* Hash in the raw treefile; this means reordering the input packages * or adding a comment will cause a recompose, but let's be conservative * here. */ { gsize len; const guint8* buf = g_bytes_get_data (self->serialized_treefile, &len); g_checksum_update (checksum, buf, len); } if (add_files) { guint i, len = json_array_get_length (add_files); for (i = 0; i < len; i++) { g_autoptr(GFile) srcfile = NULL; const char *src, *dest; JsonArray *add_el = json_array_get_array_element (add_files, i); g_autoptr(GFile) child = NULL; if (!add_el) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Element in add-files is not an array"); goto out; } src = _rpmostree_jsonutil_array_require_string_element (add_el, 0, error); if (!src) goto out; dest = _rpmostree_jsonutil_array_require_string_element (add_el, 1, error); if (!dest) goto out; srcfile = g_file_resolve_relative_path (contextdir, src); if (!_rpmostree_util_update_checksum_from_file (checksum, srcfile, NULL, error)) goto out; g_checksum_update (checksum, (const guint8 *) dest, strlen (dest)); } } /* FIXME; we should also hash the post script */ /* Hash in each package */ rpmostree_dnf_add_checksum_goal (checksum, goal); ret_checksum = g_strdup (g_checksum_get_string (checksum)); ret = TRUE; out: gs_transfer_out_value (out_checksum, &ret_checksum); if (checksum) g_checksum_free (checksum); return ret; }
static gboolean install_packages_in_root (RpmOstreeTreeComposeContext *self, RpmOstreeContext *ctx, JsonObject *treedata, GFile *yumroot, char **packages, gboolean *out_unmodified, char **out_new_inputhash, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; guint progress_sigid; GFile *contextdir = self->treefile_context_dirs->pdata[0]; g_autoptr(RpmOstreeInstall) hifinstall = { 0, }; DnfContext *hifctx; g_autofree char *ret_new_inputhash = NULL; g_autoptr(GKeyFile) treespec = g_key_file_new (); JsonArray *enable_repos = NULL; JsonArray *add_files = NULL; /* TODO - uncomment this once we have SELinux working */ #if 0 g_autofree char *cache_repo_pathstr = glnx_fdrel_abspath (self->cachedir_dfd, "repo"); g_autoptr(GFile) cache_repo_path = g_file_new_for_path (cache_repo_pathstr); glnx_unref_object OstreeRepo *ostreerepo = ostree_repo_new (cache_repo_path); if (!g_file_test (cache_repo_pathstr, G_FILE_TEST_EXISTS)) { if (!ostree_repo_create (ostreerepo, OSTREE_REPO_MODE_BARE_USER, cancellable, error)) goto out; } #endif hifctx = rpmostree_context_get_hif (ctx); if (opt_proxy) dnf_context_set_http_proxy (hifctx, opt_proxy); /* Hack this here... see https://github.com/rpm-software-management/libhif/issues/53 * but in the future we won't be using librpm at all for unpack/scripts, so it won't * matter. */ { const char *debuglevel = getenv ("RPMOSTREE_RPM_VERBOSITY"); if (!debuglevel) debuglevel = "info"; dnf_context_set_rpm_verbosity (hifctx, debuglevel); rpmlogSetFile(NULL); } dnf_context_set_repo_dir (hifctx, gs_file_get_path_cached (contextdir)); /* By default, retain packages in addition to metadata with --cachedir */ if (opt_cachedir) dnf_context_set_keep_cache (hifctx, TRUE); if (opt_cache_only) dnf_context_set_cache_age (hifctx, G_MAXUINT); g_key_file_set_string (treespec, "tree", "ref", self->ref); g_key_file_set_string_list (treespec, "tree", "packages", (const char *const*)packages, g_strv_length (packages)); /* Some awful code to translate between JSON and GKeyFile */ if (json_object_has_member (treedata, "install-langs")) { JsonArray *a = json_object_get_array_member (treedata, "install-langs"); if (!set_keyfile_string_array_from_json (treespec, "tree", "install-langs", a, error)) goto out; } /* Bind the json \"repos\" member to the hif state, which looks at the * enabled= member of the repos file. By default we forcibly enable * only repos which are specified, ignoring the enabled= flag. */ if (!json_object_has_member (treedata, "repos")) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Treefile is missing required \"repos\" member"); goto out; } enable_repos = json_object_get_array_member (treedata, "repos"); if (!set_keyfile_string_array_from_json (treespec, "tree", "repos", enable_repos, error)) goto out; { gboolean docs = TRUE; if (!_rpmostree_jsonutil_object_get_optional_boolean_member (treedata, "documentation", &docs, error)) goto out; if (!docs) g_key_file_set_boolean (treespec, "tree", "documentation", FALSE); } { g_autoptr(GError) tmp_error = NULL; g_autoptr(RpmOstreeTreespec) treespec_value = rpmostree_treespec_new_from_keyfile (treespec, &tmp_error); g_assert_no_error (tmp_error); if (!rpmostree_context_setup (ctx, gs_file_get_path_cached (yumroot), "/", treespec_value, cancellable, error)) goto out; } /* --- Downloading metadata --- */ if (!rpmostree_context_download_metadata (ctx, cancellable, error)) goto out; if (!rpmostree_context_prepare_install (ctx, &hifinstall, cancellable, error)) goto out; rpmostree_print_transaction (hifctx); if (json_object_has_member (treedata, "add-files")) add_files = json_object_get_array_member (treedata, "add-files"); /* FIXME - just do a depsolve here before we compute download requirements */ if (!compute_checksum_from_treefile_and_goal (self, dnf_context_get_goal (hifctx), contextdir, add_files, &ret_new_inputhash, error)) goto out; /* Only look for previous checksum if caller has passed *out_unmodified */ if (self->previous_checksum && out_unmodified != NULL) { g_autoptr(GVariant) commit_v = NULL; g_autoptr(GVariant) commit_metadata = NULL; const char *previous_inputhash = NULL; if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_COMMIT, self->previous_checksum, &commit_v, error)) goto out; commit_metadata = g_variant_get_child_value (commit_v, 0); if (g_variant_lookup (commit_metadata, "rpmostree.inputhash", "&s", &previous_inputhash)) { if (strcmp (previous_inputhash, ret_new_inputhash) == 0) { *out_unmodified = TRUE; ret = TRUE; goto out; } } else g_print ("Previous commit found, but without rpmostree.inputhash metadata key\n"); } if (opt_dry_run) { ret = TRUE; goto out; } /* --- Downloading packages --- */ if (!rpmostree_context_download (ctx, hifinstall, cancellable, error)) goto out; { g_auto(GLnxConsoleRef) console = { 0, }; glnx_unref_object DnfState *hifstate = dnf_state_new (); progress_sigid = g_signal_connect (hifstate, "percentage-changed", G_CALLBACK (on_hifstate_percentage_changed), "Installing packages:"); glnx_console_lock (&console); if (!dnf_transaction_commit (dnf_context_get_transaction (hifctx), dnf_context_get_goal (hifctx), hifstate, error)) goto out; g_signal_handler_disconnect (hifstate, progress_sigid); } ret = TRUE; if (out_unmodified) *out_unmodified = FALSE; gs_transfer_out_value (out_new_inputhash, &ret_new_inputhash); out: return ret; }
static gboolean parse_deployment (OstreeSysroot *self, const char *boot_link, OstreeDeployment **out_deployment, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *relative_boot_link; glnx_unref_object OstreeDeployment *ret_deployment = NULL; int entry_boot_version; int treebootserial = -1; int deployserial = -1; g_autofree char *osname = NULL; g_autofree char *bootcsum = NULL; g_autofree char *treecsum = NULL; glnx_fd_close int deployment_dfd = -1; const char *deploy_basename; g_autofree char *treebootserial_target = NULL; g_autofree char *deploy_dir = NULL; GKeyFile *origin = NULL; if (!ensure_sysroot_fd (self, error)) goto out; if (!parse_bootlink (boot_link, &entry_boot_version, &osname, &bootcsum, &treebootserial, error)) goto out; relative_boot_link = boot_link; if (*relative_boot_link == '/') relative_boot_link++; treebootserial_target = glnx_readlinkat_malloc (self->sysroot_fd, relative_boot_link, cancellable, error); if (!treebootserial_target) goto out; deploy_basename = glnx_basename (treebootserial_target); if (!_ostree_sysroot_parse_deploy_path_name (deploy_basename, &treecsum, &deployserial, error)) goto out; if (!glnx_opendirat (self->sysroot_fd, relative_boot_link, TRUE, &deployment_dfd, error)) goto out; if (!parse_origin (self, deployment_dfd, deploy_basename, &origin, cancellable, error)) goto out; ret_deployment = ostree_deployment_new (-1, osname, treecsum, deployserial, bootcsum, treebootserial); if (origin) ostree_deployment_set_origin (ret_deployment, origin); ret = TRUE; gs_transfer_out_value (out_deployment, &ret_deployment); out: if (origin) g_key_file_unref (origin); return ret; }
/* * Build up a map of files with matching basenames and similar size, * and use it to find apparently similar objects. * * @new_reachable_regfile_content is a Set<checksum> of new regular * file objects. * * Currently, @out_modified_regfile_content will be a Map<to checksum,from checksum>; * however in the future it would be easy to have this function return * multiple candidate matches. The hard part would be changing * the delta compiler to iterate over all matches, determine * a cost for each one, then pick the best. */ gboolean _ostree_delta_compute_similar_objects (OstreeRepo *repo, GVariant *from_commit, GVariant *to_commit, GHashTable *new_reachable_regfile_content, guint similarity_percent_threshold, GHashTable **out_modified_regfile_content, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GHashTable) ret_modified_regfile_content = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref); g_autoptr(GPtrArray) from_sizes = NULL; g_autoptr(GPtrArray) to_sizes = NULL; guint i, j; guint lower; guint upper; if (!build_content_sizenames_filtered (repo, from_commit, NULL, &from_sizes, cancellable, error)) goto out; if (!build_content_sizenames_filtered (repo, to_commit, new_reachable_regfile_content, &to_sizes, cancellable, error)) goto out; /* Iterate over all newly added objects, find objects which have * similar basename and sizes. * * Because the arrays are sorted by size, we can maintain a `lower` * bound on the original (from) objects to start searching. */ lower = 0; upper = from_sizes->len; for (i = 0; i < to_sizes->len; i++) { OstreeDeltaContentSizeNames *to_sizenames = to_sizes->pdata[i]; const guint64 min_threshold = to_sizenames->size * (1.0-similarity_percent_threshold/100.0); const guint64 max_threshold = to_sizenames->size * (1.0+similarity_percent_threshold/100.0); /* Don't build candidates for the empty object */ if (to_sizenames->size == 0) continue; for (j = lower; j < upper; j++) { OstreeDeltaContentSizeNames *from_sizenames = from_sizes->pdata[j]; /* Don't build candidates for the empty object */ if (from_sizenames->size == 0) continue; if (from_sizenames->size < min_threshold) { lower++; continue; } if (from_sizenames->size > max_threshold) break; if (!string_array_nonempty_intersection (from_sizenames->basenames, to_sizenames->basenames)) continue; /* Only one candidate right now */ g_hash_table_insert (ret_modified_regfile_content, g_strdup (to_sizenames->checksum), g_strdup (from_sizenames->checksum)); break; } } ret = TRUE; gs_transfer_out_value (out_modified_regfile_content, &ret_modified_regfile_content); out: return ret; }