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