mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
70ad484508
The gresource code uses libelf if available but that also depends on mmap but isn't guarded with HAVE_MMAP. This can make the build fail under MSYS2 where a mingw version of libelf exists but there is no mmap. Instead of guarting the libelf code with HAVE_LIBELF add a new macro named USE_LIBELF which is only defined if libelf and mmap support are available. Also install the mingw libelf version for CI so we catch similar errors in the future.
666 lines
15 KiB
C
666 lines
15 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.1 of the License, 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* 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 <locale.h>
|
|
|
|
#ifdef HAVE_LIBELF
|
|
#include <libelf.h>
|
|
#include <gelf.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_MMAP
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#include <gio/gio.h>
|
|
#include <glib/gstdio.h>
|
|
#include <gi18n.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include "glib/glib-private.h"
|
|
#endif
|
|
|
|
#if defined(HAVE_LIBELF) && defined(HAVE_MMAP)
|
|
#define USE_LIBELF
|
|
#endif
|
|
|
|
/* 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)
|
|
{
|
|
g_free (child);
|
|
continue;
|
|
}
|
|
|
|
if (g_resource_get_info (resource, child, 0, &size, &flags, NULL))
|
|
{
|
|
if (details)
|
|
g_print ("%s%s%6"G_GSIZE_FORMAT " %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);
|
|
}
|
|
}
|
|
|
|
/* Elf functions {{{1 */
|
|
|
|
#ifdef USE_LIBELF
|
|
|
|
static Elf *
|
|
get_elf (const gchar *file,
|
|
gint *fd)
|
|
{
|
|
Elf *elf;
|
|
|
|
if (elf_version (EV_CURRENT) == EV_NONE )
|
|
return NULL;
|
|
|
|
*fd = g_open (file, O_RDONLY, 0);
|
|
if (*fd < 0)
|
|
return NULL;
|
|
|
|
elf = elf_begin (*fd, ELF_C_READ, NULL);
|
|
if (elf == NULL)
|
|
{
|
|
g_close (*fd, NULL);
|
|
*fd = -1;
|
|
return NULL;
|
|
}
|
|
|
|
if (elf_kind (elf) != ELF_K_ELF)
|
|
{
|
|
g_close (*fd, NULL);
|
|
*fd = -1;
|
|
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;
|
|
GError *error = NULL;
|
|
|
|
bytes = g_bytes_new_static (contents + page_offset, shdr->sh_size);
|
|
resource = g_resource_new_from_data (bytes, &error);
|
|
g_bytes_unref (bytes);
|
|
if (error)
|
|
{
|
|
g_printerr ("%s\n", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
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);
|
|
|
|
if (d->section)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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 /* USE_LIBELF */
|
|
|
|
/* Toplevel commands {{{1 */
|
|
|
|
static void
|
|
cmd_sections (const gchar *file,
|
|
const gchar *section,
|
|
const gchar *path,
|
|
gboolean details)
|
|
{
|
|
GResource *resource;
|
|
|
|
#ifdef USE_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 USE_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 USE_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 USE_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 USE_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 USE_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);
|
|
|
|
#ifdef G_OS_WIN32
|
|
gchar *tmp;
|
|
#endif
|
|
|
|
setlocale (LC_ALL, "");
|
|
textdomain (GETTEXT_PACKAGE);
|
|
|
|
#ifdef G_OS_WIN32
|
|
tmp = _glib_get_locale_dir ();
|
|
bindtextdomain (GETTEXT_PACKAGE, tmp);
|
|
g_free (tmp);
|
|
#else
|
|
bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
|
|
#endif
|
|
|
|
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
#endif
|
|
|
|
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: */
|