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