/* * Copyright © 2012 Red Hat, Inc * * SPDX-License-Identifier: LGPL-2.1-or-later * * 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 . * * Author: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_LIBELF #include #include #endif #ifdef HAVE_MMAP #include #endif #include #include #include #include "glib/glib-private.h" #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; size_t 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) { elf_end (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) { int ret G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */; size_t shstrndx, shnum; size_t scnidx; Elf_Scn *scn; GElf_Shdr *shdr, shdr_mem; const gchar *section_name; ret = elf_getshdrstrndx (elf, &shstrndx); g_assert (ret == 0); ret = elf_getshdrnum (elf, &shnum); g_assert (ret == 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 = NULL; const gchar *synopsis = NULL; 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, GLIB_DEFAULT_LOCALE); 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: */