Signed-off-by: Edward Shishkin <[email protected]>

---
 grub/Makefile.am    |    2 
 stage2/btrfs.h      |   55 ++--
 stage2/builtins.c   |   10 
 stage2/disk_io.c    |    2 
 stage2/filesys.h    |    4 
 stage2/fsys_btrfs.c |  693 +++++++++++++++++++++++++++++++++++++++++-----------
 6 files changed, 595 insertions(+), 171 deletions(-)

--- grub-0.97.orig/stage2/btrfs.h
+++ grub-0.97/stage2/btrfs.h
@@ -124,6 +124,7 @@ static int btrfs_csum_sizes[] = { 4, 0 }
 #define BTRFS_DEFAULT_NUM_DEVICES     1
 #define BTRFS_DEFAULT_NODE_SIZE       4096
 #define BTRFS_DEFAULT_LEAF_SIZE       4096
+#define BTRFS_NUM_CACHED_DEVICES      128
 
 #define WARN_ON(c)
 #define cassert(cond) ({ switch (-1) { case (cond): case 0: break; } })
@@ -315,13 +316,22 @@ struct btrfs_node {
        struct btrfs_key_ptr ptrs[];
 } __attribute__ ((__packed__));
 
+struct btrfs_device {
+       /* the internal btrfs device id */
+       u64 devid;
+       /* the internal grub device representation */
+       unsigned long drive;
+       unsigned long part;
+       unsigned long length;
+};
+
 struct extent_buffer {
        /* metadata */
+       struct btrfs_device dev;
        u64 start;
        u64 dev_bytenr;
        u32 len;
-       int refs;
-       int flags;
+       /* data */
        char *data;
 };
 
@@ -555,12 +565,8 @@ struct btrfs_block_group_item {
 struct btrfs_root {
        struct extent_buffer   node;
        char                   data[4096];
-       struct extent_buffer   *commit_root;
        struct btrfs_root_item root_item;
-       struct btrfs_key       root_key;
-       struct btrfs_fs_info   *fs_info;
        u64 objectid;
-       u64 last_trans;
 
        /* data allocations are done in sectorsize units */
        u32 sectorsize;
@@ -573,42 +579,31 @@ struct btrfs_root {
 
        /* leaf allocations are done in leafsize units */
        u32 stripesize;
+};
 
-       int ref_cows;
-       int track_dirty;
-
-
-       u32 type;
-       u64 highest_inode;
-       u64 last_inode_alloc;
+struct btrfs_file_info {
+       struct btrfs_key key;
 };
 
 struct btrfs_root;
 struct btrfs_fs_devices;
 struct btrfs_fs_info {
        u8 fsid[BTRFS_FSID_SIZE];
-       u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
        struct btrfs_root fs_root;
        struct btrfs_root tree_root;
        struct btrfs_root chunk_root;
 
-       struct btrfs_key  file_info; /* currently opened file */
+       struct btrfs_file_info file_info; /* currently opened file */
        struct btrfs_path paths [LAST_LOOKUP_POOL];
 
-       u64 generation;
-       u64 last_trans_committed;
+       char mbr[SECTOR_SIZE];
 
-       u64 system_alloc_profile;
-       u64 alloc_start;
+       int sb_mirror;
+       u64 sb_transid;
+       struct btrfs_device sb_dev;
+       struct btrfs_super_block sb_copy;
 
-       struct btrfs_super_block super_temp;
-       struct btrfs_super_block super_copy;
-
-       u64 super_bytenr;
-       u64 total_pinned;
-
-       int system_allocs;
-       int readonly;
+       struct btrfs_device devices[BTRFS_NUM_CACHED_DEVICES + 1];
 };
 
 /*
@@ -1129,6 +1124,11 @@ static inline void btrfs_set_key_type(st
        key->type = val;
 }
 
+static inline u64 btrfs_super_devid(struct btrfs_super_block *disk_super)
+{
+       return le64_to_cpu(disk_super->dev_item.devid);
+}
+
 /* struct btrfs_header */
 BTRFS_SETGET_HEADER_FUNCS(header_bytenr, struct btrfs_header, bytenr, 64);
 BTRFS_SETGET_HEADER_FUNCS(header_generation, struct btrfs_header,
@@ -1317,6 +1317,7 @@ struct btrfs_fs_devices {
 };
 
 struct btrfs_bio_stripe {
+       struct btrfs_device dev;
        u64 physical;
 };
 
--- grub-0.97.orig/stage2/fsys_btrfs.c
+++ grub-0.97/stage2/fsys_btrfs.c
@@ -31,15 +31,21 @@
 #define BTRFS_FS_INFO                                                  \
        ((struct btrfs_fs_info *)((unsigned long)FSYS_BUF +             \
                                  LOOKUP_CACHE_SIZE))
-#define BTRFS_CACHE_SIZE        (sizeof(struct btrfs_fs_info) +        \
-                                LOOKUP_CACHE_SIZE)
-#define BTRFS_FILE_INFO         (&BTRFS_FS_INFO->file_info)
-#define BTRFS_TREE_ROOT         (&BTRFS_FS_INFO->tree_root)
-#define BTRFS_CHUNK_ROOT        (&BTRFS_FS_INFO->chunk_root)
-#define BTRFS_FS_ROOT           (&BTRFS_FS_INFO->fs_root)
-#define BTRFS_SUPER             (&BTRFS_FS_INFO->super_copy)
-#define LOOKUP_CACHE_BUF(id)   ((char *)((unsigned long)FSYS_BUF +     \
-                                         id * LOOKUP_CACHE_BUF_SIZE))
+#define BTRFS_CACHE_SIZE         (sizeof(struct btrfs_fs_info) +       \
+                                 LOOKUP_CACHE_SIZE)
+#define BTRFS_TREE_ROOT          (&BTRFS_FS_INFO->tree_root)
+#define BTRFS_CHUNK_ROOT         (&BTRFS_FS_INFO->chunk_root)
+#define BTRFS_FS_ROOT            (&BTRFS_FS_INFO->fs_root)
+#define BTRFS_SUPER              (&BTRFS_FS_INFO->sb_copy)
+#define BTRFS_DEVICES            (&BTRFS_FS_INFO->devices[0])
+#define BTRFS_FILE_INFO          (&BTRFS_FS_INFO->file_info)
+#define BTRFS_FILE_INFO_KEY      (&BTRFS_FILE_INFO->key)
+
+#define BTRFS_VOLATILE_DEV_CACHE                                       \
+       (&BTRFS_FS_INFO->devices[BTRFS_NUM_CACHED_DEVICES])
+
+#define LOOKUP_CACHE_BUF(id) ((char *)((unsigned long)FSYS_BUF +       \
+                                      id * LOOKUP_CACHE_BUF_SIZE))
 
 #define noop   do {; } while (0)
 
@@ -76,13 +82,19 @@ static inline struct btrfs_path *btrfs_g
        return &BTRFS_FS_INFO->paths[lpid];
 }
 
-static inline void btrfs_update_file_info(struct btrfs_path *path)
+static inline void btrfs_set_path_key(struct btrfs_path *path,
+                                     struct btrfs_key *key)
 {
        btrfs_item_key_to_cpu(&path->nodes[0],
-                             BTRFS_FILE_INFO,
+                             key,
                              path->slots[0]);
 }
 
+static inline void btrfs_update_file_info(struct btrfs_path *path)
+{
+       btrfs_set_path_key(path, BTRFS_FILE_INFO_KEY);
+}
+
 static inline void btrfs_set_root_dir_key(struct btrfs_key *key)
 {
        key->objectid = BTRFS_FIRST_FREE_OBJECTID;
@@ -120,14 +132,13 @@ static inline void init_btrfs_path(looku
 static inline void init_btrfs_info(void)
 {
        int i;
-       struct btrfs_fs_info *fs = BTRFS_FS_INFO;
 
-       memset(fs, 0, sizeof (*fs));
+       memset(BTRFS_FS_INFO, 0, sizeof(struct btrfs_fs_info));
        for(i = 0; i < LAST_LOOKUP_POOL; i++)
                init_btrfs_path(i);
-       init_btrfs_root(&fs->tree_root);
-       init_btrfs_root(&fs->chunk_root);
-       init_btrfs_root(&fs->fs_root);
+       init_btrfs_root(BTRFS_TREE_ROOT);
+       init_btrfs_root(BTRFS_CHUNK_ROOT);
+       init_btrfs_root(BTRFS_FS_ROOT);
 }
 
 static void setup_root(struct btrfs_root *root,
@@ -137,7 +148,6 @@ static void setup_root(struct btrfs_root
                       u32 stripesize,
                       u64 objectid)
 {
-       root->fs_info = BTRFS_FS_INFO;
        root->nodesize = nodesize;
        root->leafsize = leafsize;
        root->sectorsize = sectorsize;
@@ -152,7 +162,6 @@ static void setup_root(struct btrfs_root
 static int btrfs_find_last_root(struct btrfs_root *tree_root,
                                u64 objectid,
                                struct btrfs_root_item *item,
-                               struct btrfs_key *key,
                                lookup_pool_id lpid)
 {
        int ret;
@@ -175,10 +184,10 @@ static int btrfs_find_last_root(struct b
        btrfs_item_key_to_cpu(&path->nodes[0], &found_key, slot);
        if (found_key.objectid != objectid)
                return 1;
+
        read_extent_buffer(&path->nodes[0], item,
                           btrfs_item_ptr_offset(&path->nodes[0], slot),
                           sizeof(*item));
-       memcpy(key, &found_key, sizeof(found_key));
        return 0;
 }
 
@@ -210,14 +219,13 @@ static int find_setup_root(struct btrfs_
                 */
                ret = btrfs_find_last_root(tree_root, objectid,
                                           &dest_root->root_item,
-                                          &dest_root->root_key,
                                           lpid);
                if (ret)
                        return ret;
                bytenr = btrfs_root_bytenr(&dest_root->root_item);
                blocksize = btrfs_level_size(dest_root,
                                       btrfs_root_level(&dest_root->root_item));
-               generation = btrfs_root_generation(&tree_root->root_item);
+               generation = btrfs_root_generation(&dest_root->root_item);
        }
        ret = read_tree_block(dest_root,
                              &eb,
@@ -243,12 +251,34 @@ static inline int btrfs_strncmp(const ch
        return __res;
 }
 
-static int btrfs_check_super_block(struct btrfs_super_block *sb)
-{
-       if (sb->num_devices != BTRFS_DEFAULT_NUM_DEVICES) {
-               btrfs_msg("Btrfs multi-device configuration unsupported\n");
-               goto error;
+/*
+ * the same as devread, but accepts
+ * device number, start and length.
+ */
+static int btrfs_devread(unsigned long drive, unsigned long part,
+                        unsigned long dev_len, int sector,
+                        int byte_offset, int byte_len, char *buf)
+{
+       if (sector < 0
+           || ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS))
+               >= dev_len)) {
+               errnum = ERR_OUTSIDE_PART;
+               return 0;
        }
+       sector += byte_offset >> SECTOR_BITS;
+       byte_offset &= SECTOR_SIZE - 1;
+#if !defined(STAGE1_5)
+       if (disk_read_hook && debug)
+               printf ("<%d, %d, %d>", sector, byte_offset, byte_len);
+#endif /* !STAGE1_5 */
+       return rawread(drive, part + sector, byte_offset,
+                      byte_len, buf);
+}
+
+static int btrfs_check_super(void)
+{
+       struct btrfs_super_block *sb = BTRFS_SUPER;
+
        if (sb->nodesize != BTRFS_DEFAULT_NODE_SIZE) {
                btrfs_msg("Btrfs node size (%d) != %d unsupported\n",
                          sb->nodesize, BTRFS_DEFAULT_NODE_SIZE);
@@ -259,103 +289,412 @@ static int btrfs_check_super_block(struc
                          sb->leafsize, BTRFS_DEFAULT_LEAF_SIZE);
                goto error;
        }
-       return 1;
- error:
-       errnum = ERR_FSYS_MOUNT;
        return 0;
+ error:
+       return 1;
 }
 
-int btrfs_mount(void)
+/* lift the super block */
+static int btrfs_uptodate_super_copy(struct btrfs_fs_info *fs)
 {
-       int i;
-       int ret;
-       u64 transid = 0;
-       u64 bytenr;
-
-       struct btrfs_fs_info *fs = BTRFS_FS_INFO;
-       struct btrfs_super_block *sb_tmp; /* current */
-       struct btrfs_super_block *sb;     /* latest */
-
-       struct btrfs_root *tree_root = &fs->tree_root;
-       struct btrfs_root *chunk_root = &fs->chunk_root;
-       struct btrfs_root *fs_root = &fs->fs_root;
-
-       check_btrfs_cache_size();
-       init_btrfs_info();
+       errnum = ERR_NONE;
+       btrfs_devread(BTRFS_FS_INFO->sb_dev.drive,
+                     BTRFS_FS_INFO->sb_dev.part,
+                     BTRFS_FS_INFO->sb_dev.length,
+                     btrfs_sb_offset(BTRFS_FS_INFO->sb_mirror) >> SECTOR_BITS,
+                     0,
+                     sizeof(struct btrfs_super_block),
+                     (char *)BTRFS_SUPER);
+       return btrfs_check_super();
+}
 
-       sb_tmp = &fs->super_temp;
-       sb = &fs->super_copy;
+/*
+ * Looking for a btrfs super block by magic, @fsid and @devid
+ * (the last two ones are optional). Update latest transid (if
+ * any). Return 0, if such super block was found. Otherwise,
+ * return 1.
+ *
+ * NOTE:
+ * After calling this function the sb_copy of global btrfs_fs_info
+ * can contain garbage, so the caller is responsible for this to be
+ * uptodate (see the function btrfs_uptodate_super_copy()).
+ */
+static int btrfs_find_super(struct btrfs_device *dev, char *fsid, u64 *devid)
+{
+       int i, ret;
+       int found = 0;
 
-       /* pick up the latest version of superblock */
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
-               bytenr = btrfs_sb_offset(i);
-               ret = devread(bytenr >> SECTOR_BITS,
-                             0,
-                             sizeof(*sb_tmp),
-                             (char *)sb_tmp);
-               if (!ret)
-                       continue;
-
-               if (btrfs_super_bytenr(sb_tmp) != bytenr ||
-                   btrfs_strncmp((char *)(&sb_tmp->magic),
+               ret = btrfs_devread(dev->drive,
+                                   dev->part,
+                                   dev->length,
+                                   btrfs_sb_offset(i) >> SECTOR_BITS,
+                                   0,
+                                   sizeof(struct btrfs_super_block),
+                                   (char *)BTRFS_SUPER);
+               if (!ret) {
+                       if (errnum == ERR_OUTSIDE_PART) {
+                               errnum = ERR_NONE;
+                               break;
+                       } else {
+                               errnum = ERR_NONE;
+                               continue;
+                       }
+               }
+               if (btrfs_super_bytenr(BTRFS_SUPER) != btrfs_sb_offset(i) ||
+                   btrfs_strncmp((char *)(&BTRFS_SUPER->magic),
                                  BTRFS_MAGIC,
-                                 sizeof(sb_tmp->magic)))
+                                 sizeof(BTRFS_SUPER->magic)))
                        continue;
-               if (btrfs_super_generation(sb_tmp) > transid) {
-                       memcpy(sb, sb_tmp, sizeof(*sb_tmp));
-                       transid = btrfs_super_generation(sb);
+               if (fsid &&
+                   btrfs_strncmp(fsid,
+                                 (char *)BTRFS_SUPER->fsid,
+                                 BTRFS_FSID_SIZE))
+                       return 1;
+               if (devid &&
+                   *devid != btrfs_super_devid(BTRFS_SUPER))
+                       return 1;
+               found = 1;
+               dev->devid = btrfs_super_devid(BTRFS_SUPER);
+
+               if (btrfs_super_generation(BTRFS_SUPER) >
+                   BTRFS_FS_INFO->sb_transid) {
+                       BTRFS_FS_INFO->sb_transid =
+                               btrfs_super_generation(BTRFS_SUPER);
+                       BTRFS_FS_INFO->sb_mirror = i;
+                       BTRFS_FS_INFO->sb_dev.devid =
+                               btrfs_super_devid(BTRFS_SUPER);
+                       BTRFS_FS_INFO->sb_dev.drive = dev->drive;
+                       BTRFS_FS_INFO->sb_dev.part = dev->part;
+                       BTRFS_FS_INFO->sb_dev.length = dev->length;
+               }
+       }
+       return !found;
+}
+
+/*
+ * "Discern" a btrfs device by fsid and
+ * optionaly by devid (if lookup is set).
+ * Populate persistent device cache (if
+ * there are free slots).
+ */
+static int btrfs_discerner(struct btrfs_device **dev, int lookup)
+{
+       if (btrfs_find_super(*dev,
+                            (char *)BTRFS_FS_INFO->fsid,
+                            (lookup ? &(*dev)->devid : 0)))
+               /* not found */
+               return 0;
+       if (*dev < BTRFS_VOLATILE_DEV_CACHE) {
+               /* populate persistent device cache */
+               memcpy(*dev + 1, *dev, sizeof(struct btrfs_device));
+               (*dev)++;
+       }
+       return 1;
+}
+
+/*
+ * Scan available grub devices and call discerner
+ * for them. Return a number of discerned devices
+ * The scanner was stolen from print_completions().
+ *
+ * Preconditions:
+ * The global structure btrfs_fs_info contains
+ * the latest valid version of btrfs superblock
+ * (the field @sb_copy)
+ */
+static u64 scan_grub_devices(struct btrfs_device *dev,
+                            int (*discerner)(struct btrfs_device **, int),
+                            int lookup)
+{
+       int i, j;
+       u64 count = 0;
+       struct geometry geom;
+
+       for (i = 0; i < 2; i++)
+               for (j = 0; j < 8; j++) {
+                       unsigned long part = 0xFFFFFF;
+                       int type, entry, gpt_count, gpt_size;
+                       unsigned long offset, ext_offset, gpt_offset;
+
+                       dev->drive = (i * 0x80) + j;
+                       if (get_diskinfo(dev->drive, &geom))
+                               continue;
+                       while (1) {
+                               int ret;
+                               buf_drive = -1;
+                               errnum = ERR_NONE;
+                               ret = next_partition(dev->drive, 0xFFFFFF,
+                                                    &part, &type, &dev->part,
+                                                    &dev->length, &offset,
+                                                    &entry, &ext_offset,
+                                                    &gpt_offset, &gpt_count,
+                                                    &gpt_size,
+                                                    BTRFS_FS_INFO->mbr);
+                               if (!ret)
+                                       break;
+                               if (discerner(&dev, lookup)) {
+                                       count++;
+                                       if (lookup)
+                                               goto exit;
+                               }
+                       }
+               }
+       errnum = ERR_NONE;
+       if (cdrom_drive != GRUB_INVALID_DRIVE &&
+           !get_diskinfo(cdrom_drive, &geom)) {
+               dev->drive = cdrom_drive;
+               dev->part = 0;
+               dev->length = geom.total_sectors;
+               if (discerner(&dev, lookup)) {
+                       count++;
+                       if (lookup)
+                               goto exit;
+               }
+       }
+#ifdef SUPPORT_NETBOOT
+       errnum = ERR_NONE;
+       if (network_ready &&
+           !get_diskinfo(NETWORK_DRIVE, &geom)) {
+               dev->drive = NETWORK_DRIVE;
+               dev->part = 0;
+               dev->length = geom.total_sectors;
+               if (discerner(&dev, lookup)) {
+                       count++;
+                       if (lookup)
+                               goto exit;
+               }
+       }
+#endif /* SUPPORT_NETBOOT */
+ exit:
+       return count;
+}
+
+#if 0
+static int btrfs_next_item(struct btrfs_root *root,
+                          struct btrfs_path *path);
+
+/*
+ * Scan the chunk tree for dev items
+ * and call a seeker for all of them.
+ * Preconditions: chunk root is installed
+ * to the global btrfs_fs_info.
+ */
+static int scan_dev_tree(struct btrfs_device* (*seeker)(u64))
+{
+       int ret;
+       u64 num_devices = 0;
+       struct btrfs_key key;
+       struct btrfs_key found_key;
+       struct btrfs_path *path;
+       struct btrfs_root *root;
+
+       root = BTRFS_CHUNK_ROOT;
+       path = btrfs_grab_path(FIRST_EXTERNAL_LOOKUP_POOL);
+       key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+       key.type = 0;
+       key.offset = 0;
+
+       ret = aux_tree_lookup(root, &key, path);
+       if (ret == -1)
+               goto corrupted;
+       while (1) {
+               struct btrfs_device *result;
+               struct btrfs_dev_item *dev_item;
+
+               btrfs_item_key_to_cpu(&path->nodes[0],
+                                     &found_key,
+                                     path->slots[0]);
+               if (found_key.objectid != BTRFS_DEV_ITEMS_OBJECTID)
+                       break;
+               dev_item = btrfs_item_ptr(&path->nodes[0],
+                                         path->slots[0],
+                                         struct btrfs_dev_item);
+               result = seeker(btrfs_device_id(&path->nodes[0], dev_item));
+               if (result == NULL) {
+                       btrfs_msg("Btrfs device %llu is not available\n",
+                                 btrfs_device_id(&path->nodes[0], dev_item));
+                       goto missed_dev;
                }
+               num_devices++;
+               ret = btrfs_next_item(root, path);
+               if (ret)
+                       break;
        }
-       /* there might be errors when reading super mirrors */
-       if (errnum == ERR_OUTSIDE_PART)
-               errnum = ERR_NONE;
-       if (transid <= 0) {
-               btrfs_msg("No valid Btrfs superblock found\n");
+       if (num_devices == btrfs_super_num_devices(BTRFS_SUPER))
+               return 0;
+ corrupted:
+       errnum = ERR_FSYS_CORRUPT;
+       return 1;
+ missed_dev:
+       errnum = ERR_FSYS_MOUNT;
+       return 1;
+}
+#endif /* 0 */
+
+/*
+ * Find a grub btrfs device by devid.
+ * Preconditions: global btrfs_fs_info
+ * contains a copy of btrfs super block.
+ *
+ * Return pointer to the cached device on success.
+ * Otherwise return NULL.
+ */
+static struct btrfs_device *btrfs_lookup_device(u64 devid)
+{
+       int i, result;
+       struct btrfs_device *cdev;
+
+       for (i = 0; i < BTRFS_NUM_CACHED_DEVICES; i++) {
+               cdev = &BTRFS_DEVICES[i];
+               if (cdev->devid == devid)
+                       goto found_in_cache;
+               if (cdev->devid == 0)
+                       goto not_found_in_cache;
+       }
+ not_found_in_cache:
+       cdev = BTRFS_VOLATILE_DEV_CACHE;
+       cdev->devid = devid;
+       result = scan_grub_devices(cdev,
+                                  btrfs_discerner,
+                                  1);
+       if (result == 0)
+               /*
+                * At mount time we have figured out that
+                * number of available devices is not less
+                * then number of devices recorded in the
+                * super block. Hence we treat this case as
+                * file system corruption.
+                */
+               goto corrupt;
+       result = btrfs_uptodate_super_copy(BTRFS_FS_INFO);
+       if (result)
+               goto corrupt;
+ found_in_cache:
+       return cdev;
+ corrupt:
+       errnum = ERR_FSYS_CORRUPT;
+       return NULL;
+}
+
+static int btrfs_find_device(struct btrfs_device *dev)
+{
+       struct btrfs_device *cdev;
+
+       if (btrfs_super_num_devices(BTRFS_SUPER) == 1) {
+               dev->drive = current_drive;
+               dev->part = part_start;
+               dev->length = part_length;
                return 0;
        }
-       if (!btrfs_check_super_block(sb))
+       cdev = btrfs_lookup_device(dev->devid);
+       if (cdev == NULL)
+               return 1;
+       dev->drive  = cdev->drive;
+       dev->part   = cdev->part;
+       dev->length = cdev->length;
+       return 0;
+}
+
+static inline void init_btrfs_volatile_dev_cache(void)
+{
+       BTRFS_VOLATILE_DEV_CACHE->devid = 0;
+       BTRFS_VOLATILE_DEV_CACHE->drive = current_drive;
+       BTRFS_VOLATILE_DEV_CACHE->part = part_start;
+       BTRFS_VOLATILE_DEV_CACHE->length = part_length;
+}
+
+/*
+ * check availability of btrfs devices
+ * and populate the persistent device cache
+ */
+static int btrfs_check_devices(void)
+{
+       u64 num_dev;
+
+       if (btrfs_super_num_devices(BTRFS_SUPER) == 1)
                return 0;
+       num_dev = scan_grub_devices(BTRFS_DEVICES,
+                                   btrfs_discerner, 0);
+       if (btrfs_uptodate_super_copy(BTRFS_FS_INFO))
+               return 1;
+       if (num_dev < btrfs_super_num_devices(BTRFS_SUPER)) {
+               btrfs_msg("Some (%llu) Btrfs devices is not available\n",
+                         btrfs_super_num_devices(BTRFS_SUPER) - num_dev);
+               return 1;
+       }
+       return 0;
+}
+
+int btrfs_mount(void)
+{
+       int ret;
+
+       check_btrfs_cache_size();
+       init_btrfs_info();
+       init_btrfs_volatile_dev_cache();
+
+       ret = btrfs_find_super(BTRFS_VOLATILE_DEV_CACHE, NULL, NULL);
+       if (ret) {
+               btrfs_msg("Drive %lu, partition %lu: no Btrfs metadata\n",
+                         current_drive, part_start);
+               goto error;
+       }
+       ret = btrfs_uptodate_super_copy(BTRFS_FS_INFO);
+       if (ret)
+               goto error;
+       BTRFS_FS_INFO->sb_transid =
+               btrfs_super_generation(BTRFS_SUPER);
+       memcpy(BTRFS_FS_INFO->fsid,
+              BTRFS_SUPER->fsid,
+              BTRFS_FSID_SIZE);
+       ret = btrfs_check_devices();
+       if (ret)
+               goto error;
        /* setup chunk root */
        ret = find_setup_root(NULL,
-                             btrfs_super_nodesize(sb),
-                             btrfs_super_leafsize(sb),
-                             btrfs_super_sectorsize(sb),
-                             btrfs_super_stripesize(sb),
+                             btrfs_super_nodesize(BTRFS_SUPER),
+                             btrfs_super_leafsize(BTRFS_SUPER),
+                             btrfs_super_sectorsize(BTRFS_SUPER),
+                             btrfs_super_stripesize(BTRFS_SUPER),
                              BTRFS_CHUNK_TREE_OBJECTID,
-                             chunk_root,
-                             btrfs_super_chunk_root(sb),
-                             btrfs_chunk_root_level_size(sb),
-                             btrfs_super_chunk_root_generation(sb),
+                             BTRFS_CHUNK_ROOT,
+                             btrfs_super_chunk_root(BTRFS_SUPER),
+                             btrfs_chunk_root_level_size(BTRFS_SUPER),
+                             btrfs_super_chunk_root_generation(BTRFS_SUPER),
                              FIRST_EXTERNAL_LOOKUP_POOL);
        if (ret)
                return 0;
        /* setup tree root */
        ret = find_setup_root(NULL,
-                             btrfs_super_nodesize(sb),
-                             btrfs_super_leafsize(sb),
-                             btrfs_super_sectorsize(sb),
-                             btrfs_super_stripesize(sb),
+                             btrfs_super_nodesize(BTRFS_SUPER),
+                             btrfs_super_leafsize(BTRFS_SUPER),
+                             btrfs_super_sectorsize(BTRFS_SUPER),
+                             btrfs_super_stripesize(BTRFS_SUPER),
                              BTRFS_ROOT_TREE_OBJECTID,
-                             tree_root,
-                             btrfs_super_root(sb),
-                             btrfs_root_level_size(sb),
-                             btrfs_super_generation(sb),
+                             BTRFS_TREE_ROOT,
+                             btrfs_super_root(BTRFS_SUPER),
+                             btrfs_root_level_size(BTRFS_SUPER),
+                             btrfs_super_generation(BTRFS_SUPER),
                              FIRST_EXTERNAL_LOOKUP_POOL);
        if (ret)
                return 0;
        /* setup fs_root */
-       ret = find_setup_root(tree_root,
-                             btrfs_super_nodesize(sb),
-                             btrfs_super_leafsize(sb),
-                             btrfs_super_sectorsize(sb),
-                             btrfs_super_stripesize(sb),
+       ret = find_setup_root(BTRFS_TREE_ROOT,
+                             btrfs_super_nodesize(BTRFS_SUPER),
+                             btrfs_super_leafsize(BTRFS_SUPER),
+                             btrfs_super_sectorsize(BTRFS_SUPER),
+                             btrfs_super_stripesize(BTRFS_SUPER),
                              BTRFS_FS_TREE_OBJECTID,
-                             fs_root,
+                             BTRFS_FS_ROOT,
                              0,
                              0,
                              0,
                              FIRST_EXTERNAL_LOOKUP_POOL);
        return !ret;
+ error:
+       errnum = ERR_FSYS_MOUNT;
+       return 0;
 }
 
 /*
@@ -371,7 +710,7 @@ int check_read_chunk(struct btrfs_key *k
                            struct map_lookup *map,
                            u64 logical)
 {
-       int i;
+       int i, ret;
        u64 chunk_start;
        u64 chunk_size;
        int num_stripes;
@@ -397,20 +736,26 @@ int check_read_chunk(struct btrfs_key *k
        for (i = 0; i < num_stripes; i++) {
                map->stripes[i].physical =
                        btrfs_stripe_offset_nr(leaf, chunk, i);
+               map->stripes[i].dev.devid =
+                       btrfs_stripe_devid_nr(leaf, chunk, i);
+               ret = btrfs_find_device(&map->stripes[i].dev);
+               if (ret)
+                       return 0;
        }
        return 1;
 }
 
 static void init_extent_buffer(struct extent_buffer *eb,
+                              struct btrfs_device *dev,
                               u64 logical,
                               u32 blocksize,
                               u64 physical,
                               lookup_pool_id lpid)
 {
+       if (dev)
+               memcpy(&eb->dev, dev, sizeof(*dev));
        eb->start = logical;
        eb->len = blocksize;
-       eb->refs = 2;
-       eb->flags = 0;
        eb->dev_bytenr = physical;
        eb->data = grab_lookup_cache(lpid);
 }
@@ -516,8 +861,9 @@ static int chunk_tree_lookup(struct map_
  * Look for an appropriate map-extent and
  * perform a translation. Return 1 on errors.
  */
-int __btrfs_map_block(u64 logical, u64 *length, struct btrfs_multi_bio *multi,
-                     int mirror_num)
+static int btrfs_map_block(u64 logical, u64 *length,
+                          struct btrfs_multi_bio *multi,
+                          int mirror_num)
 {
        struct map_lookup map;
        u64 offset;
@@ -592,29 +938,57 @@ int __btrfs_map_block(u64 logical, u64 *
                multi->stripes[i].physical =
                        map.stripes[stripe_index].physical + stripe_offset +
                        stripe_nr * map.stripe_len;
+               memcpy(&multi->stripes[i].dev,
+                      &map.stripes[stripe_index].dev,
+                      sizeof(struct btrfs_device));
                stripe_index++;
        }
        return 0;
 }
 
-static u64 btrfs_map_block(u64 logical)
+static u64 read_data_extent(u64 logical_start, u64 to_read, char *pos)
 {
        int ret;
        u64 length;
        struct btrfs_multi_bio multi;
 
-       ret = __btrfs_map_block(logical, &length, &multi, 0);
-       if (ret) {
-               errnum = ERR_FSYS_CORRUPT;
-               return 0;
+       while (to_read) {
+               ret = btrfs_map_block(logical_start, &length, &multi, 0);
+               if (ret) {
+                       errnum = ERR_FSYS_CORRUPT;
+                       return ret;
+               }
+               if (length > to_read)
+                       length = to_read;
+               disk_read_func = disk_read_hook;
+               ret = btrfs_devread(multi.stripes[0].dev.drive,
+                                   multi.stripes[0].dev.part,
+                                   multi.stripes[0].dev.length,
+                                   multi.stripes[0].physical >> SECTOR_BITS,
+                                   logical_start & ((u64)SECTOR_SIZE - 1),
+                                   length,
+                                   pos);
+               disk_read_func = NULL;
+               if (!ret)
+                       return 1;
+               btrfs_msg("BTRFS data extent: read %llu bytes\n", length);
+               to_read -= length;
+               pos += length;
+               logical_start += length;
        }
-       return multi.stripes[0].physical;
+       return 0;
 }
 
 static int read_extent_from_disk(struct extent_buffer *eb)
 {
        WARN_ON(eb->dev_bytenr % SECTOR_BITS);
-       return devread(eb->dev_bytenr >> SECTOR_BITS, 0, eb->len, eb->data);
+       return btrfs_devread(eb->dev.drive,
+                            eb->dev.part,
+                            eb->dev.length,
+                            eb->dev_bytenr >> SECTOR_BITS,
+                            0,
+                            eb->len,
+                            eb->data);
 }
 
 static int verify_parent_transid(struct extent_buffer *eb, u64 parent_transid)
@@ -660,13 +1034,14 @@ int read_tree_block(struct btrfs_root *r
        dev_nr = 0;
        length = blocksize;
        while (1) {
-               ret = __btrfs_map_block(bytenr,
-                                       &length, &multi, mirror_num);
+               ret = btrfs_map_block(bytenr,
+                                     &length, &multi, mirror_num);
                if (ret) {
                        errnum = ERR_FSYS_CORRUPT;
                        return 0;
                }
                init_extent_buffer(eb,
+                                  &multi.stripes[0].dev,
                                   bytenr,
                                   blocksize,
                                   multi.stripes[0].physical,
@@ -814,6 +1189,7 @@ int aux_tree_lookup(struct btrfs_root *r
        int level;
        struct extent_buffer node;
        init_extent_buffer(&node,
+                          NULL,
                           0,
                           0,
                           0,
@@ -939,13 +1315,18 @@ static int btrfs_next_item(struct btrfs_
  * search for read operation
  */
 static int path_is_valid(struct btrfs_path *path,
-                        struct btrfs_key *key)
+                        struct btrfs_key *key, u64 offset)
 {
        btrfs_item_key_to_cpu(&path->nodes[0],
                              key,
                              path->slots[0]);
-       return (key->objectid == BTRFS_FILE_INFO->objectid) &&
-               (btrfs_key_type(key) == BTRFS_EXTENT_DATA_KEY);
+       if (BTRFS_FILE_INFO_KEY->objectid != key->objectid)
+               return 0;
+       if (btrfs_key_type(key) == BTRFS_INODE_ITEM_KEY)
+               return 1;
+       if (btrfs_key_type(key) != BTRFS_EXTENT_DATA_KEY)
+               return 0;
+       return BTRFS_FILE_INFO_KEY->offset <= offset;
 }
 
 /* ->read_func() */
@@ -954,31 +1335,35 @@ int btrfs_read(char *buf, int len)
        int ret;
        struct btrfs_root *fs_root;
        struct btrfs_path *path;
-       struct btrfs_key *info_key;
        struct btrfs_key  path_key;
-       u64 off;
+       u64 ioff;
        u64 bytes;
-       unsigned int to_read;
+       int to_read;
        char *pos = buf;
 
        fs_root = BTRFS_FS_ROOT;
-       info_key = BTRFS_FILE_INFO;
        path = btrfs_grab_path(FIRST_EXTERNAL_LOOKUP_POOL);
 
-       if (!path_is_valid(path, &path_key)) {
-               btrfs_set_key_type(info_key, BTRFS_EXTENT_DATA_KEY);
-               info_key->offset = filepos;
-               ret = aux_tree_lookup(fs_root, info_key, path);
+       if (!path_is_valid(path, &path_key, filepos)) {
+               ret = aux_tree_lookup(fs_root, BTRFS_FILE_INFO_KEY, path);
                if (ret < 0)
                        errnum = ERR_FSYS_CORRUPT;
        }
        while (!errnum) {
                struct btrfs_item *item;
                struct btrfs_file_extent_item *fi;
-               unsigned int from;
+               u64 from;
 
-               if (!path_is_valid(path, &path_key))
+               btrfs_item_key_to_cpu(&path->nodes[0],
+                                     &path_key,
+                                     path->slots[0]);
+               if (BTRFS_FILE_INFO_KEY->objectid != path_key.objectid)
                        break;
+               if (btrfs_key_type(&path_key) != BTRFS_EXTENT_DATA_KEY)
+                       goto next;
+               /*
+                * current position is extent item
+                */
                item = btrfs_item_nr(&path->nodes[0], path->slots[0]);
                fi = btrfs_item_ptr(&path->nodes[0],
                                    path->slots[0],
@@ -988,44 +1373,54 @@ int btrfs_read(char *buf, int len)
                       errnum = ERR_BAD_FILETYPE;
                       goto exit;
                }
-               off = filepos - path_key.offset;
+               ioff = filepos - path_key.offset;
 
                switch (btrfs_file_extent_type(&path->nodes[0], fi)) {
                case BTRFS_FILE_EXTENT_INLINE:
                        bytes = btrfs_file_extent_inline_item_len(&path->
                                                                  nodes[0],
                                                                  item);
-                       to_read = bytes - off;
+                       if (path_key.offset + bytes < filepos)
+                               goto next;
+                       to_read = bytes - ioff;
                        if (to_read > len)
                                to_read = len;
-                       from = off + btrfs_file_extent_inline_start(fi);
+                       from = ioff + btrfs_file_extent_inline_start(fi);
                        if (disk_read_hook != NULL) {
                                disk_read_func = disk_read_hook;
-                               ret = devread(path->nodes[0].dev_bytenr >>
-                                             SECTOR_BITS, from, to_read, pos);
+                               ret = btrfs_devread(path->nodes[0].dev.drive,
+                                                   path->nodes[0].dev.part,
+                                                   path->nodes[0].dev.length,
+                                                   path->nodes[0].dev_bytenr >>
+                                                   SECTOR_BITS,
+                                                   from,
+                                                   to_read,
+                                                   pos);
                                disk_read_func = NULL;
-                       } else {
+                               if (ret)
+                                       goto exit;
+                       } else
                                memcpy(pos,
                                       path->nodes[0].data + from,
                                       to_read);
-                       }
+                       btrfs_msg("BTRFS inline extent: read %d bytes pos %d\n",
+                                 to_read, filepos);
                        break;
                case BTRFS_FILE_EXTENT_REG:
                        bytes = btrfs_file_extent_num_bytes(&path->nodes[0],
                                                            fi);
-                       to_read = bytes - off;
+                       if (path_key.offset + bytes < filepos)
+                               goto next;
+                       to_read = bytes - ioff;
                        if (to_read > len)
                                to_read = len;
-                       from = off +
+                       from = ioff +
                                btrfs_file_extent_disk_bytenr(&path->nodes[0],
-                                                             fi);
-                       disk_read_func = disk_read_hook;
-                       ret = devread(btrfs_map_block(from) >> SECTOR_BITS,
-                                     from & ((u64)SECTOR_SIZE - 1),
-                                     to_read,
-                                     pos);
-                       disk_read_func = NULL;
-                       if (!ret)
+                                                             fi) +
+                               btrfs_file_extent_offset(&path->nodes[0],
+                                                        fi);
+                       ret = read_data_extent(from, to_read, pos);
+                       if (ret)
                                goto exit;
                        break;
                case BTRFS_FILE_EXTENT_PREALLOC:
@@ -1042,12 +1437,14 @@ int btrfs_read(char *buf, int len)
                if (len == 0)
                        break;
                /* not everything was read */
+       next:
                ret = btrfs_next_item(fs_root, path);
-               if (ret) {
-                       /* something should be found */
+               if (ret < 0) {
                        errnum = ERR_FSYS_CORRUPT;
                        break;
                }
+               btrfs_update_file_info(path);
+               continue;
        }
  exit:
        return errnum ? 0 : pos - buf;
@@ -1082,14 +1479,14 @@ static int btrfs_follow_link(struct btrf
        filepos = 0;
        /* extract symlink content */
        while (1) {
-               u64 oid = BTRFS_FILE_INFO->objectid;
+               u64 oid = BTRFS_FILE_INFO_KEY->objectid;
                ret = btrfs_next_item(root, path);
                if (ret)
                        break;
                btrfs_update_file_info(path);
-               if (oid != BTRFS_FILE_INFO->objectid)
+               if (oid != BTRFS_FILE_INFO_KEY->objectid)
                        break;
-               if (btrfs_key_type(BTRFS_FILE_INFO) ==
+               if (btrfs_key_type(BTRFS_FILE_INFO_KEY) ==
                    BTRFS_EXTENT_DATA_KEY)
                        goto found;
        }
@@ -1114,7 +1511,7 @@ static int update_fs_root(struct btrfs_r
 
        if (location->offset != (u64)-1)
                return 0;
-       tree_root = &fs_root->fs_info->tree_root;
+       tree_root = &BTRFS_FS_INFO->tree_root;
        ret = find_setup_root(tree_root,
                              tree_root->nodesize,
                              tree_root->leafsize,
@@ -1388,6 +1785,22 @@ int btrfs_dir(char *dirname)
        }
 }
 
+int btrfs_embed(int *start_sector, int needed_sectors)
+{
+       int ret;
+       init_btrfs_info();
+       init_btrfs_volatile_dev_cache();
+
+       ret = btrfs_find_super(BTRFS_VOLATILE_DEV_CACHE, NULL, NULL);
+       if (ret)
+               return 0;
+       ret = btrfs_uptodate_super_copy(BTRFS_FS_INFO);
+       if (ret)
+               return 0;
+       *start_sector = 1; /* reserve first sector for stage1 */
+       return needed_sectors <=
+               ((BTRFS_SUPER_INFO_OFFSET >> SECTOR_BITS) - 1);
+}
 #endif /* FSYS_BTRFS */
 
 /*
--- grub-0.97.orig/stage2/filesys.h
+++ grub-0.97/stage2/filesys.h
@@ -137,8 +137,8 @@ int iso9660_dir (char *dirname);
 #ifndef NUM_FSYS
 #define NUM_FSYS       \
   (FSYS_FFS_NUM + FSYS_FAT_NUM + FSYS_EXT2FS_NUM + FSYS_MINIX_NUM      \
-   + FSYS_REISERFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM + FSYS_XFS_NUM \
-   + FSYS_TFTP_NUM + FSYS_ISO9660_NUM + FSYS_UFS2_NUM)
+   + FSYS_REISERFS_NUM + FSYS_BTRFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM \
+   + FSYS_XFS_NUM + FSYS_TFTP_NUM + FSYS_ISO9660_NUM + FSYS_UFS2_NUM)
 #endif
 
 /* defines for the block filesystem info area */
--- grub-0.97.orig/grub/Makefile.am
+++ grub-0.97/grub/Makefile.am
@@ -8,7 +8,7 @@ endif
 
 AM_CPPFLAGS = -DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \
        -DFSYS_ISO9660=1 -DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 \
-       -DFSYS_UFS2=1 -DFSYS_VSTAFS=1 -DFSYS_XFS=1 \
+       -DFSYS_BTRFS=1 -DFSYS_UFS2=1 -DFSYS_VSTAFS=1 -DFSYS_XFS=1 \
        -DUSE_MD5_PASSWORDS=1 -DSUPPORT_HERCULES=1 \
        $(SERIAL_FLAGS) -I$(top_srcdir)/stage2 \
        -I$(top_srcdir)/stage1 -I$(top_srcdir)/lib
--- grub-0.97.orig/stage2/disk_io.c
+++ grub-0.97/stage2/disk_io.c
@@ -65,7 +65,7 @@ struct fsys_entry fsys_table[NUM_FSYS + 
   {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, reiserfs_embed},
 # endif
 # ifdef FSYS_BTRFS
-  {"btrfs", btrfs_mount, btrfs_read, btrfs_dir, 0, 0},
+  {"btrfs", btrfs_mount, btrfs_read, btrfs_dir, 0, btrfs_embed},
 # endif
 # ifdef FSYS_VSTAFS
   {"vstafs", vstafs_mount, vstafs_read, vstafs_dir, 0, 0},
--- grub-0.97.orig/stage2/builtins.c
+++ grub-0.97/stage2/builtins.c
@@ -2423,6 +2423,16 @@ install_func (char *arg, int flags)
          else
 #endif /* GRUB_UTIL */
            {
+             /*
+              * FIXME: Ugly hack.
+              * Do not write to btrfs partition
+              * without a help of the file system!
+              */
+             if (!strcmp(fsys_table[fsys_type].name, "btrfs"))
+               {
+                 errnum = ERR_BAD_ARGUMENT;
+                 goto fail;
+               }
              if (! devwrite (*saved_sector - part_start, 1, stage2_buffer))
                goto fail;
            }

Reply via email to