Support cabinet archives files with more than one firmware

This allows a vendor to upload a single file that targets different versions of
the same hardware. If this feature is used, the metainfo.xml files *must* have
something like <checksum target="content" filename="firmware2.rom"/> inside the
latest <release> tag.
This commit is contained in:
Richard Hughes 2015-10-07 17:43:10 +01:00
parent 9e1768940d
commit 5d14deff4a
14 changed files with 329 additions and 1218 deletions

View File

@ -119,7 +119,7 @@ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.45.8 gobject-2.0 gthread-2.0 gio-2.0 >= 2.
PKG_CHECK_MODULES(GUDEV, gudev-1.0)
PKG_CHECK_MODULES(POLKIT, polkit-gobject-1 >= 0.103)
PKG_CHECK_MODULES(GCAB, libgcab-1.0)
PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= 0.5.1)
PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= 0.5.2)
PKG_CHECK_MODULES(GUSB, gusb >= 0.2.2)
PKG_CHECK_MODULES(SQLITE, sqlite3)
PKG_CHECK_MODULES(ARCHIVE, libarchive)

View File

@ -83,8 +83,6 @@ pkglibexec_PROGRAMS = \
fwupd_SOURCES = \
fu-cleanup.h \
fu-cab.c \
fu-cab.h \
fu-debug.c \
fu-debug.h \
fu-device.c \
@ -151,8 +149,6 @@ check_PROGRAMS = \
fu-self-test
fu_self_test_SOURCES = \
fu-cab.c \
fu-cab.h \
fu-device.c \
fu-device.h \
fu-keyring.c \

View File

@ -1,763 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <fwupd.h>
#include <appstream-glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <libgcab.h>
#include <glib/gstdio.h>
#include <gio/gunixinputstream.h>
#include "fu-cleanup.h"
#include "fu-cab.h"
#include "fu-keyring.h"
static void fu_cab_finalize (GObject *object);
/**
* FuCabPrivate:
*
* Private #FuCab data
**/
typedef struct {
GCabCabinet *gcab;
GInputStream *cab_stream;
GKeyFile *inf_kf;
FwupdTrustFlags trust_flags;
gchar *firmware_basename;
gchar *firmware_filename;
gchar *signature_basename;
gchar *cat_basename;
gchar *description;
gchar *guid;
gchar *inf_basename;
gchar *metainfo_basename;
gchar *tmp_path;
gchar *license;
gchar *name;
gchar *summary;
gchar *url_homepage;
gchar *vendor;
gchar *version;
guint64 size;
GPtrArray *basenames_to_delete;
GPtrArray *filelist; /* with full path */
} FuCabPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (FuCab, fu_cab, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fu_cab_get_instance_private (o))
typedef struct {
FuCab *cab;
FuCabExtractFlags flags;
} FuCabExtractHelper;
/**
* fu_cab_add_file:
**/
void
fu_cab_add_file (FuCab *cab, const gchar *filename)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
const gchar *tmp;
guint i;
g_autofree gchar *basename = NULL;
/* check the same basename does not already exist */
basename = g_path_get_basename (filename);
for (i = 0; i < priv->filelist->len; i++) {
g_autofree gchar *basename_tmp = NULL;
tmp = g_ptr_array_index (priv->filelist, i);
basename_tmp = g_path_get_basename (tmp);
if (g_strcmp0 (basename_tmp, basename) == 0) {
g_debug ("%s already exists, removing old", basename_tmp);
g_ptr_array_remove_index (priv->filelist, i);
break;
}
}
/* add the full filename */
g_ptr_array_add (priv->filelist, g_strdup (filename));
}
/**
* fu_cab_read_file_list_cb:
**/
static gboolean
fu_cab_read_file_list_cb (GCabFile *file, gpointer user_data)
{
FuCab *cab = FU_CAB (user_data);
FuCabPrivate *priv = GET_PRIVATE (cab);
g_ptr_array_add (priv->filelist,
g_build_filename (priv->tmp_path,
gcab_file_get_name (file),
NULL));
return FALSE;
}
/**
* fu_cab_match_basename_flag:
**/
static FuCabExtractFlags
fu_cab_match_basename_flag (FuCab *cab, const gchar *basename)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
if (g_strcmp0 (basename, priv->firmware_basename) == 0)
return FU_CAB_EXTRACT_FLAG_FIRMWARE;
if (g_strcmp0 (basename, priv->signature_basename) == 0)
return FU_CAB_EXTRACT_FLAG_SIGNATURE;
if (g_strcmp0 (basename, priv->inf_basename) == 0)
return FU_CAB_EXTRACT_FLAG_INF;
if (g_strcmp0 (basename, priv->metainfo_basename) == 0)
return FU_CAB_EXTRACT_FLAG_METAINFO;
if (g_strcmp0 (basename, priv->cat_basename) == 0)
return FU_CAB_EXTRACT_FLAG_CATALOG;
return FU_CAB_EXTRACT_FLAG_UNKNOWN;
}
/**
* fu_cab_extract_cb:
**/
static gboolean
fu_cab_extract_cb (GCabFile *file, gpointer user_data)
{
FuCabExtractHelper *helper = (FuCabExtractHelper *) user_data;
FuCab *cab = FU_CAB (helper->cab);
FuCabPrivate *priv = GET_PRIVATE (cab);
FuCabExtractFlags match_flags;
/* only if it matches the mask */
match_flags = fu_cab_match_basename_flag (cab, gcab_file_get_name (file));
if ((helper->flags & match_flags) > 0) {
g_ptr_array_add (priv->basenames_to_delete,
g_strdup (gcab_file_get_name (file)));
return TRUE;
}
return helper->flags == FU_CAB_EXTRACT_FLAG_ALL;
}
/**
* fu_cab_parse:
**/
static gboolean
fu_cab_parse (FuCab *cab, GError **error)
{
AsRelease *rel;
FuCabPrivate *priv = GET_PRIVATE (cab);
GString *update_description;
const gchar *tmp;
guint i;
const gchar *fn;
g_autoptr(GError) error_local = NULL;
g_autofree gchar *inf_filename = NULL;
g_autoptr(AsApp) app = NULL;
g_autoptr(GFile) path = NULL;
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
/* open the file */
priv->gcab = gcab_cabinet_new ();
if (!gcab_cabinet_load (priv->gcab, priv->cab_stream, NULL, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"cannot load .cab file: %s",
error_local->message);
return FALSE;
}
/* decompress to /tmp */
priv->tmp_path = g_dir_make_tmp ("fwupd-XXXXXX", &error_local);
if (priv->tmp_path == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"failed to create temp dir: %s",
error_local->message);
return FALSE;
}
/* get the file list */
path = g_file_new_for_path (priv->tmp_path);
if (!gcab_cabinet_extract_simple (priv->gcab, path,
fu_cab_read_file_list_cb,
cab, NULL, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"failed to extract .cab file: %s",
error_local->message);
return FALSE;
}
/* find the .inf file in the file list */
for (i = 0; i < priv->filelist->len; i++) {
fn = g_ptr_array_index (priv->filelist, i);
if (g_str_has_suffix (fn, ".inf")) {
priv->inf_basename = g_path_get_basename (fn);
continue;
}
if (g_str_has_suffix (fn, ".metainfo.xml")) {
priv->metainfo_basename = g_path_get_basename (fn);
continue;
}
}
/* read .inf file */
if (priv->inf_basename == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no .inf file in.cab file");
return FALSE;
}
/* extract these */
if (!fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_INF |
FU_CAB_EXTRACT_FLAG_METAINFO, error))
return FALSE;
/* parse it */
app = as_app_new ();
inf_filename = g_build_filename (priv->tmp_path, priv->inf_basename, NULL);
if (!as_app_parse_file (app, inf_filename,
AS_APP_PARSE_FLAG_NONE, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"%s could not be loaded: %s",
inf_filename, error_local->message);
return FALSE;
}
/* merge with the metainfo file */
if (priv->metainfo_basename != NULL) {
g_autofree gchar *metainfo_filename = NULL;
g_autoptr(AsApp) app2 = NULL;
app2 = as_app_new ();
metainfo_filename = g_build_filename (priv->tmp_path,
priv->metainfo_basename, NULL);
if (!as_app_parse_file (app2, metainfo_filename,
AS_APP_PARSE_FLAG_NONE, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"%s could not be loaded: %s",
metainfo_filename,
error_local->message);
return FALSE;
}
as_app_subsume_full (app, app2, AS_APP_SUBSUME_FLAG_NONE);
}
/* extract info */
update_description = g_string_new ("");
priv->guid = g_strdup (as_app_get_id (app));
priv->vendor = g_strdup (as_app_get_developer_name (app, NULL));
priv->name = g_strdup (as_app_get_name (app, NULL));
priv->summary = g_strdup (as_app_get_comment (app, NULL));
tmp = as_app_get_description (app, NULL);
if (tmp != NULL)
g_string_append (update_description, tmp);
priv->url_homepage = g_strdup (as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE));
priv->license = g_strdup (as_app_get_project_license (app));
rel = as_app_get_release_default (app);
priv->version = g_strdup (as_release_get_version (rel));
tmp = as_release_get_description (rel, NULL);
if (tmp != NULL)
g_string_append (update_description, tmp);
priv->description = g_string_free (update_description, FALSE);
/* optional */
tmp = as_app_get_metadata_item (app, "CatalogBasename");
if (tmp != NULL)
priv->cat_basename = g_strdup (tmp);
/* find out what firmware file we have to open */
tmp = as_app_get_metadata_item (app, "FirmwareBasename");
priv->firmware_basename = g_strdup (tmp);
priv->firmware_filename = g_build_filename (priv->tmp_path, tmp, NULL);
priv->signature_basename = g_strdup_printf ("%s.asc", tmp);
/* success */
return TRUE;
}
/**
* fu_cab_load_fd:
**/
gboolean
fu_cab_load_fd (FuCab *cab, gint fd, GCancellable *cancellable, GError **error)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_autoptr(GError) error_local = NULL;
g_autoptr(GInputStream) stream = NULL;
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
/* we can't get the size of the files in the .cab file, so just return
* the size of the cab file itself, on the logic that the firmware will
* be the largest thing by far, and typically be uncompressable */
priv->size = 0;
/* GCab needs a GSeekable input stream, so buffer to RAM then load */
stream = g_unix_input_stream_new (fd, TRUE);
priv->cab_stream = g_memory_input_stream_new ();
while (1) {
g_autoptr(GBytes) data = NULL;
data = g_input_stream_read_bytes (stream, 8192,
cancellable,
&error_local);
if (g_bytes_get_size (data) == 0)
break;
if (data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
error_local->message);
return FALSE;
}
priv->size += g_bytes_get_size (data);
g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (priv->cab_stream), data);
}
/* parse */
return fu_cab_parse (cab, error);
}
/**
* fu_cab_save_file:
**/
gboolean
fu_cab_save_file (FuCab *cab, GFile *file, GCancellable *cancellable, GError **error)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
const gchar *tmp;
guint i;
_cleanup_object_unref_ GCabCabinet *gcab = NULL;
_cleanup_object_unref_ GCabFolder *folder = NULL;
g_autoptr(GOutputStream) stream = NULL;
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
/* ensure all files are decompressed */
if (!fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_ALL, error))
return FALSE;
/* create a new archive, we can't reuse the existing instance */
gcab = gcab_cabinet_new ();
folder = gcab_folder_new (GCAB_COMPRESSION_NONE);
for (i = 0; i < priv->filelist->len; i++) {
g_autofree gchar *name = NULL;
_cleanup_object_unref_ GCabFile *gfile = NULL;
g_autoptr(GFile) file_tmp = NULL;
/* only write basename as name */
tmp = g_ptr_array_index (priv->filelist, i);
name = g_path_get_basename (tmp);
/* add each file in turn */
file_tmp = g_file_new_for_path (tmp);
gfile = gcab_file_new_with_file (name, file_tmp);
if (!gcab_folder_add_file (folder, gfile, FALSE,
cancellable, error))
return FALSE;
}
if (!gcab_cabinet_add_folder (gcab, folder, error))
return FALSE;
/* write in one chunk */
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
0, NULL, error));
if (stream == NULL)
return FALSE;
if (!gcab_cabinet_write_simple (gcab, stream,
NULL, NULL,
cancellable, error))
return FALSE;
return TRUE;
}
/**
* fu_cab_load_file:
**/
gboolean
fu_cab_load_file (FuCab *cab, GFile *file, GCancellable *cancellable, GError **error)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_autoptr(GError) error_local = NULL;
g_autoptr(GFileInfo) info = NULL;
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
/* get size */
info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE, cancellable, &error_local);
if (info == NULL) {
g_autofree gchar *filename = NULL;
filename = g_file_get_path (file);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to get info for %s: %s",
filename, error_local->message);
return FALSE;
}
priv->size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
/* open file */
priv->cab_stream = G_INPUT_STREAM (g_file_read (file, cancellable, &error_local));
if (priv->cab_stream == NULL) {
g_autofree gchar *filename = NULL;
filename = g_file_get_path (file);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to open %s: %s",
filename, error_local->message);
return FALSE;
}
/* parse */
return fu_cab_parse (cab, error);
}
/**
* fu_cab_extract:
**/
gboolean
fu_cab_extract (FuCab *cab, FuCabExtractFlags flags, GError **error)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
FuCabExtractHelper helper;
g_autoptr(GError) error_local = NULL;
g_autoptr(GFile) path = NULL;
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
/* extract anything we need */
helper.cab = cab;
helper.flags = flags;
path = g_file_new_for_path (priv->tmp_path);
if (!gcab_cabinet_extract_simple (priv->gcab, path,
fu_cab_extract_cb, &helper,
NULL, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"failed to extract .cab file: %s",
error_local->message);
return FALSE;
}
return TRUE;
}
/**
* fu_cab_verify:
**/
gboolean
fu_cab_verify (FuCab *cab, GError **error)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_autoptr(GError) error_local = NULL;
g_autofree gchar *pki_dir = NULL;
g_autofree gchar *signature = NULL;
g_autofree gchar *fn = NULL;
g_autoptr(FuKeyring) kr = NULL;
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
/* no valid firmware extracted */
if (priv->firmware_basename == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"%s not already extracted",
priv->firmware_basename);
return FALSE;
}
/* check we were installed correctly */
pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL);
if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"PKI directory %s not found", pki_dir);
return FALSE;
}
/* load signature */
fn = g_build_filename (priv->tmp_path, priv->signature_basename, NULL);
if (!g_file_test (fn, G_FILE_TEST_EXISTS)) {
g_debug ("firmware archive contained no GPG signature");
return TRUE;
}
if (!g_file_get_contents (fn, &signature, NULL, error))
return FALSE;
/* verify against the system trusted keys */
kr = fu_keyring_new ();
if (!fu_keyring_add_public_keys (kr, pki_dir, error))
return FALSE;
if (!fu_keyring_verify_file (kr, priv->firmware_filename,
signature, &error_local)) {
g_warning ("untrusted as failed to verify: %s",
error_local->message);
} else {
g_debug ("marking payload as trusted");
priv->trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
}
return TRUE;
}
/**
* fu_cab_delete_temp_files:
**/
gboolean
fu_cab_delete_temp_files (FuCab *cab, GError **error)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), FALSE);
if (priv->tmp_path != NULL) {
const gchar *tmp;
guint i;
for (i = 0; i < priv->basenames_to_delete->len; i++) {
g_autofree gchar *fn = NULL;
tmp = g_ptr_array_index (priv->basenames_to_delete, i);
fn = g_build_filename (priv->tmp_path, tmp, NULL);
g_unlink (fn);
}
g_rmdir (priv->tmp_path);
g_ptr_array_set_size (priv->basenames_to_delete, 0);
}
return TRUE;
}
/**
* fu_cab_get_stream:
**/
GInputStream *
fu_cab_get_stream (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->cab_stream;
}
/**
* fu_cab_get_guid:
**/
const gchar *
fu_cab_get_guid (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->guid;
}
/**
* fu_cab_get_version:
**/
const gchar *
fu_cab_get_version (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->version;
}
/**
* fu_cab_get_vendor:
**/
const gchar *
fu_cab_get_vendor (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->vendor;
}
/**
* fu_cab_get_name:
**/
const gchar *
fu_cab_get_name (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->name;
}
/**
* fu_cab_get_summary:
**/
const gchar *
fu_cab_get_summary (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->summary;
}
/**
* fu_cab_get_description:
**/
const gchar *
fu_cab_get_description (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->description;
}
/**
* fu_cab_get_url_homepage:
**/
const gchar *
fu_cab_get_url_homepage (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->url_homepage;
}
/**
* fu_cab_get_size:
**/
guint64
fu_cab_get_size (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), 0);
return priv->size;
}
/**
* fu_cab_get_license:
**/
const gchar *
fu_cab_get_license (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->license;
}
/**
* fu_cab_get_filename_firmware:
**/
const gchar *
fu_cab_get_filename_firmware (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), NULL);
return priv->firmware_filename;
}
/**
* fu_cab_get_trust_flags:
**/
FwupdTrustFlags
fu_cab_get_trust_flags (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
g_return_val_if_fail (FU_IS_CAB (cab), 0);
return priv->trust_flags;
}
/**
* fu_cab_class_init:
**/
static void
fu_cab_class_init (FuCabClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_cab_finalize;
}
/**
* fu_cab_init:
**/
static void
fu_cab_init (FuCab *cab)
{
FuCabPrivate *priv = GET_PRIVATE (cab);
priv = GET_PRIVATE (cab);
priv->basenames_to_delete = g_ptr_array_new_with_free_func (g_free);
priv->filelist = g_ptr_array_new_with_free_func (g_free);
}
/**
* fu_cab_finalize:
**/
static void
fu_cab_finalize (GObject *object)
{
FuCab *cab = FU_CAB (object);
FuCabPrivate *priv = GET_PRIVATE (cab);
g_free (priv->firmware_basename);
g_free (priv->firmware_filename);
g_free (priv->signature_basename);
g_free (priv->cat_basename);
g_free (priv->guid);
g_free (priv->inf_basename);
g_free (priv->metainfo_basename);
g_free (priv->tmp_path);
g_free (priv->name);
g_free (priv->summary);
g_free (priv->description);
g_free (priv->url_homepage);
g_free (priv->vendor);
g_free (priv->version);
g_free (priv->license);
if (priv->cab_stream != NULL)
g_object_unref (priv->cab_stream);
if (priv->gcab != NULL)
g_object_unref (priv->gcab);
if (priv->inf_kf != NULL)
g_key_file_unref (priv->inf_kf);
g_ptr_array_unref (priv->basenames_to_delete);
g_ptr_array_unref (priv->filelist);
G_OBJECT_CLASS (fu_cab_parent_class)->finalize (object);
}
/**
* fu_cab_new:
**/
FuCab *
fu_cab_new (void)
{
FuCab *cab;
cab = g_object_new (FU_TYPE_CAB, NULL);
return FU_CAB (cab);
}

View File

@ -1,88 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __FU_CAB_H
#define __FU_CAB_H
#include <glib-object.h>
#include <gio/gio.h>
G_BEGIN_DECLS
#define FU_TYPE_CAB (fu_cab_get_type ())
G_DECLARE_DERIVABLE_TYPE (FuCab, fu_cab, FU, CAB, GObject)
struct _FuCabClass
{
GObjectClass parent_class;
};
typedef enum {
FU_CAB_EXTRACT_FLAG_UNKNOWN = 0,
FU_CAB_EXTRACT_FLAG_INF = 1,
FU_CAB_EXTRACT_FLAG_METAINFO = 2,
FU_CAB_EXTRACT_FLAG_FIRMWARE = 4,
FU_CAB_EXTRACT_FLAG_SIGNATURE = 8,
FU_CAB_EXTRACT_FLAG_CATALOG = 16,
FU_CAB_EXTRACT_FLAG_ALL = 0xff,
FU_CAB_EXTRACT_FLAG_LAST
} FuCabExtractFlags;
FuCab *fu_cab_new (void);
gboolean fu_cab_verify (FuCab *cab,
GError **error);
gboolean fu_cab_load_fd (FuCab *cab,
gint fd,
GCancellable *cancellable,
GError **error);
gboolean fu_cab_load_file (FuCab *cab,
GFile *file,
GCancellable *cancellable,
GError **error);
gboolean fu_cab_save_file (FuCab *cab,
GFile *file,
GCancellable *cancellable,
GError **error);
gboolean fu_cab_extract (FuCab *cab,
FuCabExtractFlags flags,
GError **error);
gboolean fu_cab_delete_temp_files (FuCab *cab,
GError **error);
GInputStream *fu_cab_get_stream (FuCab *cab);
const gchar *fu_cab_get_guid (FuCab *cab);
const gchar *fu_cab_get_version (FuCab *cab);
const gchar *fu_cab_get_vendor (FuCab *cab);
const gchar *fu_cab_get_summary (FuCab *cab);
const gchar *fu_cab_get_name (FuCab *cab);
const gchar *fu_cab_get_description (FuCab *cab);
const gchar *fu_cab_get_license (FuCab *cab);
const gchar *fu_cab_get_url_homepage (FuCab *cab);
const gchar *fu_cab_get_filename_firmware (FuCab *cab);
guint64 fu_cab_get_size (FuCab *cab);
FwupdTrustFlags fu_cab_get_trust_flags (FuCab *cab);
void fu_cab_add_file (FuCab *cab,
const gchar *filename);
G_END_DECLS
#endif /* __FU_CAB_H */

View File

@ -99,95 +99,6 @@ fu_keyring_setup (FuKeyring *keyring, GError **error)
return TRUE;
}
/**
* fu_keyring_list_private_keys:
**/
static void
fu_keyring_list_private_keys (FuKeyring *keyring)
{
FuKeyringPrivate *priv = GET_PRIVATE (keyring);
gpgme_key_t key;
gpgme_error_t err;
err = gpgme_op_keylist_start (priv->ctx, NULL, 1);
while (!err) {
g_autoptr(GString) str = NULL;
err = gpgme_op_keylist_next (priv->ctx, &key);
if (err)
break;
str = g_string_new (key->subkeys->keyid);
g_string_append_printf (str, "\t[secret:%i, sign:%i]",
key->subkeys->secret,
key->subkeys->can_sign);
if (key->uids && key->uids->name)
g_string_append_printf (str, " %s", key->uids->name);
if (key->uids && key->uids->email)
g_string_append_printf (str, " <%s>", key->uids->email);
g_debug ("%s", str->str);
gpgme_key_release (key);
}
if (gpg_err_code (err) != GPG_ERR_EOF) {
g_warning ("can not list keys: %s\n", gpgme_strerror (err));
return;
}
}
/**
* fu_keyring_set_signing_key:
**/
gboolean
fu_keyring_set_signing_key (FuKeyring *keyring, const gchar *key_id, GError **error)
{
FuKeyringPrivate *priv = GET_PRIVATE (keyring);
gint n_signers;
gpgme_error_t rc;
gpgme_key_t key;
/* setup context */
if (!fu_keyring_setup (keyring, error))
return FALSE;
/* list possible keys */
fu_keyring_list_private_keys (keyring);
/* find key */
rc = gpgme_get_key (priv->ctx, key_id, &key, 1);
if (rc != GPG_ERR_NO_ERROR) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to find key %s: %s",
key_id, gpgme_strerror (rc));
return FALSE;
}
/* select it to be used */
gpgme_signers_clear (priv->ctx);
rc = gpgme_signers_add (priv->ctx, key);
if (rc != GPG_ERR_NO_ERROR) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to add signing key %s: %s",
key_id, gpgme_strerror (rc));
return FALSE;
}
gpgme_key_unref (key);
/* check it's selected */
n_signers = gpgme_signers_count (priv->ctx);
if (n_signers != 1) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to check signing key %s", key_id);
return FALSE;
}
return TRUE;
}
/**
* fu_keyring_add_public_key:
**/
@ -423,76 +334,6 @@ out:
return ret;
}
/**
* fu_keyring_sign_data:
**/
GBytes *
fu_keyring_sign_data (FuKeyring *keyring, GBytes *payload, GError **error)
{
FuKeyringPrivate *priv = GET_PRIVATE (keyring);
GBytes *sig_bytes = NULL;
gchar *sig_data = NULL;
gpgme_data_t data = NULL;
gpgme_data_t sig = NULL;
gpgme_error_t rc;
gpgme_sign_result_t sign_result;
gsize sig_len = 0;
g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL);
g_return_val_if_fail (payload != NULL, NULL);
/* setup context */
if (!fu_keyring_setup (keyring, error))
return NULL;
/* load file data */
rc = gpgme_data_new_from_mem (&data,
g_bytes_get_data (payload, NULL),
g_bytes_get_size (payload), 0);
if (rc != GPG_ERR_NO_ERROR) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to load data: %s",
gpgme_strerror (rc));
goto out;
}
/* sign */
gpgme_data_new (&sig);
rc = gpgme_op_sign (priv->ctx, data, sig, GPGME_SIG_MODE_DETACH);
if (rc != GPG_ERR_NO_ERROR) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to sign data: %s",
gpgme_strerror (rc));
goto out;
}
sign_result = gpgme_op_sign_result (priv->ctx);
if (sign_result == NULL ||
sign_result->signatures == NULL ||
sign_result->signatures->fpr == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to sign data with any key");
goto out;
}
g_debug ("signed with key %s", sign_result->signatures->fpr);
/* steal signature data */
sig_data = gpgme_data_release_and_get_mem (sig, &sig_len);
sig_bytes = g_bytes_new_with_free_func (sig_data, sig_len, (GDestroyNotify) gpgme_free, sig_data);
sig = NULL;
out:
if (data != NULL)
gpgme_data_release (data);
if (sig != NULL)
gpgme_data_release (sig);
return sig_bytes;
}
/**
* fu_keyring_verify_data:
**/

View File

@ -43,9 +43,6 @@ gboolean fu_keyring_add_public_keys (FuKeyring *keyring,
gboolean fu_keyring_add_public_key (FuKeyring *keyring,
const gchar *filename,
GError **error);
gboolean fu_keyring_set_signing_key (FuKeyring *keyring,
const gchar *key_id,
GError **error);
gboolean fu_keyring_verify_file (FuKeyring *keyring,
const gchar *filename,
const gchar *signature,
@ -54,9 +51,6 @@ gboolean fu_keyring_verify_data (FuKeyring *keyring,
GBytes *payload,
GBytes *payload_signature,
GError **error);
GBytes *fu_keyring_sign_data (FuKeyring *keyring,
GBytes *payload,
GError **error);
G_END_DECLS

View File

@ -33,7 +33,6 @@
#include <stdlib.h>
#include <fcntl.h>
#include "fu-cab.h"
#include "fu-cleanup.h"
#include "fu-debug.h"
#include "fu-device.h"
@ -52,12 +51,14 @@
#include "fu-provider-uefi.h"
#endif
#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */
typedef struct {
GDBusConnection *connection;
GDBusNodeInfo *introspection_daemon;
GDBusProxy *proxy_uid;
GMainLoop *loop;
GPtrArray *devices;
GPtrArray *devices; /* of FuDeviceItem */
GPtrArray *providers;
PolkitAuthority *authority;
FwupdStatus status;
@ -197,23 +198,6 @@ fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
return NULL;
}
/**
* fu_main_get_item_by_guid:
**/
static FuDeviceItem *
fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid)
{
FuDeviceItem *item;
guint i;
for (i = 0; i < priv->devices->len; i++) {
item = g_ptr_array_index (priv->devices, i);
if (g_strcmp0 (fu_device_get_guid (item->device), guid) == 0)
return item;
}
return NULL;
}
/**
* fu_main_get_provider_by_name:
**/
@ -231,14 +215,86 @@ fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name)
return NULL;
}
/**
* fu_main_get_release_trust_flags:
**/
static gboolean
fu_main_get_release_trust_flags (AsRelease *release,
FwupdTrustFlags *trust_flags,
GError **error)
{
AsChecksum *csum_tmp;
GBytes *blob_payload;
GBytes *blob_signature;
const gchar *fn;
g_autofree gchar *pki_dir = NULL;
g_autofree gchar *fn_signature = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(FuKeyring) kr = NULL;
/* no filename? */
csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
fn = as_checksum_get_filename (csum_tmp);
if (fn == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no filename");
return FALSE;
}
/* no signature == no trust */
fn_signature = g_strdup_printf ("%s.asc", fn);
blob_signature = as_release_get_blob (release, fn_signature);
if (blob_signature == NULL) {
g_debug ("firmware archive contained no GPG signature");
return TRUE;
}
/* get payload */
blob_payload = as_release_get_blob (release, fn);
if (blob_payload == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no payload");
return FALSE;
}
/* check we were installed correctly */
pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL);
if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"PKI directory %s not found", pki_dir);
return FALSE;
}
/* verify against the system trusted keys */
kr = fu_keyring_new ();
if (!fu_keyring_add_public_keys (kr, pki_dir, error))
return FALSE;
if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) {
g_warning ("untrusted as failed to verify: %s",
error_local->message);
return TRUE;
}
/* awesome! */
g_debug ("marking payload as trusted");
*trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
return TRUE;
}
typedef struct {
GDBusMethodInvocation *invocation;
FuCab *cab;
AsStore *store;
FwupdTrustFlags trust_flags;
FuDevice *device;
FuProviderFlags flags;
gchar *id;
gint firmware_fd;
gint cab_fd;
GBytes *blob_fw;
GBytes *blob_cab;
gint vercmp;
FuMainPrivate *priv;
} FuMainAuthHelper;
@ -249,21 +305,15 @@ typedef struct {
static void
fu_main_helper_free (FuMainAuthHelper *helper)
{
/* delete temp files */
fu_cab_delete_temp_files (helper->cab, NULL);
g_object_unref (helper->cab);
/* close any open files */
if (helper->cab_fd > 0)
close (helper->cab_fd);
if (helper->firmware_fd > 0)
close (helper->firmware_fd);
/* free */
g_free (helper->id);
if (helper->device != NULL)
g_object_unref (helper->device);
if (helper->blob_fw > 0)
g_bytes_unref (helper->blob_fw);
if (helper->blob_cab > 0)
g_bytes_unref (helper->blob_cab);
g_object_unref (helper->invocation);
g_object_unref (helper->store);
g_free (helper);
}
@ -289,8 +339,8 @@ fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error)
/* run the correct provider that added this */
return fu_provider_update (item->provider,
item->device,
fu_cab_get_stream (helper->cab),
helper->firmware_fd,
helper->blob_cab,
helper->blob_fw,
helper->flags,
error);
}
@ -340,49 +390,124 @@ fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer use
fu_main_helper_free (helper);
}
/**
* fu_main_get_guids_from_store:
**/
static gchar *
fu_main_get_guids_from_store (AsStore *store)
{
AsApp *app;
AsProvide *prov;
GPtrArray *provides;
GPtrArray *apps;
GString *str = g_string_new ("");
guint i;
guint j;
/* return a string with all the firmware apps in the store */
apps = as_store_get_apps (store);
for (i = 0; i < apps->len; i++) {
app = AS_APP (g_ptr_array_index (apps, i));
provides = as_app_get_provides (app);
for (j = 0; j < provides->len; j++) {
prov = AS_PROVIDE (g_ptr_array_index (provides, j));
if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
continue;
g_string_append_printf (str, "%s,", as_provide_get_value (prov));
}
}
if (str->len == 0)
return NULL;
g_string_truncate (str, str->len - 1);
return g_string_free (str, FALSE);
}
/**
* fu_main_update_helper:
**/
static gboolean
fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
{
const gchar *guid;
AsApp *app;
AsChecksum *csum_tmp;
AsRelease *rel;
const gchar *tmp;
const gchar *version;
guint i;
/* load cab file */
fu_main_set_status (helper->priv, FWUPD_STATUS_LOADING);
if (!fu_cab_load_fd (helper->cab, helper->cab_fd, NULL, error))
/* load store file which also decompresses firmware */
fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
return FALSE;
/* are we matching *any* hardware */
guid = fu_cab_get_guid (helper->cab);
/* if we've not chosen a device, try and find anything in the
* cabinet 'store' that matches any installed device */
if (helper->device == NULL) {
FuDeviceItem *item;
item = fu_main_get_item_by_guid (helper->priv, guid);
if (item == NULL) {
for (i = 0; i < helper->priv->devices->len; i++) {
FuDeviceItem *item;
item = g_ptr_array_index (helper->priv->devices, i);
app = as_store_get_app_by_provide (helper->store,
AS_PROVIDE_KIND_FIRMWARE_FLASHED,
fu_device_get_guid (item->device));
if (app != NULL) {
helper->device = g_object_ref (item->device);
break;
}
}
/* nothing found */
if (helper->device == NULL) {
g_autofree gchar *guid = NULL;
guid = fu_main_get_guids_from_store (helper->store);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no hardware matched %s",
"no attached hardware matched %s",
guid);
return FALSE;
}
helper->device = g_object_ref (item->device);
}
tmp = fu_device_get_guid (helper->device);
if (g_strcmp0 (guid, tmp) != 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware is not for this hw: required %s got %s",
tmp, guid);
return FALSE;
} else {
/* find an application from the cabinet 'store' for the
* chosen device */
app = as_store_get_app_by_provide (helper->store,
AS_PROVIDE_KIND_FIRMWARE_FLASHED,
fu_device_get_guid (helper->device));
if (app == NULL) {
g_autofree gchar *guid = NULL;
guid = fu_main_get_guids_from_store (helper->store);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware is not for this hw: required %s got %s",
fu_device_get_guid (helper->device), guid);
return FALSE;
}
}
/* parse the DriverVer */
version = fu_cab_get_version (helper->cab);
rel = as_app_get_release_default (app);
if (rel == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no releases in the firmware component");
return FALSE;
}
/* get the blob */
csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
tmp = as_checksum_get_filename (csum_tmp);
g_assert (tmp != NULL);
helper->blob_fw = as_release_get_blob (rel, tmp);
if (helper->blob_fw == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"failed to get firmware blob");
return FALSE;
}
version = as_release_get_version (rel);
fu_device_set_metadata (helper->device, FU_DEVICE_KEY_UPDATE_VERSION, version);
/* compare to the lowest supported version, if it exists */
@ -424,25 +549,9 @@ fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
return FALSE;
}
/* now extract the firmware and set any trust flags */
fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
if (!fu_cab_extract (helper->cab, FU_CAB_EXTRACT_FLAG_FIRMWARE |
FU_CAB_EXTRACT_FLAG_SIGNATURE, error))
/* verify */
if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
return FALSE;
if (!fu_cab_verify (helper->cab, error))
return FALSE;
/* and open it */
helper->firmware_fd = g_open (fu_cab_get_filename_firmware (helper->cab),
O_CLOEXEC, 0);
if (helper->firmware_fd < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"failed to open %s",
fu_cab_get_filename_firmware (helper->cab));
return FALSE;
}
return TRUE;
}
@ -555,7 +664,7 @@ fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
gboolean is_downgrade;
/* only test the payload */
is_trusted = (fu_cab_get_trust_flags (helper->cab) & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
is_downgrade = helper->vercmp > 0;
/* relax authentication checks for removable devices */
@ -1045,6 +1154,8 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
g_autoptr(GError) error = NULL;
_cleanup_object_unref_ PolkitSubject *subject = NULL;
g_autoptr(GVariantIter) iter = NULL;
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GInputStream) stream = NULL;
/* check the id exists */
g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
@ -1094,14 +1205,25 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
return;
}
/* read the entire fd to a data blob */
stream = g_unix_input_stream_new (fd, TRUE);
blob_cab = g_input_stream_read_bytes (stream,
FU_MAIN_FIRMWARE_SIZE_MAX,
NULL, &error);
if (blob_cab == NULL){
g_dbus_method_invocation_return_gerror (invocation,
error);
return;
}
/* process the firmware */
helper = g_new0 (FuMainAuthHelper, 1);
helper->invocation = g_object_ref (invocation);
helper->cab_fd = fd;
helper->id = g_strdup (id);
helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
helper->blob_cab = g_bytes_ref (blob_cab);
helper->flags = flags;
helper->priv = priv;
helper->cab = fu_cab_new ();
helper->store = as_store_new ();
if (item != NULL)
helper->device = g_object_ref (item->device);
if (!fu_main_update_helper (helper, &error)) {
@ -1139,15 +1261,23 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
/* return 'a{sv}' */
if (g_strcmp0 (method_name, "GetDetails") == 0) {
AsApp *app = NULL;
AsRelease *rel;
GDBusMessage *message;
GPtrArray *apps;
GPtrArray *provides;
GUnixFDList *fd_list;
GVariantBuilder builder;
FwupdTrustFlags trust_flags;
FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
const gchar *tmp;
const gchar *guid = NULL;
gint32 fd_handle = 0;
guint i;
gint fd;
g_autoptr(AsStore) store = NULL;
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(FuCab) cab = NULL;
g_autoptr(GInputStream) stream = NULL;
/* check the id exists */
g_variant_get (parameters, "(h)", &fd_handle);
@ -1170,19 +1300,71 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
return;
}
/* load file */
cab = fu_cab_new ();
if (!fu_cab_load_fd (cab, fd, NULL, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
/* read the entire fd to a data blob */
stream = g_unix_input_stream_new (fd, TRUE);
blob_cab = g_input_stream_read_bytes (stream,
FU_MAIN_FIRMWARE_SIZE_MAX,
NULL, &error);
if (blob_cab == NULL){
g_dbus_method_invocation_return_gerror (invocation,
error);
return;
}
if (!fu_cab_delete_temp_files (cab, &error)) {
/* load file */
store = as_store_new ();
if (!as_store_from_bytes (store, blob_cab, NULL, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
return;
}
/* we have to extract the firmware to check the signature */
if (!fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_FIRMWARE, &error)) {
/* get default app */
apps = as_store_get_apps (store);
if (apps->len == 0) {
g_dbus_method_invocation_return_error (invocation,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no components");
return;
}
if (apps->len > 1) {
/* we've got a .cab file with multiple components,
* so try to find the first thing that's installed */
for (i = 0; i < priv->devices->len; i++) {
FuDeviceItem *item;
item = g_ptr_array_index (priv->devices, i);
app = as_store_get_app_by_provide (store,
AS_PROVIDE_KIND_FIRMWARE_FLASHED,
fu_device_get_guid (item->device));
if (app != NULL)
break;
}
}
/* well, we've tried our best, just show the first entry */
if (app == NULL)
app = AS_APP (g_ptr_array_index (apps, 0));
/* get guid */
provides = as_app_get_provides (app);
for (i = 0; i < provides->len; i++) {
AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
if (as_provide_get_kind (prov) == AS_PROVIDE_KIND_FIRMWARE_FLASHED) {
guid = as_provide_get_value (prov);
break;
}
}
if (guid == NULL) {
g_dbus_method_invocation_return_error (invocation,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"component has no GUID");
return;
}
/* verify trust */
rel = as_app_get_release_default (app);
if (!fu_main_get_release_trust_flags (rel, &trust_flags, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
return;
}
@ -1191,52 +1373,51 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_VERSION,
g_variant_new_string (fu_cab_get_version (cab)));
g_variant_new_string (as_release_get_version (rel)));
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_GUID,
g_variant_new_string (fu_cab_get_guid (cab)));
g_variant_new_string (guid));
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_SIZE,
g_variant_new_uint64 (fu_cab_get_size (cab)));
g_variant_new_uint64 (as_release_get_size (rel, AS_SIZE_KIND_INSTALLED)));
/* optional properties */
tmp = fu_cab_get_vendor (cab);
tmp = as_app_get_developer_name (app, NULL);
if (tmp != NULL) {
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_VENDOR,
g_variant_new_string (tmp));
}
tmp = fu_cab_get_name (cab);
tmp = as_app_get_name (app, NULL);
if (tmp != NULL) {
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_NAME,
g_variant_new_string (tmp));
}
tmp = fu_cab_get_summary (cab);
tmp = as_app_get_comment (app, NULL);
if (tmp != NULL) {
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_SUMMARY,
g_variant_new_string (tmp));
}
tmp = fu_cab_get_description (cab);
tmp = as_app_get_description (app, NULL);
if (tmp != NULL) {
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_DESCRIPTION,
g_variant_new_string (tmp));
}
tmp = fu_cab_get_url_homepage (cab);
tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
if (tmp != NULL) {
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_URL_HOMEPAGE,
g_variant_new_string (tmp));
}
tmp = fu_cab_get_license (cab);
tmp = as_app_get_project_license (app);
if (tmp != NULL) {
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_LICENSE,
g_variant_new_string (tmp));
}
trust_flags = fu_cab_get_trust_flags (cab);
g_variant_builder_add (&builder, "{sv}",
FU_DEVICE_KEY_TRUSTED,
g_variant_new_uint64 (trust_flags));

View File

@ -300,14 +300,13 @@ fu_provider_chug_verify (FuProvider *provider,
static gboolean
fu_provider_chug_update (FuProvider *provider,
FuDevice *device,
gint fd,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error)
{
FuProviderChug *provider_chug = FU_PROVIDER_CHUG (provider);
FuProviderChugPrivate *priv = GET_PRIVATE (provider_chug);
FuProviderChugItem *item;
g_autoptr(GInputStream) stream = NULL;
g_autoptr(GError) error_local = NULL;
/* find item */
@ -323,12 +322,7 @@ fu_provider_chug_update (FuProvider *provider,
}
/* this file is so small, just slurp it all in one go */
stream = g_unix_input_stream_new (fd, TRUE);
item->fw_bin = g_input_stream_read_bytes (stream,
FU_PROVIDER_CHUG_FIRMWARE_MAX,
NULL, error);
if (item->fw_bin == NULL)
return FALSE;
item->fw_bin = g_bytes_ref (blob_fw);
/* check this firmware is actually for this device */
if (!ch_device_check_firmware (item->usb_device,
@ -461,10 +455,6 @@ fu_provider_chug_update (FuProvider *provider,
return FALSE;
}
/* close stream */
if (!g_input_stream_close (stream, NULL, error))
return FALSE;
/* get the new firmware version */
g_debug ("ColorHug: Getting new firmware version");
item->got_version = FALSE;

View File

@ -47,7 +47,7 @@ fu_provider_fake_get_name (FuProvider *provider)
static gboolean
fu_provider_fake_update (FuProvider *provider,
FuDevice *device,
gint fd,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error)
{

View File

@ -195,7 +195,7 @@ fu_provider_rpi_explode_file (struct archive_entry *entry, const gchar *dir)
static gboolean
fu_provider_rpi_update (FuProvider *provider,
FuDevice *device,
gint fd,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error)
{
@ -213,7 +213,9 @@ fu_provider_rpi_update (FuProvider *provider,
arch = archive_read_new ();
archive_read_support_format_all (arch);
archive_read_support_filter_all (arch);
r = archive_read_open_fd (arch, fd, 1024 * 32);
r = archive_read_open_memory (arch,
(void *) g_bytes_get_data (blob_fw, NULL),
(size_t) g_bytes_get_size (blob_fw));
if (r) {
ret = FALSE;
g_set_error (error,

View File

@ -25,7 +25,9 @@
#include <fwupd.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <glib/gstdio.h>
#include <fwup.h>
#include <fcntl.h>
#include "fu-device.h"
#include "fu-pending.h"
@ -206,14 +208,40 @@ out:
static gboolean
fu_provider_uefi_update (FuProvider *provider,
FuDevice *device,
gint fd,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error)
{
g_autoptr(GError) error_local = NULL;
fwup_resource_iter *iter = NULL;
fwup_resource *re = NULL;
gboolean ret = TRUE;
guint64 hardware_instance = 0; /* FIXME */
int fd;
const gchar *fn = "/boot/fwupd-efiupdate";
/* save the data to a temp file */
if (!g_file_set_contents (fn,
g_bytes_get_data (blob_fw, NULL),
g_bytes_get_size (blob_fw),
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"Failed to write temp file: %s",
error_local->message);
return FALSE;
}
/* open the file */
fd = g_open (fn, O_CLOEXEC, 0);
if (fd < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"failed to open %s", fn);
return FALSE;
}
/* get the hardware we're referencing */
fwup_resource_iter_create (&iter);

View File

@ -35,8 +35,6 @@
static void fu_provider_finalize (GObject *object);
#define FU_PROVIDER_FIRMWARE_MAX (32 * 1024 * 1024) /* bytes */
enum {
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
@ -116,12 +114,11 @@ fu_provider_coldplug (FuProvider *provider, GError **error)
static gboolean
fu_provider_schedule_update (FuProvider *provider,
FuDevice *device,
GInputStream *stream,
GBytes *blob_cab,
GError **error)
{
gchar tmpname[] = {"XXXXXX.cap"};
guint i;
g_autoptr(GBytes) fwbin = NULL;
g_autofree gchar *dirname = NULL;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device_tmp = NULL;
@ -155,16 +152,9 @@ fu_provider_schedule_update (FuProvider *provider,
/* just copy to the temp file */
fu_provider_set_status (provider, FWUPD_STATUS_SCHEDULING);
if (!g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, error))
return FALSE;
fwbin = g_input_stream_read_bytes (stream,
FU_PROVIDER_FIRMWARE_MAX,
NULL, error);
if (fwbin == NULL)
return FALSE;
if (!g_file_set_contents (filename,
g_bytes_get_data (fwbin, NULL),
g_bytes_get_size (fwbin),
g_bytes_get_data (blob_cab, NULL),
g_bytes_get_size (blob_cab),
error))
return FALSE;
@ -202,8 +192,8 @@ fu_provider_verify (FuProvider *provider,
gboolean
fu_provider_update (FuProvider *provider,
FuDevice *device,
GInputStream *stream_cab,
gint fd_fw,
GBytes *blob_cab,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error)
{
@ -217,9 +207,9 @@ fu_provider_update (FuProvider *provider,
if (klass->update_offline == NULL)
return fu_provider_schedule_update (provider,
device,
stream_cab,
blob_cab,
error);
return klass->update_offline (provider, device, fd_fw, flags, error);
return klass->update_offline (provider, device, blob_fw, flags, error);
}
/* cancel the pending action */
@ -236,7 +226,7 @@ fu_provider_update (FuProvider *provider,
}
pending = fu_pending_new ();
device_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
if (!klass->update_online (provider, device, fd_fw, flags, &error_update)) {
if (!klass->update_online (provider, device, blob_fw, flags, &error_update)) {
/* save the error to the database */
if (device_pending != NULL) {
fu_pending_set_error_msg (pending, device,

View File

@ -59,12 +59,12 @@ struct _FuProviderClass
GError **error);
gboolean (*update_online) (FuProvider *provider,
FuDevice *device,
gint fd,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error);
gboolean (*update_offline) (FuProvider *provider,
FuDevice *device,
gint fd,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error);
gboolean (*clear_results) (FuProvider *provider,
@ -96,8 +96,8 @@ gboolean fu_provider_coldplug (FuProvider *provider,
GError **error);
gboolean fu_provider_update (FuProvider *provider,
FuDevice *device,
GInputStream *stream_cab,
gint fd_fw,
GBytes *blob_cab,
GBytes *blob_fw,
FuProviderFlags flags,
GError **error);
gboolean fu_provider_verify (FuProvider *provider,

View File

@ -27,7 +27,6 @@
#include <gio/gfiledescriptorbased.h>
#include <stdlib.h>
#include "fu-cab.h"
#include "fu-keyring.h"
#include "fu-pending.h"
#include "fu-provider-fake.h"
@ -162,64 +161,6 @@ fu_rom_all_func (void)
} while (TRUE);
}
static void
fu_cab_func (void)
{
GError *error = NULL;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autoptr(FuCab) cab = NULL;
g_autoptr(GFile) file = NULL;
cab = fu_cab_new ();
g_assert (cab != NULL);
/* load file */
filename = fu_test_get_filename ("colorhug/colorhug-als-3.0.2.cab");
g_assert (filename != NULL);
file = g_file_new_for_path (filename);
ret = fu_cab_load_file (cab, file, NULL, &error);
g_assert_no_error (error);
g_assert (ret);
/* get properties */
g_assert (fu_cab_get_stream (cab) != NULL);
g_assert_cmpstr (fu_cab_get_guid (cab), ==, "84f40464-9272-4ef7-9399-cd95f12da696");
g_assert_cmpstr (fu_cab_get_version (cab), ==, "3.0.2");
g_assert_cmpstr (fu_cab_get_url_homepage (cab), ==, "http://www.hughski.com/");
g_assert_cmpstr (fu_cab_get_license (cab), ==, "GPL-2.0+");
g_assert_cmpint (fu_cab_get_size (cab), ==, 10325);
g_assert_cmpstr (fu_cab_get_description (cab), !=, NULL);
g_assert_cmpint (fu_cab_get_trust_flags (cab), ==, FWUPD_TRUST_FLAG_NONE);
g_assert (!g_file_test (fu_cab_get_filename_firmware (cab), G_FILE_TEST_EXISTS));
/* extract firmware */
ret = fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_FIRMWARE |
FU_CAB_EXTRACT_FLAG_SIGNATURE, &error);
g_assert_no_error (error);
g_assert (ret);
g_assert (g_str_has_suffix (fu_cab_get_filename_firmware (cab), "/firmware.bin"));
g_assert (g_file_test (fu_cab_get_filename_firmware (cab), G_FILE_TEST_EXISTS));
/* this is not available in make distcheck */
ret = fu_cab_verify (cab, &error);
if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
g_clear_error (&error);
} else {
g_assert_no_error (error);
g_assert (ret);
/* check the certificate */
g_assert_cmpint (fu_cab_get_trust_flags (cab), ==, FWUPD_TRUST_FLAG_PAYLOAD);
}
/* clean up */
ret = fu_cab_delete_temp_files (cab, &error);
g_assert_no_error (error);
g_assert (ret);
g_assert (!g_file_test (fu_cab_get_filename_firmware (cab), G_FILE_TEST_EXISTS));
}
static void
_provider_status_changed_cb (FuProvider *provider, FwupdStatus status, gpointer user_data)
{
@ -538,7 +479,6 @@ main (int argc, char **argv)
/* tests go here */
g_test_add_func ("/fwupd/rom", fu_rom_func);
g_test_add_func ("/fwupd/rom{all}", fu_rom_all_func);
g_test_add_func ("/fwupd/cab", fu_cab_func);
g_test_add_func ("/fwupd/pending", fu_pending_func);
g_test_add_func ("/fwupd/provider", fu_provider_func);
g_test_add_func ("/fwupd/provider{rpi}", fu_provider_rpi_func);