BtrFS zlib compression support
This commit is contained in:
parent
6333f1e9b6
commit
3be8e5ea96
4 changed files with 197 additions and 44 deletions
|
@ -95,6 +95,7 @@ library = {
|
|||
common = grub-core/script/main.c;
|
||||
common = grub-core/script/script.c;
|
||||
common = grub-core/script/argv.c;
|
||||
common = grub-core/io/gzio.c;
|
||||
};
|
||||
|
||||
program = {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <grub/dl.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/lib/crc.h>
|
||||
#include <grub/deflate.h>
|
||||
|
||||
#define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
|
||||
|
||||
|
@ -84,6 +85,7 @@ struct grub_btrfs_data
|
|||
grub_uint64_t extstart;
|
||||
grub_uint64_t extino;
|
||||
grub_uint64_t exttree;
|
||||
grub_size_t extsize;
|
||||
struct grub_btrfs_extent_data *extent;
|
||||
};
|
||||
|
||||
|
@ -187,13 +189,20 @@ struct grub_btrfs_extent_data
|
|||
union
|
||||
{
|
||||
char inl[0];
|
||||
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
|
||||
|
||||
|
@ -840,6 +849,7 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data,
|
|||
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;
|
||||
|
@ -870,14 +880,15 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data,
|
|||
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,
|
||||
|
@ -888,6 +899,16 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data,
|
|||
switch (data->extent->type)
|
||||
{
|
||||
case GRUB_BTRFS_EXTENT_INLINE:
|
||||
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:
|
||||
|
@ -896,6 +917,32 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data,
|
|||
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,
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <grub/fs.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/deflate.h>
|
||||
|
||||
/*
|
||||
* Window Size
|
||||
|
@ -58,6 +59,9 @@ struct grub_gzio
|
|||
{
|
||||
/* 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. */
|
||||
|
@ -100,7 +104,7 @@ typedef struct grub_gzio *grub_gzio_t;
|
|||
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
|
||||
|
@ -162,7 +166,7 @@ typedef unsigned short ush;
|
|||
typedef unsigned long ulg;
|
||||
|
||||
static int
|
||||
test_header (grub_file_t file)
|
||||
test_gzip_header (grub_file_t file)
|
||||
{
|
||||
struct {
|
||||
grub_uint16_t magic;
|
||||
|
@ -226,7 +230,7 @@ test_header (grub_file_t file)
|
|||
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;
|
||||
}
|
||||
|
@ -366,13 +370,18 @@ static ush mask_bits[] =
|
|||
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)
|
||||
|
@ -384,11 +393,26 @@ get_byte (grub_file_t file)
|
|||
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
|
||||
|
@ -615,7 +639,7 @@ huft_free (struct huft *t)
|
|||
*/
|
||||
|
||||
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 */
|
||||
|
@ -624,7 +648,6 @@ inflate_codes_in_window (grub_file_t file)
|
|||
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;
|
||||
|
@ -752,11 +775,10 @@ inflate_codes_in_window (grub_file_t file)
|
|||
/* 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 */
|
||||
|
@ -786,11 +808,10 @@ init_stored_block (grub_file_t file)
|
|||
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++)
|
||||
|
@ -833,7 +854,7 @@ init_fixed_block (grub_file_t file)
|
|||
/* 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;
|
||||
|
@ -846,7 +867,6 @@ init_dynamic_block (grub_file_t file)
|
|||
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;
|
||||
|
@ -977,11 +997,10 @@ init_dynamic_block (grub_file_t file)
|
|||
|
||||
|
||||
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;
|
||||
|
@ -1004,13 +1023,13 @@ get_new_block (grub_file_t file)
|
|||
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;
|
||||
|
@ -1019,10 +1038,8 @@ get_new_block (grub_file_t file)
|
|||
|
||||
|
||||
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;
|
||||
|
||||
|
@ -1037,7 +1054,7 @@ inflate_window (grub_file_t file)
|
|||
if (gzio->last_block)
|
||||
break;
|
||||
|
||||
get_new_block (file);
|
||||
get_new_block (gzio);
|
||||
}
|
||||
|
||||
if (gzio->block_type > INFLATE_DYNAMIC)
|
||||
|
@ -1060,7 +1077,7 @@ inflate_window (grub_file_t file)
|
|||
|
||||
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--;
|
||||
}
|
||||
|
||||
|
@ -1073,7 +1090,7 @@ inflate_window (grub_file_t file)
|
|||
* 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);
|
||||
|
@ -1089,12 +1106,10 @@ inflate_window (grub_file_t file)
|
|||
|
||||
|
||||
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;
|
||||
|
@ -1139,7 +1154,7 @@ grub_gzio_open (grub_file_t io)
|
|||
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);
|
||||
|
@ -1155,16 +1170,49 @@ grub_gzio_open (grub_file_t io)
|
|||
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
|
||||
|
@ -1172,15 +1220,13 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
|
|||
* 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;
|
||||
|
@ -1201,6 +1247,12 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
|
|||
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)
|
||||
|
@ -1218,6 +1270,33 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct grub_fs grub_gzio_fs =
|
||||
|
|
26
include/grub/deflate.h
Normal file
26
include/grub/deflate.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB 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 a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_DEFLATE_HEADER
|
||||
#define GRUB_DEFLATE_HEADER 1
|
||||
|
||||
grub_ssize_t
|
||||
grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
|
||||
char *outbuf, grub_size_t outsize);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue