From 095f077e6db33fefb9c9eae6fcbb47b2bef577f8 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sun, 14 Aug 2011 11:46:05 +0200 Subject: [PATCH] Add support for LZO compression in btrfs. * Makefile.util.def (libgrubmods.a): Add minilzo.c and add required flags to cflags in cppflags. * Makefile.core.def (btrfs): Likewise. * grub-core/fs/btrfs.c: Include minilzo.h. (grub_btrfs_superblock): Add sectorsize, nodesize, leafsize, stripsize and dummy5 field. (GRUB_BTRFS_COMPRESSION_LZO): New define. (grub_btrfs_extent_read): Add support for LZO compression type. --- ChangeLog.lzo | 13 ++++ Makefile.util.def | 3 + grub-core/Makefile.core.def | 3 + grub-core/fs/btrfs.c | 136 ++++++++++++++++++++++++++++++++---- 4 files changed, 143 insertions(+), 12 deletions(-) diff --git a/ChangeLog.lzo b/ChangeLog.lzo index 0a9d852a4..c89255389 100644 --- a/ChangeLog.lzo +++ b/ChangeLog.lzo @@ -1,3 +1,16 @@ +2011-08-14 Szymon Janc + + Add support for LZO compression in btrfs. + + * Makefile.util.def (libgrubmods.a): Add minilzo.c and add required flags + to cflags in cppflags. + * Makefile.core.def (btrfs): Likewise. + * grub-core/fs/btrfs.c: Include minilzo.h. + (grub_btrfs_superblock): Add sectorsize, nodesize, leafsize, stripsize and + dummy5 field. + (GRUB_BTRFS_COMPRESSION_LZO): New define. + (grub_btrfs_extent_read): Add support for LZO compression type. + 2011-08-14 Szymon Janc * grub-core/fs/btrfs.c: Some code style fixes. diff --git a/Makefile.util.def b/Makefile.util.def index d86cb9761..3628e48f6 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -33,6 +33,8 @@ library = { library = { name = libgrubmods.a; + cflags = '$(CFLAGS_POSIX) -Wno-undef'; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(top_srcdir)/grub-core/lib/minilzo -DMINILZO_HAVE_CONFIG_H'; common_nodist = grub_script.tab.c; common_nodist = grub_script.yy.c; @@ -107,6 +109,7 @@ library = { common = grub-core/script/argv.c; common = grub-core/io/gzio.c; common = grub-core/kern/ia64/dl_helper.c; + common = grub-core/lib/minilzo/minilzo.c; }; program = { diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 0924602f3..1ff179c67 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -979,6 +979,9 @@ module = { name = btrfs; common = fs/btrfs.c; common = lib/crc.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 = { diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 1336c8f02..1d24da3e3 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -26,6 +26,7 @@ #include #include #include +#include "minilzo.h" GRUB_MOD_LICENSE ("GPLv3+"); @@ -51,10 +52,15 @@ struct grub_btrfs_superblock grub_uint64_t chunk_tree; grub_uint8_t dummy2[0x20]; grub_uint64_t root_dir_objectid; - grub_uint8_t dummy3[0x41]; + grub_uint8_t dummy3[0x8]; + grub_uint32_t sectorsize; + grub_uint32_t nodesize; + grub_uint32_t leafsize; + grub_uint32_t stripsize; + grub_uint8_t dummy4[0x29]; struct grub_btrfs_device this_device; char label[0x100]; - grub_uint8_t dummy4[0x100]; + grub_uint8_t dummy5[0x100]; grub_uint8_t bootstrap_mapping[0x800]; } __attribute__ ((packed)); @@ -216,6 +222,7 @@ struct grub_btrfs_extent_data #define GRUB_BTRFS_COMPRESSION_NONE 0 #define GRUB_BTRFS_COMPRESSION_ZLIB 1 +#define GRUB_BTRFS_COMPRESSION_LZO 2 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 @@ -954,7 +961,8 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, } if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE - && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB) + && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB + && data->extent->compression != GRUB_BTRFS_COMPRESSION_LZO) { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "compression type 0x%x not supported", @@ -980,6 +988,42 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, != (grub_ssize_t) csize) return -1; } + else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO) + { + grub_uint32_t total_size, chunk_size; + unsigned char *obuf; + unsigned char *ibuf = (unsigned char *) data->extent->inl; + lzo_uint ocnt = extoff + csize; + int ret; + + obuf = grub_malloc (extoff + csize); + if (!obuf) + return -1; + + total_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (total_size); + + chunk_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (chunk_size); + + /* Can we have multiple chunks in inline extent? */ + if (chunk_size + (sizeof (grub_uint32_t) * 2) != total_size) + { + grub_free (obuf); + return -1; + } + + ret = lzo1x_decompress_safe (ibuf, chunk_size, obuf, &ocnt, NULL); + + if (ret != LZO_E_OK) + { + grub_free (obuf); + return -1; + } + + grub_memcpy (buf, obuf + extoff, ocnt - extoff); + grub_free (obuf); + } else grub_memcpy (buf, data->extent->inl + extoff, csize); break; @@ -989,9 +1033,10 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_memset (buf, 0, csize); break; } - if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB) + + if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE) { - char *tmp; + void *tmp; grub_uint64_t zsize; zsize = grub_le_to_cpu64 (data->extent->compressed_size); tmp = grub_malloc (zsize); @@ -1005,16 +1050,83 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, 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) + + if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB) { + grub_ssize_t ret; + + ret = grub_zlib_decompress (tmp, zsize, extoff + + grub_le_to_cpu64 (data->extent->offset), + buf, csize); + grub_free (tmp); - return -1; + + if (ret != (grub_ssize_t) csize) + return -1; + + break; } - grub_free (tmp); - break; - } + else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO) + { + grub_uint32_t total_size, chunk_size, usize = 0; + grub_size_t off = extoff; + unsigned char *chunk, *ibuf = tmp; + char *obuf = buf; + /* XXX Is this correct size from sblock? */ + grub_uint32_t udata_size = grub_le_to_cpu32 (data->sblock.sectorsize); + + chunk = grub_malloc (udata_size); + if (!chunk) + { + grub_free (tmp); + return -1; + } + + /* XXX Use this for some sanity checks while decompressing. */ + total_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (total_size); + + chunk_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (chunk_size); + + /* Jump to first chunk with requested data. */ + while (off >= udata_size) + { + chunk_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (chunk_size); + ibuf += chunk_size; + off -= udata_size; + } + + while (usize < csize) + { + chunk_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + lzo_uint csize2 = udata_size; + int diff; + + ibuf += sizeof (chunk_size); + + if (lzo1x_decompress_safe (ibuf, chunk_size, chunk, &csize2, NULL) != LZO_E_OK) + { + grub_free (tmp); + grub_free (chunk); + return -1; + } + + diff = grub_min (csize2 - off, csize - usize); + + grub_memcpy (obuf, chunk + off, diff); + obuf += diff; + ibuf += chunk_size; + usize += diff; + off = 0; + } + + grub_free (tmp); + grub_free (chunk); + break; + } + } err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (data->extent->laddr) + grub_le_to_cpu64 (data->extent->offset)