From 8eef1f8244a91a1beb18bc7a94fad7347382e65e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 9 Dec 2010 20:41:41 +0100 Subject: [PATCH] Compressed fragments and compressed data support --- grub-core/fs/squash4.c | 26 +++++++++++++++------ grub-core/io/gzio.c | 53 +++++++++++++++++++++++++++++++++++++++--- include/grub/deflate.h | 4 ++++ 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 1a121c2cf..50e5c3090 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -51,13 +51,18 @@ struct grub_squash_super grub_uint32_t dummy1; grub_uint32_t creation_time; grub_uint32_t dummy2; - grub_uint32_t dummy3[4]; + grub_uint32_t dummy3[2]; + grub_uint8_t flags; +#define SQUASH_FLAG_UNCOMPRESSED_INODES 1 +#define SQUASH_FLAG_UNCOMPRESSED_DATA 2 +#define SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS 8 + grub_uint8_t dummy4[7]; grub_uint16_t root_ino_offset; grub_uint16_t root_ino_chunk; - grub_uint32_t dummy4; + grub_uint32_t dummy5; grub_uint64_t total_size; grub_uint64_t exttbloffset; - grub_uint32_t dummy5[2]; + grub_uint32_t dummy6[2]; grub_uint64_t inodeoffset; grub_uint64_t diroffset; grub_uint64_t unk1offset; @@ -399,8 +404,9 @@ static grub_ssize_t grub_squash_read (grub_file_t file, char *buf, grub_size_t len) { grub_err_t err; - grub_uint64_t a; + grub_uint64_t a, b; struct grub_squash_data *data = file->data; + int compressed = 0; if (grub_le_to_cpu16 (data->ino.fragment) == 0xffff) { @@ -408,6 +414,7 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) a = grub_le_to_cpu32 (data->ino.chunk); else a = sizeof (struct grub_squash_super); + compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_DATA); } else { @@ -418,12 +425,17 @@ grub_squash_read (grub_file_t file, char *buf, grub_size_t len) if (err) return -1; a = grub_le_to_cpu64 (frag.offset) + grub_le_to_cpu32 (data->ino.chunk); + compressed = !(data->sb.flags & SQUASH_FLAG_UNCOMPRESSED_FRAGMENTS); } - a += grub_le_to_cpu32 (data->ino.offset) + file->offset; + b = grub_le_to_cpu32 (data->ino.offset) + file->offset; - err = grub_disk_read (file->device->disk, a >> GRUB_DISK_SECTOR_BITS, - a & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); + /* FIXME: cache uncompressed chunks. */ + if (compressed) + err = grub_zlib_disk_read (file->device->disk, a, b, buf, len); + else + err = grub_disk_read (file->device->disk, (a + b) >> GRUB_DISK_SECTOR_BITS, + (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); if (err) return -1; return len; diff --git a/grub-core/io/gzio.c b/grub-core/io/gzio.c index 248a1750e..61227b4fc 100644 --- a/grub-core/io/gzio.c +++ b/grub-core/io/gzio.c @@ -41,6 +41,7 @@ #include #include #include +#include #include /* @@ -62,6 +63,9 @@ struct grub_gzio /* 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; + grub_disk_addr_t disk_input_off; + grub_disk_addr_t disk_input_start; + grub_disk_t disk_input; /* The offset at which the data starts in the underlying file. */ grub_off_t data_offset; /* The type of current block. */ @@ -383,8 +387,21 @@ get_byte (grub_gzio_t gzio) return 0; } - if (grub_file_tell (gzio->file) == (grub_off_t) gzio->data_offset - || gzio->inbuf_d == INBUFSIZ) + if (gzio->disk_input && (gzio->disk_input_off == gzio->data_offset + || gzio->inbuf_d == INBUFSIZ)) + { + grub_disk_addr_t d = gzio->disk_input_start + gzio->disk_input_off; + gzio->inbuf_d = 0; + grub_disk_read (gzio->disk_input, + d >> GRUB_DISK_SECTOR_BITS, + d & (GRUB_DISK_SECTOR_SIZE - 1), + INBUFSIZ, gzio->inbuf); + gzio->disk_input_off += INBUFSIZ; + } + + if (gzio->file && (grub_file_tell (gzio->file) + == (grub_off_t) gzio->data_offset + || gzio->inbuf_d == INBUFSIZ)) { gzio->inbuf_d = 0; grub_file_read (gzio->file, gzio->inbuf, INBUFSIZ); @@ -402,8 +419,10 @@ gzio_seek (grub_gzio_t gzio, grub_off_t off) grub_error (GRUB_ERR_OUT_OF_RANGE, "attempt to seek outside of the file"); else - gzio->mem_input_off = gzio->data_offset; + gzio->mem_input_off = off; } + else if (gzio->disk_input) + gzio->disk_input_off = off; else grub_file_seek (gzio->file, off); } @@ -1297,6 +1316,34 @@ grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, return ret; } +grub_err_t +grub_zlib_disk_read (grub_disk_t disk, grub_disk_addr_t zlibstart, + 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->disk_input_off = 0; + gzio->disk_input_start = zlibstart; + gzio->disk_input = disk; + + 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 < 0 ? grub_errno : GRUB_ERR_NONE; +} + static struct grub_fs grub_gzio_fs = diff --git a/include/grub/deflate.h b/include/grub/deflate.h index 6ec4eaa99..ae4a1f244 100644 --- a/include/grub/deflate.h +++ b/include/grub/deflate.h @@ -23,4 +23,8 @@ grub_ssize_t grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, char *outbuf, grub_size_t outsize); +grub_err_t +grub_zlib_disk_read (grub_disk_t disk, grub_disk_addr_t zlibstart, + grub_off_t off, char *outbuf, grub_size_t outsize); + #endif