Squashed 'third_party/elfutils/' content from commit 555e15e
Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdwfl/gzip.c b/libdwfl/gzip.c
new file mode 100644
index 0000000..c2c13ba
--- /dev/null
+++ b/libdwfl/gzip.c
@@ -0,0 +1,328 @@
+/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "system.h"
+
+#include <unistd.h>
+
+#ifdef LZMA
+# define USE_INFLATE 1
+# include <lzma.h>
+# define unzip __libdw_unlzma
+# define DWFL_E_ZLIB DWFL_E_LZMA
+# define MAGIC "\xFD" "7zXZ\0" /* XZ file format. */
+# define MAGIC2 "\x5d\0" /* Raw LZMA format. */
+# define Z(what) LZMA_##what
+# define LZMA_ERRNO LZMA_PROG_ERROR
+# define z_stream lzma_stream
+# define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
+# define do_inflate(z) lzma_code (z, LZMA_RUN)
+# define inflateEnd(z) lzma_end (z)
+#elif defined BZLIB
+# define USE_INFLATE 1
+# include <bzlib.h>
+# define unzip __libdw_bunzip2
+# define DWFL_E_ZLIB DWFL_E_BZLIB
+# define MAGIC "BZh"
+# define Z(what) BZ_##what
+# define BZ_ERRNO BZ_IO_ERROR
+# define z_stream bz_stream
+# define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
+# define do_inflate(z) BZ2_bzDecompress (z)
+# define inflateEnd(z) BZ2_bzDecompressEnd (z)
+#else
+# define USE_INFLATE 0
+# define crc32 loser_crc32
+# include <zlib.h>
+# define unzip __libdw_gunzip
+# define MAGIC "\037\213"
+# define Z(what) Z_##what
+#endif
+
+#define READ_SIZE (1 << 20)
+
+struct unzip_state {
+#if !USE_INFLATE
+ gzFile zf;
+#endif
+ size_t mapped_size;
+ void **whole;
+ void *buffer;
+ size_t size;
+ void *input_buffer;
+ off_t input_pos;
+};
+
+static inline bool
+bigger_buffer (struct unzip_state *state, size_t start)
+{
+ size_t more = state->size ? state->size * 2 : start;
+ char *b = realloc (state->buffer, more);
+ while (unlikely (b == NULL) && more >= state->size + 1024)
+ b = realloc (state->buffer, more -= 1024);
+ if (unlikely (b == NULL))
+ return false;
+ state->buffer = b;
+ state->size = more;
+ return true;
+}
+
+static inline void
+smaller_buffer (struct unzip_state *state, size_t end)
+{
+ state->buffer =
+ realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
+ state->size = end;
+}
+
+static inline Dwfl_Error
+fail (struct unzip_state *state, Dwfl_Error failure)
+{
+ if (state->input_pos == (off_t) state->mapped_size)
+ *state->whole = state->input_buffer;
+ else
+ {
+ free (state->input_buffer);
+ *state->whole = NULL;
+ }
+ free (state->buffer);
+ return failure;
+}
+
+static inline Dwfl_Error
+zlib_fail (struct unzip_state *state, int result)
+{
+ switch (result)
+ {
+ case Z (MEM_ERROR):
+ return fail (state, DWFL_E_NOMEM);
+ case Z (ERRNO):
+ return fail (state, DWFL_E_ERRNO);
+ default:
+ return fail (state, DWFL_E_ZLIB);
+ }
+}
+
+#if !USE_INFLATE
+static Dwfl_Error
+open_stream (int fd, off_t start_offset, struct unzip_state *state)
+{
+ int d = dup (fd);
+ if (unlikely (d < 0))
+ return DWFL_E_BADELF;
+ if (start_offset != 0)
+ {
+ off_t off = lseek (d, start_offset, SEEK_SET);
+ if (off != start_offset)
+ {
+ close (d);
+ return DWFL_E_BADELF;
+ }
+ }
+ state->zf = gzdopen (d, "r");
+ if (unlikely (state->zf == NULL))
+ {
+ close (d);
+ return zlib_fail (state, Z (MEM_ERROR));
+ }
+
+ /* From here on, zlib will close D. */
+
+ return DWFL_E_NOERROR;
+}
+#endif
+
+/* If this is not a compressed image, return DWFL_E_BADELF.
+ If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
+ Otherwise return an error for bad compressed data or I/O failure.
+ If we return an error after reading the first part of the file,
+ leave that portion malloc'd in *WHOLE, *WHOLE_SIZE. If *WHOLE
+ is not null on entry, we'll use it in lieu of repeating a read. */
+
+Dwfl_Error internal_function
+unzip (int fd, off_t start_offset,
+ void *mapped, size_t _mapped_size,
+ void **_whole, size_t *whole_size)
+{
+ struct unzip_state state =
+ {
+#if !USE_INFLATE
+ .zf = NULL,
+#endif
+ .mapped_size = _mapped_size,
+ .whole = _whole,
+ .buffer = NULL,
+ .size = 0,
+ .input_buffer = NULL,
+ .input_pos = 0
+ };
+
+ if (mapped == NULL)
+ {
+ if (*state.whole == NULL)
+ {
+ state.input_buffer = malloc (READ_SIZE);
+ if (unlikely (state.input_buffer == NULL))
+ return DWFL_E_NOMEM;
+
+ ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
+ if (unlikely (n < 0))
+ return zlib_fail (&state, Z (ERRNO));
+
+ state.input_pos = n;
+ mapped = state.input_buffer;
+ state.mapped_size = n;
+ }
+ else
+ {
+ state.input_buffer = *state.whole;
+ state.input_pos = state.mapped_size = *whole_size;
+ }
+ }
+
+#define NOMAGIC(magic) \
+ (state.mapped_size <= sizeof magic || \
+ memcmp (mapped, magic, sizeof magic - 1))
+
+ /* First, look at the header. */
+ if (NOMAGIC (MAGIC)
+#ifdef MAGIC2
+ && NOMAGIC (MAGIC2)
+#endif
+ )
+ /* Not a compressed file. */
+ return DWFL_E_BADELF;
+
+#if USE_INFLATE
+
+ /* This style actually only works with bzlib and liblzma.
+ The stupid zlib interface has nothing to grok the
+ gzip file headers except the slow gzFile interface. */
+
+ z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
+ int result = inflateInit (&z);
+ if (result != Z (OK))
+ {
+ inflateEnd (&z);
+ return zlib_fail (&state, result);
+ }
+
+ do
+ {
+ if (z.avail_in == 0 && state.input_buffer != NULL)
+ {
+ ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
+ start_offset + state.input_pos);
+ if (unlikely (n < 0))
+ {
+ inflateEnd (&z);
+ return zlib_fail (&state, Z (ERRNO));
+ }
+ z.next_in = state.input_buffer;
+ z.avail_in = n;
+ state.input_pos += n;
+ }
+ if (z.avail_out == 0)
+ {
+ ptrdiff_t pos = (void *) z.next_out - state.buffer;
+ if (!bigger_buffer (&state, z.avail_in))
+ {
+ result = Z (MEM_ERROR);
+ break;
+ }
+ z.next_out = state.buffer + pos;
+ z.avail_out = state.size - pos;
+ }
+ }
+ while ((result = do_inflate (&z)) == Z (OK));
+
+#ifdef BZLIB
+ uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
+ | z.total_out_lo32);
+ smaller_buffer (&state, total_out);
+#else
+ smaller_buffer (&state, z.total_out);
+#endif
+
+ inflateEnd (&z);
+
+ if (result != Z (STREAM_END))
+ return zlib_fail (&state, result);
+
+#else /* gzip only. */
+
+ /* Let the decompression library read the file directly. */
+
+ Dwfl_Error result = open_stream (fd, start_offset, &state);
+
+ if (result == DWFL_E_NOERROR && gzdirect (state.zf))
+ {
+ gzclose (state.zf);
+ return fail (&state, DWFL_E_BADELF);
+ }
+
+ if (result != DWFL_E_NOERROR)
+ return fail (&state, result);
+
+ ptrdiff_t pos = 0;
+ while (1)
+ {
+ if (!bigger_buffer (&state, 1024))
+ {
+ gzclose (state.zf);
+ return zlib_fail (&state, Z (MEM_ERROR));
+ }
+ int n = gzread (state.zf, state.buffer + pos, state.size - pos);
+ if (n < 0)
+ {
+ int code;
+ gzerror (state.zf, &code);
+ gzclose (state.zf);
+ return zlib_fail (&state, code);
+ }
+ if (n == 0)
+ break;
+ pos += n;
+ }
+
+ gzclose (state.zf);
+ smaller_buffer (&state, pos);
+#endif
+
+ free (state.input_buffer);
+
+ *state.whole = state.buffer;
+ *whole_size = state.size;
+
+ return DWFL_E_NOERROR;
+}