[gcab] Add gcab_file_new_with_bytes()
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gcab] Add gcab_file_new_with_bytes()
- Date: Fri, 15 Dec 2017 15:43:22 +0000 (UTC)
commit c873b05f3c5f2bef8927f82f3d84ad228fd154ee
Author: Richard Hughes <richard hughsie com>
Date: Fri Dec 15 10:26:38 2017 +0000
Add gcab_file_new_with_bytes()
At the moment fwupd decompresses each archive into a folder in /tmp, then reads
back the files into memory so that it can parse them. This is fragile, and
somewhat insecure if not done carefully.
This new API allows us to do two new things:
* Create an archive without a GFile to read from
* Decompress an archive to memory, without writing any files on disk
Allowing GCab to decompress to a GBytes buffer means we can make the fwupd code
a lot simpler and safer.
libgcab/gcab-cabinet.c | 7 +++-
libgcab/gcab-file.c | 79 ++++++++++++++++++++++++++++++++++++++++
libgcab/gcab-file.h | 2 +
libgcab/gcab-folder.c | 8 ++++
libgcab/gcab-priv.h | 1 +
libgcab/libgcab.syms | 2 +
tests/gcab-self-test.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 192 insertions(+), 2 deletions(-)
---
diff --git a/libgcab/gcab-cabinet.c b/libgcab/gcab-cabinet.c
index f86c5bb..b5e2c04 100644
--- a/libgcab/gcab-cabinet.c
+++ b/libgcab/gcab-cabinet.c
@@ -478,7 +478,7 @@ gcab_cabinet_load (GCabCabinet *self,
/**
* gcab_cabinet_extract:
* @cabinet: a #GCabCabinet
- * @path: the path to extract files
+ * @path: the path to extract files, or %NULL
* @file_callback: (allow-none) (scope call) (closure user_data): an optional #GCabFile callback,
* return %FALSE to filter out or skip files.
* @progress_callback: (allow-none) (scope call) (closure user_data): a progress callback
@@ -489,6 +489,9 @@ gcab_cabinet_load (GCabCabinet *self,
*
* Extract files to given path.
*
+ * If @path is NULL then the files are decompressed to memory blobs stored on
+ * each #GCabFile.
+ *
* Returns: %TRUE on success.
**/
gboolean
@@ -501,7 +504,7 @@ gcab_cabinet_extract (GCabCabinet *self,
GError **error)
{
g_return_val_if_fail (GCAB_IS_CABINET (self), FALSE);
- g_return_val_if_fail (G_IS_FILE (path), FALSE);
+ g_return_val_if_fail (!path || G_IS_FILE (path), FALSE);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (!error || *error == NULL, FALSE);
diff --git a/libgcab/gcab-file.c b/libgcab/gcab-file.c
index 118f1ba..cbf8a22 100644
--- a/libgcab/gcab-file.c
+++ b/libgcab/gcab-file.c
@@ -45,6 +45,7 @@ struct _GCabFile
gchar *extract_name;
GFile *file;
+ GBytes *bytes;
cfile_t *cfile;
};
@@ -53,6 +54,7 @@ enum {
PROP_NAME,
PROP_FILE,
+ PROP_BYTES,
};
G_DEFINE_TYPE (GCabFile, gcab_file, G_TYPE_OBJECT);
@@ -69,6 +71,8 @@ gcab_file_finalize (GObject *object)
if (self->file != NULL)
g_object_unref (self->file);
+ if (self->bytes != NULL)
+ g_bytes_unref (self->bytes);
cfile_free (self->cfile);
g_free (self->extract_name);
@@ -96,6 +100,17 @@ gcab_file_set_name (GCabFile *self, const gchar *name)
self->cfile->name = fname;
}
+G_GNUC_INTERNAL void
+gcab_file_set_bytes (GCabFile *self, GBytes *bytes)
+{
+ if (self->bytes != NULL)
+ g_bytes_unref (self->bytes);
+ self->bytes = g_bytes_ref (bytes);
+
+ /* this is embedded into the archive */
+ self->cfile->usize = g_bytes_get_size (bytes);
+}
+
static void
gcab_file_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
@@ -109,6 +124,9 @@ gcab_file_set_property (GObject *object, guint prop_id, const GValue *value, GPa
case PROP_FILE:
self->file = g_value_dup_object (value);
break;
+ case PROP_BYTES:
+ gcab_file_set_bytes (self, g_value_get_boxed (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -128,6 +146,9 @@ gcab_file_get_property (GObject *object, guint prop_id, GValue *value, GParamSpe
case PROP_FILE:
g_value_set_object (value, self->file);
break;
+ case PROP_BYTES:
+ g_value_set_boxed (value, self->bytes);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -152,6 +173,11 @@ gcab_file_class_init (GCabFileClass *klass)
g_param_spec_object ("file", "file", "file", G_TYPE_FILE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_BYTES,
+ g_param_spec_boxed ("bytes", "bytes", "bytes", G_TYPE_BYTES,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
}
/**
@@ -358,6 +384,24 @@ gcab_file_get_file (GCabFile *self)
}
/**
+ * gcab_file_get_bytes:
+ * @file: a #GCabFile
+ *
+ * Get the #GFile associated with @file. This will only be non-%NULL if the
+ * #GCabFile has been created using gcab_file_new_with_bytes().
+ *
+ * Returns: (transfer none): the associated #GBytes or %NULL
+ *
+ * Since: 1.0
+ **/
+GBytes *
+gcab_file_get_bytes (GCabFile *self)
+{
+ g_return_val_if_fail (GCAB_IS_FILE (self), NULL);
+ return self->bytes;
+}
+
+/**
* gcab_file_new_with_file:
* @name: name of the file within the cabinet
* @file: a #GFile to be added to the cabinet
@@ -381,6 +425,33 @@ gcab_file_new_with_file (const gchar *name, GFile *file)
return self;
}
+/**
+ * gcab_file_new_with_bytes:
+ * @name: name of the file within the cabinet
+ * @bytes: a #GBytes to be added to the cabinet
+ *
+ * Create a #GCabFile from a given #GBytes.
+ *
+ * If this file is to be added to an archive you should also probably use
+ * gcab_file_set_date() and gcab_file_set_attributes() to set sensible values.
+ *
+ * Returns: a new #GCabFile
+ *
+ * Since: 1.0
+ **/
+GCabFile *
+gcab_file_new_with_bytes (const gchar *name, GBytes *bytes)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (bytes != NULL, NULL);
+
+ GCabFile *self = g_object_new (GCAB_TYPE_FILE, NULL);
+ self->cfile = g_new0 (cfile_t, 1);
+ gcab_file_set_bytes (self, bytes);
+ gcab_file_set_name (self, name);
+ return self;
+}
+
G_GNUC_INTERNAL GCabFile *
gcab_file_new_steal_cfile (cfile_t **cfile)
{
@@ -399,6 +470,10 @@ gcab_file_get_input_stream (GCabFile *self, GCancellable *cancellable, GError **
if (self->file != NULL)
return G_INPUT_STREAM (g_file_read (self->file, cancellable, error));
+ /* backed by a GBytes */
+ if (self->bytes != NULL)
+ return g_memory_input_stream_new_from_bytes (self->bytes);
+
/* nothing to do */
g_set_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT,
"No GFile for %s", gcab_file_get_name (self));
@@ -411,6 +486,10 @@ gcab_file_get_output_stream (GCabFile *self,
GCancellable *cancellable,
GError **error)
{
+ /* not writing to a GFile */
+ if (path_extract == NULL)
+ return g_memory_output_stream_new_resizable ();
+
/* make path have UNIX directory slashes */
g_autofree gchar *fname = g_strdup (gcab_file_get_extract_name (self));
g_strdelimit (fname, "\\", '/');
diff --git a/libgcab/gcab-file.h b/libgcab/gcab-file.h
index ab3f012..bb03bf0 100644
--- a/libgcab/gcab-file.h
+++ b/libgcab/gcab-file.h
@@ -66,7 +66,9 @@ typedef enum
typedef gboolean (*GCabFileCallback) (GCabFile *file, gpointer user_data);
GCabFile * gcab_file_new_with_file (const gchar *name, GFile *file);
+GCabFile * gcab_file_new_with_bytes (const gchar *name, GBytes *bytes);
GFile * gcab_file_get_file (GCabFile *file);
+GBytes * gcab_file_get_bytes (GCabFile *file);
const gchar * gcab_file_get_name (GCabFile *file);
guint32 gcab_file_get_size (GCabFile *file);
guint32 gcab_file_get_attributes (GCabFile *file);
diff --git a/libgcab/gcab-folder.c b/libgcab/gcab-folder.c
index 1a12371..d9917bd 100644
--- a/libgcab/gcab-folder.c
+++ b/libgcab/gcab-folder.c
@@ -461,6 +461,14 @@ gcab_folder_extract (GCabFolder *self,
if (!g_output_stream_close (out, cancellable, error))
return FALSE;
+
+ /* no backing GFile */
+ if (path_extract == NULL) {
+ g_autoptr(GBytes) blob = NULL;
+ blob = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (out));
+ gcab_file_set_bytes (file, blob);
+ }
+
}
return TRUE;
diff --git a/libgcab/gcab-priv.h b/libgcab/gcab-priv.h
index 6d200e5..a3894e8 100644
--- a/libgcab/gcab-priv.h
+++ b/libgcab/gcab-priv.h
@@ -50,6 +50,7 @@ guint32 gcab_file_get_usize (GCabFile *file);
GFile *gcab_file_get_gfile (GCabFile *file);
cfile_t *gcab_file_get_cfile (GCabFile *file);
void gcab_file_add_attribute (GCabFile *file, guint32 attribute);
+void gcab_file_set_bytes (GCabFile *file, GBytes *bytes);
gsize gcab_folder_get_ndatablocks (GCabFolder *folder);
gboolean gcab_folder_extract (GCabFolder *self,
diff --git a/libgcab/libgcab.syms b/libgcab/libgcab.syms
index 80f59c5..5bff171 100644
--- a/libgcab/libgcab.syms
+++ b/libgcab/libgcab.syms
@@ -43,6 +43,8 @@ LIBGCAB1_0.6 {
} LIBGCAB1_0.5;
LIBGCAB1_1.0 {
+ gcab_file_get_bytes;
+ gcab_file_new_with_bytes;
gcab_file_set_attributes;
gcab_file_set_date;
gcab_folder_get_comptype;
diff --git a/tests/gcab-self-test.c b/tests/gcab-self-test.c
index defd278..085ca2b 100644
--- a/tests/gcab-self-test.c
+++ b/tests/gcab-self-test.c
@@ -22,6 +22,7 @@
#include <limits.h>
#include <stdlib.h>
+#include <string.h>
#include <libgcab.h>
@@ -254,6 +255,76 @@ _compute_checksum_for_file (GFile *file, GError **error)
}
static void
+gcab_test_cabinet_blob_func (void)
+{
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GCabCabinet) cabinet = gcab_cabinet_new ();
+
+ /* create folder and add to cabinet */
+ g_autoptr(GCabFolder) folder = gcab_folder_new (GCAB_COMPRESSION_NONE);
+ ret = gcab_cabinet_add_folder (cabinet, folder, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* add the two files */
+ struct {
+ const gchar *fn;
+ const gchar *contents;
+ } files[] = {
+ { "test.sh", "echo ola\n" },
+ { "test.txt", "Ola!\n" },
+ { NULL, NULL }
+ };
+ for (guint i = 0; files[i].fn != NULL; i++) {
+ g_autoptr(GBytes) bytes_tmp = g_bytes_new_static (files[i].contents,
+ strlen (files[i].contents));
+ g_autoptr(GCabFile) cabfile = gcab_file_new_with_bytes (files[i].fn, bytes_tmp);
+
+ /* set the time and attributes */
+ g_autoptr(GDateTime) dt = dt = g_date_time_new_utc (2017, 9, 15, 0, 0, 0.f);
+ GTimeVal tv;
+ ret = g_date_time_to_timeval (dt, &tv);
+ g_assert (ret);
+ gcab_file_set_date (cabfile, &tv);
+ gcab_file_set_attributes (cabfile, GCAB_FILE_ATTRIBUTE_ARCH);
+
+ /* add file to folder */
+ ret = gcab_folder_add_file (folder, cabfile, FALSE, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ }
+
+ /* write to a blob */
+ g_autoptr(GOutputStream) op = g_memory_output_stream_new_resizable ();
+ ret = gcab_cabinet_write_simple (cabinet, op, NULL, NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* get what the checksum is supposed to be */
+ g_autofree gchar *fn = gcab_test_get_filename ("test-none.cab");
+ g_assert (fn != NULL);
+ g_autoptr(GFile) file = g_file_new_for_path (fn);
+ g_assert (file != NULL);
+ g_autofree gchar *checksum = _compute_checksum_for_file (file, &error);
+ g_assert_no_error (error);
+ g_assert (checksum != NULL);
+
+ /* write for debugging */
+ ret = g_output_stream_close (op, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_autoptr(GBytes) blob = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (op));
+ ret = g_file_set_contents ("/tmp/test-none.cab", g_bytes_get_data (blob, NULL), g_bytes_get_size (blob),
&error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* verify the checksum */
+ g_autofree gchar *csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
+ g_assert_cmpstr (csum, ==, checksum);
+}
+
+static void
gcab_test_cabinet_load_func (void)
{
struct {
@@ -304,6 +375,14 @@ gcab_test_cabinet_load_func (void)
cabfolder_tmp = g_ptr_array_index (cabfolders, 0);
g_assert_cmpint (gcab_folder_get_comptype (cabfolder_tmp), ==, tests[i].comptype);
+ g_autoptr(GSList) cabfiles = gcab_folder_get_files (cabfolder_tmp);
+ for (GSList *l = cabfiles; l != NULL; l = l->next) {
+ GCabFile *cabfile = GCAB_FILE (l->data);
+ g_assert_null (gcab_file_get_file (cabfile));
+ g_assert_null (gcab_file_get_bytes (cabfile));
+ g_assert_cmpint (gcab_file_get_attributes (cabfile), ==, GCAB_FILE_ATTRIBUTE_ARCH);
+ }
+
file_tmpdir = g_file_new_for_path ("/tmp");
ret = gcab_cabinet_extract_simple (cabinet, file_tmpdir, NULL, NULL, NULL, &error);
g_assert_no_error (error);
@@ -318,6 +397,21 @@ gcab_test_cabinet_load_func (void)
g_assert (csum != NULL);
g_assert_cmpstr (csum, ==, files[j].checksum);
}
+
+ /* extract again to a memory blob on each GCabFile */
+ ret = gcab_cabinet_extract_simple (cabinet, NULL, NULL, NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* check each blob checksum */
+ for (guint j = 0; files[j].fn != NULL; j++) {
+ GCabFile *cabfile = gcab_folder_get_file_by_name (cabfolder_tmp, files[j].fn);
+ g_assert_nonnull (cabfile);
+ GBytes *blob = gcab_file_get_bytes (cabfile);
+ g_assert_nonnull (blob);
+ g_autofree gchar *csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
+ g_assert_cmpstr (csum, ==, files[j].checksum);
+ }
}
}
@@ -495,6 +589,7 @@ main (int argc, char **argv)
g_test_add_func ("/GCab/cabinet{error-cves}", gcab_test_cabinet_error_cves_func);
g_test_add_func ("/GCab/cabinet{load}", gcab_test_cabinet_load_func);
g_test_add_func ("/GCab/cabinet{write}", gcab_test_cabinet_write_func);
+ g_test_add_func ("/GCab/cabinet{blob}", gcab_test_cabinet_blob_func);
g_test_add_func ("/GCab/cabinet{signature}", gcab_test_cabinet_signature_func);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]