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: */