mirror of
https://git.proxmox.com/git/fwupd
synced 2025-07-03 20:36:48 +00:00
Provide a way for plugins to decompress a custom archive to ram
This allows plugins to load an archive supplied as the 'deliverable' of the cabinet archive. This means plugins can bundle up a set of images in a cross platform way, for instance adding boot.img+os.img+manifest.xml into a zip file, rather than having to (ab)use the DfuSe file format or deal with libarchive directly.
This commit is contained in:
parent
6a53116fb5
commit
b97f07e7be
200
src/fu-archive.c
Normal file
200
src/fu-archive.c
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define G_LOG_DOMAIN "FuArchive"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <archive_entry.h>
|
||||||
|
#include <archive.h>
|
||||||
|
|
||||||
|
#include "fu-archive.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:fu-archive
|
||||||
|
* @title: FuArchive
|
||||||
|
* @short_description: an in-memory archive decompressor
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct _FuArchive {
|
||||||
|
GObject parent_instance;
|
||||||
|
GHashTable *entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (FuArchive, fu_archive, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_archive_finalize (GObject *obj)
|
||||||
|
{
|
||||||
|
FuArchive *self = FU_ARCHIVE (obj);
|
||||||
|
|
||||||
|
g_hash_table_unref (self->entries);
|
||||||
|
G_OBJECT_CLASS (fu_archive_parent_class)->finalize (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_archive_class_init (FuArchiveClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
object_class->finalize = fu_archive_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_archive_init (FuArchive *self)
|
||||||
|
{
|
||||||
|
self->entries = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
|
g_free, (GDestroyNotify) g_bytes_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_archive_lookup_by_fn:
|
||||||
|
* @self: A #FuArchive
|
||||||
|
* @fn: A filename
|
||||||
|
* @error: A #GError, or %NULL
|
||||||
|
*
|
||||||
|
* Finds the blob referenced by filename
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a #GBytes, or %NULL if the filename was not found
|
||||||
|
**/
|
||||||
|
GBytes *
|
||||||
|
fu_archive_lookup_by_fn (FuArchive *self, const gchar *fn, GError **error)
|
||||||
|
{
|
||||||
|
GBytes *fw;
|
||||||
|
|
||||||
|
g_return_val_if_fail (FU_IS_ARCHIVE (self), NULL);
|
||||||
|
g_return_val_if_fail (fn != NULL, NULL);
|
||||||
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||||
|
|
||||||
|
fw = g_hash_table_lookup (self->entries, fn);
|
||||||
|
if (fw == NULL) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_FOUND,
|
||||||
|
"no blob for %s", fn);
|
||||||
|
}
|
||||||
|
return fw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* workaround the struct types of libarchive */
|
||||||
|
typedef struct archive _archive_read_ctx;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_archive_read_ctx_free (_archive_read_ctx *arch)
|
||||||
|
{
|
||||||
|
archive_read_close (arch);
|
||||||
|
archive_read_free (arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(_archive_read_ctx, _archive_read_ctx_free)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_archive_load (FuArchive *self, GBytes *blob, GError **error)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
g_autoptr(_archive_read_ctx) arch = NULL;
|
||||||
|
|
||||||
|
/* decompress anything matching either glob */
|
||||||
|
arch = archive_read_new ();
|
||||||
|
if (arch == NULL) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"libarchive startup failed");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
archive_read_support_format_all (arch);
|
||||||
|
archive_read_support_filter_all (arch);
|
||||||
|
r = archive_read_open_memory (arch,
|
||||||
|
(void *) g_bytes_get_data (blob, NULL),
|
||||||
|
(size_t) g_bytes_get_size (blob));
|
||||||
|
if (r != 0) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"cannot open: %s",
|
||||||
|
archive_error_string (arch));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
while (TRUE) {
|
||||||
|
const gchar *fn;
|
||||||
|
gint64 bufsz;
|
||||||
|
gssize rc;
|
||||||
|
struct archive_entry *entry;
|
||||||
|
g_autofree guint8 *buf = NULL;
|
||||||
|
|
||||||
|
r = archive_read_next_header (arch, &entry);
|
||||||
|
if (r == ARCHIVE_EOF)
|
||||||
|
break;
|
||||||
|
if (r != ARCHIVE_OK) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
"cannot read header: %s",
|
||||||
|
archive_error_string (arch));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* only extract if valid */
|
||||||
|
fn = archive_entry_pathname (entry);
|
||||||
|
if (fn == NULL)
|
||||||
|
continue;
|
||||||
|
bufsz = archive_entry_size (entry);
|
||||||
|
if (bufsz > 1024 * 1024 * 1024) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
"cannot read huge files");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
buf = g_malloc (bufsz);
|
||||||
|
rc = archive_read_data (arch, buf, (gsize) bufsz);
|
||||||
|
if (rc < 0) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
"cannot read data: %s",
|
||||||
|
archive_error_string (arch));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (rc != bufsz) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
"read %" G_GSSIZE_FORMAT " of %" G_GINT64_FORMAT,
|
||||||
|
rc, bufsz);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
g_debug ("adding %s [%" G_GINT64_FORMAT "]", fn, bufsz);
|
||||||
|
g_hash_table_insert (self->entries,
|
||||||
|
g_strdup (fn),
|
||||||
|
g_bytes_new_take (g_steal_pointer (&buf), bufsz));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_archive_new:
|
||||||
|
* @data: A #GBytes
|
||||||
|
* @flags: A #FuArchiveFlags, e.g. %FU_ARCHIVE_FLAG_NONE
|
||||||
|
* @error: A #GError, or %NULL
|
||||||
|
*
|
||||||
|
* Parses @data as an archive and decompresses all files to memory blobs.
|
||||||
|
*
|
||||||
|
* Returns: a #FuArchive, or %NULL if the archive was invalid in any way.
|
||||||
|
**/
|
||||||
|
FuArchive *
|
||||||
|
fu_archive_new (GBytes *data, FuArchiveFlags flags, GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(FuArchive) self = g_object_new (FU_TYPE_ARCHIVE, NULL);
|
||||||
|
g_return_val_if_fail (data != NULL, NULL);
|
||||||
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||||
|
if (!fu_archive_load (self, data, error))
|
||||||
|
return NULL;
|
||||||
|
return g_steal_pointer (&self);
|
||||||
|
}
|
32
src/fu-archive.h
Normal file
32
src/fu-archive.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __FU_ARCHIVE_H
|
||||||
|
#define __FU_ARCHIVE_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define FU_TYPE_ARCHIVE (fu_archive_get_type ())
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE (FuArchive, fu_archive, FU, ARCHIVE, GObject)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FU_ARCHIVE_FLAG_NONE = 0,
|
||||||
|
FU_ARCHIVE_FLAGS_LAST
|
||||||
|
} FuArchiveFlags;
|
||||||
|
|
||||||
|
FuArchive *fu_archive_new (GBytes *data,
|
||||||
|
FuArchiveFlags flags,
|
||||||
|
GError **error);
|
||||||
|
GBytes *fu_archive_lookup_by_fn (FuArchive *self,
|
||||||
|
const gchar *fn,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __FU_ARCHIVE_H */
|
@ -15,6 +15,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fu-archive.h"
|
||||||
#include "fu-common-cab.h"
|
#include "fu-common-cab.h"
|
||||||
#include "fu-common-guid.h"
|
#include "fu-common-guid.h"
|
||||||
#include "fu-common-version.h"
|
#include "fu-common-version.h"
|
||||||
@ -52,6 +53,63 @@ fu_self_test_mkroot (void)
|
|||||||
g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0);
|
g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_archive_invalid_func (void)
|
||||||
|
{
|
||||||
|
g_autofree gchar *filename = NULL;
|
||||||
|
g_autoptr(FuArchive) archive = NULL;
|
||||||
|
g_autoptr(GBytes) data = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
|
filename = fu_test_get_filename (TESTDATADIR, "metadata.xml");
|
||||||
|
g_assert_nonnull (filename);
|
||||||
|
data = fu_common_get_contents_bytes (filename, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (data);
|
||||||
|
|
||||||
|
archive = fu_archive_new (data, FU_ARCHIVE_FLAG_NONE, &error);
|
||||||
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
|
||||||
|
g_assert_null (archive);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_archive_cab_func (void)
|
||||||
|
{
|
||||||
|
g_autofree gchar *checksum1 = NULL;
|
||||||
|
g_autofree gchar *checksum2 = NULL;
|
||||||
|
g_autofree gchar *filename = NULL;
|
||||||
|
g_autoptr(FuArchive) archive = NULL;
|
||||||
|
g_autoptr(GBytes) data = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
GBytes *data_tmp;
|
||||||
|
|
||||||
|
filename = fu_test_get_filename (TESTDATADIR, "colorhug/colorhug-als-3.0.2.cab");
|
||||||
|
g_assert_nonnull (filename);
|
||||||
|
data = fu_common_get_contents_bytes (filename, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (data);
|
||||||
|
|
||||||
|
archive = fu_archive_new (data, FU_ARCHIVE_FLAG_NONE, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (archive);
|
||||||
|
|
||||||
|
data_tmp = fu_archive_lookup_by_fn (archive, "firmware.metainfo.xml", &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (data_tmp);
|
||||||
|
checksum1 = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, data_tmp);
|
||||||
|
g_assert_cmpstr (checksum1, ==, "8611114f51f7151f190de86a5c9259d79ff34216");
|
||||||
|
|
||||||
|
data_tmp = fu_archive_lookup_by_fn (archive, "firmware.bin", &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (data_tmp);
|
||||||
|
checksum2 = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, data_tmp);
|
||||||
|
g_assert_cmpstr (checksum2, ==, "7c0ae84b191822bcadbdcbe2f74a011695d783c7");
|
||||||
|
|
||||||
|
data_tmp = fu_archive_lookup_by_fn (archive, "NOTGOINGTOEXIST.xml", &error);
|
||||||
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
|
||||||
|
g_assert_null (data_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_common_version_guess_format_func (void)
|
fu_common_version_guess_format_func (void)
|
||||||
{
|
{
|
||||||
@ -3188,6 +3246,8 @@ main (int argc, char **argv)
|
|||||||
/* tests go here */
|
/* tests go here */
|
||||||
if (g_test_slow ())
|
if (g_test_slow ())
|
||||||
g_test_add_func ("/fwupd/progressbar", fu_progressbar_func);
|
g_test_add_func ("/fwupd/progressbar", fu_progressbar_func);
|
||||||
|
g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func);
|
||||||
|
g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func);
|
||||||
g_test_add_func ("/fwupd/engine{requirements-other-device}", fu_engine_requirements_other_device_func);
|
g_test_add_func ("/fwupd/engine{requirements-other-device}", fu_engine_requirements_other_device_func);
|
||||||
g_test_add_func ("/fwupd/device{incorporate}", fu_device_incorporate_func);
|
g_test_add_func ("/fwupd/device{incorporate}", fu_device_incorporate_func);
|
||||||
g_test_add_func ("/fwupd/device{poll}", fu_device_poll_func);
|
g_test_add_func ("/fwupd/device{poll}", fu_device_poll_func);
|
||||||
|
@ -25,6 +25,7 @@ endif
|
|||||||
libfwupdprivate = static_library(
|
libfwupdprivate = static_library(
|
||||||
'fwupdprivate',
|
'fwupdprivate',
|
||||||
sources : [
|
sources : [
|
||||||
|
'fu-archive.c',
|
||||||
'fu-common.c',
|
'fu-common.c',
|
||||||
'fu-common-guid.c',
|
'fu-common-guid.c',
|
||||||
'fu-common-version.c',
|
'fu-common-version.c',
|
||||||
@ -108,6 +109,7 @@ fwupdtool = executable(
|
|||||||
sources : [
|
sources : [
|
||||||
'fu-tool.c',
|
'fu-tool.c',
|
||||||
keyring_src,
|
keyring_src,
|
||||||
|
'fu-archive.c',
|
||||||
'fu-chunk.c',
|
'fu-chunk.c',
|
||||||
'fu-common.c',
|
'fu-common.c',
|
||||||
'fu-common-cab.c',
|
'fu-common-cab.c',
|
||||||
@ -188,6 +190,7 @@ executable(
|
|||||||
resources_src,
|
resources_src,
|
||||||
sources : [
|
sources : [
|
||||||
keyring_src,
|
keyring_src,
|
||||||
|
'fu-archive.c',
|
||||||
'fu-chunk.c',
|
'fu-chunk.c',
|
||||||
'fu-common.c',
|
'fu-common.c',
|
||||||
'fu-common-cab.c',
|
'fu-common-cab.c',
|
||||||
@ -259,6 +262,7 @@ if get_option('tests')
|
|||||||
sources : [
|
sources : [
|
||||||
keyring_src,
|
keyring_src,
|
||||||
'fu-self-test.c',
|
'fu-self-test.c',
|
||||||
|
'fu-archive.c',
|
||||||
'fu-chunk.c',
|
'fu-chunk.c',
|
||||||
'fu-common.c',
|
'fu-common.c',
|
||||||
'fu-common-cab.c',
|
'fu-common-cab.c',
|
||||||
@ -324,6 +328,8 @@ if get_option('introspection')
|
|||||||
gir_dep = declare_dependency(sources: gir)
|
gir_dep = declare_dependency(sources: gir)
|
||||||
gnome.generate_gir(fwupd,
|
gnome.generate_gir(fwupd,
|
||||||
sources : [
|
sources : [
|
||||||
|
'fu-archive.c',
|
||||||
|
'fu-archive.h',
|
||||||
'fu-chunk.c',
|
'fu-chunk.c',
|
||||||
'fu-chunk.h',
|
'fu-chunk.h',
|
||||||
'fu-common.c',
|
'fu-common.c',
|
||||||
|
Loading…
Reference in New Issue
Block a user