Skip to content

Commit

Permalink
WIP: ModuleIndex: add compressed file loading support
Browse files Browse the repository at this point in the history
TODO:
 * Conditionalize it for non-rpmio usage

Fixes: fedora-modularity#208

Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
  • Loading branch information
sgallagher committed Aug 30, 2019
1 parent 37e2dbf commit ab184ac
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 2 deletions.
81 changes: 79 additions & 2 deletions modulemd/modulemd-module-index.c
Expand Up @@ -14,13 +14,17 @@
#include <errno.h>
#include <glib.h>
#include <inttypes.h>
#include <rpm/rpmio.h>
#include <stdio.h>
#include <yaml.h>

#include "modulemd-errors.h"
#include "modulemd-compression.h"
#include "modulemd-module-index.h"
#include "modulemd-subdocument-info.h"
#include "private/glib-extensions.h"
#include "private/modulemd-module-private.h"
#include "private/modulemd-compression-private.h"
#include "private/modulemd-defaults-private.h"
#include "private/modulemd-defaults-v1-private.h"
#include "private/modulemd-subdocument-info-private.h"
Expand Down Expand Up @@ -472,6 +476,10 @@ modulemd_module_index_update_from_file (ModulemdModuleIndex *self,

int saved_errno;
g_autoptr (FILE) yaml_stream = NULL;
g_autoptr (GError) nested_error = NULL;
int fd;
ModulemdCompressionTypeEnum comtype;
g_autofree gchar *fmode = NULL;

yaml_stream = g_fopen (yaml_file, "rb");
saved_errno = errno;
Expand All @@ -486,8 +494,77 @@ modulemd_module_index_update_from_file (ModulemdModuleIndex *self,
return FALSE;
}

return modulemd_module_index_update_from_stream (
self, yaml_stream, strict, failures, error);
/* To avoid TOCTOU race conditions, do everything from the same opened
* file
*/
fd = fileno (yaml_stream);

/* Determine if the file is compressed */
comtype = modulemd_detect_compression (yaml_file, fd, &nested_error);
if (comtype == MODULEMD_COMPRESSION_TYPE_DETECTION_FAILED)
{
g_propagate_error (error, g_steal_pointer (&nested_error));
return FALSE;
}
else if (comtype == MODULEMD_COMPRESSION_TYPE_NO_COMPRESSION ||
comtype == MODULEMD_COMPRESSION_TYPE_UNKNOWN_COMPRESSION)
{
/* If it's not compressed (or we can't figure out what compression is in
* use), just use the libyaml function. It's fast and will fail quickly
* if the file is unreadable.
*/
return modulemd_module_index_update_from_stream (
self, yaml_stream, strict, failures, error);
}

#ifdef HAVE_RPMIO
/* We're handling a compressed input file, so we'll use librpm's "rpmio"
* suite of tools to deal with it. We need to construct a special "mode"
* argument to pass to Fdopen().
*/
FD_t rpmio_fd = NULL;
g_auto (FD_t) fd_dup = NULL;

fmode = modulemd_get_rpmio_fmode ("r", comtype);
if (!fmode)
{
g_set_error (error,
MODULEMD_ERROR,
MODULEMD_ERROR_FILE_ACCESS,
"Unable to construct rpmio fmode from comtype [%d]",
comtype);
return FALSE;
}

/* Create an rpmio file descriptor object from the current file descriptor,
* setting the appropriate "mode" so that librpm will read it with the
* correct handlers.
*/
fd_dup = fdDup (fd);
rpmio_fd = Fdopen (fd_dup, fmode);

if (!rpmio_fd)
{
g_set_error_literal (
error,
MODULEMD_ERROR,
MODULEMD_ERROR_NOT_IMPLEMENTED,
"Cannot open compressed file. Error in rpmio::Fdopen().");
return FALSE;
}

return modulemd_module_index_update_from_custom (
self, compressed_stream_read_fn, rpmio_fd, strict, failures, error);

#else /* HAVE_RPMIO */
g_set_error_literal (
error,
MODULEMD_ERROR,
MODULEMD_ERROR_NOT_IMPLEMENTED,
"Cannot open compressed file. libmodulemd was not compiled "
"with rpmio support.");
return FALSE;
#endif /* HAVE_RPMIO */
}


Expand Down
119 changes: 119 additions & 0 deletions modulemd/tests/test-modulemd-moduleindex.c
Expand Up @@ -1018,6 +1018,122 @@ module_index_test_dump_empty_index (void)
}


struct expected_compressed_read_t
{
const gchar *filename;

/* Whether this should succeed at reading */
gboolean succeeds;

/* If it fails, the expected error */
GQuark error_domain;
int error_code;
};

static void
test_module_index_read_compressed (void)
{
gboolean bret;
g_autoptr (ModulemdModuleIndex) baseline_idx = NULL;
g_autoptr (ModulemdModuleIndex) compressed_idx = NULL;
g_autofree gchar *file_path = NULL;
g_autoptr (GError) error = NULL;
g_autoptr (GPtrArray) failures = NULL;
g_autofree gchar *baseline_text = NULL;
g_autofree gchar *compressed_text = NULL;

struct expected_compressed_read_t expected[] = {
{
.filename = "bzipped",
.succeeds = TRUE,
},
{
.filename = "bzipped.yaml.bz2",
.succeeds = TRUE,
},
{ .filename = "gzipped", .succeeds = TRUE },
{ .filename = "gzipped.yaml.gz", .succeeds = TRUE },
{ .filename = "xzipped", .succeeds = TRUE },
{ .filename = "xzipped.yaml.xz", .succeeds = TRUE },

/* The zchunk read ends up in what appears to be an infinite loop.
* This needs further investigation
{ .filename = "zchunked.yaml.zst", .succeeds = TRUE }, */
{ .filename = NULL }
};

baseline_idx = modulemd_module_index_new ();
g_assert_nonnull (baseline_idx);

file_path = g_strdup_printf ("%s/compression/uncompressed.yaml",
g_getenv ("TEST_DATA_PATH"));
g_assert_nonnull (file_path);

bret = modulemd_module_index_update_from_file (
baseline_idx, file_path, TRUE, &failures, &error);
g_assert_true (bret);
g_assert_no_error (error);
g_assert_cmpint (failures->len, ==, 0);

baseline_text = modulemd_module_index_dump_to_string (baseline_idx, &error);
g_assert_nonnull (baseline_text);
g_assert_no_error (error);

g_clear_pointer (&file_path, g_free);

for (size_t i = 0; expected[i].filename; i++)
{
compressed_idx = modulemd_module_index_new ();
g_assert_nonnull (compressed_idx);

file_path = g_strdup_printf ("%s/compression/%s",
g_getenv ("TEST_DATA_PATH"),
expected[i].filename);
g_assert_nonnull (file_path);

g_debug ("Processing %s, expecting %s",
file_path,
expected[i].succeeds ? "success" : "failure");
bret = modulemd_module_index_update_from_file (
compressed_idx, file_path, TRUE, &failures, &error);

if (error)
{
g_debug ("Error: %s", error->message);
}

if (expected[i].succeeds)
{
g_assert_true (bret);
g_assert_no_error (error);
}
else
{
g_assert_false (bret);
g_assert_error (
error, expected[i].error_domain, expected[i].error_code);

g_clear_error (&error);
g_clear_pointer (&file_path, g_free);
g_clear_object (&compressed_idx);
continue;
}
g_assert_cmpint (failures->len, ==, 0);

compressed_text =
modulemd_module_index_dump_to_string (compressed_idx, &error);
g_assert_nonnull (compressed_text);
g_assert_no_error (error);

g_assert_cmpstr (baseline_text, ==, compressed_text);

g_clear_pointer (&compressed_text, g_free);
g_clear_pointer (&file_path, g_free);
g_clear_object (&compressed_idx);
}
}


int
main (int argc, char *argv[])
{
Expand Down Expand Up @@ -1097,5 +1213,8 @@ main (int argc, char *argv[])
g_test_add_func ("/modulemd/v2/module/index/empty",
module_index_test_dump_empty_index);

g_test_add_func ("/modulemd/v2/module/index/compressed",
test_module_index_read_compressed);

return g_test_run ();
}

0 comments on commit ab184ac

Please sign in to comment.