mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 02:40:34 +00:00
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:
parent
9e1768940d
commit
5d14deff4a
@ -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)
|
||||
|
@ -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 \
|
||||
|
763
src/fu-cab.c
763
src/fu-cab.c
@ -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);
|
||||
}
|
88
src/fu-cab.h
88
src/fu-cab.h
@ -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 */
|
||||
|
159
src/fu-keyring.c
159
src/fu-keyring.c
@ -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:
|
||||
**/
|
||||
|
@ -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
|
||||
|
||||
|
379
src/fu-main.c
379
src/fu-main.c
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user