(root)/
glib-2.79.0/
gio/
gresource-tool.c
       1  /*
       2   * Copyright © 2012 Red Hat, Inc
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General Public
      17   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   *
      19   * Author: Matthias Clasen
      20   */
      21  
      22  #include "config.h"
      23  
      24  #include <stdlib.h>
      25  #include <stdio.h>
      26  
      27  #include <sys/types.h>
      28  #include <sys/stat.h>
      29  #include <fcntl.h>
      30  #include <string.h>
      31  #include <locale.h>
      32  
      33  #ifdef HAVE_LIBELF
      34  #include <libelf.h>
      35  #include <gelf.h>
      36  #endif
      37  
      38  #ifdef HAVE_MMAP
      39  #include <sys/mman.h>
      40  #endif
      41  
      42  #include <gio/gio.h>
      43  #include <glib/gstdio.h>
      44  #include <gi18n.h>
      45  
      46  #include "glib/glib-private.h"
      47  
      48  #if defined(HAVE_LIBELF) && defined(HAVE_MMAP)
      49  #define USE_LIBELF
      50  #endif
      51  
      52  /* GResource functions {{{1 */
      53  static GResource *
      54  get_resource (const gchar *file)
      55  {
      56    gchar *content;
      57    gsize size;
      58    GResource *resource;
      59    GBytes *data;
      60  
      61    resource = NULL;
      62  
      63    if (g_file_get_contents (file, &content, &size, NULL))
      64      {
      65        data = g_bytes_new_take (content, size);
      66        resource = g_resource_new_from_data (data, NULL);
      67        g_bytes_unref (data);
      68      }
      69  
      70    return resource;
      71  }
      72  
      73  static void
      74  list_resource (GResource   *resource,
      75                 const gchar *path,
      76                 const gchar *section,
      77                 const gchar *prefix,
      78                 gboolean     details)
      79  {
      80    gchar **children;
      81    gsize size;
      82    guint32 flags;
      83    gint i;
      84    gchar *child;
      85    GError *error = NULL;
      86    gint len;
      87  
      88    children = g_resource_enumerate_children (resource, path, 0, &error);
      89    if (error)
      90      {
      91        g_printerr ("%s\n", error->message);
      92        g_error_free (error);
      93        return;
      94      }
      95    for (i = 0; children[i]; i++)
      96      {
      97        child = g_strconcat (path, children[i], NULL);
      98  
      99        len = MIN (strlen (child), strlen (prefix));
     100        if (strncmp (child, prefix, len) != 0)
     101          {
     102            g_free (child);
     103            continue;
     104          }
     105  
     106        if (g_resource_get_info (resource, child, 0, &size, &flags, NULL))
     107          {
     108            if (details)
     109              g_print ("%s%s%6"G_GSIZE_FORMAT " %s %s\n", section, section[0] ? " " : "", size, (flags & G_RESOURCE_FLAGS_COMPRESSED) ? "c" : "u", child);
     110            else
     111              g_print ("%s\n", child);
     112          }
     113        else
     114          list_resource (resource, child, section, prefix, details);
     115  
     116        g_free (child);
     117      }
     118    g_strfreev (children);
     119  }
     120  
     121  static void
     122  extract_resource (GResource   *resource,
     123                    const gchar *path)
     124  {
     125    GBytes *bytes;
     126  
     127    bytes = g_resource_lookup_data (resource, path, 0, NULL);
     128    if (bytes != NULL)
     129      {
     130        gconstpointer data;
     131        gsize size, written;
     132  
     133        data = g_bytes_get_data (bytes, &size);
     134        written = fwrite (data, 1, size, stdout);
     135        if (written < size)
     136          g_printerr ("Data truncated\n");
     137        g_bytes_unref (bytes);
     138      }
     139  }
     140  
     141  /* Elf functions {{{1 */
     142  
     143  #ifdef USE_LIBELF
     144  
     145  static Elf *
     146  get_elf (const gchar *file,
     147           gint        *fd)
     148  {
     149    Elf *elf;
     150  
     151    if (elf_version (EV_CURRENT) == EV_NONE )
     152      return NULL;
     153  
     154    *fd = g_open (file, O_RDONLY, 0);
     155    if (*fd < 0)
     156      return NULL;
     157  
     158    elf = elf_begin (*fd, ELF_C_READ, NULL);
     159    if (elf == NULL)
     160      {
     161        g_close (*fd, NULL);
     162        *fd = -1;
     163        return NULL;
     164      }
     165  
     166    if (elf_kind (elf) != ELF_K_ELF)
     167      {
     168        g_close (*fd, NULL);
     169        *fd = -1;
     170        return NULL;
     171      }
     172  
     173    return elf;
     174  }
     175  
     176  typedef gboolean (*SectionCallback) (GElf_Shdr   *shdr,
     177                                       const gchar *name,
     178                                       gpointer     data);
     179  
     180  static void
     181  elf_foreach_resource_section (Elf             *elf,
     182                                SectionCallback  callback,
     183                                gpointer         data)
     184  {
     185    int ret G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
     186    size_t shstrndx, shnum;
     187    size_t scnidx;
     188    Elf_Scn *scn;
     189    GElf_Shdr *shdr, shdr_mem;
     190    const gchar *section_name;
     191  
     192    ret = elf_getshdrstrndx (elf, &shstrndx);
     193    g_assert (ret == 0);
     194  
     195    ret = elf_getshdrnum (elf, &shnum);
     196    g_assert (ret == 0);
     197  
     198    for (scnidx = 1; scnidx < shnum; scnidx++)
     199      {
     200        scn = elf_getscn (elf, scnidx);
     201        if (scn == NULL)
     202          continue;
     203  
     204        shdr = gelf_getshdr (scn, &shdr_mem);
     205        if (shdr == NULL)
     206          continue;
     207  
     208        if (shdr->sh_type != SHT_PROGBITS)
     209          continue;
     210  
     211        section_name = elf_strptr (elf, shstrndx, shdr->sh_name);
     212        if (section_name == NULL ||
     213            !g_str_has_prefix (section_name, ".gresource."))
     214          continue;
     215  
     216        if (!callback (shdr, section_name + strlen (".gresource."), data))
     217          break;
     218      }
     219  }
     220  
     221  static GResource *
     222  resource_from_section (GElf_Shdr *shdr,
     223                         int        fd)
     224  {
     225    gsize page_size, page_offset;
     226    char *contents;
     227    GResource *resource;
     228  
     229    resource = NULL;
     230  
     231    page_size = sysconf(_SC_PAGE_SIZE);
     232    page_offset = shdr->sh_offset % page_size;
     233    contents = mmap (NULL,  shdr->sh_size + page_offset,
     234                     PROT_READ, MAP_PRIVATE, fd, shdr->sh_offset - page_offset);
     235    if (contents != MAP_FAILED)
     236      {
     237        GBytes *bytes;
     238        GError *error = NULL;
     239  
     240        bytes = g_bytes_new_static (contents + page_offset, shdr->sh_size);
     241        resource = g_resource_new_from_data (bytes, &error);
     242        g_bytes_unref (bytes);
     243        if (error)
     244          {
     245            g_printerr ("%s\n", error->message);
     246            g_error_free (error);
     247          }
     248      }
     249    else
     250      {
     251        g_printerr ("Can't mmap resource section");
     252      }
     253  
     254    return resource;
     255  }
     256  
     257  typedef struct
     258  {
     259    int fd;
     260    const gchar *section;
     261    const gchar *path;
     262    gboolean details;
     263    gboolean found;
     264  } CallbackData;
     265  
     266  static gboolean
     267  list_resources_cb (GElf_Shdr   *shdr,
     268                     const gchar *section,
     269                     gpointer     data)
     270  {
     271    CallbackData *d = data;
     272    GResource *resource;
     273  
     274    if (d->section && strcmp (section, d->section) != 0)
     275      return TRUE;
     276  
     277    d->found = TRUE;
     278  
     279    resource = resource_from_section (shdr, d->fd);
     280    list_resource (resource, "/",
     281                   d->section ? "" : section,
     282                   d->path,
     283                   d->details);
     284    g_resource_unref (resource);
     285  
     286    if (d->section)
     287      return FALSE;
     288  
     289    return TRUE;
     290  }
     291  
     292  static void
     293  elf_list_resources (Elf         *elf,
     294                      int          fd,
     295                      const gchar *section,
     296                      const gchar *path,
     297                      gboolean     details)
     298  {
     299    CallbackData data;
     300  
     301    data.fd = fd;
     302    data.section = section;
     303    data.path = path;
     304    data.details = details;
     305    data.found = FALSE;
     306  
     307    elf_foreach_resource_section (elf, list_resources_cb, &data);
     308  
     309    if (!data.found)
     310      g_printerr ("Can't find resource section %s\n", section);
     311  }
     312  
     313  static gboolean
     314  extract_resource_cb (GElf_Shdr   *shdr,
     315                       const gchar *section,
     316                       gpointer     data)
     317  {
     318    CallbackData *d = data;
     319    GResource *resource;
     320  
     321    if (d->section && strcmp (section, d->section) != 0)
     322      return TRUE;
     323  
     324    d->found = TRUE;
     325  
     326    resource = resource_from_section (shdr, d->fd);
     327    extract_resource (resource, d->path);
     328    g_resource_unref (resource);
     329  
     330    if (d->section)
     331      return FALSE;
     332  
     333    return TRUE;
     334  }
     335  
     336  static void
     337  elf_extract_resource (Elf         *elf,
     338                        int          fd,
     339                        const gchar *section,
     340                        const gchar *path)
     341  {
     342    CallbackData data;
     343  
     344    data.fd = fd;
     345    data.section = section;
     346    data.path = path;
     347    data.found = FALSE;
     348  
     349    elf_foreach_resource_section (elf, extract_resource_cb, &data);
     350  
     351    if (!data.found)
     352      g_printerr ("Can't find resource section %s\n", section);
     353  }
     354  
     355  static gboolean
     356  print_section_name (GElf_Shdr   *shdr,
     357                      const gchar *name,
     358                      gpointer     data)
     359  {
     360    g_print ("%s\n", name);
     361    return TRUE;
     362  }
     363  
     364  #endif /* USE_LIBELF */
     365  
     366    /* Toplevel commands {{{1 */
     367  
     368  static void
     369  cmd_sections (const gchar *file,
     370                const gchar *section,
     371                const gchar *path,
     372                gboolean     details)
     373  {
     374    GResource *resource;
     375  
     376  #ifdef USE_LIBELF
     377  
     378    Elf *elf;
     379    gint fd;
     380  
     381    if ((elf = get_elf (file, &fd)))
     382      {
     383        elf_foreach_resource_section (elf, print_section_name, NULL);
     384        elf_end (elf);
     385        close (fd);
     386      }
     387    else
     388  
     389  #endif
     390  
     391    if ((resource = get_resource (file)))
     392      {
     393        /* No sections */
     394        g_resource_unref (resource);
     395      }
     396    else
     397      {
     398        g_printerr ("Don't know how to handle %s\n", file);
     399  #ifndef USE_LIBELF
     400        g_printerr ("gresource is built without elf support\n");
     401  #endif
     402      }
     403  }
     404  
     405  static void
     406  cmd_list (const gchar *file,
     407            const gchar *section,
     408            const gchar *path,
     409            gboolean     details)
     410  {
     411    GResource *resource;
     412  
     413  #ifdef USE_LIBELF
     414    Elf *elf;
     415    int fd;
     416  
     417    if ((elf = get_elf (file, &fd)))
     418      {
     419        elf_list_resources (elf, fd, section, path ? path : "", details);
     420        elf_end (elf);
     421        close (fd);
     422      }
     423    else
     424  
     425  #endif
     426  
     427    if ((resource = get_resource (file)))
     428      {
     429        list_resource (resource, "/", "", path ? path : "", details);
     430        g_resource_unref (resource);
     431      }
     432    else
     433      {
     434        g_printerr ("Don't know how to handle %s\n", file);
     435  #ifndef USE_LIBELF
     436        g_printerr ("gresource is built without elf support\n");
     437  #endif
     438      }
     439  }
     440  
     441  static void
     442  cmd_extract (const gchar *file,
     443               const gchar *section,
     444               const gchar *path,
     445               gboolean     details)
     446  {
     447    GResource *resource;
     448  
     449  #ifdef USE_LIBELF
     450  
     451    Elf *elf;
     452    int fd;
     453  
     454    if ((elf = get_elf (file, &fd)))
     455      {
     456        elf_extract_resource (elf, fd, section, path);
     457        elf_end (elf);
     458        close (fd);
     459      }
     460    else
     461  
     462  #endif
     463  
     464    if ((resource = get_resource (file)))
     465      {
     466        extract_resource (resource, path);
     467        g_resource_unref (resource);
     468      }
     469    else
     470      {
     471        g_printerr ("Don't know how to handle %s\n", file);
     472  #ifndef USE_LIBELF
     473        g_printerr ("gresource is built without elf support\n");
     474  #endif
     475      }
     476  }
     477  
     478  static gint
     479  cmd_help (gboolean     requested,
     480            const gchar *command)
     481  {
     482    const gchar *description = NULL;
     483    const gchar *synopsis = NULL;
     484    gchar *option;
     485    GString *string;
     486  
     487    option = NULL;
     488  
     489    string = g_string_new (NULL);
     490  
     491    if (command == NULL)
     492      ;
     493  
     494    else if (strcmp (command, "help") == 0)
     495      {
     496        description = _("Print help");
     497        synopsis = _("[COMMAND]");
     498      }
     499  
     500    else if (strcmp (command, "sections") == 0)
     501      {
     502        description = _("List sections containing resources in an elf FILE");
     503        synopsis = _("FILE");
     504      }
     505  
     506    else if (strcmp (command, "list") == 0)
     507      {
     508        description = _("List resources\n"
     509                        "If SECTION is given, only list resources in this section\n"
     510                        "If PATH is given, only list matching resources");
     511        synopsis = _("FILE [PATH]");
     512        option = g_strdup_printf ("[--section %s]", _("SECTION"));
     513      }
     514  
     515    else if (strcmp (command, "details") == 0)
     516      {
     517        description = _("List resources with details\n"
     518                        "If SECTION is given, only list resources in this section\n"
     519                        "If PATH is given, only list matching resources\n"
     520                        "Details include the section, size and compression");
     521        synopsis = _("FILE [PATH]");
     522        option = g_strdup_printf ("[--section %s]", _("SECTION"));
     523      }
     524  
     525    else if (strcmp (command, "extract") == 0)
     526      {
     527        description = _("Extract a resource file to stdout");
     528        synopsis = _("FILE PATH");
     529        option = g_strdup_printf ("[--section %s]", _("SECTION"));
     530      }
     531  
     532    else
     533      {
     534        g_string_printf (string, _("Unknown command %s\n\n"), command);
     535        requested = FALSE;
     536        command = NULL;
     537      }
     538  
     539    if (command == NULL)
     540      {
     541        g_string_append (string,
     542        _("Usage:\n"
     543          "  gresource [--section SECTION] COMMAND [ARGS…]\n"
     544          "\n"
     545          "Commands:\n"
     546          "  help                      Show this information\n"
     547          "  sections                  List resource sections\n"
     548          "  list                      List resources\n"
     549          "  details                   List resources with details\n"
     550          "  extract                   Extract a resource\n"
     551          "\n"
     552          "Use “gresource help COMMAND” to get detailed help.\n\n"));
     553      }
     554    else
     555      {
     556        g_string_append_printf (string, _("Usage:\n  gresource %s%s%s %s\n\n%s\n\n"),
     557                                option ? option : "", option ? " " : "", command, synopsis[0] ? synopsis : "", description);
     558  
     559        g_string_append (string, _("Arguments:\n"));
     560  
     561        if (option)
     562          g_string_append (string,
     563                           _("  SECTION   An (optional) elf section name\n"));
     564  
     565        if (strstr (synopsis, _("[COMMAND]")))
     566          g_string_append (string,
     567                         _("  COMMAND   The (optional) command to explain\n"));
     568  
     569        if (strstr (synopsis, _("FILE")))
     570          {
     571            if (strcmp (command, "sections") == 0)
     572              g_string_append (string,
     573                               _("  FILE      An elf file (a binary or a shared library)\n"));
     574            else
     575              g_string_append (string,
     576                               _("  FILE      An elf file (a binary or a shared library)\n"
     577                                 "            or a compiled resource file\n"));
     578          }
     579  
     580        if (strstr (synopsis, _("[PATH]")))
     581          g_string_append (string,
     582                         _("  PATH      An (optional) resource path (may be partial)\n"));
     583        else if (strstr (synopsis, _("PATH")))
     584          g_string_append (string,
     585                         _("  PATH      A resource path\n"));
     586  
     587        g_string_append (string, "\n");
     588      }
     589  
     590    if (requested)
     591      g_print ("%s", string->str);
     592    else
     593      g_printerr ("%s\n", string->str);
     594  
     595    g_free (option);
     596    g_string_free (string, TRUE);
     597  
     598    return requested ? 0 : 1;
     599  }
     600  
     601  /* main {{{1 */
     602  
     603  int
     604  main (int argc, char *argv[])
     605  {
     606    gchar *section = NULL;
     607    gboolean details = FALSE;
     608    void (* function) (const gchar *, const gchar *, const gchar *, gboolean);
     609  
     610  #ifdef G_OS_WIN32
     611    gchar *tmp;
     612  #endif
     613  
     614    setlocale (LC_ALL, GLIB_DEFAULT_LOCALE);
     615    textdomain (GETTEXT_PACKAGE);
     616  
     617  #ifdef G_OS_WIN32
     618    tmp = _glib_get_locale_dir ();
     619    bindtextdomain (GETTEXT_PACKAGE, tmp);
     620    g_free (tmp);
     621  #else
     622    bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
     623  #endif
     624  
     625  #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
     626    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
     627  #endif
     628  
     629    if (argc < 2)
     630      return cmd_help (FALSE, NULL);
     631  
     632    if (argc > 3 && strcmp (argv[1], "--section") == 0)
     633      {
     634        section = argv[2];
     635        argv = argv + 2;
     636        argc -= 2;
     637      }
     638  
     639    if (strcmp (argv[1], "help") == 0)
     640      return cmd_help (TRUE, argv[2]);
     641  
     642    else if (argc == 4 && strcmp (argv[1], "extract") == 0)
     643      function = cmd_extract;
     644  
     645    else if (argc == 3 && strcmp (argv[1], "sections") == 0)
     646      function = cmd_sections;
     647  
     648    else if ((argc == 3 || argc == 4) && strcmp (argv[1], "list") == 0)
     649      {
     650        function = cmd_list;
     651        details = FALSE;
     652      }
     653    else if ((argc == 3 || argc == 4) && strcmp (argv[1], "details") == 0)
     654      {
     655        function = cmd_list;
     656        details = TRUE;
     657      }
     658    else
     659      return cmd_help (FALSE, argv[1]);
     660  
     661    (* function) (argv[2], section, argc > 3 ? argv[3] : NULL, details);
     662  
     663    return 0;
     664  }
     665  
     666  /* vim:set foldmethod=marker: */