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;
}
Exemple #3
0
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;
}
Exemple #12
0
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");
Exemple #19
0
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;
}