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