]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Compressed fragments and compressed data support
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 9 Dec 2010 19:41:41 +0000 (20:41 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 9 Dec 2010 19:41:41 +0000 (20:41 +0100)
grub-core/fs/squash4.c
grub-core/io/gzio.c
include/grub/deflate.h

index 1a121c2cfe8bb2292d4cedb69a8d01817aee7d4a..50e5c30901a3a69ceaced74070dd48cb350e6682 100644 (file)
@@ -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;
+  = 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;
index 248a1750e2ad9be8c1d9e0283bc6a9f3f0053062..61227b4fc8602d824dd580a4d10b6fd076868a9a 100644 (file)
@@ -41,6 +41,7 @@
 #include <grub/fs.h>
 #include <grub/file.h>
 #include <grub/dl.h>
+#include <grub/disk.h>
 #include <grub/deflate.h>
 
 /*
@@ -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;
+}
+
 \f
 
 static struct grub_fs grub_gzio_fs =
index 6ec4eaa998337fa1e0755c1239dfe0a41984a4ed..ae4a1f244d568fa680b59869689352c313d3a625 100644 (file)
@@ -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