]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
BtrFS support. Written by me (Vladimir) with important bugfixes and
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 11 Apr 2011 21:26:41 +0000 (23:26 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 11 Apr 2011 21:26:41 +0000 (23:26 +0200)
even more important testing by Colin.

* Makefile.util.def (libgrubmods.a): Add crc.c and gzio.c
* grub-core/Makefile.core.def (btrfs): Add crc.c.
* grub-core/fs/btrfs.c: Stub replaced with real implementation.
* grub-core/io/gzio.c (grub_gzio): New fields mem_input_size,
mem_input_off and mem_input. All users updated to accept in-RAM input.
(gzio_seek): New function.
(test_zlib_header): Likewise.
(grub_gzio_read): Likewise.
(grub_zlib_decompress): Likewise.
* grub-core/kern/emu/getroot.c (grub_find_root_device_from_mountinfo):
Accept partial and non-virtual mounts.
(grub_guess_root_device): Do rescanning after device_from_mountinfo to
avoid receiving /dev/dm-X as device.
* grub-core/kern/emu/misc.c (grub_make_system_path_relative_to_its_root):
Handle bind and partial mounts.
* grub-core/lib/crc.c: New file.
* include/grub/deflate.h: Likewise.
* include/grub/emu/misc.h (grub_find_root_device_from_mountinfo): New
proto.
* include/grub/lib/crc.h: New file.

1  2 
ChangeLog
grub-core/fs/btrfs.c
grub-core/io/gzio.c

diff --cc ChangeLog
index a3d1e7f92a3fe4d83258ee2adb3b421102d2d0b0,dbcbb96519d3b7bca86fda0f9aa15d8988904a44..29dbceec0b070bde49589029e5552adc0cbf1ad7
+++ b/ChangeLog
@@@ -1,22 -1,3 +1,49 @@@
++2011-04-11  Vladimir Serbinenko  <phcoder@gmail.com>
++2011-04-11  Colin Watson  <cjwatson@ubuntu.com>
++
++      BtrFS support. Written by me (Vladimir) with important bugfixes and
++      even more important testing by Colin.
++
++      * Makefile.util.def (libgrubmods.a): Add crc.c and gzio.c
++      * grub-core/Makefile.core.def (btrfs): Add crc.c.
++      * grub-core/fs/btrfs.c: Stub replaced with real implementation.
++      * grub-core/io/gzio.c (grub_gzio): New fields mem_input_size,
++      mem_input_off and mem_input. All users updated to accept in-RAM input.
++      (gzio_seek): New function.
++      (test_zlib_header): Likewise.
++      (grub_gzio_read): Likewise.
++      (grub_zlib_decompress): Likewise.
++      * grub-core/kern/emu/getroot.c (grub_find_root_device_from_mountinfo):
++      Accept partial and non-virtual mounts.
++      (grub_guess_root_device): Do rescanning after device_from_mountinfo to
++      avoid receiving /dev/dm-X as device.
++      * grub-core/kern/emu/misc.c (grub_make_system_path_relative_to_its_root):
++      Handle bind and partial mounts.
++      * grub-core/lib/crc.c: New file.
++      * include/grub/deflate.h: Likewise.
++      * include/grub/emu/misc.h (grub_find_root_device_from_mountinfo): New
++      proto.
++      * include/grub/lib/crc.h: New file.
++
 +2011-04-11  Vladimir Serbinenko  <phcoder@gmail.com>
 +
 +      Implement automatic module license checking according to new GNU
 +      guidelines.
 +
 +      * grub-core/kern/dl.c (grub_dl_check_license): New function.
 +      (grub_dl_load_core): Use grub_dl_check_license.
 +      * include/grub/dl.h (GRUB_MOD_SECTION): New macro.
 +      (GRUB_MOD_LICENSE): Likewise.
 +      (GRUB_MOD_DUAL_LICENSE): Likewise.
 +      All modules updated.
 +
 +2011-04-11  Colin Watson  <cjwatson@ubuntu.com>
 +
 +      * grub-core/fs/btrfs.c (grub_btrfs_fs) [GRUB_UTIL]: Set
 +      reserved_first_sector to 1.  btrfs reserves plenty of space for boot
 +      loaders.
 +      Reported by: Gene Cumm.  Fixes Ubuntu bug #757446.
 +
  2011-04-11  Vladimir Serbinenko  <phcoder@gmail.com>
  
        * util/grub-fstest.c (cmd_cmp): Check that sizes match.
index 3e73837d9d9252c738397053fe3352883fe96f91,0b0e5259c5f68c39abe955ee5870c116ec2b96ae..bbb326b98ebe427999010bb590236f0b07fb6852
  #include <grub/disk.h>
  #include <grub/dl.h>
  #include <grub/types.h>
+ #include <grub/lib/crc.h>
+ #include <grub/deflate.h>
  
- #define BTRFS_SIGNATURE "_BHRfS_M"
 +GRUB_MOD_LICENSE ("GPLv3+");
 +
+ #define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
  
- struct btrfs_superblock
+ typedef grub_uint8_t grub_btrfs_checksum_t[0x20];
+ typedef grub_uint16_t grub_btrfs_uuid_t[8];
+ struct grub_btrfs_device
+ {
+   grub_uint64_t device_id;
+   grub_uint8_t dummy[0x62 - 8];
+ } __attribute__ ((packed));
+ struct grub_btrfs_superblock
  { 
-   grub_uint8_t dummy1[32];
-   grub_uint16_t uuid[8];
-   grub_uint8_t dummy2[16];
-   grub_uint8_t signature[sizeof (BTRFS_SIGNATURE) - 1];
+   grub_btrfs_checksum_t checksum;
+   grub_btrfs_uuid_t uuid;
+   grub_uint8_t dummy[0x10];
+   grub_uint8_t signature[sizeof (GRUB_BTRFS_SIGNATURE) - 1];
+   grub_uint64_t generation;
+   grub_uint64_t root_tree;
+   grub_uint64_t chunk_tree;
+   grub_uint8_t dummy2[0x20];
+   grub_uint64_t root_dir_objectid;
+   grub_uint8_t dummy3[0x41];
+   struct grub_btrfs_device this_device;
+   char label[0x100];
+   grub_uint8_t dummy4[0x100];
+   grub_uint8_t bootstrap_mapping[0x800];
  } __attribute__ ((packed));
  
+ struct btrfs_header
+ {
+   grub_btrfs_checksum_t checksum;
+   grub_btrfs_uuid_t uuid;
+   grub_uint8_t dummy[0x30];
+   grub_uint32_t nitems;
+   grub_uint8_t level;
+ } __attribute__ ((packed));
+ struct grub_btrfs_device_desc
+ {
+   grub_device_t dev;
+   grub_uint64_t id;
+ };
  struct grub_btrfs_data
  {
-   struct btrfs_superblock sblock;
+   struct grub_btrfs_superblock sblock;
+   grub_uint64_t tree;
+   grub_uint64_t inode;
+   struct grub_btrfs_device_desc *devices_attached;
+   unsigned n_devices_attached;
+   unsigned n_devices_allocated;
+   /* Cached extent data.  */
+   grub_uint64_t extstart;
+   grub_uint64_t extend;
+   grub_uint64_t extino;
+   grub_uint64_t exttree;
+   grub_size_t extsize;
+   struct grub_btrfs_extent_data *extent;
  };
  
+ struct grub_btrfs_key
+ {
+   grub_uint64_t object_id;
+ #define GRUB_BTRFS_ITEM_TYPE_INODE_ITEM 0x01
+ #define GRUB_BTRFS_ITEM_TYPE_DIR_ITEM 0x54
+ #define GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM 0x6c
+ #define GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM 0x84
+ #define GRUB_BTRFS_ITEM_TYPE_DEVICE 0xd8
+ #define GRUB_BTRFS_ITEM_TYPE_CHUNK 0xe4
+   grub_uint8_t type;
+   grub_uint64_t offset;
+ } __attribute__ ((packed));
+ struct grub_btrfs_chunk_item
+ {
+   grub_uint64_t size;
+   grub_uint64_t dummy;
+   grub_uint64_t stripe_length;
+   grub_uint64_t type;
+ #define GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE 0x07
+ #define GRUB_BTRFS_CHUNK_TYPE_SINGLE        0x00
+ #define GRUB_BTRFS_CHUNK_TYPE_RAID0         0x08
+ #define GRUB_BTRFS_CHUNK_TYPE_RAID1         0x10
+ #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED    0x20
+ #define GRUB_BTRFS_CHUNK_TYPE_RAID10        0x40
+   grub_uint8_t dummy2[0xc];
+   grub_uint16_t nstripes;
+   grub_uint16_t nsubstripes;
+ } __attribute__ ((packed));
+ struct grub_btrfs_chunk_stripe
+ {
+   grub_uint64_t device_id;
+   grub_uint64_t offset;
+   grub_btrfs_uuid_t device_uuid;
+ } __attribute__ ((packed));
+ struct grub_btrfs_leaf_node
+ {
+   struct grub_btrfs_key key;
+   grub_uint32_t offset;
+   grub_uint32_t size;
+ } __attribute__ ((packed));
+ struct grub_btrfs_internal_node
+ {
+   struct grub_btrfs_key key;
+   grub_uint64_t addr;
+   grub_uint64_t dummy;
+ } __attribute__ ((packed));
+ struct grub_btrfs_dir_item
+ {
+   struct grub_btrfs_key key;
+   grub_uint8_t dummy[8];
+   grub_uint16_t m;
+   grub_uint16_t n;
+ #define GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR 1
+ #define GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY 2
+ #define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7
+   grub_uint8_t type;
+   char name[0];
+ } __attribute__ ((packed));
+ struct grub_btrfs_leaf_descriptor
+ {
+   unsigned depth;
+   unsigned allocated;
+   struct {
+     grub_disk_addr_t addr;
+     unsigned iter;
+     unsigned maxiter;
+     int leaf;
+   } *data;
+ };
+ struct grub_btrfs_root_item
+ {
+   grub_uint8_t dummy[0xb0];
+   grub_uint64_t tree;
+   grub_uint64_t inode;
+ };
+ struct grub_btrfs_time
+ {
+   grub_int64_t sec;
+   grub_uint32_t nanosec;
+ } __attribute__ ((aligned(4)));
+ struct grub_btrfs_inode
+ {
+   grub_uint8_t dummy1[0x10];
+   grub_uint64_t size;
+   grub_uint8_t dummy2[0x70];
+   struct grub_btrfs_time mtime;
+ } __attribute__ ((packed));
+ struct grub_btrfs_extent_data
+ {
+   grub_uint64_t dummy;
+   grub_uint64_t size;
+   grub_uint8_t compression;
+   grub_uint8_t encryption;
+   grub_uint16_t encoding;
+   grub_uint8_t type;
+   union
+   {
+     char inl[0];
+     struct
+     {
+       grub_uint64_t laddr;
+       grub_uint64_t compressed_size;
+       grub_uint64_t offset;
+       grub_uint64_t filled;
+     };
+   };
+ } __attribute__ ((packed));
+ #define GRUB_BTRFS_EXTENT_INLINE 0
+ #define GRUB_BTRFS_EXTENT_REGULAR 1
+ #define GRUB_BTRFS_COMPRESSION_NONE 0
+ #define GRUB_BTRFS_COMPRESSION_ZLIB 1
+ #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
+ static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2,
+                                                256 * 1048576 * 2,
+                                                1048576ULL * 1048576ULL * 2 };
+ static grub_err_t
+ grub_btrfs_read_logical (struct grub_btrfs_data *data,
+                        grub_disk_addr_t addr, void *buf, grub_size_t size);
+ static grub_err_t
+ read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
+ {
+   unsigned i;
+   grub_err_t err = GRUB_ERR_NONE;
+   for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++)
+     {
+       struct grub_btrfs_superblock sblock;
+       err = grub_disk_read (disk, superblock_sectors[i], 0,
+                           sizeof (sblock), &sblock);
+       if (err == GRUB_ERR_OUT_OF_RANGE)
+       break;
+       if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE,
+                      sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0)
+       break;
+       if (i == 0 || grub_le_to_cpu64 (sblock.generation)
+         > grub_le_to_cpu64 (sb->generation))
+       grub_memcpy (sb, &sblock, sizeof (sblock));
+     }
+   if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0)
+     return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem");
+   if (err == GRUB_ERR_OUT_OF_RANGE)
+     grub_errno = err = GRUB_ERR_NONE;
+   return err;
+ }
+ static int
+ key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b)
+ {
+   if (grub_cpu_to_le64 (a->object_id) < grub_cpu_to_le64 (b->object_id))
+     return -1;
+   if (grub_cpu_to_le64 (a->object_id) > grub_cpu_to_le64 (b->object_id))
+     return +1;
+   if (a->type < b->type)
+     return -1;
+   if (a->type > b->type)
+     return +1;
+   if (grub_cpu_to_le64 (a->offset) < grub_cpu_to_le64 (b->offset))
+     return -1;
+   if (grub_cpu_to_le64 (a->offset) > grub_cpu_to_le64 (b->offset))
+     return +1;
+   return 0;
+ }
+ static void
+ free_iterator (struct grub_btrfs_leaf_descriptor *desc)
+ {
+   grub_free (desc->data);
+ }
+ static grub_err_t
+ save_ref (struct grub_btrfs_leaf_descriptor *desc, 
+         grub_disk_addr_t addr, unsigned i, unsigned m, int l)
+ {
+   desc->depth++;
+   if (desc->allocated < desc->depth)
+     {
+       void *newdata;
+       desc->allocated *= 2;
+       newdata = grub_realloc (desc->data, sizeof (desc->data[0])
+                             * desc->allocated);
+       if (!newdata)
+       return grub_errno;
+       desc->data = newdata;
+     }
+   desc->data[desc->depth - 1].addr = addr;
+   desc->data[desc->depth - 1].iter = i;
+   desc->data[desc->depth - 1].maxiter = m;
+   desc->data[desc->depth - 1].leaf = l;
+   return GRUB_ERR_NONE;
+ }
+ static int
+ next (struct grub_btrfs_data *data,
+       struct grub_btrfs_leaf_descriptor *desc,
+       grub_disk_addr_t *outaddr, grub_size_t *outsize,
+       struct grub_btrfs_key *key_out)
+ {
+   grub_err_t err;
+   struct grub_btrfs_leaf_node leaf;
+   for (; desc->depth > 0; desc->depth--)
+     {
+       desc->data[desc->depth - 1].iter++;
+       if (desc->data[desc->depth - 1].iter
+         < desc->data[desc->depth - 1].maxiter)
+       break;
+     }
+   if (desc->depth == 0)
+     return 0;
+   while (!desc->data[desc->depth - 1].leaf)
+     {
+       struct grub_btrfs_internal_node node;
+       struct btrfs_header head;
+       err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
+                                    * sizeof (node)
+                                    + sizeof (struct btrfs_header)
+                                    + desc->data[desc->depth - 1].addr, &node,
+                                    sizeof (node));
+       if (err)
+       return -err;
+       err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head,
+                                    sizeof (head));
+       if (err)
+       return -err;
+       save_ref (desc, grub_le_to_cpu64 (node.addr), 0, 
+               grub_le_to_cpu32 (head.nitems), !head.level);
+     }
+   err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
+                                * sizeof (leaf)
+                                + sizeof (struct btrfs_header)
+                                + desc->data[desc->depth - 1].addr, &leaf,
+                                sizeof (leaf));
+   if (err)
+     return -err;
+   *outsize = grub_le_to_cpu32 (leaf.size);
+   *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header)
+     + grub_le_to_cpu32 (leaf.offset);
+   *key_out = leaf.key;
+   return 1;  
+ }
+ static grub_err_t
+ lower_bound (struct grub_btrfs_data *data,
+            const struct grub_btrfs_key *key_in, 
+            struct grub_btrfs_key *key_out,
+            grub_disk_addr_t root,
+            grub_disk_addr_t *outaddr, grub_size_t *outsize,
+            struct grub_btrfs_leaf_descriptor *desc)
+ {
+   grub_disk_addr_t addr = root;
+   int depth = -1;
+   if (desc)
+     {
+       desc->allocated = 16;
+       desc->depth = 0;
+       desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated);
+       if (!desc->data)
+       return grub_errno;
+     }
+   grub_dprintf ("btrfs",
+               "retrieving %" PRIxGRUB_UINT64_T
+               " %x %" PRIxGRUB_UINT64_T "\n",
+               key_in->object_id, key_in->type, key_in->offset);
+   while (1)
+     {
+       grub_err_t err;
+       struct btrfs_header head;
+     reiter:
+       depth++;
+       /* FIXME: preread few nodes into buffer. */
+       err = grub_btrfs_read_logical (data, addr, &head, sizeof (head));
+       if (err)
+       return err;
+       addr += sizeof (head);
+       if (head.level)
+       {
+         unsigned i;
+         struct grub_btrfs_internal_node node, node_last;
+         int have_last = 0;
+         grub_memset (&node_last, 0, sizeof (node_last));
+         for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
+           {
+             err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
+                                            &node, sizeof (node));
+             if (err)
+               return err;
+             grub_dprintf ("btrfs",
+                           "internal node (depth %d) %" PRIxGRUB_UINT64_T
+                           " %x %" PRIxGRUB_UINT64_T "\n", depth,
+                           node.key.object_id, node.key.type, node.key.offset);
+             
+             if (key_cmp (&node.key, key_in) == 0)
+               {
+                 err = GRUB_ERR_NONE;
+                 if (desc)
+                   err = save_ref (desc, addr - sizeof (head), i,
+                                   grub_le_to_cpu32 (head.nitems), 0);
+                 if (err)
+                   return err;
+                 addr = grub_le_to_cpu64 (node.addr);
+                 goto reiter;
+               }
+             if (key_cmp (&node.key, key_in) > 0)
+               break;
+             node_last = node;
+             have_last = 1;
+           }
+         if (have_last)
+           {
+             err = GRUB_ERR_NONE;
+             if (desc)
+               err = save_ref (desc, addr - sizeof (head), i - 1,
+                                grub_le_to_cpu32 (head.nitems), 0);
+             if (err)
+               return err;
+             addr = grub_le_to_cpu64 (node_last.addr);
+             goto reiter;
+           }
+         *outsize = 0;
+         *outaddr = 0;
+         grub_memset (key_out, 0, sizeof (*key_out));
+         if (desc)
+           return save_ref (desc, addr - sizeof (head), -1,
+                            grub_le_to_cpu32 (head.nitems), 0);
+         return GRUB_ERR_NONE;
+       }
+       {
+       unsigned i;
+       struct grub_btrfs_leaf_node leaf, leaf_last;
+       int have_last = 0;
+       for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
+         {
+           err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf),
+                                          &leaf, sizeof (leaf));
+           if (err)
+             return err;
+           
+           grub_dprintf ("btrfs",
+                         "leaf (depth %d) %" PRIxGRUB_UINT64_T
+                         " %x %" PRIxGRUB_UINT64_T "\n", depth,
+                         leaf.key.object_id, leaf.key.type, leaf.key.offset);
+           if (key_cmp (&leaf.key, key_in) == 0)
+             {
+               grub_memcpy (key_out, &leaf.key, sizeof(*key_out));
+               *outsize = grub_le_to_cpu32 (leaf.size);
+               *outaddr = addr + grub_le_to_cpu32 (leaf.offset);
+               if (desc)
+                 return save_ref (desc, addr - sizeof (head), i,
+                                  grub_le_to_cpu32 (head.nitems), 1);
+               return GRUB_ERR_NONE;         
+             }
+           
+           if (key_cmp (&leaf.key, key_in) > 0)
+             break;
+           
+           have_last = 1;
+           leaf_last = leaf;
+         }
+       if (have_last)
+         {
+           grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out));
+           *outsize = grub_le_to_cpu32 (leaf_last.size);
+           *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset);
+           if (desc)
+             return save_ref (desc, addr - sizeof (head), i - 1,
+                              grub_le_to_cpu32 (head.nitems), 1);
+           return GRUB_ERR_NONE;             
+         }
+       *outsize = 0;
+       *outaddr = 0;
+       grub_memset (key_out, 0, sizeof (*key_out));
+       if (desc)
+         return save_ref (desc, addr - sizeof (head), -1,
+                          grub_le_to_cpu32 (head.nitems), 1);
+       return GRUB_ERR_NONE;
+       }
+     }
+ }
+ static grub_device_t
+ find_device (struct grub_btrfs_data *data, grub_uint64_t id,
+            int do_rescan)
+ {
+   grub_device_t dev_found = NULL;
+   auto int hook (const char *name);
+   int hook (const char *name)
+   {
+     grub_device_t dev;
+     grub_err_t err;
+     struct grub_btrfs_superblock sb;
+     dev = grub_device_open (name);
+     if (!dev)
+       return 0;
+     if (!dev->disk)
+       {
+       grub_device_close (dev);
+       return 0;
+       }
+     err = read_sblock (dev->disk, &sb);
+     if (err == GRUB_ERR_BAD_FS)
+       {
+       grub_device_close (dev);
+       grub_errno = GRUB_ERR_NONE;
+       return 0;
+       }
+     if (err)
+       {
+       grub_device_close (dev);
+       grub_print_error ();
+       return 0;
+       }
+     if (grub_memcmp (data->sblock.uuid, sb.uuid, sizeof (sb.uuid)) != 0
+       || sb.this_device.device_id != id)
+       {
+       grub_device_close (dev);
+       return 0;
+       }
+     
+     dev_found = dev;
+     return 1;
+   }
+   unsigned i;
+   for (i = 0; i < data->n_devices_attached; i++)
+     if (id == data->devices_attached[i].id)
+       return data->devices_attached[i].dev;
+   if (do_rescan)
+     grub_device_iterate (hook);
+   if (!dev_found)
+     {
+       grub_error (GRUB_ERR_BAD_FS, "couldn't find a member device");
+       return NULL;
+     }
+   data->n_devices_attached++;
+   if (data->n_devices_attached > data->n_devices_allocated)
+     {
+       void *tmp;
+       data->n_devices_allocated = 2 * data->n_devices_attached + 1;
+       data->devices_attached
+       = grub_realloc (tmp = data->devices_attached,
+                       data->n_devices_allocated
+                       * sizeof (data->devices_attached[0]));
+       if (!data->devices_attached)
+       {
+         grub_device_close (dev_found);
+         data->devices_attached = tmp;
+         return NULL;
+       }
+     }
+   data->devices_attached[data->n_devices_attached - 1].id = id;
+   data->devices_attached[data->n_devices_attached - 1].dev = dev_found;
+   return dev_found;
+ }
+ static grub_err_t
+ grub_btrfs_read_logical (struct grub_btrfs_data *data,
+                        grub_disk_addr_t addr,
+                        void *buf, grub_size_t size)
+ {
+   while (size > 0)
+     {
+       grub_uint8_t *ptr;
+       struct grub_btrfs_key *key;
+       struct grub_btrfs_chunk_item *chunk;  
+       grub_ssize_t csize;
+       grub_err_t err; 
+       struct grub_btrfs_key key_out;
+       int challoc = 0;
+       grub_device_t dev;
+       grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n",
+                   addr);
+       for (ptr = data->sblock.bootstrap_mapping;
+          ptr < data->sblock.bootstrap_mapping
+            + sizeof (data->sblock.bootstrap_mapping)
+            - sizeof (struct grub_btrfs_key);
+          )
+       {
+         key = (struct grub_btrfs_key *) ptr;
+         if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK)
+           break;
+         chunk = (struct grub_btrfs_chunk_item *) (key + 1);
+         grub_dprintf ("btrfs", "%" PRIxGRUB_UINT64_T " %" PRIxGRUB_UINT64_T " \n",
+                       grub_le_to_cpu64 (key->offset),
+                       grub_le_to_cpu64 (chunk->size));
+         if (grub_le_to_cpu64 (key->offset) <= addr
+             && addr < grub_le_to_cpu64 (key->offset)
+             + grub_le_to_cpu64 (chunk->size))
+           goto chunk_found;
+         ptr += sizeof (*key) + sizeof (*chunk)
+           + sizeof (struct grub_btrfs_chunk_stripe)
+           * grub_le_to_cpu16 (chunk->nstripes);
+       }
+       struct grub_btrfs_key key_in;
+       grub_size_t chsize;
+       grub_disk_addr_t chaddr;
+       key_in.object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
+       key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK;
+       key_in.offset = addr;
+       err = lower_bound (data, &key_in, &key_out,
+                        grub_le_to_cpu64 (data->sblock.chunk_tree),
+                        &chaddr, &chsize, NULL);
+       if (err)
+       return err;
+       key = &key_out;
+       if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK
+         || !(grub_le_to_cpu64 (key->offset) <= addr))
+       return grub_error (GRUB_ERR_BAD_FS,
+                          "couldn't find the chunk descriptor");
+       chunk = grub_malloc (chsize);
+       if (!chunk)
+       return grub_errno;
+       challoc = 1;
+       err = grub_btrfs_read_logical (data, chaddr, chunk, chsize);
+       if (err)
+       {
+         grub_free (chunk);
+         return err;
+       }
+     chunk_found:
+       {
+       grub_uint32_t stripen;
+       grub_uint32_t stripe_offset;
+       grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset);
+       unsigned redundancy = 1;
+       unsigned i, j;
+       grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+                     "+0x%" PRIxGRUB_UINT64_T
+                     " (%d stripes (%d substripes) of %"
+                     PRIxGRUB_UINT64_T ")\n",
+                     grub_le_to_cpu64 (key->offset),
+                     grub_le_to_cpu64 (chunk->size),
+                     grub_le_to_cpu16 (chunk->nstripes),
+                     grub_le_to_cpu16 (chunk->nsubstripes),
+                     grub_le_to_cpu64 (chunk->stripe_length));
+       switch (grub_le_to_cpu64 (chunk->type)
+               & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE)
+         {
+         case GRUB_BTRFS_CHUNK_TYPE_SINGLE:
+           {
+             grub_uint32_t stripe_length;
+             stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
+                                            grub_le_to_cpu16 (chunk->nstripes),
+                                            NULL);
+             stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
+             csize = (stripen + 1) * stripe_length - off;
+             break;
+           }
+         case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED:
+         case GRUB_BTRFS_CHUNK_TYPE_RAID1:
+           {
+             stripen = 0;
+             stripe_offset = off;
+             csize = grub_le_to_cpu64 (chunk->size) - off;
+             redundancy = 2;
+             break;
+           }
+         case GRUB_BTRFS_CHUNK_TYPE_RAID0:
+           {
+             grub_uint64_t middle, high;
+             grub_uint32_t low;
+             middle = grub_divmod64 (off,
+                                     grub_le_to_cpu64 (chunk->stripe_length),
+                                     &low);
+             
+             high = grub_divmod64 (middle, grub_le_to_cpu16 (chunk->nstripes),
+                                   &stripen);
+             stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length)
+               * high;
+             csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
+             break;
+           }
+         case GRUB_BTRFS_CHUNK_TYPE_RAID10:
+           {
+             grub_uint64_t middle, high;
+             grub_uint32_t low;
+             middle = grub_divmod64 (off,
+                                     grub_le_to_cpu64 (chunk->stripe_length),
+                                     &low);
+             
+             high = grub_divmod64 (middle,
+                                   grub_le_to_cpu16 (chunk->nsubstripes),
+                                   &stripen);
+             stripen *= grub_le_to_cpu16 (chunk->nstripes)
+               / grub_le_to_cpu16 (chunk->nsubstripes);
+             redundancy = grub_le_to_cpu16 (chunk->nstripes)
+               / grub_le_to_cpu16 (chunk->nsubstripes);
+             stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length)
+               * high;
+             csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
+             break;
+           }
+         default:
+           return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                              "unsupported RAID flags %" PRIxGRUB_UINT64_T,
+                              grub_le_to_cpu64 (chunk->type));
+         }
+       if (csize <= 0)
+         return grub_error (GRUB_ERR_BAD_FS,
+                            "couldn't find the chunk descriptor");
+       if ((grub_size_t) csize > size)
+         csize = size;
+       for (j = 0; j < 2; j++)
+         {
+           for (i = 0; i < redundancy; i++)
+             {
+               struct grub_btrfs_chunk_stripe *stripe;
+               grub_disk_addr_t paddr;
+               stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
+               /* Right now the redundancy handling is easy.
+                  With RAID5-like it will be more difficult.  */
+               stripe += stripen + i;
+               paddr = stripe->offset + stripe_offset;
+               grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
+                             "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %"
+                             PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT32_T
+                             " maps to 0x%" PRIxGRUB_UINT64_T "\n",
+                             grub_le_to_cpu64 (key->offset),
+                             grub_le_to_cpu64 (chunk->size),
+                             grub_le_to_cpu16 (chunk->nstripes),
+                             grub_le_to_cpu16 (chunk->nsubstripes),
+                             grub_le_to_cpu64 (chunk->stripe_length),
+                             stripen, stripe->offset);
+               grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
+                             " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr,
+                             addr);
+               dev = find_device (data, stripe->device_id, j);
+               if (!dev)
+                 {
+                   err = grub_errno;
+                   grub_errno = GRUB_ERR_NONE;
+                   continue;
+                 }
+               err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
+                                     paddr & (GRUB_DISK_SECTOR_SIZE - 1),
+                                     csize, buf);
+               if (!err)
+                 break;
+               grub_errno = GRUB_ERR_NONE;
+             }
+           if (i != redundancy)
+             break;
+         }
+       if (err)
+         return grub_errno = err;
+       }
+       size -= csize;
+       buf = (grub_uint8_t *) buf + csize;
+       addr += csize;
+       if (challoc)
+       grub_free (chunk);
+     }
+   return GRUB_ERR_NONE;
+ }
  static struct grub_btrfs_data *
- grub_btrfs_mount (grub_disk_t disk)
+ grub_btrfs_mount (grub_device_t dev)
  {
-   struct grub_btrfs_data *data = grub_malloc (sizeof (*data));
+   struct grub_btrfs_data *data;
+   grub_err_t err;
+   if (!dev->disk)
+     {
+       grub_error (GRUB_ERR_BAD_FS, "not BtrFS");
+       return NULL;
+     }
+   data = grub_zalloc (sizeof (*data));
    if (! data)
      return NULL;
  
@@@ -123,10 -1458,10 +1460,13 @@@ static struct grub_fs grub_btrfs_fs 
      .name = "btrfs",
      .dir = grub_btrfs_dir,
      .open = grub_btrfs_open,
+     .read = grub_btrfs_read,
+     .close = grub_btrfs_close,
      .uuid = grub_btrfs_uuid,
+     .label = grub_btrfs_label,
 +#ifdef GRUB_UTIL
 +    .reserved_first_sector = 1,
 +#endif
    };
  
  GRUB_MOD_INIT(btrfs)
index ad185dcab1157396ce2646afde48baafbbece12d,84e494d83b3834312ac885a0934e7593f4fcb3d8..1397a72861eb6d368ae6a41338aa311c233f6561
  #include <grub/fs.h>
  #include <grub/file.h>
  #include <grub/dl.h>
+ #include <grub/deflate.h>
  
 +GRUB_MOD_LICENSE ("GPLv3+");
 +
  /*
   *  Window Size
   *