Blame libdwfl/gzip.c

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