]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Add support for LZO compression in btrfs.
authorSzymon Janc <szymon@janc.net.pl>
Sun, 14 Aug 2011 09:46:05 +0000 (11:46 +0200)
committerSzymon Janc <szymon@janc.net.pl>
Sun, 14 Aug 2011 09:46:05 +0000 (11:46 +0200)
* 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
Makefile.util.def
grub-core/Makefile.core.def
grub-core/fs/btrfs.c

index 0a9d852a44a7efd92646424801757d8aff78989b..c892553893626790d79f136ece90121f2198bb9d 100644 (file)
@@ -1,3 +1,16 @@
+2011-08-14  Szymon Janc <szymon@janc.net.pl>
+
+       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 <szymon@janc.net.pl>
 
        * grub-core/fs/btrfs.c: Some code style fixes.
index d86cb976183b7fe66a157b295ac00a7ebd89edd4..3628e48f65b75314c16c00354d4f976a7f2f30b7 100644 (file)
@@ -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 = {
index 0924602f37e29e8e880579c649f85a7198de3cfe..1ff179c6770fda67e60fa5187744a688304c04c0 100644 (file)
@@ -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 = {
index 1336c8f02c2d8d4e473daf207ad7371bc2bf47ae..1d24da3e3283206e11372b357991cb7c7d0dec08 100644 (file)
@@ -26,6 +26,7 @@
 #include <grub/types.h>
 #include <grub/lib/crc.h>
 #include <grub/deflate.h>
+#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)