glib/gio/gresource-tool.c
Matthias Clasen 716cf35585 Add a resource tool
This lets you poke at resources in elf files and
standalone resource bundles. So far, only listing
and extracting resources is supported. The support
for elf files requires libelf.
2012-01-23 00:24:34 -05:00

623 lines
14 KiB
C

/*
* Copyright © 2012 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the licence, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#ifdef HAVE_LIBELF
#include <libelf.h>
#include <gelf.h>
#endif
#include <gio/gio.h>
#include <gi18n.h>
/* GResource functions {{{1 */
static GResource *
get_resource (const gchar *file)
{
gchar *content;
gsize size;
GResource *resource;
GBytes *data;
resource = NULL;
if (g_file_get_contents (file, &content, &size, NULL))
{
data = g_bytes_new_take (content, size);
resource = g_resource_new_from_data (data, NULL);
g_bytes_unref (data);
}
return resource;
}
static void
list_resource (GResource *resource,
const gchar *path,
const gchar *section,
const gchar *prefix,
gboolean details)
{
gchar **children;
gsize size;
guint32 flags;
gint i;
gchar *child;
GError *error = NULL;
gint len;
children = g_resource_enumerate_children (resource, path, 0, &error);
if (error)
{
g_printerr ("%s\n", error->message);
g_error_free (error);
return;
}
for (i = 0; children[i]; i++)
{
child = g_strconcat (path, children[i], NULL);
len = MIN (strlen (child), strlen (prefix));
if (strncmp (child, prefix, len) != 0)
continue;
if (g_resource_get_info (resource, child, 0, &size, &flags, NULL))
{
if (details)
g_print ("%s%s%6ld %s %s\n", section, section[0] ? " " : "", size, flags & G_RESOURCE_FLAGS_COMPRESSED ? "c" : "u", child);
else
g_print ("%s\n", child);
}
else
list_resource (resource, child, section, prefix, details);
g_free (child);
}
g_strfreev (children);
}
static void
extract_resource (GResource *resource,
const gchar *path)
{
GBytes *bytes;
bytes = g_resource_lookup_data (resource, path, 0, NULL);
if (bytes != NULL)
{
gconstpointer data;
gsize size, written;
data = g_bytes_get_data (bytes, &size);
written = fwrite (data, 1, size, stdout);
if (written < size)
g_printerr ("Data truncated\n");
g_bytes_unref (bytes);
}
else
{
g_printerr ("Can't find resource path %s\n", path);
}
}
/* Elf functions {{{1 */
#ifdef HAVE_LIBELF
static Elf *
get_elf (const gchar *file,
gint *fd)
{
Elf *elf;
if (elf_version (EV_CURRENT) == EV_NONE )
return NULL;
*fd = open (file, O_RDONLY);
if (*fd < 0)
return NULL;
elf = elf_begin (*fd, ELF_C_READ, NULL);
if (elf == NULL)
return NULL;
if (elf_kind (elf) != ELF_K_ELF)
return NULL;
return elf;
}
typedef gboolean (*SectionCallback) (GElf_Shdr *shdr,
const gchar *name,
gpointer data);
static void
elf_foreach_resource_section (Elf *elf,
SectionCallback callback,
gpointer data)
{
size_t shstrndx, shnum;
size_t scnidx;
Elf_Scn *scn;
GElf_Shdr *shdr, shdr_mem;
const gchar *section_name;
elf_getshdrstrndx (elf, &shstrndx);
g_assert (shstrndx >= 0);
elf_getshdrnum (elf, &shnum);
g_assert (shnum >= 0);
for (scnidx = 1; scnidx < shnum; scnidx++)
{
scn = elf_getscn (elf, scnidx);
if (scn == NULL)
continue;
shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr == NULL)
continue;
if (shdr->sh_type != SHT_PROGBITS)
continue;
section_name = elf_strptr (elf, shstrndx, shdr->sh_name);
if (section_name == NULL ||
!g_str_has_prefix (section_name, ".gresource."))
continue;
if (!callback (shdr, section_name + strlen (".gresource."), data))
break;
}
}
static GResource *
resource_from_section (GElf_Shdr *shdr,
int fd)
{
gsize page_size, page_offset;
char *contents;
GResource *resource;
resource = NULL;
page_size = sysconf(_SC_PAGE_SIZE);
page_offset = shdr->sh_offset % page_size;
contents = mmap (NULL, shdr->sh_size + page_offset,
PROT_READ, MAP_PRIVATE, fd, shdr->sh_offset - page_offset);
if (contents != MAP_FAILED)
{
GBytes *bytes;
bytes = g_bytes_new_static (contents + page_offset, shdr->sh_size);
resource = g_resource_new_from_data (bytes, NULL);
g_bytes_unref (bytes);
}
else
{
g_printerr ("Can't mmap resource section");
}
return resource;
}
typedef struct
{
int fd;
const gchar *section;
const gchar *path;
gboolean details;
gboolean found;
} CallbackData;
static gboolean
list_resources_cb (GElf_Shdr *shdr,
const gchar *section,
gpointer data)
{
CallbackData *d = data;
GResource *resource;
if (d->section && strcmp (section, d->section) != 0)
return TRUE;
d->found = TRUE;
resource = resource_from_section (shdr, d->fd);
list_resource (resource, "/",
d->section ? "" : section,
d->path,
d->details);
g_resource_unref (resource);
if (d->section)
return FALSE;
return TRUE;
}
static void
elf_list_resources (Elf *elf,
int fd,
const gchar *section,
const gchar *path,
gboolean details)
{
CallbackData data;
data.fd = fd;
data.section = section;
data.path = path;
data.details = details;
data.found = FALSE;
elf_foreach_resource_section (elf, list_resources_cb, &data);
if (!data.found)
g_printerr ("Can't find resource section %s\n", section);
}
static gboolean
extract_resource_cb (GElf_Shdr *shdr,
const gchar *section,
gpointer data)
{
CallbackData *d = data;
GResource *resource;
if (d->section && strcmp (section, d->section) != 0)
return TRUE;
d->found = TRUE;
resource = resource_from_section (shdr, d->fd);
extract_resource (resource, d->path);
g_resource_unref (resource);
return FALSE;
}
static void
elf_extract_resource (Elf *elf,
int fd,
const gchar *section,
const gchar *path)
{
CallbackData data;
data.fd = fd;
data.section = section;
data.path = path;
data.found = FALSE;
elf_foreach_resource_section (elf, extract_resource_cb, &data);
if (!data.found)
g_printerr ("Can't find resource section %s\n", section);
}
static gboolean
print_section_name (GElf_Shdr *shdr,
const gchar *name,
gpointer data)
{
g_print ("%s\n", name);
return TRUE;
}
#endif /* HAVE_LIBELF */
/* Toplevel commands {{{1 */
static void
cmd_sections (const gchar *file,
const gchar *section,
const gchar *path,
gboolean details)
{
GResource *resource;
#ifdef HAVE_LIBELF
Elf *elf;
gint fd;
if ((elf = get_elf (file, &fd)))
{
elf_foreach_resource_section (elf, print_section_name, NULL);
elf_end (elf);
close (fd);
}
else
#endif
if ((resource = get_resource (file)))
{
/* No sections */
g_resource_unref (resource);
}
else
{
g_printerr ("Don't know how to handle %s\n", file);
#ifndef HAVE_LIBELF
g_printerr ("gresource is built without elf support\n");
#endif
}
}
static void
cmd_list (const gchar *file,
const gchar *section,
const gchar *path,
gboolean details)
{
GResource *resource;
#ifdef HAVE_LIBELF
Elf *elf;
int fd;
if ((elf = get_elf (file, &fd)))
{
elf_list_resources (elf, fd, section, path ? path : "", details);
elf_end (elf);
close (fd);
}
else
#endif
if ((resource = get_resource (file)))
{
list_resource (resource, "/", "", path ? path : "", details);
g_resource_unref (resource);
}
else
{
g_printerr ("Don't know how to handle %s\n", file);
#ifndef HAVE_LIBELF
g_printerr ("gresource is built without elf support\n");
#endif
}
}
static void
cmd_extract (const gchar *file,
const gchar *section,
const gchar *path,
gboolean details)
{
GResource *resource;
#ifdef HAVE_LIBELF
Elf *elf;
int fd;
if ((elf = get_elf (file, &fd)))
{
elf_extract_resource (elf, fd, section, path);
elf_end (elf);
close (fd);
}
else
#endif
if ((resource = get_resource (file)))
{
extract_resource (resource, path);
g_resource_unref (resource);
}
else
{
g_printerr ("Don't know how to handle %s\n", file);
#ifndef HAVE_LIBELF
g_printerr ("gresource is built without elf support\n");
#endif
}
}
static gint
cmd_help (gboolean requested,
const gchar *command)
{
const gchar *description;
const gchar *synopsis;
gchar *option;
GString *string;
option = NULL;
string = g_string_new (NULL);
if (command == NULL)
;
else if (strcmp (command, "help") == 0)
{
description = _("Print help");
synopsis = "[COMMAND]";
}
else if (strcmp (command, "sections") == 0)
{
description = _("List sections containing resources in an elf FILE");
synopsis = "FILE";
}
else if (strcmp (command, "list") == 0)
{
description = _("List resources\n"
"If SECTION is given, only list resources in this section\n"
"If PATH is given, only list matching resources");
synopsis = "FILE [PATH]";
option = g_strdup_printf ("[--section %s]", _("SECTION"));
}
else if (strcmp (command, "details") == 0)
{
description = _("List resources with details\n"
"If SECTION is given, only list resources in this section\n"
"If PATH is given, only list matching resources\n"
"Details include the section, size and compression");
synopsis = "FILE [PATH]";
option = g_strdup_printf ("[--section %s]", _("SECTION"));
}
else if (strcmp (command, "extract") == 0)
{
description = _("Extract a resource file to stdout");
synopsis = "FILE PATH";
option = g_strdup_printf ("[--section %s]", _("SECTION"));
}
else
{
g_string_printf (string, _("Unknown command %s\n\n"), command);
requested = FALSE;
command = NULL;
}
if (command == NULL)
{
g_string_append (string,
_("Usage:\n"
" gresource [--section SECTION] COMMAND [ARGS...]\n"
"\n"
"Commands:\n"
" help Show this information\n"
" sections List resource sections\n"
" list List resources\n"
" details List resources with details\n"
" extract Extract a resource\n"
"\n"
"Use 'gresource help COMMAND' to get detailed help.\n\n"));
}
else
{
g_string_append_printf (string, _("Usage:\n gresource %s%s%s %s\n\n%s\n\n"),
option ? option : "", option ? " " : "", command, synopsis[0] ? _(synopsis) : "", description);
g_string_append (string, _("Arguments:\n"));
if (option)
g_string_append (string,
_(" SECTION An (optional) elf section name\n"));
if (strstr (synopsis, "[COMMAND]"))
g_string_append (string,
_(" COMMAND The (optional) command to explain\n"));
if (strstr (synopsis, "FILE"))
{
if (strcmp (command, "sections") == 0)
g_string_append (string,
_(" FILE An elf file (a binary or a shared library)\n"));
else
g_string_append (string,
_(" FILE An elf file (a binary or a shared library)\n"
" or a compiled resource file\n"));
}
if (strstr (synopsis, "[PATH"))
g_string_append (string,
_(" PATH An (optional) resource path (may be partial)\n"));
else if (strstr (synopsis, "PATH"))
g_string_append (string,
_(" PATH A resource path\n"));
g_string_append (string, "\n");
}
if (requested)
g_print ("%s", string->str);
else
g_printerr ("%s\n", string->str);
g_free (option);
g_string_free (string, TRUE);
return requested ? 0 : 1;
}
/* main {{{1 */
int
main (int argc, char *argv[])
{
gchar *section = NULL;
gboolean details = FALSE;
void (* function) (const gchar *, const gchar *, const gchar *, gboolean);
g_type_init ();
if (argc < 2)
return cmd_help (FALSE, NULL);
if (argc > 3 && strcmp (argv[1], "--section") == 0)
{
section = argv[2];
argv = argv + 2;
argc -= 2;
}
if (strcmp (argv[1], "help") == 0)
return cmd_help (TRUE, argv[2]);
else if (argc == 4 && strcmp (argv[1], "extract") == 0)
function = cmd_extract;
else if (argc == 3 && strcmp (argv[1], "sections") == 0)
function = cmd_sections;
else if ((argc == 3 || argc == 4) && strcmp (argv[1], "list") == 0)
{
function = cmd_list;
details = FALSE;
}
else if ((argc == 3 || argc == 4) && strcmp (argv[1], "details") == 0)
{
function = cmd_list;
details = TRUE;
}
else
return cmd_help (FALSE, argv[1]);
(* function) (argv[2], section, argc > 3 ? argv[3] : NULL, details);
return 0;
}
/* vim:set foldmethod=marker: */