mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-03 03:00:50 +00:00

Until gi-docgen is declared stable support either of them. This effectively means that hand builds and CI builds will use gi-docgen, but distro builds use gtk-doc-tools.
260 lines
6.1 KiB
C
260 lines
6.1 KiB
C
/*
|
|
* 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>
|
|
|
|
#ifdef HAVE_LIBARCHIVE
|
|
#include <archive_entry.h>
|
|
#include <archive.h>
|
|
#endif
|
|
|
|
#include "fu-archive.h"
|
|
#include "fwupd-error.h"
|
|
|
|
/**
|
|
* FuArchive:
|
|
*
|
|
* 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: (nullable): optional return location for an error
|
|
*
|
|
* Finds the blob referenced by filename
|
|
*
|
|
* Returns: (transfer none): a #GBytes, or %NULL if the filename was not found
|
|
*
|
|
* Since: 1.2.2
|
|
**/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* fu_archive_iterate:
|
|
* @self: a #FuArchive
|
|
* @callback: (scope call): a #FuArchiveIterateFunc.
|
|
* @user_data: user data
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Iterates over the archive contents, calling the given function for each
|
|
* of the files found. If any @callback returns %FALSE scanning is aborted.
|
|
*
|
|
* Returns: True if no @callback returned FALSE
|
|
*
|
|
* Since: 1.3.4
|
|
*/
|
|
gboolean
|
|
fu_archive_iterate (FuArchive *self,
|
|
FuArchiveIterateFunc callback,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_return_val_if_fail (FU_IS_ARCHIVE (self), FALSE);
|
|
g_return_val_if_fail (callback != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
g_hash_table_iter_init (&iter, self->entries);
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
if (!callback (self, (const gchar *)key, (GBytes *)value, user_data, error))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef HAVE_LIBARCHIVE
|
|
/* 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)
|
|
#endif
|
|
|
|
static gboolean
|
|
fu_archive_load (FuArchive *self, GBytes *blob, FuArchiveFlags flags, GError **error)
|
|
{
|
|
#ifdef HAVE_LIBARCHIVE
|
|
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 gchar *fn_key = NULL;
|
|
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;
|
|
}
|
|
if (flags & FU_ARCHIVE_FLAG_IGNORE_PATH) {
|
|
fn_key = g_path_get_basename (fn);
|
|
} else {
|
|
fn_key = g_strdup (fn);
|
|
}
|
|
g_debug ("adding %s [%" G_GINT64_FORMAT "]", fn_key, bufsz);
|
|
g_hash_table_insert (self->entries,
|
|
g_steal_pointer (&fn_key),
|
|
g_bytes_new_take (g_steal_pointer (&buf), bufsz));
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
#else
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"missing libarchive support");
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_archive_new:
|
|
* @data: archive contents
|
|
* @flags: archive flags, e.g. %FU_ARCHIVE_FLAG_NONE
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* 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.
|
|
*
|
|
* Since: 1.2.2
|
|
**/
|
|
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, flags, error))
|
|
return NULL;
|
|
return g_steal_pointer (&self);
|
|
}
|