gkdbus: Fix underflow and unreachable code bug
[platform/upstream/glib.git] / 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: */