Add initial support for lzop files decompression.
* grub-core/Makefile.core.def (lzopio): New module. * grub-core/io/lzopio.c: New file. * include/grub/file.h (grub_file_filter_id): New compression filter GRUB_FILE_FILTER_LZOPIO.
This commit is contained in:
parent
095f077e6d
commit
d6beefcfc9
4 changed files with 545 additions and 0 deletions
|
@ -1,3 +1,12 @@
|
|||
2011-08-17 Szymon Janc <szymon@janc.net.pl>
|
||||
|
||||
Add initial support for lzop files decompression.
|
||||
|
||||
* grub-core/Makefile.core.def (lzopio): New module.
|
||||
* grub-core/io/lzopio.c: New file.
|
||||
* include/grub/file.h (grub_file_filter_id): New compression filter
|
||||
GRUB_FILE_FILTER_LZOPIO.
|
||||
|
||||
2011-08-14 Szymon Janc <szymon@janc.net.pl>
|
||||
|
||||
Add support for LZO compression in btrfs.
|
||||
|
|
|
@ -1651,6 +1651,14 @@ module = {
|
|||
cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed';
|
||||
};
|
||||
|
||||
module = {
|
||||
name = lzopio;
|
||||
common = io/lzopio.c;
|
||||
common = lib/minilzo/minilzo.c;
|
||||
cflags = '$(CFLAGS_POSIX) -Wno-undef';
|
||||
cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H';
|
||||
};
|
||||
|
||||
module = {
|
||||
name = testload;
|
||||
common = commands/testload.c;
|
||||
|
|
527
grub-core/io/lzopio.c
Normal file
527
grub-core/io/lzopio.c
Normal file
|
@ -0,0 +1,527 @@
|
|||
/* lzopio.c - decompression support for lzop */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2011 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/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/fs.h>
|
||||
#include <grub/dl.h>
|
||||
|
||||
#include "minilzo.h"
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
|
||||
#define LZOP_MAGIC_SIZE 9
|
||||
#define CHECK_SIZE 4
|
||||
#define NEW_LZO_LIB 0x0940
|
||||
#define MIN_HEADER_SIZE 0
|
||||
#define MAX_HEADER_SIZE 0
|
||||
|
||||
/* Header flags - copied from conf.h of LZOP source code. */
|
||||
#define F_ADLER32_D 0x00000001L
|
||||
#define F_ADLER32_C 0x00000002L
|
||||
#define F_STDIN 0x00000004L
|
||||
#define F_STDOUT 0x00000008L
|
||||
#define F_NAME_DEFAULT 0x00000010L
|
||||
#define F_DOSISH 0x00000020L
|
||||
#define F_H_EXTRA_FIELD 0x00000040L
|
||||
#define F_H_GMTDIFF 0x00000080L
|
||||
#define F_CRC32_D 0x00000100L
|
||||
#define F_CRC32_C 0x00000200L
|
||||
#define F_MULTIPART 0x00000400L
|
||||
#define F_H_FILTER 0x00000800L
|
||||
#define F_H_CRC32 0x00001000L
|
||||
#define F_H_PATH 0x00002000L
|
||||
#define F_MASK 0x00003FFFL
|
||||
|
||||
struct block_header
|
||||
{
|
||||
grub_uint32_t usize;
|
||||
grub_uint32_t csize;
|
||||
grub_uint32_t ucheck;
|
||||
grub_uint32_t ccheck;
|
||||
unsigned char *cdata;
|
||||
unsigned char *udata;
|
||||
};
|
||||
|
||||
struct grub_lzopio
|
||||
{
|
||||
grub_file_t file;
|
||||
int ucheck; /* XXX Use gcry to validate checks. */
|
||||
int ccheck;
|
||||
grub_off_t saved_off; /* Rounded down to block boundary. */
|
||||
grub_off_t start_block_off;
|
||||
struct block_header block;
|
||||
};
|
||||
|
||||
typedef struct grub_lzopio *grub_lzopio_t;
|
||||
static struct grub_fs grub_lzopio_fs;
|
||||
|
||||
/* Some helper functions. Memory allocated by those function is free either
|
||||
* on next read_block_header() or on close() so no risk of leaks. This makes
|
||||
* those function simpler. */
|
||||
|
||||
/* Read block header from file, after successful exit file points to
|
||||
* beginning of block data. */
|
||||
static int
|
||||
read_block_header (struct grub_lzopio *lzopio)
|
||||
{
|
||||
/* Free cached block data if any. */
|
||||
grub_free (lzopio->block.udata);
|
||||
grub_free (lzopio->block.cdata);
|
||||
lzopio->block.udata = NULL;
|
||||
lzopio->block.cdata = NULL;
|
||||
|
||||
if (grub_file_read (lzopio->file, &lzopio->block.usize,
|
||||
sizeof (lzopio->block.usize)) !=
|
||||
sizeof (lzopio->block.usize))
|
||||
return -1;
|
||||
|
||||
lzopio->block.usize = grub_be_to_cpu32 (lzopio->block.usize);
|
||||
|
||||
/* Last block has uncompressed data size == 0 and no other fields. */
|
||||
if (lzopio->block.usize == 0)
|
||||
{
|
||||
if (grub_file_tell (lzopio->file) == grub_file_size (lzopio->file))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read compressed data block size. */
|
||||
if (grub_file_read (lzopio->file, &lzopio->block.csize,
|
||||
sizeof (lzopio->block.csize)) !=
|
||||
sizeof (lzopio->block.csize))
|
||||
return -1;
|
||||
|
||||
lzopio->block.csize = grub_be_to_cpu32 (lzopio->block.csize);
|
||||
|
||||
/* XXX Handle incompressible data case here rather than in uncompress_block.
|
||||
* This will allow to handle switch of ccheck/ucheck easier and also
|
||||
* make uncompress_block() a bit simpler. */
|
||||
|
||||
/* Read data checks. */
|
||||
if (lzopio->ucheck)
|
||||
{
|
||||
if (grub_file_read (lzopio->file, &lzopio->block.ucheck,
|
||||
sizeof (lzopio->block.ucheck)) !=
|
||||
sizeof (lzopio->block.ucheck))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lzopio->ccheck)
|
||||
{
|
||||
if (grub_file_read (lzopio->file, &lzopio->block.ccheck,
|
||||
sizeof (lzopio->block.ccheck)) !=
|
||||
sizeof (lzopio->block.ccheck))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read block data into memory. File must be set to beginning of block data.
|
||||
* Can't be called on last block. */
|
||||
static int
|
||||
read_block_data (struct grub_lzopio *lzopio)
|
||||
{
|
||||
lzopio->block.cdata = grub_malloc (lzopio->block.csize);
|
||||
if (!lzopio->block.cdata)
|
||||
return -1;
|
||||
|
||||
if (grub_file_read (lzopio->file, lzopio->block.cdata, lzopio->block.csize)
|
||||
!= (grub_ssize_t) lzopio->block.csize)
|
||||
return -1;
|
||||
|
||||
if (lzopio->ccheck)
|
||||
{
|
||||
/* XXX Validate data checksum here. */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read block data, uncompressed and also store it in memory. */
|
||||
/* XXX Investigate possibility of in-place decompression to reduce memory
|
||||
* footprint. */
|
||||
static int
|
||||
uncompress_block (struct grub_lzopio *lzopio)
|
||||
{
|
||||
lzo_uint usize = lzopio->block.usize;
|
||||
|
||||
if (read_block_data (lzopio) < 0)
|
||||
return -1;
|
||||
|
||||
if (lzopio->block.usize > lzopio->block.csize)
|
||||
{
|
||||
lzopio->block.udata = grub_malloc (lzopio->block.usize);
|
||||
if (!lzopio->block.udata)
|
||||
return -1;
|
||||
|
||||
if (lzo1x_decompress_safe (lzopio->block.cdata, lzopio->block.csize,
|
||||
lzopio->block.udata, &usize,
|
||||
NULL) != LZO_E_OK)
|
||||
return -1;
|
||||
|
||||
if (lzopio->ucheck)
|
||||
{
|
||||
/* XXX Validate data checksum here. */
|
||||
}
|
||||
|
||||
/* Compressed data can be free now. */
|
||||
grub_free (lzopio->block.cdata);
|
||||
lzopio->block.cdata = NULL;
|
||||
}
|
||||
/* Incompressible block of data. */
|
||||
else if (lzopio->block.usize == lzopio->block.csize)
|
||||
{
|
||||
lzopio->block.udata = lzopio->block.cdata;
|
||||
lzopio->block.cdata = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Jump to next block and read its header. */
|
||||
static int
|
||||
jump_block (struct grub_lzopio *lzopio)
|
||||
{
|
||||
/* only jump if block was not decompressed (and read from disk) */
|
||||
if (!lzopio->block.udata)
|
||||
{
|
||||
grub_off_t off = grub_file_tell (lzopio->file) + lzopio->block.csize;
|
||||
|
||||
if (grub_file_seek (lzopio->file, off) == ((grub_off_t) - 1))
|
||||
return -1;
|
||||
}
|
||||
|
||||
lzopio->saved_off += lzopio->block.usize;
|
||||
|
||||
return read_block_header (lzopio);
|
||||
}
|
||||
|
||||
static int
|
||||
calculate_uncompressed_size (grub_file_t file)
|
||||
{
|
||||
grub_lzopio_t lzopio = file->data;
|
||||
grub_off_t usize_total = 0;
|
||||
|
||||
if (read_block_header (lzopio) < 0)
|
||||
return 0;
|
||||
|
||||
/* FIXME: Don't do this for not easily seekable files. */
|
||||
while (lzopio->block.usize != 0)
|
||||
{
|
||||
usize_total += lzopio->block.usize;
|
||||
|
||||
if (jump_block (lzopio) < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->size = usize_total;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* XXX Do something with this function, it is insane now:) */
|
||||
static int
|
||||
test_header (grub_file_t file)
|
||||
{
|
||||
grub_lzopio_t lzopio = file->data;
|
||||
unsigned char magic[LZOP_MAGIC_SIZE];
|
||||
grub_uint16_t lzopver, ver, ver_ext;
|
||||
grub_uint8_t method, level, name_len;
|
||||
grub_uint32_t flags, mode, filter, mtime_lo, mtime_hi, checksum;
|
||||
unsigned char *name = NULL;
|
||||
|
||||
if (grub_file_read (lzopio->file, magic, sizeof (magic)) != sizeof (magic))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "no lzop magic found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (grub_memcmp (magic, LZOP_MAGIC, LZOP_MAGIC_SIZE) != 0)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FILE_TYPE, "no lzop magic found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LZOP version. */
|
||||
if (grub_file_read (lzopio->file, &lzopver, sizeof (lzopver)) !=
|
||||
sizeof (lzopver))
|
||||
goto CORRUPTED;
|
||||
|
||||
/* LZO lib version. */
|
||||
if (grub_file_read (lzopio->file, &ver, sizeof (ver)) != sizeof (ver))
|
||||
goto CORRUPTED;
|
||||
|
||||
ver = grub_be_to_cpu16 (ver);
|
||||
|
||||
if (ver >= NEW_LZO_LIB)
|
||||
{
|
||||
/* Read version of lib needed to extract data. */
|
||||
if (grub_file_read (lzopio->file, &ver_ext, sizeof (ver_ext)) !=
|
||||
sizeof (ver_ext))
|
||||
goto CORRUPTED;
|
||||
|
||||
/* Too new version, should upgrade minilzo? */
|
||||
if (grub_be_to_cpu16 (ver_ext) > MINILZO_VERSION)
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
|
||||
"unsupported LZO version");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (grub_file_read (lzopio->file, &method, sizeof (method)) !=
|
||||
sizeof (method))
|
||||
goto CORRUPTED;
|
||||
|
||||
if (ver >= NEW_LZO_LIB)
|
||||
{
|
||||
if (grub_file_read (lzopio->file, &level, sizeof (level)) !=
|
||||
sizeof (level))
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
if (grub_file_read (lzopio->file, &flags, sizeof (flags)) != sizeof (flags))
|
||||
goto CORRUPTED;
|
||||
|
||||
flags = grub_be_to_cpu32 (flags);
|
||||
|
||||
if (flags & F_CRC32_D)
|
||||
lzopio->ucheck = 1;
|
||||
else if (flags & F_ADLER32_D)
|
||||
lzopio->ucheck = 2;
|
||||
|
||||
if (flags & F_CRC32_C)
|
||||
lzopio->ccheck = 1;
|
||||
else if (flags & F_ADLER32_C)
|
||||
lzopio->ccheck = 2;
|
||||
|
||||
if (flags & F_H_FILTER)
|
||||
{
|
||||
if (grub_file_read (lzopio->file, &filter, sizeof (filter)) !=
|
||||
sizeof (filter))
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
if (grub_file_read (lzopio->file, &mode, sizeof (mode)) != sizeof (mode))
|
||||
goto CORRUPTED;
|
||||
|
||||
if (grub_file_read (lzopio->file, &mtime_lo, sizeof (mtime_lo)) !=
|
||||
sizeof (mtime_lo))
|
||||
goto CORRUPTED;
|
||||
|
||||
if (ver >= NEW_LZO_LIB)
|
||||
{
|
||||
if (grub_file_read (lzopio->file, &mtime_hi, sizeof (mtime_hi)) !=
|
||||
sizeof (mtime_hi))
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
if (grub_file_read (lzopio->file, &name_len, sizeof (name_len)) !=
|
||||
sizeof (name_len))
|
||||
goto CORRUPTED;
|
||||
|
||||
if (name_len != 0)
|
||||
{
|
||||
name = grub_malloc (name_len);
|
||||
if (!name)
|
||||
return 0;
|
||||
|
||||
if (grub_file_read (lzopio->file, name, name_len) != name_len)
|
||||
{
|
||||
grub_free (name);
|
||||
goto CORRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (grub_file_read (lzopio->file, &checksum, sizeof (checksum)) !=
|
||||
sizeof (checksum))
|
||||
{
|
||||
grub_free (name);
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
grub_free (name);
|
||||
|
||||
/* XXX Validate header checksum here. */
|
||||
if (checksum == checksum)
|
||||
{
|
||||
lzopio->start_block_off = grub_file_tell (lzopio->file);
|
||||
|
||||
if (calculate_uncompressed_size (file) < 0)
|
||||
goto CORRUPTED;
|
||||
|
||||
lzopio->saved_off = 0;
|
||||
|
||||
/* Get back to start block. */
|
||||
grub_file_seek (lzopio->file, lzopio->start_block_off);
|
||||
|
||||
/* Read first block - grub_lzopio_read() expects valid block. */
|
||||
if (read_block_header (lzopio) < 0)
|
||||
goto CORRUPTED;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
CORRUPTED:
|
||||
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "lzop file corrupted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_file_t
|
||||
grub_lzopio_open (grub_file_t io)
|
||||
{
|
||||
grub_file_t file;
|
||||
grub_lzopio_t lzopio;
|
||||
|
||||
file = (grub_file_t) grub_zalloc (sizeof (*file));
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
lzopio = grub_zalloc (sizeof (*lzopio));
|
||||
if (!lzopio)
|
||||
{
|
||||
grub_free (file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lzopio->file = io;
|
||||
|
||||
file->device = io->device;
|
||||
file->offset = 0;
|
||||
file->data = lzopio;
|
||||
file->read_hook = 0;
|
||||
file->fs = &grub_lzopio_fs;
|
||||
file->size = GRUB_FILE_SIZE_UNKNOWN;
|
||||
file->not_easily_seekable = 1;
|
||||
|
||||
if (grub_file_tell (lzopio->file) != 0)
|
||||
grub_file_seek (lzopio->file, 0);
|
||||
|
||||
if (!test_header (file))
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
grub_file_seek (io, 0);
|
||||
grub_free (lzopio);
|
||||
grub_free (file);
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len)
|
||||
{
|
||||
grub_lzopio_t lzopio = file->data;
|
||||
grub_ssize_t ret = 0;
|
||||
|
||||
/* Backward seek before last read block. */
|
||||
if (lzopio->saved_off > grub_file_tell (file))
|
||||
{
|
||||
lzopio->saved_off = 0;
|
||||
grub_file_seek (lzopio->file, lzopio->start_block_off);
|
||||
|
||||
if (read_block_header (lzopio) < 0)
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
/* Forward to first block with requested data. */
|
||||
while (lzopio->saved_off + lzopio->block.usize <= grub_file_tell (file))
|
||||
{
|
||||
/* EOF, could be possible files with unknown size. */
|
||||
if (lzopio->block.usize == 0)
|
||||
return 0;
|
||||
|
||||
if (jump_block (lzopio) < 0)
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
while (len != 0 && lzopio->block.usize != 0)
|
||||
{
|
||||
grub_off_t off = grub_file_tell (file) - lzopio->saved_off;
|
||||
long to_copy;
|
||||
|
||||
/* Block not decompressed yet. */
|
||||
if (!lzopio->block.udata && uncompress_block (lzopio) < 0)
|
||||
goto CORRUPTED;
|
||||
|
||||
/* Copy requested data into buffer. */
|
||||
to_copy = grub_min (lzopio->block.usize - off, len);
|
||||
grub_memcpy (buf, lzopio->block.udata + off, to_copy);
|
||||
|
||||
len -= to_copy;
|
||||
buf += to_copy;
|
||||
ret += to_copy;
|
||||
|
||||
/* Read to next block if needed. */
|
||||
if (len > 0 && read_block_header (lzopio) < 0)
|
||||
goto CORRUPTED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
CORRUPTED:
|
||||
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "lzop file corrupted");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Release everything, including the underlying file object. */
|
||||
static grub_err_t
|
||||
grub_lzopio_close (grub_file_t file)
|
||||
{
|
||||
grub_lzopio_t lzopio = file->data;
|
||||
|
||||
grub_file_close (lzopio->file);
|
||||
grub_free (lzopio->block.cdata);
|
||||
grub_free (lzopio->block.udata);
|
||||
grub_free (lzopio);
|
||||
|
||||
/* Device must not be closed twice. */
|
||||
file->device = 0;
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static struct grub_fs grub_lzopio_fs = {
|
||||
.name = "lzopio",
|
||||
.dir = 0,
|
||||
.open = 0,
|
||||
.read = grub_lzopio_read,
|
||||
.close = grub_lzopio_close,
|
||||
.label = 0,
|
||||
.next = 0
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT (lzopio)
|
||||
{
|
||||
grub_file_filter_register (GRUB_FILE_FILTER_LZOPIO, grub_lzopio_open);
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (lzopio)
|
||||
{
|
||||
grub_file_filter_unregister (GRUB_FILE_FILTER_LZOPIO);
|
||||
}
|
|
@ -56,6 +56,7 @@ typedef enum grub_file_filter_id
|
|||
{
|
||||
GRUB_FILE_FILTER_GZIO,
|
||||
GRUB_FILE_FILTER_XZIO,
|
||||
GRUB_FILE_FILTER_LZOPIO,
|
||||
GRUB_FILE_FILTER_MAX,
|
||||
GRUB_FILE_FILTER_COMPRESSION_FIRST = GRUB_FILE_FILTER_GZIO,
|
||||
GRUB_FILE_FILTER_COMPRESSION_LAST = GRUB_FILE_FILTER_XZIO,
|
||||
|
|
Loading…
Reference in a new issue