Various bug fixes and improvements in lzopio. Should properly handle more

files now. Also added support for lzop checksums verification.
This commit is contained in:
Szymon Janc 2011-08-21 18:48:19 +02:00
parent 6401c37058
commit 90b0a05579

View file

@ -22,17 +22,15 @@
#include <grub/file.h> #include <grub/file.h>
#include <grub/fs.h> #include <grub/fs.h>
#include <grub/dl.h> #include <grub/dl.h>
#include <grub/crypto.h>
#include "minilzo.h" #include "minilzo.h"
GRUB_MOD_LICENSE ("GPLv3+"); GRUB_MOD_LICENSE ("GPLv3+");
#define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a" #define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
#define LZOP_MAGIC_SIZE 9 #define LZOP_MAGIC_SIZE 9
#define CHECK_SIZE 4 #define LZOP_CHECK_SIZE 4
#define NEW_LZO_LIB 0x0940 #define LZOP_NEW_LIB 0x0940
#define MIN_HEADER_SIZE 0
#define MAX_HEADER_SIZE 0
/* Header flags - copied from conf.h of LZOP source code. */ /* Header flags - copied from conf.h of LZOP source code. */
#define F_ADLER32_D 0x00000001L #define F_ADLER32_D 0x00000001L
@ -64,8 +62,10 @@ struct block_header
struct grub_lzopio struct grub_lzopio
{ {
grub_file_t file; grub_file_t file;
int ucheck; /* XXX Use gcry to validate checks. */ int has_ccheck;
int ccheck; int has_ucheck;
const gcry_md_spec_t *ucheck_fun;
const gcry_md_spec_t *ccheck_fun;
grub_off_t saved_off; /* Rounded down to block boundary. */ grub_off_t saved_off; /* Rounded down to block boundary. */
grub_off_t start_block_off; grub_off_t start_block_off;
struct block_header block; struct block_header block;
@ -74,15 +74,16 @@ struct grub_lzopio
typedef struct grub_lzopio *grub_lzopio_t; typedef struct grub_lzopio *grub_lzopio_t;
static struct grub_fs grub_lzopio_fs; static struct grub_fs grub_lzopio_fs;
/* Some helper functions. Memory allocated by those function is free either /* Some helper functions. On errors memory allocated by those function is free
* on next read_block_header() or on close() so no risk of leaks. This makes * either on close() so no risk of leaks. This makes functions simpler. */
* those function simpler. */
/* Read block header from file, after successful exit file points to /* Read block header from file, after successful exit file points to
* beginning of block data. */ * beginning of block data. */
static int static int
read_block_header (struct grub_lzopio *lzopio) read_block_header (struct grub_lzopio *lzopio)
{ {
lzopio->saved_off += lzopio->block.usize;
/* Free cached block data if any. */ /* Free cached block data if any. */
grub_free (lzopio->block.udata); grub_free (lzopio->block.udata);
grub_free (lzopio->block.cdata); grub_free (lzopio->block.cdata);
@ -113,26 +114,40 @@ read_block_header (struct grub_lzopio *lzopio)
lzopio->block.csize = grub_be_to_cpu32 (lzopio->block.csize); lzopio->block.csize = grub_be_to_cpu32 (lzopio->block.csize);
/* XXX Handle incompressible data case here rather than in uncompress_block. /* Corrupted. */
* This will allow to handle switch of ccheck/ucheck easier and also if (lzopio->block.csize > lzopio->block.usize)
* make uncompress_block() a bit simpler. */ return -1;
/* Read data checks. */ /* Read checksum of uncompressed data. */
if (lzopio->ucheck) if (lzopio->has_ucheck)
{ {
if (grub_file_read (lzopio->file, &lzopio->block.ucheck, if (grub_file_read (lzopio->file, &lzopio->block.ucheck,
sizeof (lzopio->block.ucheck)) != sizeof (lzopio->block.ucheck)) !=
sizeof (lzopio->block.ucheck)) sizeof (lzopio->block.ucheck))
return -1; return -1;
lzopio->block.ucheck = grub_be_to_cpu32 (lzopio->block.ucheck);
} }
if (lzopio->ccheck) /* Read checksum of compressed data. */
if (lzopio->has_ccheck)
{ {
if (grub_file_read (lzopio->file, &lzopio->block.ccheck, /* Incompressible data block. */
sizeof (lzopio->block.ccheck)) != if (lzopio->block.csize == lzopio->block.usize)
sizeof (lzopio->block.ccheck)) {
return -1; lzopio->block.ccheck = lzopio->block.ucheck;
}
else
{
if (grub_file_read (lzopio->file, &lzopio->block.ccheck,
sizeof (lzopio->block.ccheck)) !=
sizeof (lzopio->block.ccheck))
return -1;
lzopio->block.ccheck = grub_be_to_cpu32 (lzopio->block.ccheck);
}
} }
return 0; return 0;
} }
@ -149,9 +164,19 @@ read_block_data (struct grub_lzopio *lzopio)
!= (grub_ssize_t) lzopio->block.csize) != (grub_ssize_t) lzopio->block.csize)
return -1; return -1;
if (lzopio->ccheck) if (lzopio->ccheck_fun)
{ {
/* XXX Validate data checksum here. */ grub_uint8_t context[lzopio->ccheck_fun->contextsize];
lzopio->ccheck_fun->init (context);
lzopio->ccheck_fun->write (context, lzopio->block.cdata,
lzopio->block.csize);
lzopio->ccheck_fun->final (context);
if (grub_memcmp
(lzopio->ccheck_fun->read (context), &lzopio->block.ccheck,
sizeof (lzopio->block.ccheck)) != 0)
return -1;
} }
return 0; return 0;
@ -159,7 +184,7 @@ read_block_data (struct grub_lzopio *lzopio)
/* Read block data, uncompressed and also store it in memory. */ /* Read block data, uncompressed and also store it in memory. */
/* XXX Investigate possibility of in-place decompression to reduce memory /* XXX Investigate possibility of in-place decompression to reduce memory
* footprint. */ * footprint. Or try to uncompress directly to buf if possible. */
static int static int
uncompress_block (struct grub_lzopio *lzopio) uncompress_block (struct grub_lzopio *lzopio)
{ {
@ -168,35 +193,41 @@ uncompress_block (struct grub_lzopio *lzopio)
if (read_block_data (lzopio) < 0) if (read_block_data (lzopio) < 0)
return -1; return -1;
if (lzopio->block.usize > lzopio->block.csize) /* Incompressible data. */
{ if (lzopio->block.csize == lzopio->block.usize)
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.udata = lzopio->block.cdata;
lzopio->block.cdata = NULL; lzopio->block.cdata = NULL;
} }
else else
{ {
return -1; 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_fun)
{
grub_uint8_t context[lzopio->ucheck_fun->contextsize];
lzopio->ucheck_fun->init (context);
lzopio->ucheck_fun->write (context, lzopio->block.udata,
lzopio->block.usize);
lzopio->ucheck_fun->final (context);
if (grub_memcmp
(lzopio->ucheck_fun->read (context), &lzopio->block.ucheck,
sizeof (lzopio->block.ucheck)) != 0)
return -1;
}
/* Compressed data can be free now. */
grub_free (lzopio->block.cdata);
lzopio->block.cdata = NULL;
} }
return 0; return 0;
@ -215,8 +246,6 @@ jump_block (struct grub_lzopio *lzopio)
return -1; return -1;
} }
lzopio->saved_off += lzopio->block.usize;
return read_block_header (lzopio); return read_block_header (lzopio);
} }
@ -243,7 +272,7 @@ calculate_uncompressed_size (grub_file_t file)
return 1; return 1;
} }
/* XXX Do something with this function, it is insane now:) */ /* XXX Do something with this function... */
static int static int
test_header (grub_file_t file) test_header (grub_file_t file)
{ {
@ -253,6 +282,7 @@ test_header (grub_file_t file)
grub_uint8_t method, level, name_len; grub_uint8_t method, level, name_len;
grub_uint32_t flags, mode, filter, mtime_lo, mtime_hi, checksum; grub_uint32_t flags, mode, filter, mtime_lo, mtime_hi, checksum;
unsigned char *name = NULL; unsigned char *name = NULL;
const gcry_md_spec_t *hcheck;
if (grub_file_read (lzopio->file, magic, sizeof (magic)) != sizeof (magic)) if (grub_file_read (lzopio->file, magic, sizeof (magic)) != sizeof (magic))
{ {
@ -277,7 +307,7 @@ test_header (grub_file_t file)
ver = grub_be_to_cpu16 (ver); ver = grub_be_to_cpu16 (ver);
if (ver >= NEW_LZO_LIB) if (ver >= LZOP_NEW_LIB)
{ {
/* Read version of lib needed to extract data. */ /* Read version of lib needed to extract data. */
if (grub_file_read (lzopio->file, &ver_ext, sizeof (ver_ext)) != if (grub_file_read (lzopio->file, &ver_ext, sizeof (ver_ext)) !=
@ -297,7 +327,7 @@ test_header (grub_file_t file)
sizeof (method)) sizeof (method))
goto CORRUPTED; goto CORRUPTED;
if (ver >= NEW_LZO_LIB) if (ver >= LZOP_NEW_LIB)
{ {
if (grub_file_read (lzopio->file, &level, sizeof (level)) != if (grub_file_read (lzopio->file, &level, sizeof (level)) !=
sizeof (level)) sizeof (level))
@ -310,14 +340,33 @@ test_header (grub_file_t file)
flags = grub_be_to_cpu32 (flags); flags = grub_be_to_cpu32 (flags);
if (flags & F_CRC32_D) if (flags & F_CRC32_D)
lzopio->ucheck = 1; {
lzopio->has_ucheck = 1;
lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("crc32");
}
else if (flags & F_ADLER32_D) else if (flags & F_ADLER32_D)
lzopio->ucheck = 2; {
lzopio->has_ucheck = 1;
lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("adler32");
}
if (flags & F_CRC32_C) if (flags & F_CRC32_C)
lzopio->ccheck = 1; {
lzopio->has_ccheck = 1;
lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("crc32");
}
else if (flags & F_ADLER32_C) else if (flags & F_ADLER32_C)
lzopio->ccheck = 2; {
lzopio->has_ccheck = 1;
lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("adler32");
}
if (flags & F_H_CRC32)
hcheck = grub_crypto_lookup_md_by_name ("crc32");
else
hcheck = grub_crypto_lookup_md_by_name ("adler32");
hcheck++;
if (flags & F_H_FILTER) if (flags & F_H_FILTER)
{ {
@ -333,7 +382,7 @@ test_header (grub_file_t file)
sizeof (mtime_lo)) sizeof (mtime_lo))
goto CORRUPTED; goto CORRUPTED;
if (ver >= NEW_LZO_LIB) if (ver >= LZOP_NEW_LIB)
{ {
if (grub_file_read (lzopio->file, &mtime_hi, sizeof (mtime_hi)) != if (grub_file_read (lzopio->file, &mtime_hi, sizeof (mtime_hi)) !=
sizeof (mtime_hi)) sizeof (mtime_hi))
@ -374,8 +423,6 @@ test_header (grub_file_t file)
if (calculate_uncompressed_size (file) < 0) if (calculate_uncompressed_size (file) < 0)
goto CORRUPTED; goto CORRUPTED;
lzopio->saved_off = 0;
/* Get back to start block. */ /* Get back to start block. */
grub_file_seek (lzopio->file, lzopio->start_block_off); grub_file_seek (lzopio->file, lzopio->start_block_off);
@ -383,6 +430,7 @@ test_header (grub_file_t file)
if (read_block_header (lzopio) < 0) if (read_block_header (lzopio) < 0)
goto CORRUPTED; goto CORRUPTED;
lzopio->saved_off = 0;
return 1; return 1;
} }
@ -439,15 +487,17 @@ grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len)
{ {
grub_lzopio_t lzopio = file->data; grub_lzopio_t lzopio = file->data;
grub_ssize_t ret = 0; grub_ssize_t ret = 0;
grub_off_t off;
/* Backward seek before last read block. */ /* Backward seek before last read block. */
if (lzopio->saved_off > grub_file_tell (file)) if (lzopio->saved_off > grub_file_tell (file))
{ {
lzopio->saved_off = 0;
grub_file_seek (lzopio->file, lzopio->start_block_off); grub_file_seek (lzopio->file, lzopio->start_block_off);
if (read_block_header (lzopio) < 0) if (read_block_header (lzopio) < 0)
goto CORRUPTED; goto CORRUPTED;
lzopio->saved_off = 0;
} }
/* Forward to first block with requested data. */ /* Forward to first block with requested data. */
@ -461,9 +511,10 @@ grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len)
goto CORRUPTED; goto CORRUPTED;
} }
off = grub_file_tell (file) - lzopio->saved_off;
while (len != 0 && lzopio->block.usize != 0) while (len != 0 && lzopio->block.usize != 0)
{ {
grub_off_t off = grub_file_tell (file) - lzopio->saved_off;
long to_copy; long to_copy;
/* Block not decompressed yet. */ /* Block not decompressed yet. */
@ -477,8 +528,9 @@ grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len)
len -= to_copy; len -= to_copy;
buf += to_copy; buf += to_copy;
ret += to_copy; ret += to_copy;
off = 0;
/* Read to next block if needed. */ /* Read next block if needed. */
if (len > 0 && read_block_header (lzopio) < 0) if (len > 0 && read_block_header (lzopio) < 0)
goto CORRUPTED; goto CORRUPTED;
} }