Beispiel #1
0
static
gboolean do_lookup (GResource             *resource,
                    const gchar           *path,
                    GResourceLookupFlags   lookup_flags,
                    gsize                 *size,
                    guint32               *flags,
                    const void           **data,
                    gsize                 *data_size,
                    GError               **error)
{
  char *free_path = NULL;
  gsize path_len;
  gboolean res = FALSE;
  GVariant *value;

  path_len = strlen (path);
  if (path[path_len-1] == '/')
    {
      path = free_path = g_strdup (path);
      free_path[path_len-1] = 0;
    }

  value = gvdb_table_get_raw_value (resource->table, path);

  if (value == NULL)
    {
      g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
                   _("The resource at '%s' does not exist"),
                   path);
    }
  else
    {
      guint32 _size, _flags;
      GVariant *array;

      g_variant_get (value, "(uu@ay)",
                     &_size,
                     &_flags,
                     &array);

      _size = GUINT32_FROM_LE (_size);
      _flags = GUINT32_FROM_LE (_flags);

      if (size)
        *size = _size;
      if (flags)
        *flags = _flags;
      if (data)
        *data = g_variant_get_data (array);
      if (data_size)
        {
          /* Don't report trailing newline that non-compressed files has */
          if (_flags & G_RESOURCE_FLAGS_COMPRESSED)
            *data_size = g_variant_get_size (array);
          else
            *data_size = g_variant_get_size (array) - 1;
        }
      g_variant_unref (array);
      g_variant_unref (value);

      res = TRUE;
    }

  g_free (free_path);
  return res;
}
gboolean
_ostree_static_delta_part_open (GInputStream   *part_in,
                                GBytes         *inline_part_bytes,
                                OstreeStaticDeltaOpenFlags flags,
                                const char     *expected_checksum,
                                GVariant    **out_part,
                                GCancellable *cancellable,
                                GError      **error)
{
  const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0;
  const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0;
  gsize bytes_read;
  guint8 comptype;
  g_autoptr(GChecksum) checksum = NULL;
  g_autoptr(GInputStream) checksum_in = NULL;
  GInputStream *source_in;

  /* We either take a fd or a GBytes reference */
  g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (part_in) || inline_part_bytes != NULL, FALSE);
  g_return_val_if_fail (skip_checksum || expected_checksum != NULL, FALSE);

  if (!skip_checksum)
    {
      checksum = g_checksum_new (G_CHECKSUM_SHA256);
      checksum_in = (GInputStream*)ostree_checksum_input_stream_new (part_in, checksum);
      source_in = checksum_in;
    }
  else
    {
      source_in = part_in;
    }

  { guint8 buf[1];
    /* First byte is compression type */
    if (!g_input_stream_read_all (source_in, buf, sizeof(buf), &bytes_read,
                                  cancellable, error))
      return glnx_prefix_error (error, "Reading initial compression flag byte");
    comptype = buf[0];
  }

  g_autoptr(GVariant) ret_part = NULL;
  switch (comptype)
    {
    case 0:
      if (!inline_part_bytes)
        {
          int part_fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)part_in);

          /* No compression, no checksums - a fast path */
          if (!ot_util_variant_map_fd (part_fd, 1, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                       trusted, &ret_part, error))
            return FALSE;
        }
      else
        {
          g_autoptr(GBytes) content_bytes = g_bytes_new_from_bytes (inline_part_bytes, 1,
                                                                    g_bytes_get_size (inline_part_bytes) - 1);
          ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                               content_bytes, trusted);
          g_variant_ref_sink (ret_part);
        }

      if (!skip_checksum)
        g_checksum_update (checksum, g_variant_get_data (ret_part),
                           g_variant_get_size (ret_part));

      break;
    case 'x':
      {
        g_autoptr(GConverter) decomp = (GConverter*) _ostree_lzma_decompressor_new ();
        g_autoptr(GInputStream) convin = g_converter_input_stream_new (source_in, decomp);
        g_autoptr(GBytes) buf = ot_map_anonymous_tmpfile_from_content (convin, cancellable, error);
        if (!buf)
          return FALSE;

        ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                             buf, FALSE);
      }
      break;
    default:
      return glnx_throw (error, "Invalid compression type '%u'", comptype);
    }

  if (checksum)
    {
      const char *actual_checksum = g_checksum_get_string (checksum);
      g_assert (expected_checksum != NULL);
      if (strcmp (actual_checksum, expected_checksum) != 0)
        return glnx_throw (error, "Checksum mismatch in static delta part; expected=%s actual=%s",
                           expected_checksum, actual_checksum);
    }

  *out_part = g_steal_pointer (&ret_part);
  return TRUE;
}
OstreeDeltaEndianness
_ostree_delta_get_endianness (GVariant *superblock,
                              gboolean *out_was_heuristic)
{
    guint8 endianness_char;
    g_autoptr(GVariant) delta_meta = NULL;
    g_autoptr(GVariantDict) delta_metadict = NULL;
    guint64 total_size = 0;
    guint64 total_usize = 0;
    guint total_objects = 0;

    delta_meta = g_variant_get_child_value (superblock, 0);
    delta_metadict = g_variant_dict_new (delta_meta);

    if (out_was_heuristic)
        *out_was_heuristic = FALSE;

    if (g_variant_dict_lookup (delta_metadict, "ostree.endianness", "y", &endianness_char))
    {
        switch (endianness_char)
        {
        case 'l':
            return OSTREE_DELTA_ENDIAN_LITTLE;
        case 'B':
            return OSTREE_DELTA_ENDIAN_BIG;
        default:
            return OSTREE_DELTA_ENDIAN_INVALID;
        }
    }

    if (out_was_heuristic)
        *out_was_heuristic = TRUE;

    {   g_autoptr(GVariant) meta_entries = NULL;
        guint n_parts;
        guint i;
        gboolean is_byteswapped = FALSE;

        g_variant_get_child (superblock, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries);
        n_parts = g_variant_n_children (meta_entries);

        for (i = 0; i < n_parts; i++)
        {
            g_autoptr(GVariant) objects = NULL;
            guint64 size, usize;
            guint n_objects;

            g_variant_get_child (meta_entries, i, "(u@aytt@ay)", NULL, NULL, &size, &usize, &objects);
            n_objects = (guint)(g_variant_get_size (objects) / OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN);

            total_objects += n_objects;
            total_size += size;
            total_usize += usize;

            if (size > usize)
            {
                double ratio = ((double)size)/((double)usize);

                /* This should really never happen where compressing things makes it more than 50% bigger.
                 */
                if (ratio > 1.2)
                {
                    is_byteswapped = TRUE;
                    break;
                }
            }
        }

        if (!is_byteswapped)
        {
            /* If the average object size is greater than 4GiB, let's assume
             * we're dealing with opposite endianness.  I'm fairly confident
             * no one is going to be shipping peta- or exa- byte size ostree
             * deltas, period.  Past the gigabyte scale you really want
             * bittorrent or something.
             */
            if ((total_size / total_objects) > G_MAXUINT32)
            {
                is_byteswapped = TRUE;
            }
        }

        if (is_byteswapped)
        {
            switch (G_BYTE_ORDER)
            {
            case G_BIG_ENDIAN:
                return OSTREE_DELTA_ENDIAN_LITTLE;
            case G_LITTLE_ENDIAN:
                return OSTREE_DELTA_ENDIAN_BIG;
            default:
                g_assert_not_reached ();
            }
        }

        return G_BYTE_ORDER;
    }
}
gboolean
_ostree_repo_static_delta_dump (OstreeRepo                    *self,
                                const char                    *delta_id,
                                GCancellable                  *cancellable,
                                GError                      **error)
{
    gboolean ret = FALSE;
    g_autofree char *from = NULL;
    g_autofree char *to = NULL;
    g_autofree char *superblock_path = NULL;
    glnx_fd_close int superblock_fd = -1;
    g_autoptr(GVariant) delta_superblock = NULL;
    guint64 total_size = 0, total_usize = 0;
    guint64 total_fallback_size = 0, total_fallback_usize = 0;
    guint i;
    OstreeDeltaEndianness endianness;
    gboolean swap_endian = FALSE;

    _ostree_parse_delta_name (delta_id, &from, &to);
    superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to);

    if (!ot_util_variant_map_at (self->repo_dir_fd, superblock_path,
                                 (GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
                                 OT_VARIANT_MAP_TRUSTED, &delta_superblock, error))
        goto out;

    g_print ("%s\n", g_variant_print (delta_superblock, 1));

    g_print ("Delta: %s\n", delta_id);
    {   const char *endianness_description;
        gboolean was_heuristic;

        endianness = _ostree_delta_get_endianness (delta_superblock, &was_heuristic);

        switch (endianness)
        {
        case OSTREE_DELTA_ENDIAN_BIG:
            if (was_heuristic)
                endianness_description = "big (heuristic)";
            else
                endianness_description = "big";
            if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
                swap_endian = TRUE;
            break;
        case OSTREE_DELTA_ENDIAN_LITTLE:
            if (was_heuristic)
                endianness_description = "little (heuristic)";
            else
                endianness_description = "little";
            if (G_BYTE_ORDER == G_BIG_ENDIAN)
                swap_endian = TRUE;
            break;
        case OSTREE_DELTA_ENDIAN_INVALID:
            endianness_description = "invalid";
            break;
        default:
            g_assert_not_reached ();
        }

        g_print ("Endianness: %s\n", endianness_description);
    }
    {   guint64 ts;
        g_variant_get_child (delta_superblock, 1, "t", &ts);
        g_print ("Timestamp: %" G_GUINT64_FORMAT "\n", GUINT64_FROM_BE (ts));
    }
    {   g_autoptr(GVariant) recurse = NULL;
        g_variant_get_child (delta_superblock, 5, "@ay", &recurse);
        g_print ("Number of parents: %u\n", (guint)(g_variant_get_size (recurse) / (OSTREE_SHA256_DIGEST_LEN * 2)));
    }
    {   g_autoptr(GVariant) fallback = NULL;
        guint n_fallback;

        g_variant_get_child (delta_superblock, 7, "@a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT, &fallback);
        n_fallback = g_variant_n_children (fallback);

        g_print ("Number of fallback entries: %u\n", n_fallback);

        for (i = 0; i < n_fallback; i++)
        {
            guint64 size, usize;
            g_variant_get_child (fallback, i, "(y@aytt)", NULL, NULL, &size, &usize);
            size = maybe_swap_endian_u64 (swap_endian, size);
            usize = maybe_swap_endian_u64 (swap_endian, usize);
            total_fallback_size += size;
            total_fallback_usize += usize;
        }
        {   g_autofree char *sizestr = g_format_size (total_fallback_size);
            g_autofree char *usizestr = g_format_size (total_fallback_usize);
            g_print ("Total Fallback Size: %" G_GUINT64_FORMAT " (%s)\n", total_fallback_size, sizestr);
            g_print ("Total Fallback Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", total_fallback_usize, usizestr);
        }
    }
    {   g_autoptr(GVariant) meta_entries = NULL;
        guint n_parts;

        g_variant_get_child (delta_superblock, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries);
        n_parts = g_variant_n_children (meta_entries);
        g_print ("Number of parts: %u\n", n_parts);

        for (i = 0; i < n_parts; i++)
        {
            if (!show_one_part (self, swap_endian, from, to, meta_entries, i,
                                &total_size, &total_usize,
                                cancellable, error))
                goto out;
        }
    }

    {   g_autofree char *sizestr = g_format_size (total_size);
        g_autofree char *usizestr = g_format_size (total_usize);
        g_print ("Total Part Size: %" G_GUINT64_FORMAT " (%s)\n", total_size, sizestr);
        g_print ("Total Part Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", total_usize, usizestr);
    }
    {   guint64 overall_size = total_size + total_fallback_size;
        guint64 overall_usize = total_usize + total_fallback_usize;
        g_autofree char *sizestr = g_format_size (overall_size);
        g_autofree char *usizestr = g_format_size (overall_usize);
        g_print ("Total Size: %" G_GUINT64_FORMAT " (%s)\n", overall_size, sizestr);
        g_print ("Total Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", overall_usize, usizestr);
    }

    ret = TRUE;
out:
    return ret;
}
static gboolean
show_one_part (OstreeRepo                    *self,
               gboolean                       swap_endian,
               const char                    *from,
               const char                    *to,
               GVariant                      *meta_entries,
               guint                          i,
               guint64                       *total_size_ref,
               guint64                       *total_usize_ref,
               GCancellable                  *cancellable,
               GError                      **error)
{
    gboolean ret = FALSE;
    guint32 version;
    guint64 size, usize;
    g_autoptr(GVariant) objects = NULL;
    g_autoptr(GInputStream) part_in = NULL;
    g_autoptr(GVariant) part = NULL;
    g_autofree char *part_path = _ostree_get_relative_static_delta_part_path (from, to, i);
    gint part_fd = -1;

    g_variant_get_child (meta_entries, i, "(u@aytt@ay)", &version, NULL, &size, &usize, &objects);
    size = maybe_swap_endian_u64 (swap_endian, size);
    usize = maybe_swap_endian_u64 (swap_endian, usize);
    *total_size_ref += size;
    *total_usize_ref += usize;
    g_print ("PartMeta%u: nobjects=%u size=%" G_GUINT64_FORMAT " usize=%" G_GUINT64_FORMAT "\n",
             i, (guint)(g_variant_get_size (objects) / OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN), size, usize);

    part_fd = openat (self->repo_dir_fd, part_path, O_RDONLY | O_CLOEXEC);
    if (part_fd < 0)
    {
        glnx_set_error_from_errno (error);
        goto out;
    }

    part_in = g_unix_input_stream_new (part_fd, FALSE);

    if (!_ostree_static_delta_part_open (part_in, NULL,
                                         OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM,
                                         NULL,
                                         &part,
                                         cancellable, error))
        goto out;

    {   g_autoptr(GVariant) modes = NULL;
        g_autoptr(GVariant) xattrs = NULL;
        g_autoptr(GVariant) blob = NULL;
        g_autoptr(GVariant) ops = NULL;
        OstreeDeltaExecuteStats stats = { { 0, }, };

        g_variant_get (part, "(@a(uuu)@aa(ayay)@ay@ay)",
                       &modes, &xattrs, &blob, &ops);

        g_print ("PartPayload%u: nmodes=%" G_GUINT64_FORMAT
                 " nxattrs=%" G_GUINT64_FORMAT
                 " blobsize=%" G_GUINT64_FORMAT
                 " opsize=%" G_GUINT64_FORMAT
                 "\n",
                 i,
                 (guint64)g_variant_n_children (modes),
                 (guint64)g_variant_n_children (xattrs),
                 (guint64)g_variant_n_children (blob),
                 (guint64)g_variant_n_children (ops));

        if (!_ostree_static_delta_part_execute (self, objects,
                                                part, TRUE, TRUE,
                                                &stats, cancellable, error))
            goto out;

        {   const guint *n_ops = stats.n_ops_executed;
            g_print ("PartPayloadOps%u: openspliceclose=%u open=%u write=%u setread=%u "
                     "unsetread=%u close=%u bspatch=%u\n",
                     i, n_ops[0], n_ops[1], n_ops[2], n_ops[3], n_ops[4], n_ops[5], n_ops[6]);
        }
    }

    ret = TRUE;
out:
    return ret;
}
gboolean
_ostree_static_delta_part_open (GInputStream   *part_in,
                                GBytes         *inline_part_bytes,
                                OstreeStaticDeltaOpenFlags flags,
                                const char     *expected_checksum,
                                GVariant    **out_part,
                                GCancellable *cancellable,
                                GError      **error)
{
    gboolean ret = FALSE;
    const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0;
    const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0;
    gsize bytes_read;
    guint8 comptype;
    g_autoptr(GChecksum) checksum = NULL;
    g_autoptr(GInputStream) checksum_in = NULL;
    g_autoptr(GVariant) ret_part = NULL;
    GInputStream *source_in;

    /* We either take a fd or a GBytes reference */
    g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (part_in) || inline_part_bytes != NULL, FALSE);
    g_return_val_if_fail (skip_checksum || expected_checksum != NULL, FALSE);

    if (!skip_checksum)
    {
        checksum = g_checksum_new (G_CHECKSUM_SHA256);
        checksum_in = (GInputStream*)ostree_checksum_input_stream_new (part_in, checksum);
        source_in = checksum_in;
    }
    else
    {
        source_in = part_in;
    }

    {   guint8 buf[1];
        /* First byte is compression type */
        if (!g_input_stream_read_all (source_in, buf, sizeof(buf), &bytes_read,
                                      cancellable, error))
        {
            g_prefix_error (error, "Reading initial compression flag byte: ");
            goto out;
        }
        comptype = buf[0];
    }

    switch (comptype)
    {
    case 0:
        if (!inline_part_bytes)
        {
            int part_fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)part_in);

            /* No compression, no checksums - a fast path */
            if (!ot_util_variant_map_fd (part_fd, 1, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                         trusted, &ret_part, error))
                goto out;
        }
        else
        {
            g_autoptr(GBytes) content_bytes = g_bytes_new_from_bytes (inline_part_bytes, 1,
                                              g_bytes_get_size (inline_part_bytes) - 1);
            ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                                 content_bytes, trusted);
            g_variant_ref_sink (ret_part);
        }

        if (!skip_checksum)
            g_checksum_update (checksum, g_variant_get_data (ret_part),
                               g_variant_get_size (ret_part));

        break;
    case 'x':
    {
        g_autofree char *tmppath = g_strdup ("/var/tmp/ostree-delta-XXXXXX");
        g_autoptr(GConverter) decomp = (GConverter*) _ostree_lzma_decompressor_new ();
        g_autoptr(GInputStream) convin = g_converter_input_stream_new (source_in, decomp);
        g_autoptr(GOutputStream) unpacked_out = NULL;
        glnx_fd_close int unpacked_fd = -1;
        gssize n_bytes_written;

        unpacked_fd = g_mkstemp_full (tmppath, O_RDWR | O_CLOEXEC, 0640);
        if (unpacked_fd < 0)
        {
            glnx_set_error_from_errno (error);
            goto out;
        }

        /* Now make it autocleanup on process exit - in the future, we
         * should consider caching unpacked deltas as well.
         */
        if (unlink (tmppath) < 0)
        {
            glnx_set_error_from_errno (error);
            goto out;
        }

        unpacked_out = g_unix_output_stream_new (unpacked_fd, FALSE);

        n_bytes_written = g_output_stream_splice (unpacked_out, convin,
                          G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
                          G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
                          cancellable, error);
        if (n_bytes_written < 0)
            goto out;

        if (!ot_util_variant_map_fd (unpacked_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                     trusted, &ret_part, error))
            goto out;
    }
    break;
    default:
        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                     "Invalid compression type '%u'", comptype);
        goto out;
    }

    if (checksum)
    {
        const char *actual_checksum = g_checksum_get_string (checksum);
        g_assert (expected_checksum != NULL);
        if (strcmp (actual_checksum, expected_checksum) != 0)
        {
            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                         "Checksum mismatch in static delta part; expected=%s actual=%s",
                         expected_checksum, actual_checksum);
            goto out;
        }
    }

    ret = TRUE;
    *out_part = g_steal_pointer (&ret_part);
out:
    return ret;
}
Beispiel #7
0
static gboolean
handle_configure_remote (FlatpakSystemHelper *object,
                         GDBusMethodInvocation *invocation,
                         guint arg_flags,
                         const gchar *arg_remote,
                         const gchar *arg_config,
                         GVariant *arg_gpg_key)
{
  g_autoptr(FlatpakDir) system = dir_get_system ();
  g_autoptr(GError) error = NULL;
  g_autoptr(GKeyFile) config = g_key_file_new ();
  g_autofree char *group = g_strdup_printf ("remote \"%s\"", arg_remote);
  g_autoptr(GBytes) gpg_data = NULL;
  gboolean force_remove;

  g_debug ("ConfigureRemote %u %s", arg_flags, arg_remote);

  if (*arg_remote == 0 || strchr (arg_remote, '/') != NULL)
    {
      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                             "Invalid remote name: %s", arg_remote);
      return TRUE;
    }

  if ((arg_flags & ~FLATPAK_HELPER_CONFIGURE_REMOTE_FLAGS_ALL) != 0)
    {
      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                             "Unsupported flags enabled: 0x%x", (arg_flags & ~FLATPAK_HELPER_CONFIGURE_REMOTE_FLAGS_ALL));
      return TRUE;
    }

  if (!g_key_file_load_from_data (config, arg_config, strlen (arg_config),
                                  G_KEY_FILE_NONE, &error))
    {
      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                             "Invalid config: %s\n", error->message);
      return TRUE;
    }

  if (!flatpak_dir_ensure_repo (system, NULL, &error))
    {
      g_dbus_method_invocation_return_gerror  (invocation, error);
      return TRUE;
    }

  if (g_variant_get_size (arg_gpg_key) > 0)
    gpg_data = g_variant_get_data_as_bytes (arg_gpg_key);

  force_remove = (arg_flags & FLATPAK_HELPER_CONFIGURE_REMOTE_FLAGS_FORCE_REMOVE) != 0;

  if (g_key_file_has_group (config, group))
    {
      /* Add/Modify */
      if (!flatpak_dir_modify_remote (system, arg_remote, config,
                                      gpg_data, NULL, &error))
        {
          g_dbus_method_invocation_return_gerror  (invocation, error);
          return TRUE;
        }
    }
  else
    {
      /* Remove */
      if (!flatpak_dir_remove_remote (system,
                                      force_remove,
                                      arg_remote,
                                      NULL, &error))
        {
          g_dbus_method_invocation_return_gerror  (invocation, error);
          return TRUE;
        }
    }

  flatpak_system_helper_complete_configure_remote (object, invocation);

  return TRUE;
}
gboolean
_ostree_static_delta_part_execute_raw (OstreeRepo      *repo,
                                       GVariant        *objects,
                                       GVariant        *part,
                                       GCancellable    *cancellable,
                                       GError         **error)
{
  gboolean ret = FALSE;
  guint8 *checksums_data;
  g_autoptr(GVariant) checksums = NULL;
  g_autoptr(GVariant) mode_dict = NULL;
  g_autoptr(GVariant) xattr_dict = NULL;
  g_autoptr(GVariant) payload = NULL;
  g_autoptr(GVariant) ops = NULL;
  StaticDeltaExecutionState statedata = { 0, };
  StaticDeltaExecutionState *state = &statedata;
  guint n_executed = 0;

  state->repo = repo;
  state->async_error = error;

  if (!_ostree_static_delta_parse_checksum_array (objects,
                                                  &checksums_data,
                                                  &state->n_checksums,
                                                  error))
    goto out;

  state->checksums = checksums_data;
  g_assert (state->n_checksums > 0);

  g_variant_get (part, "(@a(uuu)@aa(ayay)@ay@ay)",
                 &mode_dict,
                 &xattr_dict,
                 &payload, &ops);

  state->mode_dict = mode_dict;
  state->xattr_dict = xattr_dict;

  state->payload_data = g_variant_get_data (payload);
  state->payload_size = g_variant_get_size (payload);

  state->oplen = g_variant_n_children (ops);
  state->opdata = g_variant_get_data (ops);

  while (state->oplen > 0)
    {
      guint8 opcode;

      opcode = state->opdata[0];
      state->oplen--;
      state->opdata++;

      switch (opcode)
        {
        case OSTREE_STATIC_DELTA_OP_OPEN_SPLICE_AND_CLOSE:
          if (!dispatch_open_splice_and_close (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_OPEN:
          if (!dispatch_open (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_WRITE:
          if (!dispatch_write (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_SET_READ_SOURCE:
          if (!dispatch_set_read_source (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_UNSET_READ_SOURCE:
          if (!dispatch_unset_read_source (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_CLOSE:
          if (!dispatch_close (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_BSPATCH:
          if (!dispatch_bspatch (repo, state, cancellable, error))
            goto out;
          break;
        default:
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                       "Unknown opcode %u at offset %u", opcode, n_executed);
          goto out;
        }

      n_executed++;
    }

  if (state->caught_error)
    goto out;

  ret = TRUE;
 out:
  return ret;
}
static void
write_translations_dictionary (GList * licenses, const gchar * dict_filename)
{
  /* maps C string => (dictionary of: locale => translation) */
  GVariantBuilder array;
  /* maps C string => boolean (if it's in the dictionary already */
  GHashTable *translations;
  GVariant *var;
  GList *l;
  FILE *f;

  /* sort langs for prettiness / to make variant dumps easier to read */
  langs = g_list_sort (langs, (GCompareFunc) strcmp);

  g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);

  translations = g_hash_table_new (g_str_hash, g_str_equal);

  for (l = licenses; l != NULL; l = l->next) {
    const gchar *en;
    License *license;

    license = l->data;

    if (license->packed_into_source)
      continue;

    /* add title + translations */
    en = g_hash_table_lookup (license->titles, "en");
    g_assert (en != NULL);

    /* check if we already have added translations for this string */
    if (!g_hash_table_lookup (translations, (gpointer) en)) {
      GVariant *trans;

      trans = create_translation_dict (license->titles, en);
      if (trans != NULL) {
        g_variant_builder_add_value (&array,
            g_variant_new_dict_entry (g_variant_new_string (en), trans));
        g_hash_table_insert (translations, (gpointer) en,
            GINT_TO_POINTER (TRUE));
      }
    }

    /* add description + translations */
    if (license->descriptions == NULL)
      continue;

    en = g_hash_table_lookup (license->descriptions, "en");
    g_assert (en != NULL);

    /* check if we already have added translations for this string */
    if (!g_hash_table_lookup (translations, (gpointer) en)) {
      GVariant *trans;

      trans = create_translation_dict (license->descriptions, en);
      if (trans != NULL) {
        g_variant_builder_add_value (&array,
            g_variant_new_dict_entry (g_variant_new_string (en), trans));
        g_hash_table_insert (translations, (gpointer) en,
            GINT_TO_POINTER (TRUE));
      }
    }
  }

  var = g_variant_builder_end (&array);

  f = fopen (dict_filename, "wb");
  if (fwrite (g_variant_get_data (var), g_variant_get_size (var), 1, f) != 1) {
    g_error ("failed to write dict to file: %s", g_strerror (errno));
  }
  fclose (f);

  g_printerr ("Wrote dictionary to %s, size: %u, type: %s\n", dict_filename,
      (guint) g_variant_get_size (var), (gchar *) g_variant_get_type (var));

  g_variant_unref (var);
  g_hash_table_destroy (translations);
}