static gboolean builder_source_git_download (BuilderSource *source, gboolean update_vcs, BuilderContext *context, GError **error) { BuilderSourceGit *self = BUILDER_SOURCE_GIT (source); g_autofree char *location = NULL; location = get_url_or_path (self, context, error); if (location == NULL) return FALSE; if (!builder_git_mirror_repo (location, NULL, update_vcs, TRUE, self->disable_fsckobjects, get_branch (self), context, error)) return FALSE; if (self->commit != NULL && self->branch != NULL) { g_autofree char *current_commit = builder_git_get_current_commit (location,get_branch (self), context, error); if (current_commit == NULL) return FALSE; if (strcmp (current_commit, self->commit) != 0) return flatpak_fail (error, "Git commit for branch %s is %s, but expected %s\n", self->branch, current_commit, self->commit); } return TRUE; }
static gboolean metadata_get_arch (GFile *file, char **out_arch, GError **error) { g_autofree char *path = NULL; g_autoptr(GKeyFile) keyfile = NULL; g_autofree char *runtime = NULL; g_auto(GStrv) parts = NULL; if (opt_arch != NULL) { *out_arch = g_strdup (opt_arch); return TRUE; } keyfile = g_key_file_new (); path = g_file_get_path (file); if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, error)) return FALSE; runtime = g_key_file_get_string (keyfile, "Application", "runtime", NULL); if (runtime == NULL) runtime = g_key_file_get_string (keyfile, "Application", "sdk", NULL); if (runtime == NULL) runtime = g_key_file_get_string (keyfile, "Runtime", "runtime", NULL); if (runtime == NULL) runtime = g_key_file_get_string (keyfile, "Runtime", "sdk", NULL); if (runtime == NULL) { *out_arch = g_strdup (flatpak_get_arch ()); return TRUE; } parts = g_strsplit (runtime, "/", 0); if (g_strv_length (parts) != 3) return flatpak_fail (error, "Failed to determine arch from metadata runtime key: %s", runtime); *out_arch = g_strdup (parts[1]); return TRUE; }
gboolean flatpak_remote_commit (FlatpakRemote *self, FlatpakDir *dir, GCancellable *cancellable, GError **error) { FlatpakRemotePrivate *priv = flatpak_remote_get_instance_private (self); g_autofree char *url = NULL; g_autoptr(GKeyFile) config = NULL; g_autofree char *group = g_strdup_printf ("remote \"%s\"", priv->name); url = flatpak_remote_get_url (self); if (url == NULL || *url == 0) return flatpak_fail (error, "No url specified"); config = ostree_repo_copy_config (flatpak_dir_get_repo (dir)); if (priv->local_url_set) g_key_file_set_string (config, group, "url", priv->local_url); if (priv->local_title_set) g_key_file_set_string (config, group, "xa.title", priv->local_title); if (priv->local_gpg_verify_set) { g_key_file_set_boolean (config, group, "gpg-verify", priv->local_gpg_verify); g_key_file_set_boolean (config, group, "gpg-verify-summary", priv->local_gpg_verify); } if (priv->local_noenumerate_set) g_key_file_set_boolean (config, group, "xa.noenumerate", priv->local_noenumerate); if (priv->local_disabled_set) g_key_file_set_boolean (config, group, "xa.disable", priv->local_disabled); if (priv->local_prio_set) { g_autofree char *prio_as_string = g_strdup_printf ("%d", priv->local_prio); g_key_file_set_string (config, group, "xa.prio", prio_as_string); } return flatpak_dir_modify_remote (dir, priv->name, config, priv->local_gpg_key, cancellable, error); }
static gboolean validate_icon_file (GFile *file, GError **error) { g_autoptr(GPtrArray) args = NULL; const char *name; int status; g_autofree char *err = NULL; const char *validate_icon = LIBEXECDIR "/flatpak-validate-icon"; name = flatpak_file_get_path_cached (file); if (g_getenv ("FLATPAK_VALIDATE_ICON")) validate_icon = g_getenv ("FLATPAK_VALIDATE_ICON"); args = g_ptr_array_new_with_free_func (g_free); #ifndef DISABLE_SANDBOXED_TRIGGERS if (!opt_disable_sandbox) add_args (args, validate_icon, "--sandbox", "512", "512", name, NULL); else #endif add_args (args, validate_icon, "512", "512", name, NULL); g_ptr_array_add (args, NULL); if (!g_spawn_sync (NULL, (char **) args->pdata, NULL, 0, NULL, NULL, NULL, &err, &status, error)) { g_debug ("Icon validation: %s", (*error)->message); return FALSE; } if (!g_spawn_check_exit_status (status, NULL)) { g_debug ("Icon validation: %s", err); return flatpak_fail (error, "%s is not a valid icon: %s", name, err); } return TRUE; }
gboolean flatpak_builtin_update (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(FlatpakDir) dir = NULL; const char *name = NULL; const char *branch = NULL; gboolean failed = FALSE; int i; context = g_option_context_new ("[NAME [BRANCH]] - Update an application or runtime"); if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) return FALSE; if (argc < 1) return usage_error (context, "NAME must be specified", error); if (argc >= 2) name = argv[1]; if (argc >= 3) branch = argv[2]; if (opt_arch == NULL) opt_arch = (char *)flatpak_get_arch (); if (!opt_app && !opt_runtime) opt_app = opt_runtime = TRUE; if (opt_appstream) return update_appstream (dir, name, cancellable, error); if (opt_app) { g_auto(GStrv) refs = NULL; if (!flatpak_dir_list_refs (dir, "app", &refs, cancellable, error)) return FALSE; for (i = 0; refs != NULL && refs[i] != NULL; i++) { g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], error); if (parts == NULL) return FALSE; if (name != NULL && strcmp (parts[1], name) != 0) continue; if (strcmp (parts[2], opt_arch) != 0) continue; if (branch != NULL && strcmp (parts[3], branch) != 0) continue; g_print ("Updating application %s %s\n", parts[1], parts[3]); if (!do_update (dir, refs[i], cancellable, error)) return FALSE; } } if (opt_runtime) { g_auto(GStrv) refs = NULL; if (!flatpak_dir_list_refs (dir, "runtime", &refs, cancellable, error)) return FALSE; for (i = 0; refs != NULL && refs[i] != NULL; i++) { g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], error); g_autoptr(GError) local_error = NULL; if (parts == NULL) return FALSE; if (name != NULL && strcmp (parts[1], name) != 0) continue; if (strcmp (parts[2], opt_arch) != 0) continue; if (branch != NULL && strcmp (parts[3], branch) != 0) continue; g_print ("Updating runtime %s %s\n", parts[1], parts[3]); if (!do_update (dir, refs[i], cancellable, &local_error)) { g_printerr ("error updating: %s\n", local_error->message); failed = TRUE; } } } flatpak_dir_cleanup_removed (dir, cancellable, NULL); if (failed) return flatpak_fail (error, "One or more updates failed"); return TRUE; }
gboolean flatpak_builtin_build_sign (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) repofile = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *location; const char *branch; const char *id; g_autofree char *commit_checksum = NULL; g_autofree char *ref = NULL; char **iter; context = g_option_context_new ("LOCATION ID [BRANCH] - Create a repository from a build directory"); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 3) { usage_error (context, "LOCATION and DIRECTORY must be specified", error); return FALSE; } location = argv[1]; id = argv[2]; if (argc >= 4) branch = argv[3]; else branch = "master"; if (!flatpak_is_valid_name (id)) return flatpak_fail (error, "'%s' is not a valid name", id); if (!flatpak_is_valid_branch (branch)) return flatpak_fail (error, "'%s' is not a valid branch name", branch); if (opt_gpg_key_ids == NULL) return flatpak_fail (error, "No gpg key ids specified"); if (opt_runtime) ref = flatpak_build_runtime_ref (id, branch, opt_arch); else ref = flatpak_build_app_ref (id, branch, opt_arch); repofile = g_file_new_for_commandline_arg (location); repo = ostree_repo_new (repofile); if (!ostree_repo_open (repo, cancellable, error)) return FALSE; if (!ostree_repo_resolve_rev (repo, ref, TRUE, &commit_checksum, error)) return FALSE; for (iter = opt_gpg_key_ids; iter && *iter; iter++) { const char *keyid = *iter; if (!ostree_repo_sign_commit (repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, error)) return FALSE; } return TRUE; }
gboolean flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(FlatpakDir) dir = NULL; const char *repository; const char *name; const char *branch = NULL; g_autofree char *ref = NULL; gboolean is_app; g_autoptr(GFile) deploy_dir = NULL; g_autoptr(GPtrArray) related = NULL; int i; context = g_option_context_new ("REPOSITORY NAME [BRANCH] - Install an application or runtime"); if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) return FALSE; if (opt_bundle) return install_bundle (dir, context, argc, argv, cancellable, error); if (argc < 3) return usage_error (context, "REPOSITORY and NAME must be specified", error); repository = argv[1]; name = argv[2]; if (argc >= 4) branch = argv[3]; if (!opt_app && !opt_runtime) opt_app = opt_runtime = TRUE; ref = flatpak_dir_find_remote_ref (dir, repository, name, branch, opt_arch, opt_app, opt_runtime, &is_app, cancellable, error); if (ref == NULL) return FALSE; deploy_dir = flatpak_dir_get_if_deployed (dir, ref, NULL, cancellable); if (deploy_dir != NULL) { g_auto(GStrv) parts = flatpak_decompose_ref (ref, error); return flatpak_fail (error, "%s %s, branch %s is already installed", is_app ? "App" : "Runtime", name, parts[3]); } if (!flatpak_dir_install (dir, opt_no_pull, opt_no_deploy, ref, repository, (const char **)opt_subpaths, NULL, cancellable, error)) return FALSE; if (!opt_no_related) { g_autoptr(GError) local_error = NULL; if (opt_no_pull) related = flatpak_dir_find_local_related (dir, ref, repository, NULL, &local_error); else related = flatpak_dir_find_remote_related (dir, ref, repository, NULL, &local_error); if (related == NULL) { g_printerr ("Warning: Problem looking for related refs: %s\n", local_error->message); g_clear_error (&local_error); } else { for (i = 0; i < related->len; i++) { FlatpakRelated *rel = g_ptr_array_index (related, i); g_auto(GStrv) parts = NULL; if (!rel->download) continue; parts = g_strsplit (rel->ref, "/", 0); g_print ("Installing related: %s\n", parts[1]); if (!flatpak_dir_install_or_update (dir, opt_no_pull, opt_no_deploy, rel->ref, repository, (const char **)rel->subpaths, NULL, cancellable, &local_error)) { g_printerr ("Warning: Failed to install related ref: %s\n", rel->ref); g_clear_error (&local_error); } } } } return TRUE; }
static gboolean rewrite_delta (OstreeRepo *src_repo, const char *src_commit, OstreeRepo *dst_repo, const char *dst_commit, GVariant *dst_commitv, const char *from, GError **error) { g_autoptr(GFile) src_delta_file = NULL; g_autoptr(GFile) dst_delta_file = NULL; g_autofree char *src_detached_key = _ostree_get_relative_static_delta_path (from, src_commit, "commitmeta"); g_autofree char *dst_detached_key = _ostree_get_relative_static_delta_path (from, dst_commit, "commitmeta"); g_autofree char *src_delta_dir = _ostree_get_relative_static_delta_path (from, src_commit, NULL); g_autofree char *dst_delta_dir = _ostree_get_relative_static_delta_path (from, dst_commit, NULL); g_autofree char *src_superblock_path = _ostree_get_relative_static_delta_path (from, src_commit, "superblock"); g_autofree char *dst_superblock_path = _ostree_get_relative_static_delta_path (from, dst_commit, "superblock"); GMappedFile *mfile = NULL; g_auto(GVariantBuilder) superblock_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER; g_autoptr(GVariant) src_superblock = NULL; g_autoptr(GVariant) dst_superblock = NULL; g_autoptr(GBytes) bytes = NULL; g_autoptr(GVariant) dst_detached = NULL; g_autoptr(GVariant) src_metadata = NULL; g_autoptr(GVariant) src_recurse = NULL; g_autoptr(GVariant) src_parts = NULL; g_auto(GVariantDict) dst_metadata_dict = FLATPAK_VARIANT_DICT_INITIALIZER; int i; src_delta_file = g_file_resolve_relative_path (ostree_repo_get_path (src_repo), src_superblock_path); mfile = g_mapped_file_new (flatpak_file_get_path_cached (src_delta_file), FALSE, NULL); if (mfile == NULL) return TRUE; /* No superblock, not an error */ bytes = g_mapped_file_get_bytes (mfile); g_mapped_file_unref (mfile); src_superblock = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT), bytes, FALSE)); src_metadata = g_variant_get_child_value (src_superblock, 0); src_recurse = g_variant_get_child_value (src_superblock, 5); src_parts = g_variant_get_child_value (src_superblock, 6); if (g_variant_n_children (src_recurse) != 0) return flatpak_fail (error, "Recursive deltas not supported, ignoring"); g_variant_builder_init (&superblock_builder, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT)); g_variant_dict_init (&dst_metadata_dict, src_metadata); g_variant_dict_remove (&dst_metadata_dict, src_detached_key); if (ostree_repo_read_commit_detached_metadata (dst_repo, dst_commit, &dst_detached, NULL, NULL) && dst_detached != NULL) g_variant_dict_insert_value (&dst_metadata_dict, dst_detached_key, dst_detached); g_variant_builder_add_value (&superblock_builder, g_variant_dict_end (&dst_metadata_dict)); g_variant_builder_add_value (&superblock_builder, g_variant_get_child_value (src_superblock, 1)); /* timestamp */ g_variant_builder_add_value (&superblock_builder, from ? ostree_checksum_to_bytes_v (from) : new_bytearray ((guchar *) "", 0)); g_variant_builder_add_value (&superblock_builder, ostree_checksum_to_bytes_v (dst_commit)); g_variant_builder_add_value (&superblock_builder, dst_commitv); g_variant_builder_add_value (&superblock_builder, src_recurse); g_variant_builder_add_value (&superblock_builder, src_parts); g_variant_builder_add_value (&superblock_builder, g_variant_get_child_value (src_superblock, 7)); /* fallback */ dst_superblock = g_variant_ref_sink (g_variant_builder_end (&superblock_builder)); if (!glnx_shutil_mkdir_p_at (ostree_repo_get_dfd (dst_repo), dst_delta_dir, 0755, NULL, error)) return FALSE; for (i = 0; i < g_variant_n_children (src_parts); i++) { g_autofree char *src_part_path = g_strdup_printf ("%s/%d", src_delta_dir, i); g_autofree char *dst_part_path = g_strdup_printf ("%s/%d", dst_delta_dir, i); if (!glnx_file_copy_at (ostree_repo_get_dfd (src_repo), src_part_path, NULL, ostree_repo_get_dfd (dst_repo), dst_part_path, GLNX_FILE_COPY_OVERWRITE | GLNX_FILE_COPY_NOXATTRS, NULL, error)) return FALSE; } dst_delta_file = g_file_resolve_relative_path (ostree_repo_get_path (dst_repo), dst_superblock_path); if (!flatpak_variant_save (dst_delta_file, dst_superblock, NULL, error)) return FALSE; return TRUE; }
gboolean flatpak_builtin_build_commit_from (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) dst_repofile = NULL; g_autoptr(OstreeRepo) dst_repo = NULL; g_autoptr(GFile) src_repofile = NULL; g_autoptr(OstreeRepo) src_repo = NULL; g_autofree char *src_repo_uri = NULL; const char *dst_repo_arg; const char **dst_refs; int n_dst_refs = 0; g_autoptr(FlatpakRepoTransaction) transaction = NULL; g_autoptr(GPtrArray) src_refs = NULL; g_autoptr(GPtrArray) resolved_src_refs = NULL; OstreeRepoCommitState src_commit_state; struct timespec ts; guint64 timestamp; int i; context = g_option_context_new (_("DST-REPO [DST-REF…] - Make a new commit from existing commits")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 2) return usage_error (context, _("DST-REPO must be specified"), error); dst_repo_arg = argv[1]; dst_refs = (const char **) argv + 2; n_dst_refs = argc - 2; if (opt_src_repo == NULL && n_dst_refs != 1) return usage_error (context, _("If --src-repo is not specified, exactly one destination ref must be specified"), error); if (opt_src_ref != NULL && n_dst_refs != 1) return usage_error (context, _("If --src-ref is specified, exactly one destination ref must be specified"), error); if (opt_src_repo == NULL && opt_src_ref == NULL) return flatpak_fail (error, _("Either --src-repo or --src-ref must be specified.")); /* Always create a commit if we're eol:ing, even though the app is the same */ if (opt_endoflife != NULL || opt_endoflife_rebase != NULL) opt_force = TRUE; if (opt_endoflife_rebase) { opt_endoflife_rebase_new = g_new0 (char *, g_strv_length (opt_endoflife_rebase)); for (i = 0; opt_endoflife_rebase[i] != NULL; i++) { char *rebase_old = opt_endoflife_rebase[i]; char *rebase_new = strchr (rebase_old, '='); if (rebase_new == NULL) { return usage_error (context, _("Invalid argument format of use --end-of-life-rebase=OLDID=NEWID"), error); } *rebase_new = 0; rebase_new++; if (!flatpak_is_valid_name (rebase_old, error)) return glnx_prefix_error (error, _("Invalid name %s in --end-of-life-rebase"), rebase_old); if (!flatpak_is_valid_name (rebase_new, error)) return glnx_prefix_error (error, _("Invalid name %s in --end-of-life-rebase"), rebase_new); opt_endoflife_rebase_new[i] = rebase_new; } } if (opt_timestamp) { if (!parse_datetime (&ts, opt_timestamp, NULL)) return flatpak_fail (error, _("Could not parse '%s'"), opt_timestamp); } dst_repofile = g_file_new_for_commandline_arg (dst_repo_arg); if (!g_file_query_exists (dst_repofile, cancellable)) return flatpak_fail (error, _("'%s' is not a valid repository"), dst_repo_arg); dst_repo = ostree_repo_new (dst_repofile); if (!ostree_repo_open (dst_repo, cancellable, error)) return FALSE; if (opt_disable_fsync) ostree_repo_set_disable_fsync (dst_repo, TRUE); if (opt_src_repo) { src_repofile = g_file_new_for_commandline_arg (opt_src_repo); if (!g_file_query_exists (src_repofile, cancellable)) return flatpak_fail (error, _("'%s' is not a valid repository"), opt_src_repo); src_repo_uri = g_file_get_uri (src_repofile); src_repo = ostree_repo_new (src_repofile); if (!ostree_repo_open (src_repo, cancellable, error)) return FALSE; } else { src_repo = g_object_ref (dst_repo); } src_refs = g_ptr_array_new_with_free_func (g_free); if (opt_src_ref) { g_assert (n_dst_refs == 1); g_ptr_array_add (src_refs, g_strdup (opt_src_ref)); } else { g_assert (opt_src_repo != NULL); if (n_dst_refs == 0) { g_autofree const char **keys = NULL; g_autoptr(GHashTable) all_src_refs = NULL; if (!ostree_repo_list_refs (src_repo, NULL, &all_src_refs, cancellable, error)) return FALSE; keys = (const char **) g_hash_table_get_keys_as_array (all_src_refs, NULL); for (i = 0; keys[i] != NULL; i++) { if (g_str_has_prefix (keys[i], "runtime/") || g_str_has_prefix (keys[i], "app/")) g_ptr_array_add (src_refs, g_strdup (keys[i])); } n_dst_refs = src_refs->len; dst_refs = (const char **) src_refs->pdata; } else { for (i = 0; i < n_dst_refs; i++) g_ptr_array_add (src_refs, g_strdup (dst_refs[i])); } } resolved_src_refs = g_ptr_array_new_with_free_func (g_free); for (i = 0; i < src_refs->len; i++) { const char *src_ref = g_ptr_array_index (src_refs, i); char *resolved_ref; if (!ostree_repo_resolve_rev (src_repo, src_ref, FALSE, &resolved_ref, error)) return FALSE; g_ptr_array_add (resolved_src_refs, resolved_ref); } if (src_repo_uri != NULL) { OstreeRepoPullFlags pullflags = 0; GVariantBuilder builder; g_autoptr(OstreeAsyncProgress) progress = NULL; g_auto(GLnxConsoleRef) console = { 0, }; g_autoptr(GVariant) options = NULL; gboolean res; if (opt_untrusted) pullflags |= OSTREE_REPO_PULL_FLAGS_UNTRUSTED; glnx_console_lock (&console); if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{s@v}", "flags", g_variant_new_variant (g_variant_new_int32 (pullflags))); g_variant_builder_add (&builder, "{s@v}", "refs", g_variant_new_variant (g_variant_new_strv ((const char * const *) resolved_src_refs->pdata, resolved_src_refs->len))); g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (0))); options = g_variant_ref_sink (g_variant_builder_end (&builder)); res = ostree_repo_pull_with_options (dst_repo, src_repo_uri, options, progress, cancellable, error); if (progress) ostree_async_progress_finish (progress); if (!res) return FALSE; } /* By now we have the commit with commit_id==resolved_ref and dependencies in dst_repo. We now create a new * commit based on the toplevel tree ref from that commit. * This is equivalent to: * ostree commit --skip-if-unchanged --repo=${destrepo} --tree=ref=${resolved_ref} */ transaction = flatpak_repo_transaction_start (dst_repo, cancellable, error); if (transaction == NULL) return FALSE; for (i = 0; i < resolved_src_refs->len; i++) { const char *dst_ref = dst_refs[i]; const char *resolved_ref = g_ptr_array_index (resolved_src_refs, i); g_autofree char *dst_parent = NULL; g_autoptr(GFile) dst_parent_root = NULL; g_autoptr(GFile) src_ref_root = NULL; g_autoptr(GVariant) src_commitv = NULL; g_autoptr(GVariant) dst_commitv = NULL; g_autoptr(OstreeMutableTree) mtree = NULL; g_autoptr(GFile) dst_root = NULL; g_autoptr(GVariant) commitv_metadata = NULL; g_autoptr(GVariant) metadata = NULL; const char *subject; const char *body; g_autofree char *commit_checksum = NULL; GVariantBuilder metadata_builder; gint j; const char *dst_collection_id = NULL; const char *main_collection_id = NULL; g_autoptr(GPtrArray) collection_ids = NULL; if (!ostree_repo_resolve_rev (dst_repo, dst_ref, TRUE, &dst_parent, error)) return FALSE; if (dst_parent != NULL && !ostree_repo_read_commit (dst_repo, dst_parent, &dst_parent_root, NULL, cancellable, error)) return FALSE; if (!ostree_repo_read_commit (dst_repo, resolved_ref, &src_ref_root, NULL, cancellable, error)) return FALSE; if (!ostree_repo_load_commit (dst_repo, resolved_ref, &src_commitv, &src_commit_state, error)) return FALSE; if (src_commit_state & OSTREE_REPO_COMMIT_STATE_PARTIAL) return flatpak_fail (error, _("Can't commit from partial source commit.")); /* Don't create a new commit if this is the same tree */ if (!opt_force && dst_parent_root != NULL && g_file_equal (dst_parent_root, src_ref_root)) { g_print (_("%s: no change\n"), dst_ref); continue; } mtree = ostree_mutable_tree_new (); if (!ostree_repo_write_directory_to_mtree (dst_repo, src_ref_root, mtree, NULL, cancellable, error)) return FALSE; if (!ostree_repo_write_mtree (dst_repo, mtree, &dst_root, cancellable, error)) return FALSE; commitv_metadata = g_variant_get_child_value (src_commitv, 0); g_variant_get_child (src_commitv, 3, "&s", &subject); if (opt_subject) subject = (const char *) opt_subject; g_variant_get_child (src_commitv, 4, "&s", &body); if (opt_body) body = (const char *) opt_body; dst_collection_id = ostree_repo_get_collection_id (dst_repo); collection_ids = g_ptr_array_new_with_free_func (g_free); if (dst_collection_id) { main_collection_id = dst_collection_id; g_ptr_array_add (collection_ids, g_strdup (dst_collection_id)); } if (opt_extra_collection_ids != NULL) { for (j = 0; opt_extra_collection_ids[j] != NULL; j++) { const char *cid = opt_extra_collection_ids[j]; if (main_collection_id == NULL) main_collection_id = cid; /* Fall back to first arg */ if (g_strcmp0 (cid, dst_collection_id) != 0) g_ptr_array_add (collection_ids, g_strdup (cid)); } } g_ptr_array_sort (collection_ids, (GCompareFunc) flatpak_strcmp0_ptr); /* Copy old metadata */ g_variant_builder_init (&metadata_builder, G_VARIANT_TYPE ("a{sv}")); /* Bindings. xa.ref is deprecated but added anyway for backwards compatibility. */ g_variant_builder_add (&metadata_builder, "{sv}", "ostree.collection-binding", g_variant_new_string (main_collection_id ? main_collection_id : "")); if (collection_ids->len > 0) { g_autoptr(GVariantBuilder) cr_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ss)")); for (j = 0; j < collection_ids->len; j++) g_variant_builder_add (cr_builder, "(ss)", g_ptr_array_index (collection_ids, j), dst_ref); g_variant_builder_add (&metadata_builder, "{sv}", "ostree.collection-refs-binding", g_variant_builder_end (cr_builder)); } g_variant_builder_add (&metadata_builder, "{sv}", "ostree.ref-binding", g_variant_new_strv (&dst_ref, 1)); g_variant_builder_add (&metadata_builder, "{sv}", "xa.ref", g_variant_new_string (dst_ref)); /* Record the source commit. This is nice to have, but it also means the commit-from gets a different commit id, which avoids problems with e.g. sharing .commitmeta files (signatures) */ g_variant_builder_add (&metadata_builder, "{sv}", "xa.from_commit", g_variant_new_string (resolved_ref)); for (j = 0; j < g_variant_n_children (commitv_metadata); j++) { g_autoptr(GVariant) child = g_variant_get_child_value (commitv_metadata, j); g_autoptr(GVariant) keyv = g_variant_get_child_value (child, 0); const char *key = g_variant_get_string (keyv, NULL); if (strcmp (key, "xa.ref") == 0 || strcmp (key, "xa.from_commit") == 0 || strcmp (key, "ostree.collection-binding") == 0 || strcmp (key, "ostree.collection-refs-binding") == 0 || strcmp (key, "ostree.ref-binding") == 0) continue; if (opt_endoflife && strcmp (key, OSTREE_COMMIT_META_KEY_ENDOFLIFE) == 0) continue; if (opt_endoflife_rebase && strcmp (key, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE) == 0) continue; g_variant_builder_add_value (&metadata_builder, child); } if (opt_endoflife && *opt_endoflife) g_variant_builder_add (&metadata_builder, "{sv}", OSTREE_COMMIT_META_KEY_ENDOFLIFE, g_variant_new_string (opt_endoflife)); if (opt_endoflife_rebase) { g_auto(GStrv) dst_ref_parts = g_strsplit (dst_ref, "/", 0); for (j = 0; opt_endoflife_rebase[j] != NULL; j++) { const char *old_prefix = opt_endoflife_rebase[j]; if (flatpak_has_name_prefix (dst_ref_parts[1], old_prefix)) { g_autofree char *new_id = g_strconcat (opt_endoflife_rebase_new[j], dst_ref_parts[1] + strlen(old_prefix), NULL); g_autofree char *rebased_ref = g_build_filename (dst_ref_parts[0], new_id, dst_ref_parts[2], dst_ref_parts[3], NULL); g_variant_builder_add (&metadata_builder, "{sv}", OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE, g_variant_new_string (rebased_ref)); break; } } } timestamp = ostree_commit_get_timestamp (src_commitv); if (opt_timestamp) timestamp = ts.tv_sec; metadata = g_variant_ref_sink (g_variant_builder_end (&metadata_builder)); if (!ostree_repo_write_commit_with_time (dst_repo, dst_parent, subject, body, metadata, OSTREE_REPO_FILE (dst_root), timestamp, &commit_checksum, cancellable, error)) return FALSE; g_print ("%s: %s\n", dst_ref, commit_checksum); if (!ostree_repo_load_commit (dst_repo, commit_checksum, &dst_commitv, NULL, error)) return FALSE; /* This doesn't copy the detached metadata. I'm not sure if this is a problem. * The main thing there is commit signatures, and we can't copy those, as the commit hash changes. */ if (opt_gpg_key_ids) { char **iter; for (iter = opt_gpg_key_ids; iter && *iter; iter++) { const char *keyid = *iter; g_autoptr(GError) my_error = NULL; if (!ostree_repo_sign_commit (dst_repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, &my_error) && !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_propagate_error (error, g_steal_pointer (&my_error)); return FALSE; } } } if (dst_collection_id != NULL) { OstreeCollectionRef ref = { (char *) dst_collection_id, (char *) dst_ref }; ostree_repo_transaction_set_collection_ref (dst_repo, &ref, commit_checksum); } else { ostree_repo_transaction_set_ref (dst_repo, NULL, dst_ref, commit_checksum); } if (opt_extra_collection_ids) { for (j = 0; opt_extra_collection_ids[j] != NULL; j++) { OstreeCollectionRef ref = { (char *) opt_extra_collection_ids[j], (char *) dst_ref }; ostree_repo_transaction_set_collection_ref (dst_repo, &ref, commit_checksum); } } /* Copy + Rewrite any deltas */ { const char *from[2]; gsize j, n_from = 0; if (dst_parent != NULL) from[n_from++] = dst_parent; from[n_from++] = NULL; for (j = 0; j < n_from; j++) { g_autoptr(GError) local_error = NULL; if (!rewrite_delta (src_repo, resolved_ref, dst_repo, commit_checksum, dst_commitv, from[j], &local_error)) g_debug ("Failed to copy delta: %s", local_error->message); } } } if (!ostree_repo_commit_transaction (dst_repo, NULL, cancellable, error)) return FALSE; if (opt_update_appstream && !flatpak_repo_generate_appstream (dst_repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, 0, cancellable, error)) return FALSE; if (!opt_no_update_summary && !flatpak_repo_update (dst_repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, cancellable, error)) return FALSE; return TRUE; }
gboolean flatpak_builtin_enter (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; int rest_argv_start, rest_argc; const char *ns_name[5] = { "user", "ipc", "net", "pid", "mnt" }; int ns_fd[G_N_ELEMENTS (ns_name)]; char pid_ns[256]; ssize_t pid_ns_len; char self_ns[256]; ssize_t self_ns_len; char *pid_s; int pid, i; g_autofree char *environment_path = NULL; g_autoptr(GPtrArray) argv_array = NULL; g_autoptr(GPtrArray) envp_array = NULL; g_autofree char *environment = NULL; gsize environment_len; char *e; g_autofree char *pulse_path = NULL; g_autofree char *session_bus_path = NULL; g_autofree char *xdg_runtime_dir = NULL; int status; uid = getuid (); gid = getgid (); context = g_option_context_new ("MONITORPID [COMMAND [args...]] - Run a command inside a running sandbox"); rest_argc = 0; for (i = 1; i < argc; i++) { /* The non-option is the command, take it out of the arguments */ if (argv[i][0] != '-') { rest_argv_start = i; rest_argc = argc - i; argc = i; break; } } if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (rest_argc < 2) { usage_error (context, "MONITORPID and COMMAND must be specified", error); return FALSE; } pid_s = argv[rest_argv_start]; pid = atoi (pid_s); if (pid <= 0) return flatpak_fail (error, "Invalid pid %s\n", pid_s); environment_path = g_strdup_printf ("/proc/%d/environ", pid); if (!g_file_get_contents (environment_path, &environment, &environment_len, error)) return FALSE; for (i = 0; i < G_N_ELEMENTS (ns_name); i++) { g_autofree char *path = g_strdup_printf ("/proc/%d/ns/%s", pid, ns_name[i]); g_autofree char *self_path = g_strdup_printf ("/proc/self/ns/%s", ns_name[i]); pid_ns_len = readlink (path, pid_ns, sizeof (pid_ns) - 1); if (pid_ns_len <= 0) return flatpak_fail (error, "Invalid %s namespace for pid %d\n", ns_name[i], pid); pid_ns[pid_ns_len] = 0; self_ns_len = readlink (self_path, self_ns, sizeof (self_ns) - 1); if (self_ns_len <= 0) return flatpak_fail (error, "Invalid %s namespace for self\n", ns_name[i]); self_ns[self_ns_len] = 0; if (strcmp (self_ns, pid_ns) == 0) { /* No need to setns to the same namespace, it will only fail */ ns_fd[i] = -1; } else { ns_fd[i] = open (path, O_RDONLY); if (ns_fd[i] == -1) return flatpak_fail (error, "Can't open %s namespace: %s", ns_name[i], strerror (errno)); } } for (i = 0; i < G_N_ELEMENTS (ns_fd); i++) { if (ns_fd[i] != -1) { if (setns (ns_fd[i], 0) == -1) return flatpak_fail (error, "Can't enter %s namespace: %s", ns_name[i], strerror (errno)); close (ns_fd[i]); } } envp_array = g_ptr_array_new_with_free_func (g_free); for (e = environment; e < environment + environment_len; e = e + strlen (e) + 1) { if (*e != 0 && !g_str_has_prefix (e, "DISPLAY=") && !g_str_has_prefix (e, "PULSE_SERVER=") && !g_str_has_prefix (e, "PULSE_CLIENTCONFIG=") && !g_str_has_prefix (e, "XDG_RUNTIME_DIR=") && !g_str_has_prefix (e, "DBUS_SYSTEM_BUS_ADDRESS=") && !g_str_has_prefix (e, "DBUS_SESSION_BUS_ADDRESS=")) { if (g_str_has_prefix (e, "_LD_LIBRARY_PATH=")) e++; g_ptr_array_add (envp_array, g_strdup (e)); } } xdg_runtime_dir = g_strdup_printf ("/run/user/%d", uid); g_ptr_array_add (envp_array, g_strdup_printf ("XDG_RUNTIME_DIR=%s", xdg_runtime_dir)); if (g_file_test ("/tmp/.X11-unix/X99", G_FILE_TEST_EXISTS)) g_ptr_array_add (envp_array, g_strdup ("DISPLAY=:99.0")); pulse_path = g_strdup_printf ("/run/user/%d/pulse/native", uid); if (g_file_test (pulse_path, G_FILE_TEST_EXISTS)) { g_ptr_array_add (envp_array, g_strdup_printf ("PULSE_SERVER=unix:%s", pulse_path)); g_ptr_array_add (envp_array, g_strdup_printf ("PULSE_CLIENTCONFIG=/run/user/%d/pulse/config", uid)); } session_bus_path = g_strdup_printf ("/run/user/%d/bus", uid); if (g_file_test (session_bus_path, G_FILE_TEST_EXISTS)) g_ptr_array_add (envp_array, g_strdup_printf ("DBUS_SESSION_BUS_ADDRESS=unix:%s", session_bus_path)); if (g_file_test ("/run/dbus/system_bus_socket", G_FILE_TEST_EXISTS)) g_ptr_array_add (envp_array, g_strdup ("DBUS_SYSTEM_BUS_ADDRESS=unix:/run/dbus/system_bus_socket")); g_ptr_array_add (envp_array, NULL); argv_array = g_ptr_array_new_with_free_func (g_free); for (i = 1; i < rest_argc; i++) g_ptr_array_add (argv_array, g_strdup (argv[rest_argv_start + i])); g_ptr_array_add (argv_array, NULL); if (!g_spawn_sync (NULL, (char **) argv_array->pdata, (char **) envp_array->pdata, G_SPAWN_SEARCH_PATH_FROM_ENVP | G_SPAWN_CHILD_INHERITS_STDIN, child_setup, NULL, NULL, NULL, &status, error)) return FALSE; exit (status); }
gboolean flatpak_builtin_build (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(FlatpakDeploy) runtime_deploy = NULL; g_autoptr(GVariant) runtime_deploy_data = NULL; g_autoptr(FlatpakDeploy) extensionof_deploy = NULL; g_autoptr(GFile) var = NULL; g_autoptr(GFile) var_tmp = NULL; g_autoptr(GFile) var_lib = NULL; g_autoptr(GFile) usr = NULL; g_autoptr(GFile) res_deploy = NULL; g_autoptr(GFile) res_files = NULL; g_autoptr(GFile) app_files = NULL; gboolean app_files_ro = FALSE; g_autoptr(GFile) runtime_files = NULL; g_autoptr(GFile) metadata = NULL; g_autofree char *metadata_contents = NULL; g_autofree char *runtime = NULL; g_autofree char *runtime_ref = NULL; g_autofree char *extensionof_ref = NULL; g_autofree char *extensionof_tag = NULL; g_autofree char *extension_point = NULL; g_autofree char *extension_tmpfs_point = NULL; g_autoptr(GKeyFile) metakey = NULL; g_autoptr(GKeyFile) runtime_metakey = NULL; g_autoptr(FlatpakBwrap) bwrap = NULL; g_auto(GStrv) minimal_envp = NULL; gsize metadata_size; const char *directory = NULL; const char *command = "/bin/sh"; g_autofree char *id = NULL; int i; int rest_argv_start, rest_argc; g_autoptr(FlatpakContext) arg_context = NULL; g_autoptr(FlatpakContext) app_context = NULL; gboolean custom_usr; g_auto(GStrv) runtime_ref_parts = NULL; FlatpakRunFlags run_flags; const char *group = NULL; const char *runtime_key = NULL; const char *dest = NULL; gboolean is_app = FALSE; gboolean is_extension = FALSE; gboolean is_app_extension = FALSE; g_autofree char *app_info_path = NULL; g_autofree char *app_extensions = NULL; g_autofree char *runtime_extensions = NULL; g_autofree char *instance_id_host_dir = NULL; char pid_str[64]; g_autofree char *pid_path = NULL; g_autoptr(GFile) app_id_dir = NULL; context = g_option_context_new (_("DIRECTORY [COMMAND [ARGUMENT…]] - Build in directory")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); rest_argc = 0; for (i = 1; i < argc; i++) { /* The non-option is the directory, take it out of the arguments */ if (argv[i][0] != '-') { rest_argv_start = i; rest_argc = argc - i; argc = i; break; } } arg_context = flatpak_context_new (); g_option_context_add_group (context, flatpak_context_get_options (arg_context)); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (rest_argc == 0) return usage_error (context, _("DIRECTORY must be specified"), error); directory = argv[rest_argv_start]; if (rest_argc >= 2) command = argv[rest_argv_start + 1]; res_deploy = g_file_new_for_commandline_arg (directory); metadata = g_file_get_child (res_deploy, opt_metadata ? opt_metadata : "metadata"); if (!g_file_query_exists (res_deploy, NULL) || !g_file_query_exists (metadata, NULL)) return flatpak_fail (error, _("Build directory %s not initialized, use flatpak build-init"), directory); if (!g_file_load_contents (metadata, cancellable, &metadata_contents, &metadata_size, NULL, error)) return FALSE; metakey = g_key_file_new (); if (!g_key_file_load_from_data (metakey, metadata_contents, metadata_size, 0, error)) return FALSE; if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_APPLICATION)) { group = FLATPAK_METADATA_GROUP_APPLICATION; is_app = TRUE; } else if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_RUNTIME)) { group = FLATPAK_METADATA_GROUP_RUNTIME; } else return flatpak_fail (error, _("metadata invalid, not application or runtime")); extensionof_ref = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_EXTENSION_OF, FLATPAK_METADATA_KEY_REF, NULL); if (extensionof_ref != NULL) { is_extension = TRUE; if (g_str_has_prefix (extensionof_ref, "app/")) is_app_extension = TRUE; } extensionof_tag = g_key_file_get_string (metakey, FLATPAK_METADATA_GROUP_EXTENSION_OF, FLATPAK_METADATA_KEY_TAG, NULL); id = g_key_file_get_string (metakey, group, FLATPAK_METADATA_KEY_NAME, error); if (id == NULL) return FALSE; if (opt_runtime) runtime_key = FLATPAK_METADATA_KEY_RUNTIME; else runtime_key = FLATPAK_METADATA_KEY_SDK; runtime = g_key_file_get_string (metakey, group, runtime_key, error); if (runtime == NULL) return FALSE; runtime_ref = g_build_filename ("runtime", runtime, NULL); runtime_ref_parts = flatpak_decompose_ref (runtime_ref, error); if (runtime_ref_parts == NULL) return FALSE; custom_usr = FALSE; usr = g_file_get_child (res_deploy, opt_sdk_dir ? opt_sdk_dir : "usr"); if (g_file_query_exists (usr, cancellable)) { custom_usr = TRUE; runtime_files = g_object_ref (usr); } else { runtime_deploy = flatpak_find_deploy_for_ref (runtime_ref, NULL, cancellable, error); if (runtime_deploy == NULL) return FALSE; runtime_deploy_data = flatpak_deploy_get_deploy_data (runtime_deploy, FLATPAK_DEPLOY_VERSION_ANY, cancellable, error); if (runtime_deploy_data == NULL) return FALSE; runtime_metakey = flatpak_deploy_get_metadata (runtime_deploy); runtime_files = flatpak_deploy_get_files (runtime_deploy); } var = g_file_get_child (res_deploy, "var"); var_tmp = g_file_get_child (var, "tmp"); if (!flatpak_mkdir_p (var_tmp, cancellable, error)) return FALSE; var_lib = g_file_get_child (var, "lib"); if (!flatpak_mkdir_p (var_lib, cancellable, error)) return FALSE; res_files = g_file_get_child (res_deploy, "files"); if (is_app) { app_files = g_object_ref (res_files); if (opt_with_appdir) app_id_dir = flatpak_ensure_data_dir (id, cancellable, NULL); } else if (is_extension) { g_autoptr(GKeyFile) x_metakey = NULL; g_autofree char *x_group = NULL; g_autofree char *x_dir = NULL; g_autofree char *x_subdir_suffix = NULL; char *x_subdir = NULL; g_autofree char *bare_extension_point = NULL; extensionof_deploy = flatpak_find_deploy_for_ref (extensionof_ref, NULL, cancellable, error); if (extensionof_deploy == NULL) return FALSE; x_metakey = flatpak_deploy_get_metadata (extensionof_deploy); /* Since we have tagged extensions, it is possible that an extension could * be listed more than once in the "parent" flatpak. In that case, we should * try and disambiguate using the following rules: * * 1. Use the 'tag=' key in the ExtensionOfSection and if not found: * 2. Use the only extension point available if there is only one. * 3. If there are no matching groups, return NULL. * 4. In all other cases, error out. */ if (!find_matching_extension_group_in_metakey (x_metakey, id, extensionof_tag, &x_group, error)) return FALSE; if (x_group == NULL) { /* Failed, look for subdirectories=true parent */ char *last_dot = strrchr (id, '.'); if (last_dot != NULL) { char *parent_id = g_strndup (id, last_dot - id); if (!find_matching_extension_group_in_metakey (x_metakey, parent_id, extensionof_tag, &x_group, error)) return FALSE; if (x_group != NULL && g_key_file_get_boolean (x_metakey, x_group, FLATPAK_METADATA_KEY_SUBDIRECTORIES, NULL)) x_subdir = last_dot + 1; } if (x_subdir == NULL) return flatpak_fail (error, _("No extension point matching %s in %s"), id, extensionof_ref); } x_dir = g_key_file_get_string (x_metakey, x_group, FLATPAK_METADATA_KEY_DIRECTORY, error); if (x_dir == NULL) return FALSE; x_subdir_suffix = g_key_file_get_string (x_metakey, x_group, FLATPAK_METADATA_KEY_SUBDIRECTORY_SUFFIX, NULL); if (is_app_extension) { app_files = flatpak_deploy_get_files (extensionof_deploy); app_files_ro = TRUE; if (x_subdir != NULL) extension_tmpfs_point = g_build_filename ("/app", x_dir, NULL); bare_extension_point = g_build_filename ("/app", x_dir, x_subdir, NULL); } else { if (x_subdir != NULL) extension_tmpfs_point = g_build_filename ("/usr", x_dir, NULL); bare_extension_point = g_build_filename ("/usr", x_dir, x_subdir, NULL); } extension_point = g_build_filename (bare_extension_point, x_subdir_suffix, NULL); } app_context = flatpak_app_compute_permissions (metakey, runtime_metakey, error); if (app_context == NULL) return FALSE; flatpak_context_allow_host_fs (app_context); flatpak_context_merge (app_context, arg_context); minimal_envp = flatpak_run_get_minimal_env (TRUE, FALSE); bwrap = flatpak_bwrap_new (minimal_envp); flatpak_bwrap_add_args (bwrap, flatpak_get_bwrap (), NULL); run_flags = FLATPAK_RUN_FLAG_DEVEL | FLATPAK_RUN_FLAG_MULTIARCH | FLATPAK_RUN_FLAG_NO_SESSION_HELPER | FLATPAK_RUN_FLAG_SET_PERSONALITY | FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY; if (opt_die_with_parent) run_flags |= FLATPAK_RUN_FLAG_DIE_WITH_PARENT; if (custom_usr) run_flags |= FLATPAK_RUN_FLAG_WRITABLE_ETC; run_flags |= flatpak_context_get_run_flags (app_context); /* Unless manually specified, we disable dbus proxy */ if (!flatpak_context_get_needs_session_bus_proxy (arg_context)) run_flags |= FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY; if (!flatpak_context_get_needs_system_bus_proxy (arg_context)) run_flags |= FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY; if (opt_log_session_bus) run_flags |= FLATPAK_RUN_FLAG_LOG_SESSION_BUS; if (opt_log_system_bus) run_flags |= FLATPAK_RUN_FLAG_LOG_SYSTEM_BUS; /* Never set up an a11y bus for builds */ run_flags |= FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY; if (!flatpak_run_setup_base_argv (bwrap, runtime_files, app_id_dir, runtime_ref_parts[2], run_flags, error)) return FALSE; flatpak_bwrap_add_args (bwrap, (custom_usr && !opt_readonly) ? "--bind" : "--ro-bind", flatpak_file_get_path_cached (runtime_files), "/usr", NULL); if (!custom_usr) flatpak_bwrap_add_args (bwrap, "--lock-file", "/usr/.ref", NULL); if (app_files) flatpak_bwrap_add_args (bwrap, (app_files_ro || opt_readonly) ? "--ro-bind" : "--bind", flatpak_file_get_path_cached (app_files), "/app", NULL); else flatpak_bwrap_add_args (bwrap, "--dir", "/app", NULL); if (extension_tmpfs_point) flatpak_bwrap_add_args (bwrap, "--tmpfs", extension_tmpfs_point, NULL); /* We add the actual bind below so that we're not shadowed by other extensions or their tmpfs */ if (extension_point) dest = extension_point; else if (is_app) dest = g_strdup ("/app"); else dest = g_strdup ("/usr"); flatpak_bwrap_add_args (bwrap, "--setenv", "FLATPAK_DEST", dest, "--setenv", "FLATPAK_ID", id, "--setenv", "FLATPAK_ARCH", runtime_ref_parts[2], NULL); /* Persist some stuff in /var. We can't persist everything because that breaks /var things * from the host to work. For example the /home -> /var/home on atomic. * The interesting things to contain during the build is /var/tmp (for tempfiles shared during builds) * and things like /var/lib/rpm, if the installation uses packages. */ flatpak_bwrap_add_args (bwrap, "--bind", flatpak_file_get_path_cached (var_lib), "/var/lib", NULL); flatpak_bwrap_add_args (bwrap, "--bind", flatpak_file_get_path_cached (var_tmp), "/var/tmp", NULL); flatpak_run_apply_env_vars (bwrap, app_context); if (is_app) { /* We don't actually know the final branchname yet, so use "nobranch" as fallback to avoid unexpected matches. This means any extension point used at build time must have explicit versions to work. */ g_autofree char *fake_ref = g_strdup_printf ("app/%s/%s/nobranch", id, runtime_ref_parts[2]); if (!flatpak_run_add_extension_args (bwrap, metakey, fake_ref, FALSE, &app_extensions, cancellable, error)) return FALSE; } if (!custom_usr && !flatpak_run_add_extension_args (bwrap, runtime_metakey, runtime_ref, FALSE, &runtime_extensions, cancellable, error)) return FALSE; /* Mount this after the above extensions so we always win */ if (extension_point) flatpak_bwrap_add_args (bwrap, "--bind", flatpak_file_get_path_cached (res_files), extension_point, NULL); if (!flatpak_run_add_app_info_args (bwrap, app_files, NULL, app_extensions, runtime_files, runtime_deploy_data, runtime_extensions, id, NULL, runtime_ref, app_id_dir, app_context, NULL, FALSE, TRUE, TRUE, &app_info_path, &instance_id_host_dir, error)) return FALSE; if (!flatpak_run_add_environment_args (bwrap, app_info_path, run_flags, id, app_context, app_id_dir, NULL, cancellable, error)) return FALSE; for (i = 0; opt_bind_mounts != NULL && opt_bind_mounts[i] != NULL; i++) { char *split = strchr (opt_bind_mounts[i], '='); if (split == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, _("Missing '=' in bind mount option '%s'"), opt_bind_mounts[i]); return FALSE; } *split++ = 0; flatpak_bwrap_add_args (bwrap, "--bind", split, opt_bind_mounts[i], NULL); } if (opt_build_dir != NULL) { flatpak_bwrap_add_args (bwrap, "--chdir", opt_build_dir, NULL); } if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error)) return FALSE; flatpak_bwrap_add_args (bwrap, command, NULL); flatpak_bwrap_append_argsv (bwrap, &argv[rest_argv_start + 2], rest_argc - 2); g_ptr_array_add (bwrap->argv, NULL); g_snprintf (pid_str, sizeof (pid_str), "%d", getpid ()); pid_path = g_build_filename (instance_id_host_dir, "pid", NULL); g_file_set_contents (pid_path, pid_str, -1, NULL); /* Ensure we unset O_CLOEXEC */ child_setup (bwrap->fds); if (execvpe (flatpak_get_bwrap (), (char **) bwrap->argv->pdata, bwrap->envp) == -1) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), _("Unable to start app")); return FALSE; } /* Not actually reached... */ return TRUE; }
static gboolean handle_dwarf2_line (DebuginfoData *data, uint32_t off, char *comp_dir, GHashTable *files, GError **error) { unsigned char *ptr = data->debug_sections[DEBUG_LINE].data, *dir; unsigned char **dirt; unsigned char *endsec = ptr + data->debug_sections[DEBUG_LINE].size; unsigned char *endcu, *endprol; unsigned char opcode_base; uint32_t value, dirt_cnt; size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); /* XXX: RhBug:929365, should we error out instead of ignoring? */ if (ptr == NULL) return TRUE; ptr += off; endcu = ptr + 4; endcu += read_32 (ptr); if (endcu == ptr + 0xffffffff) return flatpak_fail (error, "%s: 64-bit DWARF not supported", data->filename); if (endcu > endsec) return flatpak_fail (error, "%s: .debug_line CU does not fit into section", data->filename); value = read_16 (ptr); if (value != 2 && value != 3 && value != 4) return flatpak_fail (error, "%s: DWARF version %d unhandled", data->filename, value); endprol = ptr + 4; endprol += read_32 (ptr); if (endprol > endcu) return flatpak_fail (error, "%s: .debug_line CU prologue does not fit into CU", data->filename); opcode_base = ptr[4 + (value >= 4)]; ptr = dir = ptr + 4 + (value >= 4) + opcode_base; /* dir table: */ value = 1; while (*ptr != 0) { ptr = (unsigned char *) strchr ((char *) ptr, 0) + 1; ++value; } dirt = (unsigned char **) alloca (value * sizeof (unsigned char *)); dirt[0] = (unsigned char *) "."; dirt_cnt = 1; ptr = dir; while (*ptr != 0) { dirt[dirt_cnt++] = ptr; ptr = (unsigned char *) strchr ((char *) ptr, 0) + 1; } ptr++; /* file table: */ while (*ptr != 0) { char *s, *file; size_t file_len, dir_len; file = (char *) ptr; ptr = (unsigned char *) strchr ((char *) ptr, 0) + 1; value = read_uleb128 (ptr); if (value >= dirt_cnt) return flatpak_fail (error, "%s: Wrong directory table index %u", data->filename, value); file_len = strlen (file); dir_len = strlen ((char *) dirt[value]); s = g_malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1); if (*file == '/') { memcpy (s, file, file_len + 1); } else if (*dirt[value] == '/') { memcpy (s, dirt[value], dir_len); s[dir_len] = '/'; memcpy (s + dir_len + 1, file, file_len + 1); } else { char *p = s; if (comp_dir_len != 0) { memcpy (s, comp_dir, comp_dir_len); s[comp_dir_len] = '/'; p += comp_dir_len + 1; } memcpy (p, dirt[value], dir_len); p[dir_len] = '/'; memcpy (p + dir_len + 1, file, file_len + 1); } canonicalize_path (s, s); if (s) g_hash_table_insert (files, s, NULL); (void) read_uleb128 (ptr); (void) read_uleb128 (ptr); } ++ptr; return TRUE; }
gboolean flatpak_builtin_build_init (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) var_deploy_files = NULL; g_autoptr(GFile) base = NULL; g_autoptr(GFile) files_dir = NULL; g_autoptr(GFile) usr_dir = NULL; g_autoptr(GFile) var_dir = NULL; g_autoptr(GFile) var_tmp_dir = NULL; g_autoptr(GFile) var_run_dir = NULL; g_autoptr(GFile) metadata_file = NULL; g_autoptr(GString) metadata_contents = NULL; g_autoptr(GError) my_error = NULL; const char *app_id; const char *directory; const char *sdk; const char *runtime; const char *branch = "master"; g_autofree char *runtime_ref = NULL; g_autofree char *var_ref = NULL; g_autofree char *sdk_ref = NULL; int i; context = g_option_context_new (_("DIRECTORY APPNAME SDK RUNTIME [BRANCH] - Initialize a directory for building")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 5) return usage_error (context, _("RUNTIME must be specified"), error); if (argc > 6) return usage_error (context, _("Too many arguments"), error); directory = argv[1]; app_id = argv[2]; sdk = argv[3]; runtime = argv[4]; if (argc >= 6) branch = argv[5]; if (!flatpak_is_valid_name (app_id, &my_error)) return flatpak_fail (error, _("'%s' is not a valid application name: %s"), app_id, my_error->message); if (!flatpak_is_valid_name (runtime, &my_error)) return flatpak_fail (error, _("'%s' is not a valid runtime name: %s"), runtime, my_error->message); if (!flatpak_is_valid_name (sdk, &my_error)) return flatpak_fail (error, _("'%s' is not a valid sdk name: %s"), sdk, my_error->message); if (!flatpak_is_valid_branch (branch, &my_error)) return flatpak_fail (error, _("'%s' is not a valid branch name: %s"), branch, my_error->message); runtime_ref = flatpak_build_untyped_ref (runtime, branch, opt_arch); sdk_ref = flatpak_build_untyped_ref (sdk, branch, opt_arch); base = g_file_new_for_commandline_arg (directory); if (!flatpak_mkdir_p (base, cancellable, error)) return FALSE; files_dir = g_file_get_child (base, "files"); if (opt_sdk_dir) usr_dir = g_file_get_child (base, opt_sdk_dir); else usr_dir = g_file_get_child (base, "usr"); var_dir = g_file_get_child (base, "var"); var_tmp_dir = g_file_get_child (var_dir, "tmp"); var_run_dir = g_file_get_child (var_dir, "run"); metadata_file = g_file_get_child (base, "metadata"); if (!opt_update && g_file_query_exists (files_dir, cancellable)) return flatpak_fail (error, _("Build directory %s already initialized"), directory); if (opt_writable_sdk) { g_autofree char *full_sdk_ref = g_strconcat ("runtime/", sdk_ref, NULL); g_autoptr(GFile) sdk_deploy_files = NULL; g_autoptr(FlatpakDeploy) sdk_deploy = NULL; sdk_deploy = flatpak_find_deploy_for_ref (full_sdk_ref, cancellable, error); if (sdk_deploy == NULL) return FALSE; if (!flatpak_rm_rf (usr_dir, NULL, &my_error)) { if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_propagate_error (error, g_steal_pointer (&my_error)); return FALSE; } g_clear_error (&my_error); } sdk_deploy_files = flatpak_deploy_get_files (sdk_deploy); if (!flatpak_cp_a (sdk_deploy_files, usr_dir, FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; if (opt_sdk_extensions && !copy_extensions (sdk_deploy, branch, opt_sdk_extensions, usr_dir, cancellable, error)) return FALSE; } if (opt_var) { var_ref = flatpak_build_runtime_ref (opt_var, branch, opt_arch); var_deploy_files = flatpak_find_files_dir_for_ref (var_ref, cancellable, error); if (var_deploy_files == NULL) return FALSE; } if (opt_update) return TRUE; if (!g_file_make_directory (files_dir, cancellable, error)) return FALSE; if (opt_base) { const char *base_branch; g_autofree char *base_ref = NULL; g_autoptr(GFile) base_deploy_files = NULL; g_autoptr(FlatpakDeploy) base_deploy = NULL; base_branch = opt_base_version ? opt_base_version : "master"; base_ref = flatpak_build_app_ref (opt_base, base_branch, opt_arch); base_deploy = flatpak_find_deploy_for_ref (base_ref, cancellable, error); if (base_deploy == NULL) return FALSE; base_deploy_files = flatpak_deploy_get_files (base_deploy); if (!flatpak_cp_a (base_deploy_files, files_dir, FLATPAK_CP_FLAGS_MERGE | FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; if (opt_base_extensions && !copy_extensions (base_deploy, base_branch, opt_base_extensions, files_dir, cancellable, error)) return FALSE; } if (var_deploy_files) { if (!flatpak_cp_a (var_deploy_files, var_dir, FLATPAK_CP_FLAGS_NONE, cancellable, error)) return FALSE; } else { if (!g_file_make_directory (var_dir, cancellable, error)) return FALSE; } if (!flatpak_mkdir_p (var_tmp_dir, cancellable, error)) return FALSE; if (!g_file_query_exists (var_run_dir, cancellable) && !g_file_make_symbolic_link (var_run_dir, "/run", cancellable, error)) return FALSE; metadata_contents = g_string_new ("[Application]\n"); g_string_append_printf (metadata_contents, "name=%s\n" "runtime=%s\n" "sdk=%s\n", app_id, runtime_ref, sdk_ref); if (opt_tags != NULL) { g_string_append (metadata_contents, "tags="); for (i = 0; opt_tags[i] != NULL; i++) { g_string_append (metadata_contents, opt_tags[i]); g_string_append_c (metadata_contents, ';'); } g_string_append_c (metadata_contents, '\n'); } if (!g_file_replace_contents (metadata_file, metadata_contents->str, metadata_contents->len, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, cancellable, error)) return FALSE; return TRUE; }
gboolean flatpak_builtin_remote_add (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GPtrArray) dirs = NULL; FlatpakDir *dir; g_autoptr(GFile) file = NULL; g_auto(GStrv) remotes = NULL; g_autofree char *remote_url = NULL; const char *remote_name; const char *location = NULL; g_autoptr(GKeyFile) config = NULL; g_autoptr(GBytes) gpg_data = NULL; gboolean changed = FALSE; g_autoptr(GError) local_error = NULL; gboolean is_oci; context = g_option_context_new (_("NAME LOCATION - Add a remote repository")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); g_option_context_add_main_entries (context, common_options, NULL); if (!flatpak_option_context_parse (context, add_options, &argc, &argv, FLATPAK_BUILTIN_FLAG_ONE_DIR | FLATPAK_BUILTIN_FLAG_OPTIONAL_REPO, &dirs, cancellable, error)) return FALSE; dir = g_ptr_array_index (dirs, 0); if (argc < 2) return usage_error (context, _("NAME must be specified"), error); if (argc < 3) return usage_error (context, _("LOCATION must be specified"), error); if (argc > 3) return usage_error (context, _("Too many arguments"), error); if (opt_collection_id != NULL && !ostree_validate_collection_id (opt_collection_id, &local_error)) return flatpak_fail (error, _("‘%s’ is not a valid collection ID: %s"), opt_collection_id, local_error->message); if (opt_collection_id != NULL && (opt_no_gpg_verify || opt_gpg_import == NULL || opt_gpg_import[0] == NULL)) return flatpak_fail (error, _("GPG verification is required if collections are enabled")); remote_name = argv[1]; location = argv[2]; remotes = flatpak_dir_list_remotes (dir, cancellable, error); if (remotes == NULL) return FALSE; if (g_strv_contains ((const char **) remotes, remote_name)) { if (opt_if_not_exists) return TRUE; /* Do nothing */ return flatpak_fail (error, _("Remote %s already exists"), remote_name); } if (opt_from || flatpak_file_arg_has_suffix (location, ".flatpakrepo")) { load_options (location, &gpg_data); if (opt_url == NULL) return flatpak_fail (error, _("No url specified in flatpakrepo file")); } else { file = g_file_new_for_commandline_arg (location); if (g_file_is_native (file)) remote_url = g_file_get_uri (file); else remote_url = g_strdup (location); opt_url = remote_url; } /* Default to gpg verify, except for OCI registries */ is_oci = opt_url && g_str_has_prefix (opt_url, "oci+"); if (!opt_no_gpg_verify && !is_oci) opt_do_gpg_verify = TRUE; config = get_config_from_opts (dir, remote_name, &changed); if (opt_gpg_import != NULL) { gpg_data = flatpak_load_gpg_keys (opt_gpg_import, cancellable, error); if (gpg_data == NULL) return FALSE; } if (!flatpak_dir_modify_remote (dir, remote_name, config, gpg_data, cancellable, error)) return FALSE; /* Reload previously changed configuration */ if (!flatpak_dir_recreate_repo (dir, cancellable, error)) return FALSE; /* We can't retrieve the extra metadata until the remote has been added locally, since ostree_repo_remote_fetch_summary() works with the repository's name, not its URL. Don't propagate IO failed errors here because we might just be offline - the remote should already be usable. */ if (!flatpak_dir_update_remote_configuration (dir, remote_name, cancellable, &local_error)) { if (local_error->domain == G_RESOLVER_ERROR || g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED)) { g_printerr (_("Warning: Could not update extra metadata for '%s': %s\n"), remote_name, local_error->message); } else { g_propagate_error (error, g_steal_pointer (&local_error)); return FALSE; } } return TRUE; }
gboolean flatpak_builtin_build_init (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) var_deploy_base = NULL; g_autoptr(GFile) var_deploy_files = NULL; g_autoptr(GFile) base = NULL; g_autoptr(GFile) files_dir = NULL; g_autoptr(GFile) usr_dir = NULL; g_autoptr(GFile) var_dir = NULL; g_autoptr(GFile) var_tmp_dir = NULL; g_autoptr(GFile) var_run_dir = NULL; g_autoptr(GFile) metadata_file = NULL; g_autoptr(GString) metadata_contents = NULL; const char *app_id; const char *directory; const char *sdk; const char *runtime; const char *branch = "master"; g_autofree char *runtime_ref = NULL; g_autofree char *var_ref = NULL; g_autofree char *sdk_ref = NULL; int i; context = g_option_context_new ("DIRECTORY APPNAME SDK RUNTIME [BRANCH] - Initialize a directory for building"); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 5) return usage_error (context, "RUNTIME must be specified", error); directory = argv[1]; app_id = argv[2]; sdk = argv[3]; runtime = argv[4]; if (argc >= 6) branch = argv[5]; if (!flatpak_is_valid_name (app_id)) return flatpak_fail (error, "'%s' is not a valid application name", app_id); if (!flatpak_is_valid_name (runtime)) return flatpak_fail (error, "'%s' is not a valid runtime name", runtime); if (!flatpak_is_valid_name (sdk)) return flatpak_fail (error, "'%s' is not a valid sdk name", sdk); if (!flatpak_is_valid_branch (branch)) return flatpak_fail (error, "'%s' is not a valid branch name", branch); runtime_ref = flatpak_build_untyped_ref (runtime, branch, opt_arch); sdk_ref = flatpak_build_untyped_ref (sdk, branch, opt_arch); base = g_file_new_for_commandline_arg (directory); if (!gs_file_ensure_directory (base, TRUE, cancellable, error)) return FALSE; files_dir = g_file_get_child (base, "files"); if (opt_sdk_dir) usr_dir = g_file_get_child (base, opt_sdk_dir); else usr_dir = g_file_get_child (base, "usr"); var_dir = g_file_get_child (base, "var"); var_tmp_dir = g_file_get_child (var_dir, "tmp"); var_run_dir = g_file_get_child (var_dir, "run"); metadata_file = g_file_get_child (base, "metadata"); if (!opt_update && g_file_query_exists (files_dir, cancellable)) return flatpak_fail (error, "Build directory %s already initialized", directory); if (opt_writable_sdk) { g_autofree char *full_sdk_ref = g_strconcat ("runtime/", sdk_ref, NULL); g_autoptr(GError) my_error = NULL; g_autoptr(GFile) sdk_deploy_files = NULL; g_autoptr(FlatpakDeploy) sdk_deploy = NULL; sdk_deploy = flatpak_find_deploy_for_ref (full_sdk_ref, cancellable, error); if (sdk_deploy == NULL) return FALSE; if (!gs_shutil_rm_rf (usr_dir, NULL, &my_error)) { if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_propagate_error (error, g_steal_pointer (&my_error)); return FALSE; } g_clear_error (&my_error); } sdk_deploy_files = flatpak_deploy_get_files (sdk_deploy); if (!flatpak_cp_a (sdk_deploy_files, usr_dir, FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; if (opt_sdk_extensions) { g_autoptr(GKeyFile) metakey = flatpak_deploy_get_metadata (sdk_deploy); GList *extensions = NULL, *l; /* We leak this on failure, as we have no autoptr for deep lists.. */ extensions = flatpak_list_extensions (metakey, opt_arch, branch); for (i = 0; opt_sdk_extensions[i] != NULL; i++) { const char *requested_extension = opt_sdk_extensions[i]; gboolean found = FALSE; for (l = extensions; l != NULL; l = l->next) { FlatpakExtension *ext = l->data; if (strcmp (ext->installed_id, requested_extension) == 0 || strcmp (ext->id, requested_extension) == 0) { g_autoptr(GFile) ext_deploy_dir = flatpak_find_deploy_dir_for_ref (ext->ref, cancellable, NULL); if (ext_deploy_dir != NULL) { g_autoptr(GFile) ext_deploy_files = g_file_get_child (ext_deploy_dir, "files"); g_autoptr(GFile) target = g_file_resolve_relative_path (usr_dir, ext->directory); g_autoptr(GFile) target_parent = g_file_get_parent (target); if (!gs_file_ensure_directory (target_parent, TRUE, cancellable, error)) return FALSE; /* An extension overrides whatever is there before, so we clean up first */ if (!gs_shutil_rm_rf (target, cancellable, error)) return FALSE; if (!flatpak_cp_a (ext_deploy_files, target, FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; found = TRUE; } else { g_list_free_full (extensions, (GDestroyNotify) flatpak_extension_free); return flatpak_fail (error, "Requested extension %s not installed\n", requested_extension); } } } if (!found) return flatpak_fail (error, "No extension %s in sdk\n", requested_extension); } g_list_free_full (extensions, (GDestroyNotify) flatpak_extension_free); } } if (opt_var) { var_ref = flatpak_build_runtime_ref (opt_var, branch, opt_arch); var_deploy_base = flatpak_find_deploy_dir_for_ref (var_ref, cancellable, error); if (var_deploy_base == NULL) return FALSE; var_deploy_files = g_file_get_child (var_deploy_base, "files"); } if (opt_update) return TRUE; if (!g_file_make_directory (files_dir, cancellable, error)) return FALSE; if (var_deploy_files) { if (!gs_shutil_cp_a (var_deploy_files, var_dir, cancellable, error)) return FALSE; } else { if (!g_file_make_directory (var_dir, cancellable, error)) return FALSE; } if (!gs_file_ensure_directory (var_tmp_dir, FALSE, cancellable, error)) return FALSE; if (!g_file_query_exists (var_run_dir, cancellable) && !g_file_make_symbolic_link (var_run_dir, "/run", cancellable, error)) return FALSE; metadata_contents = g_string_new ("[Application]\n"); g_string_append_printf (metadata_contents, "name=%s\n" "runtime=%s\n" "sdk=%s\n", app_id, runtime_ref, sdk_ref); if (opt_tags != NULL) { g_string_append (metadata_contents, "tags="); for (i = 0; opt_tags[i] != NULL; i++) { g_string_append (metadata_contents, opt_tags[i]); g_string_append_c (metadata_contents, ';'); } g_string_append_c (metadata_contents, '\n'); } if (!g_file_replace_contents (metadata_file, metadata_contents->str, metadata_contents->len, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, cancellable, error)) return FALSE; return TRUE; }
gboolean flatpak_builtin_build_init (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) var_deploy_files = NULL; g_autoptr(GFile) base = NULL; g_autoptr(GFile) files_dir = NULL; g_autoptr(GFile) usr_dir = NULL; g_autoptr(GFile) var_dir = NULL; g_autoptr(GFile) var_tmp_dir = NULL; g_autoptr(GFile) var_run_dir = NULL; g_autoptr(GFile) metadata_file = NULL; g_autoptr(GString) metadata_contents = NULL; g_autoptr(GError) my_error = NULL; const char *app_id; const char *directory; const char *sdk_pref; const char *runtime_pref; const char *default_branch = NULL; g_autofree char *runtime_ref = NULL; g_autofree char *var_ref = NULL; g_autofree char *sdk_ref = NULL; FlatpakKinds kinds; int i; g_autoptr(FlatpakDir) sdk_dir = NULL; g_autoptr(FlatpakDir) runtime_dir = NULL; gboolean is_app = FALSE; gboolean is_extension = FALSE; gboolean is_runtime = FALSE; context = g_option_context_new (_("DIRECTORY APPNAME SDK RUNTIME [BRANCH] - Initialize a directory for building")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 5) return usage_error (context, _("RUNTIME must be specified"), error); if (argc > 6) return usage_error (context, _("Too many arguments"), error); directory = argv[1]; app_id = argv[2]; sdk_pref = argv[3]; runtime_pref = argv[4]; if (argc >= 6) default_branch = argv[5]; if (opt_type != NULL) { if (strcmp (opt_type, "app") == 0) is_app = TRUE; else if (strcmp (opt_type, "extension") == 0) is_extension = TRUE; else if (strcmp (opt_type, "runtime") == 0) is_runtime = TRUE; else return flatpak_fail (error, _("'%s' is not a valid build type name, use app, runtime or extension"), opt_type); } else is_app = TRUE; if (!flatpak_is_valid_name (app_id, &my_error)) return flatpak_fail (error, _("'%s' is not a valid application name: %s"), app_id, my_error->message); kinds = FLATPAK_KINDS_RUNTIME; sdk_dir = flatpak_find_installed_pref (sdk_pref, kinds, opt_arch, default_branch, TRUE, FALSE, FALSE, NULL, &sdk_ref, cancellable, error); if (sdk_dir == NULL) return FALSE; kinds = FLATPAK_KINDS_RUNTIME; if (is_extension) kinds |= FLATPAK_KINDS_APP; runtime_dir = flatpak_find_installed_pref (runtime_pref, kinds, opt_arch, default_branch, TRUE, FALSE, FALSE, NULL, &runtime_ref, cancellable, error); if (runtime_dir == NULL) return FALSE; base = g_file_new_for_commandline_arg (directory); if (!flatpak_mkdir_p (base, cancellable, error)) return FALSE; files_dir = g_file_get_child (base, "files"); if (opt_sdk_dir) usr_dir = g_file_get_child (base, opt_sdk_dir); else usr_dir = g_file_get_child (base, "usr"); var_dir = g_file_get_child (base, "var"); var_tmp_dir = g_file_get_child (var_dir, "tmp"); var_run_dir = g_file_get_child (var_dir, "run"); metadata_file = g_file_get_child (base, "metadata"); if (!opt_update && g_file_query_exists (files_dir, cancellable)) return flatpak_fail (error, _("Build directory %s already initialized"), directory); if (opt_writable_sdk || is_runtime) { g_autoptr(GFile) sdk_deploy_files = NULL; g_autoptr(FlatpakDeploy) sdk_deploy = NULL; sdk_deploy = flatpak_dir_load_deployed (sdk_dir, sdk_ref, NULL, cancellable, error); if (sdk_deploy == NULL) return FALSE; if (!flatpak_rm_rf (usr_dir, NULL, &my_error)) { if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_propagate_error (error, g_steal_pointer (&my_error)); return FALSE; } g_clear_error (&my_error); } sdk_deploy_files = flatpak_deploy_get_files (sdk_deploy); if (!flatpak_cp_a (sdk_deploy_files, usr_dir, FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; if (opt_sdk_extensions && !copy_extensions (sdk_deploy, default_branch, opt_sdk_extensions, usr_dir, cancellable, error)) return FALSE; } if (opt_var) { var_ref = flatpak_build_runtime_ref (opt_var, default_branch, opt_arch); var_deploy_files = flatpak_find_files_dir_for_ref (var_ref, cancellable, error); if (var_deploy_files == NULL) return FALSE; } if (opt_update) return TRUE; if (!g_file_make_directory (files_dir, cancellable, error)) return FALSE; if (opt_base) { const char *base_branch; g_autofree char *base_ref = NULL; g_autoptr(GFile) base_deploy_files = NULL; g_autoptr(FlatpakDeploy) base_deploy = NULL; base_branch = opt_base_version ? opt_base_version : "master"; base_ref = flatpak_build_app_ref (opt_base, base_branch, opt_arch); base_deploy = flatpak_find_deploy_for_ref (base_ref, cancellable, error); if (base_deploy == NULL) return FALSE; base_deploy_files = flatpak_deploy_get_files (base_deploy); if (!flatpak_cp_a (base_deploy_files, files_dir, FLATPAK_CP_FLAGS_MERGE | FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; if (opt_base_extensions && !copy_extensions (base_deploy, base_branch, opt_base_extensions, files_dir, cancellable, error)) return FALSE; } if (var_deploy_files) { if (!flatpak_cp_a (var_deploy_files, var_dir, FLATPAK_CP_FLAGS_NONE, cancellable, error)) return FALSE; } else { if (!g_file_make_directory (var_dir, cancellable, error)) return FALSE; } if (!flatpak_mkdir_p (var_tmp_dir, cancellable, error)) return FALSE; if (!g_file_query_exists (var_run_dir, cancellable) && !g_file_make_symbolic_link (var_run_dir, "/run", cancellable, error)) return FALSE; metadata_contents = g_string_new (""); if (is_app) g_string_append (metadata_contents, "[Application]\n"); else g_string_append (metadata_contents, "[Runtime]\n"); g_string_append_printf (metadata_contents, "name=%s\n", app_id); /* The "runtime" can be an app in case we're building an extension */ if (g_str_has_prefix (runtime_ref, "runtime/")) g_string_append_printf (metadata_contents, "runtime=%s\n", runtime_ref + strlen ("runtime/")); if (g_str_has_prefix (sdk_ref, "runtime/")) g_string_append_printf (metadata_contents, "sdk=%s\n", sdk_ref + strlen ("runtime/")); if (opt_tags != NULL) { g_string_append (metadata_contents, "tags="); for (i = 0; opt_tags[i] != NULL; i++) { g_string_append (metadata_contents, opt_tags[i]); g_string_append_c (metadata_contents, ';'); } g_string_append_c (metadata_contents, '\n'); } if (is_extension) g_string_append_printf (metadata_contents, "\n" "[ExtensionOf]\n" "ref=%s\n", runtime_ref); if (!g_file_replace_contents (metadata_file, metadata_contents->str, metadata_contents->len, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, cancellable, error)) return FALSE; return TRUE; }
gboolean flatpak_builtin_build_update_repo (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) repofile = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *location; g_autoptr(GPtrArray) unwanted_deltas = NULL; context = g_option_context_new (_("LOCATION - Update repository metadata")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 2) return usage_error (context, _("LOCATION must be specified"), error); if (opt_static_delta_jobs <= 0) opt_static_delta_jobs = g_get_num_processors (); location = argv[1]; repofile = g_file_new_for_commandline_arg (location); repo = ostree_repo_new (repofile); if (!ostree_repo_open (repo, cancellable, error)) return FALSE; if (opt_generate_delta_to) { if (!generate_one_delta (repo, opt_generate_delta_from, opt_generate_delta_to, opt_generate_delta_ref, cancellable, error)) return FALSE; return TRUE; } if (opt_title && !flatpak_repo_set_title (repo, opt_title[0] ? opt_title : NULL, error)) return FALSE; if (opt_comment && !flatpak_repo_set_comment (repo, opt_comment[0] ? opt_comment : NULL, error)) return FALSE; if (opt_description && !flatpak_repo_set_description (repo, opt_description[0] ? opt_description : NULL, error)) return FALSE; if (opt_homepage && !flatpak_repo_set_homepage (repo, opt_homepage[0] ? opt_homepage : NULL, error)) return FALSE; if (opt_icon && !flatpak_repo_set_icon (repo, opt_icon[0] ? opt_icon : NULL, error)) return FALSE; if (opt_redirect_url && !flatpak_repo_set_redirect_url (repo, opt_redirect_url[0] ? opt_redirect_url : NULL, error)) return FALSE; if (opt_default_branch && !flatpak_repo_set_default_branch (repo, opt_default_branch[0] ? opt_default_branch : NULL, error)) return FALSE; if (opt_collection_id != NULL) { /* Only allow a transition from no collection ID to a non-empty collection ID. * Changing the collection ID between two different non-empty values is too * dangerous: it will break all clients who have previously pulled from the repository. * Require the user to recreate the repository from scratch in that case. */ const char *old_collection_id = ostree_repo_get_collection_id (repo); const char *new_collection_id = opt_collection_id[0] ? opt_collection_id : NULL; if (old_collection_id != NULL && g_strcmp0 (old_collection_id, new_collection_id) != 0) return flatpak_fail (error, "The collection ID of an existing repository cannot be changed. " "Recreate the repository to change or clear its collection ID."); if (!flatpak_repo_set_collection_id (repo, new_collection_id, error)) return FALSE; } if (opt_deploy_collection_id && !flatpak_repo_set_deploy_collection_id (repo, TRUE, error)) return FALSE; if (opt_gpg_import) { g_autoptr(GBytes) gpg_data = flatpak_load_gpg_keys (opt_gpg_import, cancellable, error); if (gpg_data == NULL) return FALSE; if (!flatpak_repo_set_gpg_keys (repo, gpg_data, error)) return FALSE; } if (!opt_no_update_appstream) { g_print (_("Updating appstream branch\n")); if (!flatpak_repo_generate_appstream (repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, 0, cancellable, error)) return FALSE; } if (opt_generate_deltas && !generate_all_deltas (repo, &unwanted_deltas, cancellable, error)) return FALSE; if (unwanted_deltas != NULL) { int i; for (i = 0; i < unwanted_deltas->len; i++) { const char *delta = g_ptr_array_index (unwanted_deltas, i); g_print ("Deleting unwanted delta: %s\n", delta); g_autoptr(GError) my_error = NULL; if (!_ostree_repo_static_delta_delete (repo, delta, cancellable, &my_error)) g_printerr ("Unable to delete delta %s: %s\n", delta, my_error->message); } } if (!opt_no_update_summary) { g_print (_("Updating summary\n")); if (!flatpak_repo_update (repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, cancellable, error)) return FALSE; } if (opt_prune) { gint n_objects_total; gint n_objects_pruned; guint64 objsize_total; g_autofree char *formatted_freed_size = NULL; g_print ("Pruning old commits\n"); if (!ostree_repo_prune (repo, OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY, opt_prune_depth, &n_objects_total, &n_objects_pruned, &objsize_total, cancellable, error)) return FALSE; formatted_freed_size = g_format_size_full (objsize_total, 0); g_print (_("Total objects: %u\n"), n_objects_total); if (n_objects_pruned == 0) g_print (_("No unreachable objects\n")); else g_print (_("Deleted %u objects, %s freed\n"), n_objects_pruned, formatted_freed_size); } return TRUE; }
static gboolean import_oci (OstreeRepo *repo, GFile *file, GCancellable *cancellable, GError **error) { #if !defined(HAVE_OSTREE_EXPORT_PATH_PREFIX) /* This code actually doesn't user path_prefix, but it need the support for reading commits from the transaction that was added at the same time. */ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "This version of ostree is to old to support OCI exports"); return FALSE; #elif !defined(HAVE_LIBARCHIVE) g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "This version of flatpak is not compiled with libarchive support"); return FALSE; #else g_autoptr(OstreeMutableTree) archive_mtree = NULL; g_autoptr(OstreeMutableTree) mtree = NULL; g_autoptr(OstreeMutableTree) files_mtree = NULL; g_autoptr(OstreeMutableTree) export_mtree = NULL; g_autoptr(GFile) archive_root = NULL; g_autoptr(GFile) root = NULL; g_autoptr(GFile) files = NULL; g_autoptr(GFile) export = NULL; g_autoptr(GFile) ref = NULL; g_autoptr(GFile) commit = NULL; g_autoptr(GFile) commitmeta = NULL; g_autoptr(GFile) metadata = NULL; g_autofree char *commit_checksum = NULL; g_autofree char *ref_data = NULL; g_autofree char *commit_data = NULL; gsize commit_size; g_autofree char *commitmeta_data = NULL; g_autofree char *parent = NULL; const char *subject; const char *body; const char *target_ref; const char *files_source; gsize commitmeta_size; g_autoptr(GVariant) commitv = NULL; g_autoptr(GVariant) commitv_metadata = NULL; g_autoptr(GVariant) commitmetav = NULL; if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) return FALSE; /* There is no way to write a subset of the archive to a mtree, so instead we write all of it and then build a new mtree with the subset */ archive_mtree = ostree_mutable_tree_new (); if (!ostree_repo_write_archive_to_mtree (repo, file, archive_mtree, NULL, TRUE, cancellable, error)) return FALSE; if (!ostree_repo_write_mtree (repo, archive_mtree, &archive_root, cancellable, error)) return FALSE; if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile *) archive_root, error)) return FALSE; ref = g_file_resolve_relative_path (archive_root, "rootfs/ref"); metadata = g_file_resolve_relative_path (archive_root, "rootfs/metadata"); commit = g_file_resolve_relative_path (archive_root, "rootfs/commit"); commitmeta = g_file_resolve_relative_path (archive_root, "rootfs/commitmeta"); if (!g_file_query_exists (ref, NULL)) return flatpak_fail (error, "Required file ref not in tarfile"); if (!g_file_query_exists (metadata, NULL)) return flatpak_fail (error, "Required file metadata not in tarfile"); if (!g_file_query_exists (commit, NULL)) return flatpak_fail (error, "Required file commit not in tarfile"); if (!g_file_load_contents (ref, cancellable, &ref_data, NULL, NULL, error)) return FALSE; if (g_str_has_prefix (ref_data, "app/")) files_source = "rootfs/app"; else files_source = "rootfs/usr"; files = g_file_resolve_relative_path (archive_root, files_source); if (!g_file_query_exists (files, NULL)) return flatpak_fail (error, "Required directory %s not in tarfile", files_source); export = g_file_resolve_relative_path (archive_root, "rootfs/export");
static gboolean handle_dwarf2_section (DebuginfoData *data, GHashTable *files, GError **error) { Elf_Data *e_data; int i; debug_section_t *debug_sections; ptr_size = 0; if (data->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) { do_read_16 = buf_read_ule16; do_read_32 = buf_read_ule32; } else if (data->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) { do_read_16 = buf_read_ube16; do_read_32 = buf_read_ube32; } else { return flatpak_fail (error, "%s: Wrong ELF data encoding", data->filename); } debug_sections = data->debug_sections; if (debug_sections[DEBUG_INFO].data != NULL) { unsigned char *ptr, *endcu, *endsec; uint32_t value; struct abbrev_tag *t; g_autofree REL *relbuf = NULL; if (debug_sections[DEBUG_INFO].relsec) { Elf_Scn *scn; int ndx, maxndx; GElf_Rel rel; GElf_Rela rela; GElf_Sym sym; GElf_Addr base = data->shdr[debug_sections[DEBUG_INFO].sec].sh_addr; Elf_Data *symdata = NULL; int rtype; i = debug_sections[DEBUG_INFO].relsec; scn = data->scns[i]; e_data = elf_getdata (scn, NULL); g_assert (e_data != NULL && e_data->d_buf != NULL); g_assert (elf_getdata (scn, e_data) == NULL); g_assert (e_data->d_off == 0); g_assert (e_data->d_size == data->shdr[i].sh_size); maxndx = data->shdr[i].sh_size / data->shdr[i].sh_entsize; relbuf = g_malloc (maxndx * sizeof (REL)); reltype = data->shdr[i].sh_type; symdata = elf_getdata (data->scns[data->shdr[i].sh_link], NULL); g_assert (symdata != NULL && symdata->d_buf != NULL); g_assert (elf_getdata (data->scns[data->shdr[i].sh_link], symdata) == NULL); g_assert (symdata->d_off == 0); g_assert (symdata->d_size == data->shdr[data->shdr[i].sh_link].sh_size); for (ndx = 0, relend = relbuf; ndx < maxndx; ++ndx) { if (data->shdr[i].sh_type == SHT_REL) { gelf_getrel (e_data, ndx, &rel); rela.r_offset = rel.r_offset; rela.r_info = rel.r_info; rela.r_addend = 0; } else { gelf_getrela (e_data, ndx, &rela); } gelf_getsym (symdata, ELF64_R_SYM (rela.r_info), &sym); /* Relocations against section symbols are uninteresting in REL. */ if (data->shdr[i].sh_type == SHT_REL && sym.st_value == 0) continue; /* Only consider relocations against .debug_str, .debug_line and .debug_abbrev. */ if (sym.st_shndx != debug_sections[DEBUG_STR].sec && sym.st_shndx != debug_sections[DEBUG_LINE].sec && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec) continue; rela.r_addend += sym.st_value; rtype = ELF64_R_TYPE (rela.r_info); switch (data->ehdr.e_machine) { case EM_SPARC: case EM_SPARC32PLUS: case EM_SPARCV9: if (rtype != R_SPARC_32 && rtype != R_SPARC_UA32) goto fail; break; case EM_386: if (rtype != R_386_32) goto fail; break; case EM_PPC: case EM_PPC64: if (rtype != R_PPC_ADDR32 && rtype != R_PPC_UADDR32) goto fail; break; case EM_S390: if (rtype != R_390_32) goto fail; break; case EM_IA_64: if (rtype != R_IA64_SECREL32LSB) goto fail; break; case EM_X86_64: if (rtype != R_X86_64_32) goto fail; break; case EM_ALPHA: if (rtype != R_ALPHA_REFLONG) goto fail; break; #if defined(EM_AARCH64) && defined(R_AARCH64_ABS32) case EM_AARCH64: if (rtype != R_AARCH64_ABS32) goto fail; break; #endif case EM_68K: if (rtype != R_68K_32) goto fail; break; default: fail: return flatpak_fail (error, "%s: Unhandled relocation %d in .debug_info section", data->filename, rtype); } relend->ptr = debug_sections[DEBUG_INFO].data + (rela.r_offset - base); relend->addend = rela.r_addend; ++relend; } if (relbuf == relend) { g_free (relbuf); relbuf = NULL; relend = NULL; } else { qsort (relbuf, relend - relbuf, sizeof (REL), rel_cmp); } } ptr = debug_sections[DEBUG_INFO].data; relptr = relbuf; endsec = ptr + debug_sections[DEBUG_INFO].size; while (ptr != NULL && ptr < endsec) { g_autoptr(GHashTable) abbrev = NULL; if (ptr + 11 > endsec) return flatpak_fail (error, "%s: .debug_info CU header too small", data->filename); endcu = ptr + 4; endcu += read_32 (ptr); if (endcu == ptr + 0xffffffff) return flatpak_fail (error, "%s: 64-bit DWARF not supported", data->filename); if (endcu > endsec) return flatpak_fail (error, "%s: .debug_info too small", data->filename); cu_version = read_16 (ptr); if (cu_version != 2 && cu_version != 3 && cu_version != 4) return flatpak_fail (error, "%s: DWARF version %d unhandled", data->filename, cu_version); value = read_32_relocated (ptr); if (value >= debug_sections[DEBUG_ABBREV].size) { if (debug_sections[DEBUG_ABBREV].data == NULL) return flatpak_fail (error, "%s: .debug_abbrev not present", data->filename); else return flatpak_fail (error, "%s: DWARF CU abbrev offset too large", data->filename); } if (ptr_size == 0) { ptr_size = read_1 (ptr); if (ptr_size != 4 && ptr_size != 8) return flatpak_fail (error, "%s: Invalid DWARF pointer size %d", data->filename, ptr_size); } else if (read_1 (ptr) != ptr_size) { return flatpak_fail (error, "%s: DWARF pointer size differs between CUs", data->filename); } abbrev = read_abbrev (data, debug_sections[DEBUG_ABBREV].data + value); while (ptr < endcu) { guint entry = read_uleb128 (ptr); if (entry == 0) continue; t = g_hash_table_lookup (abbrev, GINT_TO_POINTER (entry)); if (t == NULL) { g_warning ("%s: Could not find DWARF abbreviation %d", data->filename, entry); } else { ptr = handle_attributes (data, ptr, t, files, error); if (ptr == NULL) return FALSE; } } } } return TRUE; }
static gboolean install_bundle (FlatpakDir *dir, GOptionContext *context, int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GFile) file = NULL; const char *filename; g_autoptr(GBytes) gpg_data = NULL; g_autoptr(FlatpakTransaction) transaction = NULL; if (argc < 2) return usage_error (context, _("Bundle filename must be specified"), error); if (argc > 2) return usage_error (context, _("Too many arguments"), error); filename = argv[1]; file = g_file_new_for_commandline_arg (filename); if (!g_file_is_native (file)) return flatpak_fail (error, _("Remote bundles are not supported")); if (opt_gpg_file != NULL) { /* Override gpg_data from file */ gpg_data = read_gpg_data (cancellable, error); if (gpg_data == NULL) return FALSE; } if (opt_noninteractive) transaction = flatpak_quiet_transaction_new (dir, error); else transaction = flatpak_cli_transaction_new (dir, opt_yes, TRUE, error); if (transaction == NULL) return FALSE; flatpak_transaction_set_no_pull (transaction, opt_no_pull); flatpak_transaction_set_no_deploy (transaction, opt_no_deploy); flatpak_transaction_set_disable_static_deltas (transaction, opt_no_static_deltas); flatpak_transaction_set_disable_dependencies (transaction, opt_no_deps); flatpak_transaction_set_disable_related (transaction, opt_no_related); flatpak_transaction_set_reinstall (transaction, opt_reinstall); if (!flatpak_transaction_add_install_bundle (transaction, file, gpg_data, error)) return FALSE; if (!flatpak_transaction_run (transaction, cancellable, error)) { if (g_error_matches (*error, FLATPAK_ERROR, FLATPAK_ERROR_ABORTED)) { g_clear_error (error); return TRUE; } return FALSE; } return TRUE; }
static gboolean copy_extensions (FlatpakDeploy *src_deploy, const char *default_branch, char *src_extensions[], GFile *top_dir, GCancellable *cancellable, GError **error) { g_autoptr(GKeyFile) metakey = flatpak_deploy_get_metadata (src_deploy); GList *extensions = NULL, *l; int i; /* We leak this on failure, as we have no autoptr for deep lists.. */ extensions = flatpak_list_extensions (metakey, opt_arch, default_branch); for (i = 0; src_extensions[i] != NULL; i++) { const char *requested_extension = src_extensions[i]; gboolean found = FALSE; for (l = extensions; l != NULL; l = l->next) { FlatpakExtension *ext = l->data; if (strcmp (ext->installed_id, requested_extension) == 0 || strcmp (ext->id, requested_extension) == 0) { g_autoptr(GFile) target = g_file_resolve_relative_path (top_dir, ext->directory); g_autoptr(GFile) target_parent = g_file_get_parent (target); g_autoptr(GFile) ext_deploy_files = g_file_new_for_path (ext->files_path); if (!ext->is_unmaintained) { g_autoptr(FlatpakDir) src_dir = NULL; g_autoptr(GFile) deploy = NULL; g_autoptr(GVariant) deploy_data = NULL; const char **subpaths; deploy = flatpak_find_deploy_dir_for_ref (ext->ref, &src_dir, cancellable, error); if (deploy == NULL) return FALSE; deploy_data = flatpak_dir_get_deploy_data (src_dir, ext->ref, cancellable, error); if (deploy_data == NULL) return FALSE; subpaths = flatpak_deploy_data_get_subpaths (deploy_data); if (subpaths[0] != NULL) return flatpak_fail (error, _("Requested extension %s is only partially installed"), ext->installed_id); } if (!flatpak_mkdir_p (target_parent, cancellable, error)) return FALSE; /* An extension overrides whatever is there before, so we clean up first */ if (!flatpak_rm_rf (target, cancellable, error)) return FALSE; if (!flatpak_cp_a (ext_deploy_files, target, FLATPAK_CP_FLAGS_NO_CHOWN, cancellable, error)) return FALSE; found = TRUE; } } if (!found) { g_list_free_full (extensions, (GDestroyNotify) flatpak_extension_free); return flatpak_fail (error, _("Requested extension %s not installed"), requested_extension); } } g_list_free_full (extensions, (GDestroyNotify) flatpak_extension_free); return TRUE; }
gboolean flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GPtrArray) dirs = NULL; g_autoptr(FlatpakDir) dir = NULL; g_autofree char *remote = NULL; g_autofree char *remote_url = NULL; char **prefs = NULL; int i, n_prefs; g_autofree char *target_branch = NULL; g_autofree char *default_branch = NULL; FlatpakKinds kinds; g_autoptr(FlatpakTransaction) transaction = NULL; g_autoptr(FlatpakDir) dir_with_remote = NULL; gboolean auto_remote = FALSE; context = g_option_context_new (_("[LOCATION/REMOTE] [REF…] - Install applications or runtimes")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_ALL_DIRS | FLATPAK_BUILTIN_FLAG_OPTIONAL_REPO, &dirs, cancellable, error)) return FALSE; /* Start with the default or specified dir, this is fine for opt_bundle or opt_from */ dir = g_object_ref (g_ptr_array_index (dirs, 0)); if (!opt_bundle && !opt_from && argc >= 2) { if (flatpak_file_arg_has_suffix (argv[1], ".flatpakref")) opt_from = TRUE; if (flatpak_file_arg_has_suffix (argv[1], ".flatpak")) opt_bundle = TRUE; } if (opt_bundle) return install_bundle (dir, context, argc, argv, cancellable, error); if (opt_from) return install_from (dir, context, argc, argv, cancellable, error); if (argc < 2) return usage_error (context, _("At least one REF must be specified"), error); if (argc == 2) auto_remote = TRUE; kinds = flatpak_kinds_from_bools (opt_app, opt_runtime); if (!opt_noninteractive) g_print (_("Looking for matches…\n")); if (!auto_remote && (g_path_is_absolute (argv[1]) || g_str_has_prefix (argv[1], "./"))) { g_autoptr(GFile) remote_file = g_file_new_for_commandline_arg (argv[1]); remote_url = g_file_get_uri (remote_file); remote = g_strdup (remote_url); } else { g_autoptr(GError) local_error = NULL; /* If the remote was used, and no single dir was specified, find which * one based on the remote. If the remote isn't found assume it's a ref * and we should auto-detect the remote. */ if (!auto_remote && !flatpak_resolve_duplicate_remotes (dirs, argv[1], &dir_with_remote, cancellable, &local_error)) { if (g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_REMOTE_NOT_FOUND)) { auto_remote = TRUE; } else { g_propagate_error (error, g_steal_pointer (&local_error)); return FALSE; } } if (!auto_remote) { remote = g_strdup (argv[1]); g_clear_object (&dir); dir = g_object_ref (dir_with_remote); } else { g_autoptr(GPtrArray) remote_dir_pairs = NULL; RemoteDirPair *chosen_pair = NULL; remote_dir_pairs = g_ptr_array_new_with_free_func ((GDestroyNotify) remote_dir_pair_free); /* Search all remotes for a matching ref. This is imperfect * because it only takes the first specified ref into account and * doesn't distinguish between an exact match and a fuzzy match, but * that's okay because the user will be asked to confirm the remote */ for (i = 0; i < dirs->len; i++) { FlatpakDir *this_dir = g_ptr_array_index (dirs, i); g_auto(GStrv) remotes = NULL; guint j = 0; remotes = flatpak_dir_list_remotes (this_dir, cancellable, error); if (remotes == NULL) return FALSE; for (j = 0; remotes[j] != NULL; j++) { const char *this_remote = remotes[j]; g_autofree char *this_default_branch = NULL; g_autofree char *id = NULL; g_autofree char *arch = NULL; g_autofree char *branch = NULL; FlatpakKinds matched_kinds; g_auto(GStrv) refs = NULL; g_autoptr(GError) local_error = NULL; if (flatpak_dir_get_remote_disabled (this_dir, this_remote) || flatpak_dir_get_remote_noenumerate (this_dir, this_remote)) continue; this_default_branch = flatpak_dir_get_remote_default_branch (this_dir, this_remote); flatpak_split_partial_ref_arg_novalidate (argv[1], kinds, opt_arch, target_branch, &matched_kinds, &id, &arch, &branch); if (opt_no_pull) refs = flatpak_dir_find_local_refs (this_dir, this_remote, id, branch, this_default_branch, arch, flatpak_get_default_arch (), matched_kinds, FIND_MATCHING_REFS_FLAGS_FUZZY, cancellable, &local_error); else refs = flatpak_dir_find_remote_refs (this_dir, this_remote, id, branch, this_default_branch, arch, flatpak_get_default_arch (), matched_kinds, FIND_MATCHING_REFS_FLAGS_FUZZY, cancellable, &local_error); if (refs == NULL) { g_warning ("An error was encountered searching remote ‘%s’ for ‘%s’: %s", this_remote, argv[1], local_error->message); continue; } if (g_strv_length (refs) == 0) continue; else { RemoteDirPair *pair = remote_dir_pair_new (this_remote, this_dir); g_ptr_array_add (remote_dir_pairs, pair); } } } if (remote_dir_pairs->len == 0) return flatpak_fail (error, _("No remote refs found similar to ‘%s’"), argv[1]); if (!flatpak_resolve_matching_remotes (opt_yes, remote_dir_pairs, argv[1], &chosen_pair, error)) return FALSE; remote = g_strdup (chosen_pair->remote_name); g_clear_object (&dir); dir = g_object_ref (chosen_pair->dir); } } if (auto_remote) { prefs = &argv[1]; n_prefs = argc - 1; } else { prefs = &argv[2]; n_prefs = argc - 2; } /* Backwards compat for old "REMOTE NAME [BRANCH]" argument version */ if (argc == 4 && flatpak_is_valid_name (argv[2], NULL) && looks_like_branch (argv[3])) { target_branch = g_strdup (argv[3]); n_prefs = 1; } default_branch = flatpak_dir_get_remote_default_branch (dir, remote); if (opt_noninteractive) transaction = flatpak_quiet_transaction_new (dir, error); else transaction = flatpak_cli_transaction_new (dir, opt_yes, TRUE, error); if (transaction == NULL) return FALSE; flatpak_transaction_set_no_pull (transaction, opt_no_pull); flatpak_transaction_set_no_deploy (transaction, opt_no_deploy); flatpak_transaction_set_disable_static_deltas (transaction, opt_no_static_deltas); flatpak_transaction_set_disable_dependencies (transaction, opt_no_deps); flatpak_transaction_set_disable_related (transaction, opt_no_related); flatpak_transaction_set_reinstall (transaction, opt_reinstall); for (i = 0; i < n_prefs; i++) { const char *pref = prefs[i]; FlatpakKinds matched_kinds; g_autofree char *id = NULL; g_autofree char *arch = NULL; g_autofree char *branch = NULL; g_autofree char *ref = NULL; g_auto(GStrv) refs = NULL; guint refs_len; g_autoptr(GError) local_error = NULL; flatpak_split_partial_ref_arg_novalidate (pref, kinds, opt_arch, target_branch, &matched_kinds, &id, &arch, &branch); /* We used _novalidate so that the id can be partial, but we can still validate the branch */ if (branch != NULL && !flatpak_is_valid_branch (branch, &local_error)) return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Invalid branch %s: %s"), branch, local_error->message); if (opt_no_pull) refs = flatpak_dir_find_local_refs (dir, remote, id, branch, default_branch, arch, flatpak_get_default_arch (), matched_kinds, FIND_MATCHING_REFS_FLAGS_FUZZY, cancellable, error); else refs = flatpak_dir_find_remote_refs (dir, remote, id, branch, default_branch, arch, flatpak_get_default_arch (), matched_kinds, FIND_MATCHING_REFS_FLAGS_FUZZY, cancellable, error); if (refs == NULL) return FALSE; refs_len = g_strv_length (refs); if (refs_len == 0) { if (opt_no_pull) g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Nothing matches %s in local repository for remote %s"), id, remote); else g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Nothing matches %s in remote %s"), id, remote); return FALSE; } if (!flatpak_resolve_matching_refs (remote, dir, opt_yes, refs, id, &ref, error)) return FALSE; if (!flatpak_transaction_add_install (transaction, remote, ref, (const char **)opt_subpaths, error)) { if (!g_error_matches (*error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED)) return FALSE; g_printerr (_("Skipping: %s\n"), (*error)->message); g_clear_error (error); } } if (!flatpak_transaction_run (transaction, cancellable, error)) { if (g_error_matches (*error, FLATPAK_ERROR, FLATPAK_ERROR_ABORTED)) { g_clear_error (error); return TRUE; } return FALSE; } return TRUE; }