From 67ec898265c37823e7ced6ee1ab72d05104f77d0 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 3 Mar 2015 11:39:27 +0000 Subject: [PATCH] Extract the .cab file and parse the .inf file --- configure.ac | 2 + contrib/fwupd.spec.in | 2 + src/Makefile.am | 4 + src/fu-common.h | 1 + src/fu-main.c | 254 +++++++++++++++++++++++++++++++++++++++-- src/fu-provider-uefi.c | 1 + src/fu-util.c | 1 + 7 files changed, 253 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 993a663a7..16b9b52e6 100644 --- a/configure.ac +++ b/configure.ac @@ -88,6 +88,8 @@ dnl --------------------------------------------------------------------------- PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.36.0 gobject-2.0 gthread-2.0 gio-2.0 >= 2.25.9 gio-unix-2.0) 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) AC_PATH_PROG(DOCBOOK2MAN, docbook2man) dnl --------------------------------------------------------------------------- diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index e35720b6f..abcc6d8c4 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -14,6 +14,8 @@ BuildRequires: glib2-devel BuildRequires: intltool BuildRequires: libgudev1-devel BuildRequires: polkit-devel >= 0.103 +BuildRequires: libgcab1-devel +BuildRequires: libappstream-devel %description fwupd is a daemon to allow session software to update UEFI firmware. diff --git a/src/Makefile.am b/src/Makefile.am index dbaf1728c..142dbb180 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,9 @@ dist_introspection_DATA = \ org.freedesktop.fwupd.xml AM_CPPFLAGS = \ + $(APPSTREAM_GLIB_CFLAGS) \ $(PIE_CFLAGS) \ + $(GCAB_CFLAGS) \ $(GLIB_CFLAGS) \ $(POLKIT_CFLAGS) \ $(GUDEV_CFLAGS) \ @@ -72,8 +74,10 @@ fwupd_SOURCES = \ fu-main.c fwupd_LDADD = \ + $(APPSTREAM_GLIB_LIBS) \ $(POLKIT_LIBS) \ $(GUDEV_LIBS) \ + $(GCAB_LIBS) \ $(GLIB_LIBS) fwupd_LDFLAGS = \ diff --git a/src/fu-common.h b/src/fu-common.h index 3bc2f2663..90e845f2a 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -31,5 +31,6 @@ #define FU_DEVICE_KEY_VERSION "Version" #define FU_DEVICE_KEY_PROVIDER "Provider" +#define FU_DEVICE_KEY_GUID "Guid" #endif /* __FU_COMMON_H */ diff --git a/src/fu-main.c b/src/fu-main.c index f2ac67ac9..f0e3d7978 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -21,12 +21,17 @@ #include "config.h" -#include +#include +#include #include #include +#include #include +#include #include #include +#include +#include #include "fu-cleanup.h" #include "fu-common.h" @@ -50,10 +55,10 @@ typedef struct { } FuDeviceItem; /** - * fu_device_to_variants: + * fu_main_device_array_to_variant: **/ static GVariant * -fu_device_to_variants (FuMainPrivate *priv) +fu_main_device_array_to_variant (FuMainPrivate *priv) { GVariantBuilder builder; guint i; @@ -99,10 +104,17 @@ fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id) typedef struct { GDBusMethodInvocation *invocation; - FuMainPrivate *priv; + FuDevice *device; FuProviderFlags flags; - gint fd; gchar *id; + gchar *firmware_basename; + gchar *firmware_filename; + gint firmware_fd; + gchar *inf_filename; + GKeyFile *inf_kf; + gchar *tmp_path; + gint cab_fd; + FuMainPrivate *priv; } FuMainAuthHelper; /** @@ -111,8 +123,29 @@ typedef struct { static void fu_main_helper_free (FuMainAuthHelper *helper) { - g_object_unref (helper->invocation); + /* clean the temp space we created */ + if (helper->inf_filename != NULL) + g_unlink (helper->inf_filename); + if (helper->firmware_filename != NULL) + g_unlink (helper->firmware_filename); + if (helper->tmp_path != NULL) + g_rmdir (helper->tmp_path); + + /* 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); + g_free (helper->tmp_path); + g_free (helper->inf_filename); + g_free (helper->firmware_basename); + g_free (helper->firmware_filename); + g_key_file_unref (helper->inf_kf); + g_object_unref (helper->device); + g_object_unref (helper->invocation); g_free (helper); } @@ -165,7 +198,7 @@ fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer use /* run the correct provider that added this */ if (!fu_provider_update (item->provider, item->device, - helper->fd, + helper->firmware_fd, helper->flags, &error)) { g_dbus_method_invocation_return_gerror (helper->invocation, @@ -179,6 +212,195 @@ fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer use fu_main_helper_free (helper); } +/** + * fu_main_cab_extract_inf_cb: + **/ +static gboolean +fu_main_cab_extract_inf_cb (GCabFile *file, gpointer user_data) +{ + FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data; + + /* only extract the first .inf file found in the .cab file */ + if (helper->inf_filename == NULL && + g_str_has_suffix (gcab_file_get_name (file), ".inf")) { + helper->inf_filename = g_build_filename (helper->tmp_path, + gcab_file_get_name (file), + NULL); + return TRUE; + } + return FALSE; +} + +/** + * fu_main_cab_extract_firmware_cb: + **/ +static gboolean +fu_main_cab_extract_firmware_cb (GCabFile *file, gpointer user_data) +{ + FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data; + + /* only extract the firmware file listed in the .inf file */ + if (helper->firmware_filename == NULL && + g_strcmp0 (gcab_file_get_name (file), + helper->firmware_basename) == 0) { + helper->firmware_filename = g_build_filename (helper->tmp_path, + gcab_file_get_name (file), + NULL); + return TRUE; + } + return FALSE; +} + +/** + * fu_main_update_helper: + **/ +static gboolean +fu_main_update_helper (FuMainAuthHelper *helper, GError **error) +{ + const gchar *guid_tmp; + _cleanup_error_free_ GError *error_local = NULL; + _cleanup_free_ gchar *guid = NULL; + _cleanup_object_unref_ GCabCabinet *cab = NULL; + _cleanup_object_unref_ GFile *path = NULL; + _cleanup_object_unref_ GInputStream *stream_buf = NULL; + _cleanup_object_unref_ GInputStream *stream = NULL; + + /* GCab needs a GSeekable input stream, so buffer to RAM then load */ + stream = g_unix_input_stream_new (helper->cab_fd, TRUE); + stream_buf = g_memory_input_stream_new (); + while (1) { + _cleanup_bytes_unref_ GBytes *data = NULL; + data = g_input_stream_read_bytes (stream, 8192, NULL, &error_local); + if (g_bytes_get_size (data) == 0) + break; + if (data == NULL) { + g_set_error_literal (error, + FU_ERROR, + FU_ERROR_INTERNAL, + error_local->message); + return FALSE; + } + g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), data); + } + cab = gcab_cabinet_new (); + if (!gcab_cabinet_load (cab, stream_buf, NULL, &error_local)) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "cannot load .cab file: %s", + error_local->message); + return FALSE; + } + + /* decompress to /tmp */ + helper->tmp_path = g_dir_make_tmp ("fwupd-XXXXXX", &error_local); + if (helper->tmp_path == NULL) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "failed to create temp dir: %s", + error_local->message); + return FALSE; + } + + path = g_file_new_for_path (helper->tmp_path); + if (!gcab_cabinet_extract_simple (cab, path, + fu_main_cab_extract_inf_cb, + helper, NULL, &error_local)) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "failed to extract .cab file: %s", + error_local->message); + return FALSE; + } + + /* read .inf file */ + if (helper->inf_filename == NULL) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "no .inf file in.cab file"); + return FALSE; + } + g_debug ("loading %s", helper->inf_filename); + helper->inf_kf = as_utils_load_inf_file (helper->inf_filename, &error_local); + if (helper->inf_kf == NULL) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + ".inf file could not be loaded: %s", + error_local->message); + return FALSE; + } + + /* check hardware matches */ + guid = g_key_file_get_string (helper->inf_kf, + "Version", "ClassGuid", &error_local); + if (guid == NULL) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + ".inf file not firmmare: %s", + error_local->message); + return FALSE; + } + guid_tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_GUID); + if (g_strcmp0 (guid, guid_tmp) != 0) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "firmware is not for this hw: required %s got %s", + guid_tmp, guid); + return FALSE; + } + + /* find out what firmware file we have to open */ + helper->firmware_basename = g_key_file_get_string (helper->inf_kf, + "Firmware_CopyFiles", + "Value000", NULL); + if (helper->firmware_basename == NULL) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + ".inf file has no Firmware_CopyFiles"); + return FALSE; + } + + /* now extract the firmware */ + g_debug ("extracting %s", helper->firmware_basename); + if (!gcab_cabinet_extract_simple (cab, path, + fu_main_cab_extract_firmware_cb, + helper, NULL, &error_local)) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "failed to extract .cab file: %s", + error_local->message); + return FALSE; + } + if (helper->firmware_filename == NULL) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "%s not found in cab file", + helper->firmware_basename); + return FALSE; + } + + /* and open it */ + helper->firmware_fd = g_open (helper->firmware_filename, O_CLOEXEC, 0); + if (helper->firmware_fd < 0) { + g_set_error (error, + FU_ERROR, + FU_ERROR_INTERNAL, + "failed to open %s", + helper->firmware_basename); + return FALSE; + } + return TRUE; +} + /** * fu_main_daemon_method_call: **/ @@ -195,7 +417,7 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, if (g_strcmp0 (method_name, "GetDevices") == 0) { _cleanup_strv_free_ gchar **devices = NULL; g_debug ("Called %s()", method_name); - val = fu_device_to_variants (priv); + val = fu_main_device_array_to_variant (priv); g_dbus_method_invocation_return_value (invocation, val); return; } @@ -256,16 +478,24 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, return; } - - /* do authorization async */ + /* process the firmware */ helper = g_new0 (FuMainAuthHelper, 1); helper->invocation = g_object_ref (invocation); - helper->fd = fd; + helper->cab_fd = fd; helper->id = g_strdup (id); helper->flags = flags; helper->priv = priv; + helper->device = g_object_ref (item->device); + if (!fu_main_update_helper (helper, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, + error); + fu_main_helper_free (helper); + return; + } + + /* authenticate */ subject = polkit_system_bus_name_new (sender); - polkit_authority_check_authorization (priv->authority, subject, + polkit_authority_check_authorization (helper->priv->authority, subject, "org.freedesktop.fwupd.update", NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, diff --git a/src/fu-provider-uefi.c b/src/fu-provider-uefi.c index afb7aed89..b4785d8ae 100644 --- a/src/fu-provider-uefi.c +++ b/src/fu-provider-uefi.c @@ -70,6 +70,7 @@ fu_provider_uefi_coldplug (FuProvider *provider, GError **error) dev = fu_device_new (); fu_device_set_id (dev, "UEFI-819b858e-c52c-402f-80e1-5b311b6c1959-dev1"); fu_device_set_metadata (dev, FU_DEVICE_KEY_PROVIDER, "UEFI"); + fu_device_set_metadata (dev, FU_DEVICE_KEY_GUID, "819b858e-c52c-402f-80e1-5b311b6c1959"); fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION, "12345"); fu_provider_emit_added (provider, dev); return TRUE; diff --git a/src/fu-util.c b/src/fu-util.c index 931074b22..712e1b66c 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -246,6 +246,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) const gchar *value; const gchar *keys[] = { FU_DEVICE_KEY_PROVIDER, + FU_DEVICE_KEY_GUID, FU_DEVICE_KEY_VERSION, NULL }; for (i = 0; i < devices->len; i++) {