grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/2] btrfs support for grub-2


From: Surbhi Palande
Subject: [PATCH 2/2] btrfs support for grub-2
Date: Mon, 5 Jul 2010 16:47:13 +0300

From: surbhi <address@hidden>

This patch is usable provided the license for this patch resolves to GPLv3.

Signed-off-by: Surbhi Palande <address@hidden>
---
 fs/btrfs.c | 1434 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs.h |  385 ++++++++++++++++
 2 files changed, 1819 insertions(+), 0 deletions(-)
 create mode 100644 fs/btrfs.c
 create mode 100644 fs/btrfs.h

diff --git a/fs/btrfs.c b/fs/btrfs.c
new file mode 100644
index 0000000..7725e0a
--- /dev/null
+++ b/fs/btrfs.c
@@ -0,0 +1,1434 @@
+/* btrfs.c - Binary Tree F.S */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  This file can be re-distributed and/or modified under the terms of
+ *  the GNU General Public License as published by the Free Software 
+ *  Foundation, either version 3 of the License, or (at your option) 
+ *  any later version.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * btrfs: Copyright (C) 2007 Oracle.  All rights reserved.
+ *
+ * btrfs.c -- readonly btrfs support for grub-2
+ * author: address@hidden
+ * License for this file yet needs to be resolved!! 
+ * You cannot use this file as of now. Will be usable only if the file
+ * can be accepted as GPLv3
+ */
+
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/types.h>
+#include <grub/fshelp.h>
+
+#include "btrfs.h"
+
+#define BTRFS_SUPER_MIRROR_SHIFT 12
+
+#define assert(boolean) real_assert (boolean, __FILE__, __LINE__)
+static inline void
+real_assert (int boolean, const char *file, const int line)
+{
+        if (! boolean)
+                grub_printf ("Assertion failed at %s:%d\n", file, line);
+}
+
+#define perror(str) grub_printf("\n ##str %d", grub_errno);
+
+
+static grub_dl_t my_mod;
+
+#define u32 unsigned int
+#define u8 unsigned char
+static u32 crc32c_table[256];
+/*
+ *  * Steps through buffer one byte at at time, calculates reflected
+ *   * crc using table.
+ *    */
+
+
+static inline u32 crc32c_le(u32 crc, const char *data, u32 length)
+{
+        while (length--)
+                crc = crc32c_table[(u8)(crc ^ *data++)] ^ (crc >> 8);
+
+        return crc;
+}
+
+static inline void btrfs_init_crc32c(void)
+{
+        int i, j;
+        u32 v;
+        const u32 poly = 0x82F63B78; /* Bit-reflected CRC32C polynomial */
+
+        for (i = 0; i < 256; i++) {
+                v = i;
+                for (j = 0; j < 8; j++) {
+                        v = (v >> 1) ^ ((v & 1) ? poly : 0);
+                }
+                crc32c_table[i] = v;
+        }
+}
+
+struct btrfs_fs_cache fs_cache;
+
+static void clear_mem(char * ptr, unsigned int size)
+{
+        unsigned int i;
+        for(i = 0; i< size; i++)
+        {
+                ptr[i] = 0;
+        }
+}
+
+
+static inline u64 btrfs_sb_offset(int mirror)
+{
+        u64 start = 16 * 1024;
+        if (mirror)
+                return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror);
+        return BTRFS_SUPER_INFO_OFFSET;
+}
+
+/*
+ * returns 1 if superblock is found and returns 0 otherwise  
+ *
+ */
+static int btrfs_read_superblock(grub_disk_t disk, struct btrfs_fs_info * 
fs_info)
+{
+        int i, ret;
+        int found = 1;
+        u64 offset;
+        unsigned fsid[BTRFS_FSID_SIZE];
+
+        struct btrfs_super_block sb;
+        fs_info->sb_transid = 0;
+
+        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+                /*
+                * read from cache if cache is already populated, else
+                * read from disk
+                */
+                offset = btrfs_sb_offset(i);
+                if(offset > disk->total_sectors) {
+                        if( found == 1)
+                                grub_errno = GRUB_ERR_OUT_OF_RANGE;
+                        break;
+                }
+                ret = grub_disk_read(disk, offset >> SECTOR_BITS,
+                                (offset % (1<<SECTOR_BITS)),
+                                 sizeof(struct btrfs_super_block), (char *) 
&sb);
+                if (grub_errno || ret < 0)
+                        break;
+                if (sb.bytenr != offset) {
+                        continue;
+                }
+                if(grub_strncmp((char *)(&(sb.magic)), BTRFS_MAGIC,
+                              sizeof(sb.magic))) {
+                        continue;
+                }
+
+                if(i == 0) {
+                        grub_memcpy(fsid, sb.fsid, sizeof(fsid));
+                }
+                else if(grub_memcmp(fsid, sb.fsid, sizeof(sb.fsid)))
+                                continue;
+                found = 0;
+                if (sb.generation > fs_info->sb_transid) {
+                        grub_memcpy(&fs_info->sb_copy, &sb, sizeof(sb));
+                        //grub_printf("\n copied superblock, magic num 
matches!\n");
+                        fs_info->sb_transid = sb.generation;
+                        fs_info->sb_mirror = i;
+                }
+        }
+        if (grub_errno == GRUB_ERR_OUT_OF_RANGE) {
+                if(found == 1)
+                        grub_error (GRUB_ERR_BAD_FS, "not a btrfs filesystem");
+                else
+                        grub_errno = GRUB_ERR_NONE;
+        }
+        return found;
+}
+
+/* compare function used for bin_search */
+typedef int (*cmp_func)(void *ptr1, void *ptr2);
+
+
+/* simple but useful bin search, used for chunk search and btree search
+ * you always compare the item stored in the middle
+ * 0 is a perfect match, 1 indicates slot has the next stored slot number
+ */
+static int bin_search(void *ptr, int item_size, void *cmp_item, cmp_func func,
+                              int min, int max, int *slot)
+{
+        int low = min;
+        int high = max;
+        int mid;
+        int ret;
+        unsigned long offset;
+        void *item;
+
+        while (low < high) {
+                mid = (low + high) / 2;
+                offset = mid * item_size;
+
+                item = (char *) ptr + offset;
+                ret = func(item, cmp_item);
+               // ret<0 then item < cmp_item. ret >0 then item > cm_item
+                if (ret < 0) {
+                        low = mid + 1;
+               }
+                else if (ret > 0) {
+                        high = mid;
+               }
+                else {
+                        *slot = mid;
+                        return 0;
+                }
+        }
+       // this is the next item which is just bigger than what we are 
searching.
+        *slot = low;
+        return 1;
+}
+
+
+static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item *m1,
+                                struct btrfs_chunk_map_item *m2)
+{
+        if (m1->logical > m2->logical)
+                return 1;
+        if (m1->logical < m2->logical)
+                return -1;
+        return 0;
+}
+
+
+
+
+/* insert a new chunk mapping item */
+static void insert_map(struct btrfs_chunk_map_item *item, struct btrfs_fs_info 
* fs_info)
+{
+        int ret;
+        int slot;
+        int i;
+       struct btrfs_chunk_map * chunk_map = &(fs_info->chunk_map);
+
+
+        if (chunk_map->map == NULL) { /* first item */
+                chunk_map->map_length = BTRFS_MAX_CHUNK_ENTRIES;
+                chunk_map->map = (struct btrfs_chunk_map_item *)
+                        grub_malloc(chunk_map->map_length * 
sizeof(*chunk_map->map));
+                chunk_map->map[0] = *item;
+                chunk_map->cur_length = 1;
+                return;
+        }
+        ret = bin_search(chunk_map->map, sizeof(*item), item,
+                        (cmp_func)btrfs_comp_chunk_map, 0,
+                        chunk_map->cur_length, &slot);
+        if (ret == 0)/* already in map */
+       {
+                return;
+       }
+        if (chunk_map->cur_length == BTRFS_MAX_CHUNK_ENTRIES) {
+                /* should be impossible */
+                return;
+        }
+
+        for (i = chunk_map->cur_length; i > slot; i--)
+                chunk_map->map[i] = chunk_map->map[i-1];
+
+        chunk_map->map[slot] = *item;
+        chunk_map->cur_length++;
+        return;
+}
+
+static inline unsigned long btrfs_chunk_item_size(int num_stripes)
+{
+        assert(num_stripes > 0);
+        return sizeof(struct btrfs_chunk) +
+                sizeof(struct btrfs_stripe) * (num_stripes - 1);
+}
+
+static void btrfs_read_sys_chunk_array(struct btrfs_fs_info * fs_info)
+{
+        struct btrfs_chunk_map_item item;
+        struct btrfs_disk_key *key;
+        struct btrfs_chunk *chunk;
+        int cur;
+           int ret;
+
+        /* read chunk array in superblock */
+        cur = 0;
+        while (cur < fs_info->sb_copy.sys_chunk_array_size) {
+                key = (struct btrfs_disk_key 
*)(fs_info->sb_copy.sys_chunk_array + cur);
+                cur += sizeof(struct btrfs_disk_key);
+                chunk = (struct btrfs_chunk 
*)(fs_info->sb_copy.sys_chunk_array + cur);
+                if(chunk->num_stripes <= 0) {
+                    grub_errno = GRUB_ERR_BAD_FS;
+                    break;
+                }
+                ret = btrfs_chunk_item_size(chunk->num_stripes);
+                       cur = cur + ret;
+                /* insert to mapping table, ignore multi stripes */
+                item.logical = key->offset;
+                item.length = chunk->length;
+                item.devid = chunk->stripe.devid;
+                item.physical = chunk->stripe.offset;/*ignore other stripes */
+                insert_map(&item, fs_info);
+        }
+}
+
+
+/*
+ *  * from sys_chunk_array or chunk_tree, we can convert a logical address to
+ *   * a physical address we can not support multi device case yet
+ *    */
+static u64 logical_physical(struct btrfs_chunk_map * chunk_map, u64 logical)
+{
+        struct btrfs_chunk_map_item item;
+        int slot, ret;
+
+        item.logical = logical;
+        ret = bin_search(chunk_map->map, sizeof(*chunk_map->map), &item,
+                        (cmp_func)btrfs_comp_chunk_map, 0,
+                        chunk_map->cur_length, &slot);
+        if (ret == 0)
+                slot++;
+        else if (slot == 0)
+                return -1;
+        if (logical >=
+                chunk_map->map[slot-1].logical + chunk_map->map[slot-1].length)
+                return -1;
+        //grub_printf("\n conversion: logical: %llx, physical: %llx ", 
chunk_map->map[slot-1].logical, chunk_map->map[slot-1].physical);
+        return chunk_map->map[slot-1].physical + logical -
+                        chunk_map->map[slot-1].logical;
+}
+
+/* if stored item > searched item then return 1, else return -1 or 0 */
+static int btrfs_comp_keys(struct btrfs_disk_key *k1, struct btrfs_disk_key 
*k2)
+{
+        if (k1->objectid > k2->objectid)
+                return 1;
+        if (k1->objectid < k2->objectid)
+                return -1;
+        if (k1->type > k2->type)
+                return 1;
+        if (k1->type < k2->type)
+                return -1;
+        if (k1->offset > k2->offset)
+                return 1;
+        if (k1->offset < k2->offset)
+                return -1;
+        return 0;
+}
+
+
+/* compare keys but ignore offset, is useful to enumerate all same kind keys
+ * */
+static int btrfs_comp_keys_type(struct btrfs_disk_key *k1,
+                                        struct btrfs_disk_key *k2)
+{
+        if (k1->objectid > k2->objectid)
+                return 1;
+        if (k1->objectid < k2->objectid)
+                return -1;
+        if (k1->type > k2->type)
+                return 1;
+        if (k1->type < k2->type)
+                return -1;
+        return 0;
+}
+
+/* seach tree directly on disk ... */
+static int search_tree(grub_disk_t disk, struct btrfs_fs_info * fs_info, u64 
loffset, struct btrfs_disk_key *key, struct btrfs_path *path)
+{
+        u8 buf[BTRFS_MAX_LEAF_SIZE];
+        struct btrfs_header *header = (struct btrfs_header *)buf;
+        struct btrfs_node *node = (struct btrfs_node *)buf;
+        struct btrfs_leaf *leaf = (struct btrfs_leaf *)buf;
+        int slot, ret;
+        u64 offset;
+       struct btrfs_super_block sb = fs_info->sb_copy;
+
+        offset = logical_physical(&(fs_info->chunk_map), loffset);
+        grub_disk_read(disk, offset>>SECTOR_BITS, (offset % (1<<SECTOR_BITS)), 
sizeof(*header), (char *)header);
+        if (header->level) {/*node*/
+                grub_disk_read(disk, (offset + sizeof(*header)) >> SECTOR_BITS,
+                                          ((offset + sizeof(*header)) % 
(1<<SECTOR_BITS)),
+                                          sb.nodesize - sizeof(*header), (char 
*)&node->ptrs[0]);
+                path->itemsnr[header->level] = header->nritems;
+                path->offsets[header->level] = loffset;
+                ret = bin_search(&node->ptrs[0], sizeof(struct btrfs_key_ptr),
+                        key, (cmp_func)btrfs_comp_keys,
+                        path->slots[header->level], header->nritems, &slot);
+                if (ret && slot > path->slots[header->level])
+                        slot--;
+                path->slots[header->level] = slot;
+                ret = search_tree(disk, fs_info, node->ptrs[slot].blockptr, 
key, path);
+        } else {/*leaf*/
+                grub_disk_read(disk, (offset + sizeof(*header)) >> SECTOR_BITS,
+                                          ((offset + sizeof(*header)) % 
(1<<SECTOR_BITS)),
+                                          sb.leafsize - sizeof(*header), (char 
*)&leaf->items);
+                path->itemsnr[0] = header->nritems;
+                path->offsets[0] = loffset;
+                ret = bin_search(&leaf->items[0], sizeof(struct btrfs_item),
+                        key, (cmp_func)btrfs_comp_keys, path->slots[0],
+                        header->nritems, &slot);
+                if (ret && slot > path->slots[header->level])
+                        slot--;
+                path->slots[0] = slot;
+                path->item = leaf->items[slot];
+                if(leaf->items[slot].size > BTRFS_MAX_LEAF_SIZE) {
+                        grub_printf("\n leaf->items[%d].size: %x greater than 
BTRFS_MAX_LEAF_SIZE \n", slot, leaf->items[slot].size);
+                        return -1;
+                }
+                grub_disk_read(disk, (offset + sizeof(*header) + 
leaf->items[slot].offset) >> SECTOR_BITS,
+                               ((offset + sizeof(*header) + 
leaf->items[slot].offset) % (1<<SECTOR_BITS)),
+                               leaf->items[slot].size, (char *)(&path->data));
+        }
+        return ret;
+}
+
+static int get_next_slot(grub_disk_t disk , u64 loffset, struct btrfs_path * 
path, int slot)
+{
+        u8 buf[BTRFS_MAX_LEAF_SIZE];
+        u64 offset = logical_physical(&fs_cache.fs_info.chunk_map, loffset);
+        struct btrfs_leaf * leaf = (struct btrfs_leaf *) buf;
+        offset = offset + sizeof(struct btrfs_header);
+
+        grub_disk_read(disk, offset >> SECTOR_BITS, (offset % 
(1<<SECTOR_BITS)), 
+                                                
fs_cache.fs_info.sb_copy.leafsize - sizeof(struct btrfs_header), 
+                                                (char *)&leaf->items);
+        offset = offset + leaf->items[slot].offset;
+        path->item = leaf->items[slot];
+        grub_disk_read(disk, offset >> SECTOR_BITS, 
+                                (offset % (1<<SECTOR_BITS)), 
+                                leaf->items[slot].size, (char 
*)(&path->data)); 
+        return 0;
+}
+
+/* return 0 if slot found */
+static int next_slot(grub_disk_t disk, struct btrfs_path *path)
+{
+        int slot;
+        if (!path->itemsnr[0])
+                return 1;
+        slot = path->slots[0] + 1;
+        if (slot >= path->itemsnr[0])
+                return 1;
+        path->slots[0] = slot;
+        get_next_slot(disk, path->offsets[0], path, path->slots[0]);
+        return 0;
+}
+
+
+/* return 0 if leaf found */
+static int next_leaf(grub_disk_t disk, struct btrfs_fs_info * fs_info, struct 
btrfs_disk_key *key, struct btrfs_path *path)
+{
+        int slot;
+        int level = 1;
+
+        while (level < BTRFS_MAX_LEVEL) {
+                if (!path->itemsnr[level]) /* no more nodes */
+                        return 1;
+                slot = path->slots[level] + 1;
+                if (slot >= path->itemsnr[level]) {
+                        level++;
+                        continue;;
+                }
+                path->slots[level] = slot;
+                path->slots[level-1] = 0; /* reset low level slots info */
+                search_tree(disk, fs_info, path->offsets[level], key, path);
+                break;
+        }
+        if (level == BTRFS_MAX_LEVEL)
+                return 1;
+        return 0;
+}
+
+
+
+
+/* read chunk items from chunk_tree and insert them to chunk map
+ * since you start from key.offset=0, you will pick up every chunk in the
+ * chunk tree with the key.objectid: BTRFS_FIRST_CHUNK_TREE_OBJECTID and
+ * key.type: BTRFS_FIRST_CHUNK_TREE_OBJECTID, iff min=0 and max is the last
+ * element. Or else you will pick every chunk after or including min.
+ */
+static void btrfs_read_chunk_tree(grub_disk_t disk, struct btrfs_fs_info * 
fs_info)
+{
+        struct btrfs_disk_key search_key;
+        struct btrfs_chunk chunk;
+        struct btrfs_chunk_map_item item;
+        struct btrfs_path path;
+        struct btrfs_super_block sb = fs_info->sb_copy;
+
+        if (!(sb.flags & BTRFS_SUPER_FLAG_METADUMP)) {
+                if (sb.num_devices > 1) {
+                           grub_printf("warning: only support single device 
btrfs\n");
+               }
+                /* read chunk from chunk_tree */
+                search_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+                search_key.type = BTRFS_CHUNK_ITEM_KEY;
+                search_key.offset = 0;
+               clear_mem((char *)&path, sizeof(struct btrfs_path));
+                search_tree(disk, fs_info, sb.chunk_root, &search_key, &path);
+                while(1) {
+                        /* insert into the chunk_map every slot in the
+                        * leaf with key.offset >= search_offset and the
+                        * same key.type and key.object_id.
+                        */
+                        if (btrfs_comp_keys_type(&search_key, &path.item.key))
+                                break;
+                        grub_memcpy(&chunk, (struct btrfs_chunk *)(path.data), 
sizeof(struct btrfs_chunk));
+                        /* insert to mapping table, ignore stripes */
+                        item.logical = path.item.key.offset;
+                        item.length = chunk.length;
+                        item.devid = chunk.stripe.devid;
+                        item.physical = chunk.stripe.offset;
+                        insert_map(&item, fs_info);
+                        if(next_slot(disk, &path))
+                                if(next_leaf(disk, fs_info, &search_key, 
&path))
+                                        break;
+                } 
+        }
+}
+
+static void btrfs_get_fs_tree(grub_disk_t disk, struct  btrfs_fs_info * 
fs_info)
+{
+       struct btrfs_disk_key search_key;
+       struct btrfs_path path;
+       struct btrfs_root_item tree;
+       search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+       search_key.type = BTRFS_ROOT_ITEM_KEY;
+       search_key.offset = 0;
+       clear_mem((char *)&path, sizeof(struct btrfs_path));
+       search_tree(disk, fs_info, fs_info->sb_copy.root, &search_key, &path);
+       grub_memcpy(&tree, (struct btrfs_root_item *)path.data, sizeof(struct 
btrfs_root_item));
+       fs_info->fs_root = tree.bytenr;
+        fs_cache.default_fs_root = tree.bytenr;
+}
+
+
+static void free_cache(void)
+{
+        struct btrfs_cache * cache, * temp1;
+        cache = &fs_cache.dentry_cache;
+        temp1 = cache;
+        while(cache->next) {
+                temp1 = cache->next;
+                cache->next = temp1->next;
+                grub_free(temp1);
+//grub_printf("\n freed item from cache with value (comp: %s, ino: %lld, 
parent_ino:parent_ino:  %lld) \n", temp1->path, temp1->inum, 
temp1->parent_inum);
+        }
+        fs_cache.dentry_cache.next = NULL;
+}
+
+static void free_btrfs_cache(void)
+{
+  //      grub_printf("\n freed cache \n");
+        free_cache();
+        clear_mem((char *)&fs_cache, sizeof(struct btrfs_fs_cache));
+}
+
+static int init_btrfs_cache(grub_disk_t disk)
+{
+        // read the latest super block in fs_info.sb
+        if(btrfs_read_superblock(disk, &fs_cache.fs_info) || (grub_errno != 
GRUB_ERR_NONE)) {
+                return -1;
+        }
+        if(grub_strncmp((char *)(&(fs_cache.fs_info.sb_copy.magic)), 
BTRFS_MAGIC,
+                                sizeof(fs_cache.fs_info.sb_copy.magic))!= 0) {
+                grub_printf("\n This should not happen!");
+                grub_errno = GRUB_ERR_BAD_FS;
+                return grub_errno;
+        } 
+        btrfs_read_sys_chunk_array(&fs_cache.fs_info);
+        if(grub_errno == GRUB_ERR_NONE) {
+                btrfs_read_chunk_tree(disk, &fs_cache.fs_info);
+                if(grub_errno == GRUB_ERR_NONE)
+                        btrfs_get_fs_tree(disk, &fs_cache.fs_info);
+         }
+        if(grub_errno == GRUB_ERR_NONE)
+        {
+                //grub_printf("\n fscache is populated! ");
+                fs_cache.populated=1;
+                fs_cache.disk_id = disk->id;
+                btrfs_init_crc32c();
+        } else {
+                clear_mem((char *)&fs_cache, sizeof(struct btrfs_fs_cache)); 
// clear anything that was written
+        }
+        return grub_errno;
+}
+
+
+
+
+
+static struct grub_btrfs_data * btrfs_init_fs(grub_disk_t disk)
+{
+        struct grub_btrfs_data * data;
+        if((fs_cache.populated == 1) && (fs_cache.disk_id != disk->id)) {
+                grub_errno = GRUB_ERR_BAD_FS;
+                return NULL;
+        }
+        if(fs_cache.populated == 0) {
+                if(init_btrfs_cache(disk))
+                        return NULL;
+        }
+        data = grub_zalloc (sizeof (struct grub_btrfs_data));
+        if (!data)
+                return NULL;
+        data->fs_info = &fs_cache.fs_info;
+        data->cache = &fs_cache.dentry_cache;
+        data->disk = disk;
+        if(fs_cache.fs_info.fs_root != fs_cache.default_fs_root)
+                fs_cache.fs_info.fs_root = fs_cache.default_fs_root; //set it 
to the original root for future searches
+        return data;
+}
+
+static void get_component(const char * str, int *start, int * end)
+{
+       int i=*start;
+       while(str[i] == '/') {
+               i++;
+       }
+       if(str[i] == '\0') {
+               *start = grub_strlen(str) - 1;
+               *end = (*start) + 1;
+               return;
+       }
+       *start = i;
+       while(str[i]!= '\0' && str[i] != '/')
+               i++;
+       *end = i;
+       return;
+}
+
+static inline void btrfs_item_key_to_cpu(struct btrfs_disk_key * key, struct 
btrfs_path * path)
+{
+       key->type = path->item.key.type;
+       key->offset = path->item.key.offset;
+       key->objectid = path->item.key.objectid;
+}
+
+static struct btrfs_dir_item * btrfs_match_dir_item_name(struct btrfs_path * 
path, char * name, int len)
+{
+        struct btrfs_dir_item *dir_item;
+        unsigned long name_ptr;
+        u32 total_len;
+        u32 cur = 0;
+        u32 this_len;
+
+       dir_item = (struct btrfs_dir_item *) path->data;
+        total_len = path->item.size;
+        while (cur < total_len) {
+                this_len = sizeof(*dir_item) +
+                               dir_item->name_len + dir_item->data_len;
+                name_ptr = (unsigned long)(dir_item + 1);
+
+               if((dir_item->name_len < 0) || (dir_item->name_len > PATH_MAX))
+                        return NULL;
+               if(((char *)name_ptr)[dir_item->name_len] != '\0')
+                        ((char *)name_ptr)[dir_item->name_len] = '\0';
+
+                if (dir_item->name_len == len &&
+                                         grub_strcmp((char *)name_ptr, name)== 
0) {
+                        return dir_item;
+               }
+                cur = cur + this_len;
+                dir_item = (struct btrfs_dir_item *)((char *)dir_item +
+                                                     this_len);
+        }
+        return NULL;
+}
+
+
+static inline u64 btrfs_name_hash(const char *name, int len)
+{
+        return crc32c_le((u32)~1, name, len);
+}
+
+static int read_extent(grub_disk_t disk, struct btrfs_file_extent_item * 
extent_item, int offset, int disk_len, int mem_len, char * buf)
+{
+        long long total_len = 0;
+        //grub_printf("\n extent_item->compression: %d mem_len: %d", 
extent_item->compression, mem_len);
+        if(extent_item->compression != 0) {
+                return -mem_len;
+        }
+        while(disk_len > 0) {
+                // read at max 4K at a time
+                if(disk_len > 4096) {
+                        disk_len = disk_len - 4096;
+                        grub_disk_read(disk, offset>>SECTOR_BITS, (offset % 
(1<<SECTOR_BITS)), 4096, buf);
+                        if(grub_errno)
+                                break;
+                        offset = offset + 4096;
+                        buf = buf + 4096;
+                        total_len = total_len + 4096;
+                } else {
+                        grub_disk_read(disk, offset>>SECTOR_BITS, (offset % 
(1<<SECTOR_BITS)), disk_len, buf);
+                        if(grub_errno)
+                                break;
+                        total_len = total_len + disk_len;
+                        buf = buf + disk_len;
+                        disk_len = 0;
+                }
+        }
+        if(grub_errno)
+                grub_printf("\n GRUB ERROR while reading ! \n");
+        grub_printf("\n total_len read by read_extent: %lld \n", total_len);
+        return grub_errno ? -1 : total_len;
+}
+
+/* buf is already allocated  "size" memory. This function handles all the 
following cases
+ * when size < extent.length, size = extent.lenght and size > extent.length, 
so the next
+ * extent has to be searched and read. This function reads only 4K data from 
the disk at
+ * at a time. This is for now and should/can be changed
+ */
+
+static grub_ssize_t read_file_inr(grub_disk_t disk, struct btrfs_fs_info * 
fs_info, __le64 inum, __le64 size, char * buf)
+{
+       struct btrfs_disk_key key;
+       struct btrfs_path path;
+               int ret = 0;
+       struct btrfs_file_extent_item extent_item;
+       u64 offset, loffset;
+       u64 disk_len = 0, rem_len = 0;
+
+
+       clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+       key.objectid = inum;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = 0;
+
+       rem_len = size;
+
+        while(rem_len > 0) {
+                if(search_tree(disk, fs_info, fs_info->fs_root, &key, &path)) {
+                        grub_errno = GRUB_ERR_FILE_READ_ERROR;
+                        return(-1);
+                }
+                grub_memcpy(&extent_item, (struct btrfs_file_extent_item *) 
path.data, sizeof(struct btrfs_file_extent_item));
+                if (extent_item.type == BTRFS_FILE_EXTENT_INLINE) {
+                        // verify this part
+                        loffset = path.offsets[0] + sizeof(struct 
btrfs_header) \
+                                        + path.item.offset \
+                                        + (u32)(&(((struct 
btrfs_file_extent_item *) 0)->disk_bytenr));
+                        disk_len = path.item.size;
+                        if(disk_len > rem_len)
+                                disk_len = rem_len;
+                } else {
+                        loffset = extent_item.disk_bytenr;
+                        assert(extent_item.num_bytes == 
extent_item.disk_num_bytes);
+                        disk_len = extent_item.disk_num_bytes;
+                        if(disk_len > rem_len)
+                                disk_len =  rem_len;
+                }
+                offset = logical_physical(&(fs_info->chunk_map), loffset);
+                ret = read_extent(disk, &extent_item, offset, disk_len, 
rem_len, buf);         
+                if(ret < 0) {
+                        grub_errno = GRUB_ERR_FILE_READ_ERROR;
+                        return -1;
+                }
+                rem_len = rem_len - ret;
+                key.offset = size - rem_len;  //offset + rem_len = size
+        }
+        return size;
+}
+
+static inline __le64 get_root_inum(void)
+{
+       return BTRFS_FIRST_FREE_OBJECTID;
+}
+
+static __le64 get_resolved_inum(struct grub_btrfs_data * data, __le64 
cwd_inum, __le64 inum, int *lnk_count, struct btrfs_inode_item * inode);
+
+static __le64 search_comp(struct grub_btrfs_data * data, __le64 dirid, char * 
name, int name_len);
+
+static int change_fs_root(grub_disk_t disk, struct btrfs_fs_info * fs_info, 
char * subvolName)
+{
+        struct btrfs_disk_key search_key;
+        struct btrfs_path path;
+        struct btrfs_root_item tree;
+        int subvol_ok = -1;
+        char * name_ptr;
+        struct btrfs_root_ref *ref;
+
+        search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+        search_key.type = BTRFS_ROOT_REF_KEY;
+        search_key.offset = 0;
+        clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+        if (search_tree(disk, fs_info, fs_info->sb_copy.root, &search_key, 
&path))
+                next_slot(disk, &path);
+        while(1) {
+                if (btrfs_comp_keys_type(&search_key, &path.item.key))
+                        break;
+                ref = (struct btrfs_root_ref *)path.data;
+                name_ptr = (char *) (ref + 1); 
+                if(name_ptr[ref->name_len] != '\0')
+                        name_ptr[ref->name_len] = '\0';
+                // grub_printf("\n subvol: %s found! ", (char *) (ref + 1));
+                if (!grub_strcmp(name_ptr, subvolName)) {
+                        subvol_ok = 0;
+                        break;
+                }
+                if(next_slot(disk, &path))
+                        if(next_leaf(disk, fs_info, &search_key, &path))
+                               break; 
+        }
+        if(subvol_ok == 0) {
+                search_key.objectid = path.item.key.offset;
+                search_key.type = BTRFS_ROOT_ITEM_KEY;
+                search_key.offset = 0;
+                clear_mem((char *)&path, sizeof(struct btrfs_path));
+                if(fs_info->sb_copy.root) {
+                        search_tree(disk, fs_info, fs_info->sb_copy.root, 
&search_key, &path);
+                }
+                else
+                        return -1; //this would be terrible. some coding error
+                grub_memcpy(&tree, (struct btrfs_root_item *)path.data, 
sizeof(struct btrfs_root_item));
+                fs_info->fs_root = tree.bytenr;
+                if(grub_errno)
+                        subvol_ok = 0;
+                }
+        return subvol_ok;
+}
+
+static __le64 get_ino_cache(char * comp, __le64 parent_ino, u64 fs_root)
+{
+        struct btrfs_cache * temp = fs_cache.dentry_cache.next;
+        //grub_printf("\n searching for parent_ino: %lld", parent_ino);
+        while(temp){
+                //grub_printf("\n stored parent_inum: %lld", 
temp->parent_inum);
+                if((temp->parent_inum == parent_ino) && (temp->fs_root == 
fs_root)) {
+                        //grub_printf("\n found parent, about to match comp: 
%s temp->path: %s " , comp, temp->path);
+                        if(grub_strcmp(temp->path, comp) == 0) {
+                                if(temp->resolved_inum != -1)
+                                        return temp->resolved_inum;
+                                else {
+                                        grub_printf("\n This should really not 
happen! inode found, resolved_inum = -1");
+                                        return -1;
+                                }
+                        }
+                }
+                temp = temp->next;
+        }
+        return 0;
+}
+
+
+static __le64 get_parent(__le64 inum, u64 fs_root)
+{
+        struct btrfs_cache * temp = &fs_cache.dentry_cache;
+        if(inum == get_root_inum())
+                return inum;
+        while(temp->next) {
+                temp = temp->next;
+                if((temp->inum == inum) && (temp->fs_root == fs_root))
+                        return temp->parent_inum;
+        }
+        return -1;
+}
+
+static int insert_new_cache_item(__le64 inum, __le64 resolved_inum, __le64 
parent_inum, u64 fs_root, char * comp)
+{
+        struct btrfs_cache * temp;
+        int len = grub_strlen(comp);
+        if(get_parent(inum, fs_root) == parent_inum) //entry found
+                return 0;
+        temp =  (struct btrfs_cache *) grub_zalloc(sizeof(struct btrfs_cache));
+        if(temp == NULL) {
+                return grub_errno;
+        }
+        temp->path = grub_malloc(len + 1);
+        if(!temp->path) {
+                return grub_errno;
+        }
+        temp->next = fs_cache.dentry_cache.next;
+        fs_cache.dentry_cache.next = temp;
+        temp->inum = inum;
+        grub_strcpy(temp->path, comp);
+        temp->path[len] = '\0';
+        temp->parent_inum = parent_inum;
+        temp->resolved_inum = resolved_inum;       
+        temp->fs_root = fs_root;
+        //grub_printf("\n Inserted new item in cache at addr: %lx with value 
(comp: %s, ino: %lld, parent_ino:  %lld) ", (long) temp, comp, ino, 
parent_inum);
+        return 0;
+} 
+
+
+
+
+static int stripped_len(const char * str)
+{
+        int len = grub_strlen(str);
+        while((len > 1) && (str[len-1] == '/')) {
+                len = len-1;
+        }
+        return len;
+}
+
+/* could use grubs parser for this as is used in ext2. but for now  shall use 
this code itself */
+
+static __le64 search_path(struct grub_btrfs_data * data, __le64 root_ino, 
const char * str, int * lnk_count, struct btrfs_inode_item * inode_item)
+{
+        char * comp;
+        int start=0, end=0;
+        int len=stripped_len(str);
+        __le64 cwd_ino = -1;
+        __le64 ino = -1;
+        __le64 resolved_ino = -1;
+
+
+        if(len == 1) {
+                if(grub_strcmp(str, "/") == 0) {
+                        ino = root_ino;  
+                        cwd_ino = root_ino;
+                        resolved_ino = get_resolved_inum(data, cwd_ino, ino, 
lnk_count, inode_item);
+                        return resolved_ino;
+                }
+                return -1;
+        }
+
+        comp = grub_malloc(grub_strlen(str) + 1);
+        if(!comp) {
+                perror("\n could not allocate memory to comp because:");
+                return -1;
+        }
+
+        cwd_ino = root_ino;
+        ino = root_ino;
+
+        while(end < len) {
+                get_component(str, &start, &end);
+                grub_strcpy(comp, &str[start]);
+                comp[end - start] = '\0';
+                if(grub_strcmp(comp, ".") == 0) {
+                        ino = cwd_ino;
+                        cwd_ino = get_parent(ino, data->fs_info->fs_root);  
+                        if(cwd_ino == -1) {
+                                ino = -1;
+                                break;
+                        }
+                        start = end;
+                        continue;
+                } else  if(grub_strcmp(comp, "..")== 0) {
+                        ino = get_parent(cwd_ino, data->fs_info->fs_root);  
+                        cwd_ino = get_parent(ino, data->fs_info->fs_root);  
+                        if(cwd_ino == -1) {
+                                ino = -1;
+                                break;
+                        }
+                        start = end;
+                        continue;
+                } 
+                if((resolved_ino = get_ino_cache(comp, cwd_ino, 
data->fs_info->fs_root)) > 0) {
+                        cwd_ino = resolved_ino;
+                        grub_printf("\n from cache: resolved_ino: %lld ", 
resolved_ino);
+                        start = end;
+                        continue;
+                }
+                /* searching for component in dir with inode: ino */
+                ino = search_comp(data, cwd_ino, comp, end - start);
+                if(ino < 0) {
+                        ino = -1;
+                        break;
+                }
+                /* subvolume related - ret_ino = parent's inode i.e ino, then 
the dir is a subvolume */
+                if (cwd_ino == ino) {
+                        //grub_printf("\n %s is a subvolume ", comp);
+                        change_fs_root(data->disk, data->fs_info, comp);
+                }
+                start = end;
+                /* we will resolve the ino in case its a sym link and also to 
populate inode_item  */
+                resolved_ino = get_resolved_inum(data, cwd_ino, ino, 
lnk_count, inode_item);
+                if(resolved_ino <= 0) {
+                        ino = -1;
+                        break;
+                }
+                insert_new_cache_item(ino, resolved_ino, cwd_ino, 
data->fs_info->fs_root, comp);
+                cwd_ino = resolved_ino; 
+        }
+
+        if(ino > 0) {
+                /* we do this only to fetch the inode_item, incase it was not 
done 
+                 * due to previously fetching inode num from 
fs_cache.dentry_cache 
+                 */
+                resolved_ino = get_resolved_inum(data, cwd_ino, ino, 
lnk_count, inode_item);
+        } else {
+               grub_errno = GRUB_ERR_FILE_NOT_FOUND;
+        }
+
+        grub_free(comp);
+        return ino;
+}
+
+static u32 follow_link(struct grub_btrfs_data * data, __le64 cwd_inum, __le64 
* inum, int * lnk_count, struct btrfs_inode_item * inode)
+{
+       struct btrfs_disk_key key;
+       struct btrfs_path path;
+       struct btrfs_file_extent_item extent_item;
+       u64 offset, loffset;
+       u64 len = 0;
+       char link_buf[PATH_MAX];
+       __le64 root_ino = 0;
+       struct btrfs_fs_info * fs_info = data->fs_info;
+
+
+       clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+       key.objectid = *inum;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = 0;
+
+
+       if(search_tree(data->disk, fs_info, fs_info->fs_root, &key, &path)) {
+                grub_errno = GRUB_ERR_FILE_NOT_FOUND;
+               return(-1);
+       }
+       grub_memcpy(&extent_item, (struct btrfs_file_extent_item *)path.data, 
sizeof(struct btrfs_file_extent_item));
+       if (extent_item.type == BTRFS_FILE_EXTENT_INLINE) {
+               // verify this part
+               loffset = path.offsets[0] + sizeof(struct btrfs_header) \
+                               + path.item.offset \
+                               + (u32)(&(((struct btrfs_file_extent_item *) 
0)->disk_bytenr));
+               len = inode->size;
+       } else {
+               loffset = extent_item.disk_bytenr;
+               assert(extent_item.num_bytes == extent_item.disk_num_bytes);
+               len = extent_item.num_bytes;
+               if(len > inode->size)
+                       len =  inode->size;
+       }
+       offset = logical_physical(&fs_info->chunk_map, loffset);
+       assert(len < PATH_MAX);
+       grub_disk_read(data->disk, offset>>SECTOR_BITS, (offset % 
(1<<SECTOR_BITS)), len, link_buf);
+       link_buf[len] = '\0';
+       /* link_buf contains the path. resolve this and give back the final
+        * inode number back. If the path is absolute then start the search
+        * from the root inode. else the search should be from the current
+        * inode
+        */
+       if(link_buf[0] == '/') {
+               root_ino = get_root_inum();
+       }
+       else {
+               // relative path - need to handle '.' and '..' as special 
cases. 
+        if((link_buf[0] == '.') && (link_buf[1] != '.'))
+            root_ino = cwd_inum;
+        else
+            root_ino = get_parent(cwd_inum, data->fs_info->fs_root);
+       }
+    if(root_ino < 0)
+        return -1;
+       *inum = search_path(data, root_ino, link_buf, lnk_count, inode);
+       return 0;
+}
+
+/* returns the inode number. In case of anything other than the symbolic link,
+ * the inode number is the same as what is passed. But in case of the symbolic
+ * link the inode number is different than what is passed here. This function
+ * will return the ultimately resolved inode number of a symbolic link
+ * eg: path: a/b/c/d; say b->e/f i.e path: a/e/f/c/d i.e c has to be searched
+ * in f. So inode number of b will be passed to this function. if b is a
+ * symbolic link, the inode number of f will be returned back as b points to
+ * e/f ultimately
+ * On error -1 is returned
+ * For DIR/REG Files etc, the same inode number is returned
+ * Input: cwd_inum: inode of the directory in which the items are searched
+ *        inum: inode which we have to resolve incase its a link
+ *        cwd_inum will be used for searching in case the link has a relative
+ *        path
+ * Note: inode should be allocated memory before this function is called
+ */
+static __le64 get_resolved_inum(struct grub_btrfs_data * data, __le64 
cwd_inum, __le64 inum, int *lnk_count, struct btrfs_inode_item * inode)
+{
+       struct btrfs_disk_key key;
+       struct btrfs_path path;
+       int ret = 0;
+       struct btrfs_fs_info * fs_info = data->fs_info;
+       key.objectid = inum;
+       key.type = BTRFS_INODE_ITEM_KEY;
+       key.offset = 0;
+
+       clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+       if(search_tree(data->disk, data->fs_info, fs_info->fs_root, &key, 
&path)) {
+                grub_errno = GRUB_ERR_FILE_NOT_FOUND;
+               return -1;
+       }
+       grub_memcpy(inode, (struct btrfs_inode_item *)path.data, sizeof(struct 
btrfs_inode_item));
+       inode->mode = (((inode->mode) & 0170000) >> 12);
+       if (inode->mode == DT_LNK) {
+               /* follow the link and return back the new
+                * object id and the new mode. the link could be a link to a
+                * file or to a dir
+                */
+               *lnk_count = *lnk_count + 1;
+               if(*lnk_count >= MAX_LINK_COUNT) {
+                       return -1;
+               }
+               ret = follow_link(data, cwd_inum, &inum, lnk_count, inode);
+               if(ret < 0) {
+                       return ret;
+               }
+               assert(inode->mode != DT_LNK);
+       }
+       return inum;
+}
+
+
+
+static __le64 search_comp(struct grub_btrfs_data * data, __le64 dirid, char * 
name, int name_len)
+{
+        struct btrfs_disk_key key;
+        struct btrfs_path path;
+        struct btrfs_dir_item *di;
+
+        key.objectid = dirid;
+        key.type = BTRFS_DIR_ITEM_KEY;
+        key.offset = btrfs_name_hash(name, name_len);
+        clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+        if(search_tree(data->disk, data->fs_info, data->fs_info->fs_root, 
&key, &path)) 
+                return -1;
+        // walk through the dir
+        while(1) {
+                btrfs_item_key_to_cpu(&key, &path);
+                if (key.objectid != dirid || key.type != BTRFS_DIR_ITEM_KEY) {
+                        break;
+                }
+                di = btrfs_match_dir_item_name(&path, name, name_len);
+                if (di) {
+                        return(di->location.objectid);
+                }
+                if(next_slot(data->disk, &path))
+                        if(next_leaf(data->disk, data->fs_info, &key, &path))
+                               break; 
+        }
+        return -1;
+}
+
+static __le64 init_file(struct grub_btrfs_data * data, struct btrfs_inode_item 
* inode_item, const char * path)
+{
+       int lnk_count = 0;
+       __le64 inum;
+       __le64 root_ino = get_root_inum();
+
+       inum = search_path(data, root_ino, path, &lnk_count, inode_item);
+       if(inum < 0) {
+               return -1;
+       }
+       return inum;
+}
+
+/* Open a file named NAME and initialize FILE.  */
+static grub_err_t grub_btrfs_open (struct grub_file *file, const char *name)
+{
+       struct grub_btrfs_data * data = file->data;
+       __le64 inum;
+
+       grub_dl_ref (my_mod);
+
+        if(file->data == NULL) {
+                data =  btrfs_init_fs(file->device->disk);
+                if(!data) {
+                        return grub_errno;
+                }
+                file->data = data;
+        }
+       inum = init_file(data, &data->inode, name);
+       if(inum < 0) {
+               return grub_errno;
+       }
+       data->ino = inum;
+       file->size = grub_le_to_cpu64 (data->inode.size);
+       file->offset = 0;
+       return 0;
+}
+
+static int get_item_key(struct grub_btrfs_data *data, u64 loffset, int slot, 
struct btrfs_disk_key * found_key)
+{
+        u8 buf[BTRFS_MAX_LEAF_SIZE];
+        u64 offset = logical_physical(&data->fs_info->chunk_map, loffset);
+        struct btrfs_leaf * leaf = (struct btrfs_leaf *) buf;
+
+        offset = offset + sizeof(struct btrfs_header);
+
+        grub_disk_read(data->disk, offset >> SECTOR_BITS, (offset % 
(1<<SECTOR_BITS)), data->fs_info->sb_copy.leafsize - sizeof(struct 
btrfs_header), (char *)&leaf->items);
+        found_key->type = leaf->items[slot].key.type;
+        found_key->objectid = leaf->items[slot].key.objectid;
+        found_key->offset = leaf->items[slot].key.offset;
+        return 0;
+}
+
+
+
+/* Read LEN bytes data from FILE into BUF.  */
+static grub_ssize_t
+grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
+{
+        long long ret;
+       struct grub_btrfs_data *data = (struct grub_btrfs_data *) file->data;
+       u64 ino = data->ino;
+       clear_mem(buf, len);
+       if(len > data->inode.size) {
+               len = data->inode.size;
+       }
+       ret = read_file_inr(data->disk, data->fs_info, ino, len, buf);
+        if(ret > 0)
+                file->offset = ret;
+        return ret;
+}
+
+
+static int get_dirent_info(struct grub_btrfs_data * data, __le64 inum, struct 
grub_dirhook_info * info)
+{
+        struct btrfs_disk_key key;
+        struct btrfs_path path;
+        struct btrfs_inode_item inode;
+
+        key.objectid = inum;
+        key.type = BTRFS_INODE_ITEM_KEY;
+        key.offset = 0;
+
+        clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+        if(search_tree(data->disk, data->fs_info, data->fs_info->fs_root, 
&key, &path)) {
+                grub_errno = GRUB_ERR_FILE_NOT_FOUND;
+                return -1;
+        }
+
+        inode = *(struct btrfs_inode_item *)path.data;
+        inode.mode = (inode.mode & 0170000) >> 12;
+        if(inode.mode == DT_DIR) {
+                info->dir = 1;
+        }
+
+        return(0);
+}
+
+static struct btrfs_dir_item * btrfs_print_dir_item_names(struct 
grub_btrfs_data * data, struct btrfs_path * path, int (*hook) (const char 
*filename, const struct grub_dirhook_info *info))
+{
+        struct btrfs_dir_item *dir_item;
+        unsigned long name_ptr;
+        u32 total_len;
+        u32 cur;
+        u32 this_len;
+        struct grub_dirhook_info info;
+
+        info.dir = 0;
+        info.mtimeset=0;
+        info.case_insensitive=0;
+
+        assert(path != NULL);
+        dir_item = (struct btrfs_dir_item *) path->data;
+        total_len = path->item.size;
+        this_len = 0;
+        cur = 0;
+
+        while (cur < total_len) {
+                dir_item = (struct btrfs_dir_item *)((char *)dir_item +
+                                                     this_len);
+                this_len = sizeof(*dir_item) +
+                                dir_item->name_len + dir_item->data_len;
+                name_ptr = (unsigned long)(dir_item + 1);
+                cur = cur + this_len;
+                if(dir_item->name_len <= 0) {
+                        break;
+                }
+                if(((char *)name_ptr)[dir_item->name_len] != '\0')
+                        ((char *)name_ptr)[dir_item->name_len] = '\0';
+                /* get information about this direntry by searching for the 
inode*/
+                get_dirent_info(data, dir_item->location.objectid, &info);
+
+                hook((char *)name_ptr, &info);
+
+                cur = cur + this_len;
+        }
+        return NULL;
+}
+
+static int read_dir(struct grub_btrfs_data * data, __le64 inum, int (*hook) 
(const char *filename,
+                                 const struct grub_dirhook_info *info))
+{
+        struct btrfs_disk_key key, found_key;
+        struct btrfs_path path;
+        struct btrfs_dir_item * dir_item;
+        u32 level;
+
+        key.type = BTRFS_DIR_INDEX_KEY;
+        key.offset = 2;
+        key.objectid = inum;
+
+        clear_mem((char *)&path, sizeof(struct btrfs_path));
+
+        if(search_tree(data->disk, data->fs_info, data->fs_info->fs_root, 
&key, &path)) {
+                grub_errno = GRUB_ERR_FILE_NOT_FOUND;
+                return -1;
+        }
+        dir_item = (struct btrfs_dir_item *) path.data;
+        // now we should print the dir items
+        level = 0;
+        while(1) {
+                assert(path.data != NULL);
+
+                dir_item = (struct btrfs_dir_item *) path.data;
+                assert(dir_item  != NULL);
+                get_item_key(data, path.offsets[0], path.slots[level], 
&found_key);
+                if(found_key.type != BTRFS_DIR_INDEX_KEY || found_key.objectid 
!= inum) {
+                        break;
+                }
+                btrfs_print_dir_item_names(data, &path, hook);
+                if(next_slot(data->disk, &path))
+                        if(next_leaf(data->disk, data->fs_info, &key, &path))
+                                break;
+
+        }
+        return 0;
+}
+
+/* Check if the path represents a dir */
+static grub_err_t grub_btrfs_dir(grub_device_t device, const char *path,
+                                int (*hook) (const char *filename,
+                                const struct grub_dirhook_info *info))
+{
+       struct grub_btrfs_data * data;
+       int lnk_count = 0;
+       __le64 inum;
+       __le64 root_ino;
+
+       grub_dl_ref (my_mod);
+       grub_errno = GRUB_ERR_NONE;
+
+        data =  btrfs_init_fs(device->disk);
+        if(!data) {
+                grub_dl_unref(my_mod);
+                return grub_errno;
+        }
+
+       root_ino = get_root_inum();
+       inum = search_path(data, root_ino, path, &lnk_count, &data->inode);
+       if(inum < 0) {
+               grub_errno = GRUB_ERR_FILE_NOT_FOUND;
+       } else if (data->inode.mode != DT_DIR) {
+               grub_errno = GRUB_ERR_BAD_FILE_TYPE;
+       }
+        if(!grub_errno) {
+                read_dir(data, inum, hook);
+        }
+       grub_dl_unref (my_mod);
+        grub_free(data);
+       return grub_errno;
+}
+
+static grub_err_t grub_btrfs_close (grub_file_t file)
+{
+        if(file->data) {
+                grub_free (file->data);
+        }
+        grub_dl_unref (my_mod);
+        return GRUB_ERR_NONE;
+}
+
+/* copy the label from the super block to the char ** label
+ * assign an address to * label or NULL if error
+ */
+static grub_err_t
+grub_btrfs_label (grub_device_t device, char **label)
+{
+       struct grub_btrfs_data * data;
+
+       grub_dl_ref (my_mod);
+
+       data =  btrfs_init_fs(device->disk);
+       if(data) {
+               *label = grub_strndup (data->fs_info->sb_copy.label, 
BTRFS_LABEL_SIZE);
+               grub_free(data);
+       } else {
+               *label = NULL;
+       }
+       grub_dl_unref (my_mod);
+       return grub_errno;
+}
+
+/* copy the uuid from the super block to the char ** uuid
+ * assign an address to * uuid or NULL if error
+ */
+static grub_err_t grub_btrfs_uuid (grub_device_t device, char **uuid)
+{
+       struct grub_btrfs_data * data;
+
+       grub_dl_ref (my_mod);
+
+       data =  btrfs_init_fs(device->disk);
+       if(data) {
+               *uuid = grub_strndup ((const char 
*)data->fs_info->sb_copy.fsid, BTRFS_FSID_SIZE);
+               grub_free(data);
+       } else {
+               *uuid = NULL;
+       }
+       grub_dl_unref (my_mod);
+
+       return grub_errno;
+}
+
+/* copy the mtime from the super block to the char ** tm
+ * assign an address to * tm or NULL if error
+ */
+static grub_err_t grub_btrfs_mtime (grub_device_t device, grub_int32_t *tm)
+{
+       *tm = 0;
+       device = NULL;
+       return grub_errno;
+}
+
+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,
+       .label = grub_btrfs_label,
+       .uuid = grub_btrfs_uuid,
+       .mtime = grub_btrfs_mtime,
+#ifdef GRUB_UTIL
+       .reserved_first_sector = 1,
+#endif
+       .next = 0
+};
+
+GRUB_MOD_INIT(btrfs)
+{
+       grub_fs_register (&grub_btrfs_fs);
+       my_mod = mod;
+}
+
+GRUB_MOD_FINI(btrfs)
+{
+        free_btrfs_cache();
+       grub_fs_unregister (&grub_btrfs_fs);
+}
+
+
diff --git a/fs/btrfs.h b/fs/btrfs.h
new file mode 100644
index 0000000..d447fb9
--- /dev/null
+++ b/fs/btrfs.h
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2007 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#define BTRFS_MAGIC "_BHRfS_M"
+#define le64_to_cpu(x) ((__u64) (x))
+#define BTRFS_MAX_CHUNK_ENTRIES 256
+#define BTRFS_MAX_LEAF_SIZE 4096
+#define BTRFS_MAX_LEVEL 8
+#define BTRFS_UUID_SIZE 16
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_SUPER_MIRROR_MAX  3
+#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+#define BTRFS_SUPER_FLAG_METADUMP       (1ULL << 33)
+
+#define BTRFS_INODE_ITEM_KEY    1
+#define BTRFS_DIR_ITEM_KEY      84
+#define BTRFS_DIR_INDEX_KEY     96
+#define BTRFS_EXTENT_DATA_KEY   108
+#define BTRFS_ROOT_ITEM_KEY     132
+#define BTRFS_ROOT_REF_KEY      156
+#define BTRFS_CHUNK_ITEM_KEY    228
+
+
+#define BTRFS_FS_TREE_OBJECTID 5ULL
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
+
+#define BTRFS_FILE_EXTENT_INLINE 0
+#define MAX_LINK_COUNT           5
+#define PATH_MAX                 1024
+
+
+//<TODO> temporary - copy in the original code from grub somewhere. not done
+#define SECTOR_BITS 9 // sector size is 512 bytes which is 2^9
+
+
+
+#define __le64 long long
+#define __le32 int
+#define __le16 short
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+#define u64 unsigned long long
+
+
+enum dirent_type {
+        DT_UNKNOWN  =  0,
+        DT_FIFO     =  1,
+        DT_CHR      =  2,
+        DT_DIR      =  4,
+        DT_BLK      =  6,
+        DT_REG      =  8,
+        DT_LNK      = 10,
+        DT_SOCK     = 12,
+        DT_WHT      = 14,
+};
+
+
+
+struct extent_buffer{
+       u64 start;
+       u64 dev_bytenr;
+       u32 len;
+       char * data;
+};
+
+struct btrfs_timespec {
+       __le64 sec;
+       __le32 nsec;
+} __attribute__ ((__packed__));
+
+
+struct btrfs_inode_item {
+        /* nfs style generation number */
+        __le64 generation;
+        /* transid that last touched this inode */
+        __le64 transid;
+        u64 size;
+        __le64 nbytes;
+        __le64 block_group;
+        __le32 nlink;
+        __le32 uid;
+        __le32 gid;
+        __le32 mode;
+        __le64 rdev;
+        __le64 flags;
+
+        /* modification sequence number for NFS */
+        __le64 sequence;
+        /*
+         * a little future expansion, for more than this we can
+         * just grow the inode item and version it
+         */
+        __le64 reserved[4];
+        struct btrfs_timespec atime;
+        struct btrfs_timespec ctime;
+        struct btrfs_timespec mtime;
+        struct btrfs_timespec otime;
+}__attribute__ ((__packed__));
+
+struct btrfs_disk_key {
+        __le64 objectid;
+        u8 type;
+        __le64 offset;
+}__attribute__ ((__packed__));
+
+struct btrfs_root_item{
+       struct btrfs_inode_item inode;
+        __le64 generation;
+        __le64 root_dirid;
+        __le64 bytenr;
+        __le64 byte_limit;
+        __le64 bytes_used;
+        __le64 last_snapshot;
+        __le64 flags;
+        __le32 refs;
+        struct btrfs_disk_key drop_progress;
+        u8 drop_level;
+        u8 level;
+}__attribute__ ((__packed__));
+
+struct btrfs_root{
+       struct extent_buffer   node;
+       char                   data[4096];
+       struct btrfs_root_item root_item;
+       u64 objectid;
+
+       /* data allocations are done in sectorsize units */
+       u32 sectorsize;
+       /* node allocations are done in nodesize units */
+       u32 nodesize;
+       /* leaf allocations are done in leafsize units */
+       u32 leafsize;
+       /* leaf allocations are done in leafsize units */
+       u32 stripesize;
+
+       u32 type;
+}__attribute__ ((__packed__));
+
+/* this represents a divice in a chunk tree */
+struct btrfs_dev_item {
+       __le64 devid; /* internal device id */
+       __le64 total_bytes; /* size of the device */
+       __le64 bytes_used;
+       __le32 io_align; /* optimal io alignment */
+       __le32 io_width; /* optimal io width */
+       __le32 sector_size; /* minimal io size */
+       __le64 type; /* type and info about this device */
+       __le64 generation; /* expected generation */
+        __le64 start_offset; /* of the partition on a device */
+
+       /* info for allocation decisions */
+       __le32 dev_group;
+
+        u8 seek_speed; /* 0-100 (100 is fastest) */
+       u8 bandwidth;  /* 0-100 (100 is fastest) */
+
+        u8 uuid[BTRFS_UUID_SIZE]; /* dev uuid generated by btrfs */
+       u8 fsid[BTRFS_UUID_SIZE]; /* uuid of the host FS */
+} __attribute__ ((__packed__));
+
+
+struct btrfs_super_block{
+       u8 csum[BTRFS_CSUM_SIZE];
+       /* the first 4 fields must match struct btrfs_header */
+       u8 fsid[BTRFS_FSID_SIZE];    /* FS specific uuid */
+       u64 bytenr; /* this block number */
+       __le64 flags;
+
+       /* allowed to be different from the btrfs_header from here own down */
+       __le64 magic;
+       u64 generation;
+       __le64 root;
+       __le64 chunk_root;
+       __le64 log_root;
+
+       /* this will help find the new super based on the log root */
+       __le64 log_root_transid;
+       __le64 total_bytes;
+       __le64 bytes_used;
+       __le64 root_dir_objectid;
+       __le64 num_devices;
+       __le32 sectorsize;
+       __le32 nodesize;
+       __le32 leafsize;
+       __le32 stripesize;
+       __le32 sys_chunk_array_size;
+       __le64 chunk_root_generation;
+       __le64 compat_flags;
+       __le64 compat_ro_flags;
+       __le64 incompat_flags;
+       __le16 csum_type;
+       u8 root_level;
+       u8 chunk_root_level;
+       u8 log_root_level;
+       struct btrfs_dev_item dev_item;
+
+       char label[BTRFS_LABEL_SIZE];
+
+       /* future expansion */
+       __le64 reserved[32];
+       u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
+}__attribute__ ((__packed__));
+
+
+struct btrfs_stripe {
+        __le64 devid;
+        __le64 offset;
+        u8 dev_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+
+
+struct btrfs_chunk {
+        __le64 length;
+        __le64 owner;
+        __le64 stripe_len;
+        __le64 type;
+        __le32 io_align;
+        __le32 io_width;
+        __le32 sector_size;
+        __le16 num_stripes;
+        __le16 sub_stripes;
+        struct btrfs_stripe stripe;
+} __attribute__ ((__packed__));
+
+
+/* store logical offset to physical offset mapping */
+struct btrfs_chunk_map_item {
+        u64 logical;
+        u64 length;
+        u64 devid;
+        u64 physical;
+}__attribute__ ((__packed__));
+
+struct btrfs_chunk_map {
+       struct btrfs_chunk_map_item *map;
+        u32 map_length;
+        u32 cur_length;
+}__attribute__ ((__packed__));
+
+
+struct btrfs_fs_info {
+       u8 fsid[BTRFS_FSID_SIZE];
+       struct btrfs_super_block sb_copy;
+       struct btrfs_root chunk_root;
+       //stores the logical bytenr of where the fs_root is stored
+       u64 fs_root;
+       u64 sb_transid;
+       int fd;
+       int sb_mirror;
+       struct btrfs_chunk_map chunk_map;
+}__attribute__ ((__packed__));
+
+/* parent_inum for resolving "..", resolved_inum for getting the resolved
+ * inode given an inode of a symbolic link. path is used for fetching the
+ * resolved inode given the path instead of the inode num*/
+
+struct btrfs_cache {
+    __le64 inum;
+    __le64 resolved_inum;
+    __le64 parent_inum;
+    u64 fs_root;
+    char * path;
+    struct btrfs_cache * next;
+}__attribute__((__packed__));
+
+/* Information about a "mounted" btrfs filesystem.  */
+struct grub_btrfs_data
+{
+       struct btrfs_fs_info *fs_info;
+       struct btrfs_inode_item inode;
+       grub_disk_t disk; //pointer to a disk
+       u64 ino;
+       u64 offset;
+        struct btrfs_cache * cache;
+}__attribute__ ((__packed__));
+
+struct btrfs_fs_cache
+{
+    struct btrfs_fs_info fs_info;
+    struct btrfs_cache dentry_cache;
+    int populated;
+    unsigned long disk_id;
+    u64 default_fs_root;
+}__attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+        struct btrfs_disk_key location;
+        __le64 transid;
+        __le16 data_len;
+        __le16 name_len;
+        u8 type;
+} __attribute__ ((__packed__));
+
+/* Item header for per-leaf lookup */
+struct btrfs_item {
+       struct btrfs_disk_key key;
+       __le32 offset;
+       __le32 size;
+} __attribute__ ((__packed__));
+
+
+/* remember how we get to a node/leaf */
+struct btrfs_path {
+        u64 offsets[BTRFS_MAX_LEVEL];
+        int itemsnr[BTRFS_MAX_LEVEL];
+        int slots[BTRFS_MAX_LEVEL];
+        /* remember last slot's item and data */
+        struct btrfs_item item;
+        u8 data[BTRFS_MAX_LEAF_SIZE];
+}__attribute__ ((__packed__));
+
+struct btrfs_header {
+        /* these first four must match the super block */
+        u8 csum[BTRFS_CSUM_SIZE];
+        u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+        __le64 bytenr; /* which block this node is supposed to live in */
+        __le64 flags;
+
+        /* allowed to be different from the super from here on down */
+        u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+        __le64 generation;
+        __le64 owner;
+        __le32 nritems;
+        u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_leaf {
+        struct btrfs_header header;
+        struct btrfs_item items[];
+} __attribute__ ((__packed__));
+
+
+struct btrfs_key_ptr {
+        struct btrfs_disk_key key;
+        __le64 blockptr;
+        __le64 generation;
+} __attribute__ ((__packed__));
+
+struct btrfs_node {
+        struct btrfs_header header;
+        struct btrfs_key_ptr ptrs[];
+} __attribute__ ((__packed__));
+
+struct btrfs_file_extent_item {
+        __le64 generation;
+        __le64 ram_bytes;
+        u8 compression;
+        u8 encryption;
+        __le16 other_encoding; /* spare for later use */
+        u8 type;
+        u64 disk_bytenr;
+        __le64 disk_num_bytes;
+        __le64 offset;
+        __le64 num_bytes;
+} __attribute__ ((__packed__));
+
+struct btrfs_root_ref {
+        __le64 dirid;
+        __le64 sequence;
+        __le16 name_len;
+} __attribute__ ((__packed__));
+        
+
+
-- 
1.7.0.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]