#include <grub/dl.h>
#include <grub/types.h>
#include <grub/lib/crc.h>
+#include <grub/deflate.h>
#define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
grub_uint64_t extstart;
grub_uint64_t extino;
grub_uint64_t exttree;
+ grub_size_t extsize;
struct grub_btrfs_extent_data *extent;
};
union
{
char inl[0];
- grub_uint64_t laddr;
+ struct
+ {
+ grub_uint64_t laddr;
+ grub_uint64_t compressed_size;
+ grub_uint64_t offset;
+ };
};
} __attribute__ ((packed));
#define GRUB_BTRFS_EXTENT_INLINE 0
#define GRUB_BTRFS_EXTENT_REGULAR 1
+#define GRUB_BTRFS_COMPRESSION_NONE 0
+#define GRUB_BTRFS_COMPRESSION_ZLIB 1
#define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
return -1;
}
data->extstart = grub_le_to_cpu64 (key_out.offset);
+ data->extsize = elemsize;
data->extent = grub_malloc (elemsize);
data->extino = ino;
data->exttree = tree;
return -1;
}
- if (data->extent->compression)
+ if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE
+ && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB)
{
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
- "compression not supported");
+ "compression type 0x%x not supported",
+ data->extent->compression);
return -1;
}
-
if (data->extent->encoding)
{
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
switch (data->extent->type)
{
case GRUB_BTRFS_EXTENT_INLINE:
- grub_memcpy (buf, data->extent->inl + extoff, csize);
+ if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
+ {
+ if (grub_zlib_decompress (data->extent->inl, data->extsize -
+ ((grub_uint8_t *) data->extent->inl
+ - (grub_uint8_t *) data->extent),
+ extoff, buf, csize)
+ != (grub_ssize_t) csize)
+ return -1;
+ }
+ else
+ grub_memcpy (buf, data->extent->inl + extoff, csize);
break;
case GRUB_BTRFS_EXTENT_REGULAR:
if (!data->extent->laddr)
grub_memset (buf, 0, csize);
break;
}
+ if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
+ {
+ char *tmp;
+ grub_uint64_t zsize;
+ zsize = grub_le_to_cpu64 (data->extent->compressed_size);
+ tmp = grub_malloc (zsize);
+ if (!tmp)
+ return -1;
+ err = grub_btrfs_read_logical (data,
+ grub_le_to_cpu64 (data->extent->laddr),
+ tmp, zsize);
+ if (err)
+ {
+ grub_free (tmp);
+ return -1;
+ }
+ if (grub_zlib_decompress (tmp, zsize, extoff
+ + grub_le_to_cpu64 (data->extent->offset),
+ buf, csize) != (grub_ssize_t) csize)
+ {
+ grub_free (tmp);
+ return -1;
+ }
+ grub_free (tmp);
+ break;
+ }
err = grub_btrfs_read_logical (data,
grub_le_to_cpu64 (data->extent->laddr)
+ extoff,
#include <grub/fs.h>
#include <grub/file.h>
#include <grub/dl.h>
+#include <grub/deflate.h>
/*
* Window Size
{
/* The underlying file object. */
grub_file_t file;
+ /* If input is in memory following fields are used instead of file. */
+ grub_size_t mem_input_size, mem_input_off;
+ grub_uint8_t *mem_input;
/* The offset at which the data starts in the underlying file. */
grub_off_t data_offset;
/* The type of current block. */
static struct grub_fs grub_gzio_fs;
/* Function prototypes */
-static void initialize_tables (grub_file_t file);
+static void initialize_tables (grub_gzio_t);
/* Eat variable-length header fields. */
static int
typedef unsigned long ulg;
static int
-test_header (grub_file_t file)
+test_gzip_header (grub_file_t file)
{
struct {
grub_uint16_t magic;
But how can we know the real original size? */
file->size = grub_le_to_cpu32 (orig_len);
- initialize_tables (file);
+ initialize_tables (gzio);
return 1;
}
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
-#define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(file))<<k;k+=8;}} while (0)
+#define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(gzio))<<k;k+=8;}} while (0)
#define DUMPBITS(n) do {b>>=(n);k-=(n);} while (0)
static int
-get_byte (grub_file_t file)
+get_byte (grub_gzio_t gzio)
{
- grub_gzio_t gzio = file->data;
+ if (gzio->mem_input)
+ {
+ if (gzio->mem_input_off < gzio->mem_input_size)
+ return gzio->mem_input[gzio->mem_input_off++];
+ return 0;
+ }
if (grub_file_tell (gzio->file) == (grub_off_t) gzio->data_offset
|| gzio->inbuf_d == INBUFSIZ)
return gzio->inbuf[gzio->inbuf_d++];
}
+static void
+gzio_seek (grub_gzio_t gzio, grub_off_t off)
+{
+ if (gzio->mem_input)
+ {
+ if (off > gzio->mem_input_size)
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "attempt to seek outside of the file");
+ else
+ gzio->mem_input_off = gzio->data_offset;
+ }
+ else
+ grub_file_seek (gzio->file, off);
+}
+
/* more function prototypes */
static int huft_build (unsigned *, unsigned, unsigned, ush *, ush *,
struct huft **, int *);
static int huft_free (struct huft *);
-static int inflate_codes_in_window (grub_file_t);
+static int inflate_codes_in_window (grub_gzio_t);
/* Given a list of code lengths and a maximum table size, make a set of
*/
static int
-inflate_codes_in_window (grub_file_t file)
+inflate_codes_in_window (grub_gzio_t gzio)
{
register unsigned e; /* table entry flag/number of extra bits */
unsigned n, d; /* length and index for copy */
unsigned ml, md; /* masks for bl and bd bits */
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
- grub_gzio_t gzio = file->data;
/* make local copies of globals */
d = gzio->inflate_d;
/* get header for an inflated type 0 (stored) block. */
static void
-init_stored_block (grub_file_t file)
+init_stored_block (grub_gzio_t gzio)
{
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
- grub_gzio_t gzio = file->data;
/* make local copies of globals */
b = gzio->bb; /* initialize bit buffer */
Huffman tables. */
static void
-init_fixed_block (grub_file_t file)
+init_fixed_block (grub_gzio_t gzio)
{
int i; /* temporary variable */
unsigned l[288]; /* length list for huft_build */
- grub_gzio_t gzio = file->data;
/* set up literal table */
for (i = 0; i < 144; i++)
/* get header for an inflated type 2 (dynamic Huffman codes) block. */
static void
-init_dynamic_block (grub_file_t file)
+init_dynamic_block (grub_gzio_t gzio)
{
int i; /* temporary variables */
unsigned j;
unsigned ll[286 + 30]; /* literal/length and distance code lengths */
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
- grub_gzio_t gzio = file->data;
/* make local bit buffer */
b = gzio->bb;
static void
-get_new_block (grub_file_t file)
+get_new_block (grub_gzio_t gzio)
{
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
- grub_gzio_t gzio = file->data;
/* make local bit buffer */
b = gzio->bb;
switch (gzio->block_type)
{
case INFLATE_STORED:
- init_stored_block (file);
+ init_stored_block (gzio);
break;
case INFLATE_FIXED:
- init_fixed_block (file);
+ init_fixed_block (gzio);
break;
case INFLATE_DYNAMIC:
- init_dynamic_block (file);
+ init_dynamic_block (gzio);
break;
default:
break;
static void
-inflate_window (grub_file_t file)
+inflate_window (grub_gzio_t gzio)
{
- grub_gzio_t gzio = file->data;
-
/* initialize window */
gzio->wp = 0;
if (gzio->last_block)
break;
- get_new_block (file);
+ get_new_block (gzio);
}
if (gzio->block_type > INFLATE_DYNAMIC)
while (gzio->block_len && w < WSIZE && grub_errno == GRUB_ERR_NONE)
{
- gzio->slide[w++] = get_byte (file);
+ gzio->slide[w++] = get_byte (gzio);
gzio->block_len--;
}
* Expand other kind of block.
*/
- if (inflate_codes_in_window (file))
+ if (inflate_codes_in_window (gzio))
{
huft_free (gzio->tl);
huft_free (gzio->td);
static void
-initialize_tables (grub_file_t file)
+initialize_tables (grub_gzio_t gzio)
{
- grub_gzio_t gzio = file->data;
-
gzio->saved_offset = 0;
- grub_file_seek (gzio->file, gzio->data_offset);
+ gzio_seek (gzio, gzio->data_offset);
/* Initialize the bit buffer. */
gzio->bk = 0;
file->fs = &grub_gzio_fs;
file->not_easly_seekable = 1;
- if (! test_header (file))
+ if (! test_gzip_header (file))
{
grub_free (gzio);
grub_free (file);
return file;
}
+static int
+test_zlib_header (grub_gzio_t gzio)
+{
+ grub_uint8_t cmf, flg;
+
+ cmf = get_byte (gzio);
+ flg = get_byte (gzio);
+
+ /* Check that compression method is DEFLATE. */
+ if ((cmf & 0xf) != DEFLATED)
+ {
+ grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format");
+ return 0;
+ }
+
+ if ((cmf * 256 + flg) % 31)
+ {
+ grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format");
+ return 0;
+ }
+
+ /* Dictionary isn't supported. */
+ if (flg & 0x20)
+ {
+ grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format");
+ return 0;
+ }
+
+ gzio->data_offset = 2;
+ initialize_tables (gzio);
+
+ return 1;
+}
+
static grub_ssize_t
-grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
+grub_gzio_read_real (grub_gzio_t gzio, grub_off_t offset,
+ char *buf, grub_size_t len)
{
grub_ssize_t ret = 0;
- grub_gzio_t gzio = file->data;
- grub_off_t offset;
/* Do we reset decompression to the beginning of the file? */
- if (gzio->saved_offset > file->offset + WSIZE)
- initialize_tables (file);
+ if (gzio->saved_offset > offset + WSIZE)
+ initialize_tables (gzio);
/*
* This loop operates upon uncompressed data only. The only
* window is within the range of data it needs.
*/
- offset = file->offset;
-
while (len > 0 && grub_errno == GRUB_ERR_NONE)
{
register grub_size_t size;
register char *srcaddr;
while (offset >= gzio->saved_offset)
- inflate_window (file);
+ inflate_window (gzio);
srcaddr = (char *) ((offset & (WSIZE - 1)) + gzio->slide);
size = gzio->saved_offset - offset;
return ret;
}
+static grub_ssize_t
+grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
+{
+ return grub_gzio_read_real (file->data, file->offset, buf, len);
+}
+
/* Release everything, including the underlying file object. */
static grub_err_t
grub_gzio_close (grub_file_t file)
return grub_errno;
}
+grub_ssize_t
+grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
+ char *outbuf, grub_size_t outsize)
+{
+ grub_gzio_t gzio = 0;
+ grub_ssize_t ret;
+
+ gzio = grub_zalloc (sizeof (*gzio));
+ if (! gzio)
+ return -1;
+ gzio->mem_input = (grub_uint8_t *) inbuf;
+ gzio->mem_input_size = insize;
+ gzio->mem_input_off = 0;
+
+ if (!test_zlib_header (gzio))
+ {
+ grub_free (gzio);
+ return -1;
+ }
+
+ ret = grub_gzio_read_real (gzio, off, outbuf, outsize);
+ grub_free (gzio);
+
+ /* FIXME: Check Adler. */
+ return ret;
+}
+
\f
static struct grub_fs grub_gzio_fs =